🛠️
Microsoft Teams のカスタム ボットで OpenAI を使ってコマンドに応答できるようにする
はじめに
Microsoft Teams のカスタム ボットでは、チャットで会話できる機能の他に、コマンドを指定して任意の操作をさせる機能があります。
例えば、予約ボットを作成した場合、ユーザーは開発者によって定義された予約に関するコマンドを呼び出すことができます。開発者はボット アプリでコマンドを受け取り、指定されたコマンドに対して操作します。
実装例は以下のようになります。コマンドはチャットの文字列として渡されます。
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var command = turnContext.Activity.RemoveRecipientMention();
var message = command switch
{
"予約作成" => "予約を作成しました。",
"予約変更" => "予約を変更しました。",
"予約削除" => "予約を削除しました。",
_ => "認識できないコマンドです。"
};
await turnContext.SendActivityAsync(MessageFactory.Text(message, message), cancellationToken);
}
ただし、この方法では応答が単調になってしまいます。そこで、Azure OpenAI Service を使って、よりスマートにコマンドへ応答できるようにします。
サンプル コード
コマンドの解析には OpenAI の Function Calling を使用します。Function Calling は JSON スキーマを指定することで関数呼び出し用の JSON データを生成する機能です。1 回の OpenAI 呼び出しに対して複数の Function を指定できるため、今回はコマンドごとに Function を定義します。
public async Task<ReservationParameter?> GetReservationParameter(string text, CancellationToken cancellationToken = default)
{
var options = new ChatCompletionsOptions(
this.deploymentName,
[
new ChatRequestUserMessage(text)
])
{
FunctionCall = FunctionDefinition.Auto
};
options.Functions.Add(new FunctionDefinition()
{
Name = "CreateReservation",
Description = "予約を作成します。",
Parameters = BinaryData.FromString(CreateReservationPatameters)
});
options.Functions.Add(new FunctionDefinition()
{
Name = "UpdateReservation",
Description = "予約を変更します。",
Parameters = BinaryData.FromString(UpdateReservationPatameters)
});
options.Functions.Add(new FunctionDefinition()
{
Name = "RemoveReservation",
Description = "予約を削除します。",
Parameters = BinaryData.FromString(RemoveReservationPatameters)
});
var response = await this.client.GetChatCompletionsAsync(options, cancellationToken);
var arguments = response.Value.Choices[0]?.Message?.FunctionCall?.Arguments;
return arguments is null ? null : JsonSerializer.Deserialize<ReservationParameter>(arguments);
}
コマンド応答の部分を、上記のメソッドを呼び出すように修正します。
protected override async Task OnMessageActivityAsync(ITurnContext<IMessageActivity> turnContext, CancellationToken cancellationToken)
{
var command = turnContext.Activity.RemoveRecipientMention();
- var message = command switch
- {
- "予約作成" => "予約を作成しました。",
- "予約変更" => "予約を変更しました。",
- "予約削除" => "予約を削除しました。",
- _ => "認識できないコマンドです。"
- };
+ var parameter = await this.openAIService.GetReservationParameter(command, cancellationToken: cancellationToken);
+ var message = parameter?.Command switch
+ {
+ "予約作成" => $"{parameter?.Location ?? "<不明な場所>"}の予約を作成しました。",
+ "予約変更" => $"{parameter?.Location ?? "<不明な場所>"}の予約を変更しました。",
+ "予約削除" => $"{parameter?.Location ?? "<不明な場所>"}の予約を削除しました。",
+ _ => "認識できないコマンドです。"
+ };
await turnContext.SendActivityAsync(MessageFactory.Text(message, message), cancellationToken);
}
このように実装することで、通常のコマンド呼び出しだけでなく、文章に対しても応答できるようになります。また、パラメーター(今回の場合は場所)を文章から抽出してコマンドを実行できます。
なお、OpenAI の応答は必ずしも完璧でないことがあります。OpenAI は入力補助であり、必要に応じてダイアログ (Task モジュール) やアダプティブ カードも活用することが重要です。
おわりに
今回の方法を使用すると、Copilot プラグインと同じことを簡単に実現できます。ぜひお試しください。
Discussion