OpenAI に毎回返事をしてほしくないという要望を実現してみよう

2023/11/01に公開

今日、Kazuyuki Sakemi さんと話していて、OpenAI とチャットをしていて、毎回返事をしてほしくないという話題が出てきました。

例えば普段人と LINE とかをするときは結構細切れに複数回チャットメッセージを送って、それを受けて相手が返事をするというケースがよくあると思います。以下のような感じですね。

Aさん: 今日は暑いね
Aさん: 今日行く予定だった、公園だと日差し強すぎて無理そうだけどどうする?
Bさん: そうだね。今日は暑いから、公園はやめようかな
Bさん: じゃあ、ショッピングモールとかはどう?
Aさん: いいね。そうしようか。

この時、AさんとBさんは 2 回ずつメッセージを送ってやり取りをしていますが OpenAI とチャットをするような素直な実装だと A さんが 1 回チャットを送った時点で返事を返してくれます。ちゃんと作ってあるアプリだと、返信を途中でキャンセルする機能があったりしますが、キャンセルしないと次のチャットメッセージを遅れないケースがほとんどです。
わざわざキャンセルしたり、返事がすべて終わるまで次のメッセージを打てないのはめんどうだという話でした。

確かに。ということで、そういう要望に答えてみましょう。

方針

Function calling で相手から明確に返事を求められていない時に呼び出すような関数を定義して、返事が不要なときは、それを返してもらうようにシステムプロンプトで指示をするという形で実現してみたいと思います。

やってみましょう。

実装

コンソールアプリのプロジェクトを作って以下の 2 つのパッケージを追加します。

  • Azure.Identity
  • Azure.AI.OpenAI

個人的には Function calling のコードを素の SDK で書くのはめんどいので Semantic Kernel あたりを使いたいのですが、残念ながら Semantic Kernel は現時点で Function calling に完全には対応していないので、素の SDK を使います。

using Azure.AI.OpenAI;
using Azure.Identity;

// エンドポイントとモデルのデプロイ名
const string Endpoint = "https://<リソース名>.openai.azure.com/";
const string ModelDeployName = "<モデルのデプロイ名>";

// Azure CLI の資格情報を使って AOAI を呼び出すようにする
// Azure CLI でログインしているユーザーに対して Azure OpenAI のリソースに対して
// Cognitive Service OpenAI User のロールを割り当てる必要があります。
var client = new OpenAIClient(new Uri(Endpoint), new AzureCliCredential());

var options = new ChatCompletionsOptions
{
    // ユーザーには無駄な返信をしないように指示するシステムプロンプト
    Messages =
    {
        new(ChatRole.System, """
            ユーザーとの雑談相手として振舞ってください。使用する言語は日本語を使ってください。
            ただし、ユーザーは無駄なおしゃべりを望んでいません。
            挨拶などに対しては反応をしないで requestMoreInput を呼び出してください。
            文が不完全で、ユーザーがまだ続きのメッセージの入力をしようとしている場合は、返信を返さずに requestMoreInput を呼び出してください。
            完全な文章の場合でも、ユーザーが明示的にあなたに問いかけていない場合は、返信を返さず提供された関数の一覧から requestMoreInput を呼び出してください。
            """),
    },
    // ユーザーに返信する内容がない時に呼び出す関数を定義
    Functions = new FunctionDefinition[]
    {
        new FunctionDefinition("requestMoreInput")
        {
            Description = "Please call this function when the flow of conversation does not explicitly require a reply from the user.",
            Parameters = BinaryData.FromObjectAsJson(new
            {
                type = "object",
                properties = new
                {
                }
            }),
        },
    },
    FunctionCall = FunctionDefinition.Auto,
};

while (true)
{
    var userInput = Console.ReadLine();

    options.Messages.Add(new(ChatRole.User, userInput));
    var result = await client.GetChatCompletionsAsync(ModelDeployName, options);

    var response = result.Value.Choices[0];
    if (response.FinishReason == CompletionsFinishReason.FunctionCall)
    {
        // 返事をする内容がないケース
        Console.WriteLine("system > Function calling");
    }
    else
    {
        // 返事をする内容がある場合にはメッセージを表示する
        Console.WriteLine($"{response.Message.Role} > {response.Message.Content}");
        options.Messages.Add(response.Message);
    }
}

とりあえずシステムプロンプトで強めに指示をしてみました。実行してみましょう。

こんにちは
system > Function calling
えっと
system > Function calling
今日はお客様と
system > Function calling
打合せをしました。
assistant > それはどうでしたか? 良い結果が得られましたか?
まぁまぁ
system > Function calling
とりあえず、ダメだしをされたんですが
system > Function calling
私はどうしたらよかったですか?アドバイスを頂けないでしょうか。
assistant > どのようなフィードバックを受けたのか詳しく教えていただけますか?その情報に基づいて、できる限り具体的なアドバイスを提供します。
なんか
system > Function calling
具体例が欲しいって言われました。
assistant > それなら、提案や主張について具体的な例を示すことをおすすめします。それにより相手はあなたのアイディアを理解しやすくなり、またそのアイディアが実際の状況にどのように適用できるかも明確になります。例を挙げる際には、可能なら実際に起こった事例を用いるとより説得力が増します。

system > Function calling と表示されている行は AI が返事をする必要がないと判断した部分になります。結構いい感じのタイミングで返事をしてくれるようになってますね。

まとめ

Function calling を使うと、こういうユーザーに対してどういう反応を行うべきかの判断とかも AI に任せることができるので、結構便利ですね。

ということで小ネタでした。

Microsoft (有志)

Discussion