3

Microsoft の Luis + bot フレームワークをいじってみると、「これは良い型プロバイダーになるだろう」という感覚がうずき始めました。残念ながら、型プロバイダーは判別共用体を出力できません。私は次のようなことをしたいと思っていましたが、それは不可能です:

type Luis = LuisProvider<@"LuisId",@"LuisPasskey">
let IntentMatcher Intent =
    match intent with
    | Luis.Intents.Greeting -> GreetingHandler()
    | Luis.Intents.SetAlarm title startDate startTime -> AlarmHandler title startDate startTime
    | _ -> CouldNotUnderstand()

Luis のインテントとそのパラメーターはすべて Api を介して利用できるため、typeProviderization の優れた候補となります

参考までに、サンプルの C# ボットのハンドラーを次に示します (F# ではよりクリーンで、よりタイプ セーフになると思います)。

public const string Entity_Alarm_Title = "builtin.alarm.title";
public const string Entity_Alarm_Start_Time = "builtin.alarm.start_time";
public const string Entity_Alarm_Start_Date = "builtin.alarm.start_date";
public const string DefaultAlarmWhat = "default";

[LuisIntent("builtin.intent.alarm.set_alarm")]
public async Task SetAlarm(IDialogContext context, LuisResult result)
{
        EntityRecommendation title;
        if (!result.TryFindEntity(Entity_Alarm_Title, out title))
        {
            title = new EntityRecommendation(type: Entity_Alarm_Title) { Entity = DefaultAlarmWhat };
        }
        EntityRecommendation date;
        if (!result.TryFindEntity(Entity_Alarm_Start_Date, out date))
        {
            date = new EntityRecommendation(type: Entity_Alarm_Start_Date) { Entity = string.Empty };
        }
        EntityRecommendation time;
        if (!result.TryFindEntity(Entity_Alarm_Start_Time, out time))
        {
            time = new EntityRecommendation(type: Entity_Alarm_Start_Time) { Entity = string.Empty };
        }
        var parser = new Chronic.Parser();
        var span = parser.Parse(date.Entity + " " + time.Entity);
        if (span != null)
        {
            var when = span.Start ?? span.End;
            var alarm = new Alarm() { What = title.Entity, When = when.Value };
            this.alarmByWhat[alarm.What] = alarm;
            string reply = $"alarm {alarm} created";
            await context.PostAsync(reply);
        }
        else
        {
            await context.PostAsync("could not find time for alarm");
        }
        context.Wait(MessageReceived);
}

とにかく質問は: 型プロバイダーの構築経験が豊富な人は、実際に構築可能な読み取り可能な DSL を構築する方法について何か良いアイデアを持っていますか?

4

1 に答える 1

7

私はボット フレームワークに特に精通しているわけではありませんが、判別共用体についてはコメントできます。F# データでも同様の問題に直面しています。

をお持ちの場合は、ケースと の<One name="string" /><Two id="42" />識別結合を提供するとよいでしょう。代わりに、型を提供します。One of stringTwo of int

type OneOrTwo =
  member One : option<string>
  member Two : option<int>

同じパターンに従って、次のような API を公開できます。

type Luis = LuisProvider<"LuisId", "LuisPasskey">

let intentMatcher (intent:Luis.Intents) =
  match intent.Greetings, intent.SetAlarm with
  | Some(), _ -> greetingHandler()
  | _, Some(title, startDate, startTime) -> alarmHandler title startDate startTime
  | _ -> couldNotUnderstand()

Luis.Connect().OnIntent
|> Observable.subscribe intentMatcher

これは、差別された共用体ほどエレガントではありませんが、技術的には実行可能であるはずです。

別の方法として、個々のアクションのハンドラーを個別のイベントとして公開し、次のように書くことができると思います。

type Luis = LuisProvider<"LuisId", "LuisPasskey">

let luis = Luis.Connect()

luis.BuiltIn.Greetings 
|> Observable.add greetingHandler

luis.BuiltIn.SetAlarm 
|> Observable.add (fun (title, startDate, startTime) -> 
     alarmHandler title startDate startTime)

考えてみれば、このほうがいいのかもしれませんが、ボット フレームワークの典型的な用途によって異なります。

于 2016-08-19T10:47:19.983 に答える