Azure OpenAI Client Library で応答をストリームで受信する

2024/03/05
★★

応答をストリームで受信

Azure OpenAI client library for .NET では OpenAIClient クラスの GetChatCompletionsStreaming メソッドで、生成された回答をストリームで取得できます。ストリーム取得することで、生成された回答の文字を順次取得することができます。

Azure OpenAI client library for .NET では、以下のようなコードで、ストリームで応答を受信できます。

OpenAIClient openAIClient = new(new Uri(aoaiEndpoint), new AzureKeyCredential(aoaiApiKey));

ChatCompletionsOptions chatCompletionsOptions = new()
{
    DeploymentName = aoaiModelName,
    Messages =
    {
        new ChatRequestSystemMessage(systemMessage),
        new ChatRequestUserMessage(userMessage),
    }
};

int outputMillisecondsDelay = 33;

// 非同期 foreach で展開
await foreach (var chatUpdate in openAIClient.GetChatCompletionsStreaming(chatCompletionsOptions, cancellationToken))
{
    if (chatUpdate.Role.HasValue)
    {
        // ロールの書き出し
        Console.WriteLine($"{chatUpdate.Role.Value.ToString()}:");
    }
    if (!string.IsNullOrEmpty(chatUpdate.ContentUpdate))
    {
		// メッセージ断片の取得と書き出し
        Console.Write(chatUpdate.ContentUpdate);
    }

    await Task.Delay(outputMillisecondsDelay);
}

Server-Sent Events による実装

コード実行時の HTTP 応答ヘッダーを見ると、以下が含まれていることが分かります。

  • Cache-Control:no-cache, must-revalidate
  • Transfer-Encoding:chunked
  • Content-Type:text/event-stream

HTTP ヘッダーの確認方法は以下に。

Content-Type:text/event-stream から分かるように、サーバーからの応答には、Server-Sent Events が使用されています。 Server-Sent Events は、クライアントとのコネクションを維持し、クライアントからのあらたな要求なしに、サーバーからプッシュでデータを送信できます。Azure OpenAI Service のストリームでは、Server-Sent Events を使用し、断片的な回答生成の都度、クライアントに回答を送信しています。このため、クライアントは、サーバー側ですべての回答が生成されるのを待つことなく、順次、回答を断片で受信し、ユーザーへ表示することで、体感的な待ち時間を減らすことができます。

以下は、コード実行時の Body の抜粋です。このような応答がサーバーから、順次送信されてきます。
(*) 改行コードを分かりやすくするために、<改行> と表現しています。

 data: {"choices":[],"created":0,"id":"","model":"","object":"","prompt_filter_results":[{"prompt_index":0,"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}}}]}<改行>
 <改行>
{"choices":[{"content_filter_results":{},"delta":{"role":"assistant"},"finish_reason":null,"index":0}],"created":1709644040,"id":"chatcmpl-xxxxxxxxxxxxx","model":"gpt-35-turbo","object":"chat.completion.chunk","system_fingerprint":null}<改行>
<改行>
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"望"},"finish_reason":null,"index":0}],"created":1709644040,"id":"chatcmpl-xxxxxxxxxxxxx","model":"gpt-35-turbo","object":"chat.completion.chunk","system_fingerprint":null}<改行>
<改行>
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"月"},"finish_reason":null,"index":0}],"created":1709644040,"id":"chatcmpl-xxxxxxxxxxxxx","model":"gpt-35-turbo","object":"chat.completion.chunk","system_fingerprint":null}<改行>
<改行>
data: {"choices":[{"content_filter_results":{"hate":{"filtered":false,"severity":"safe"},"self_harm":{"filtered":false,"severity":"safe"},"sexual":{"filtered":false,"severity":"safe"},"violence":{"filtered":false,"severity":"safe"}},"delta":{"content":"の"},"finish_reason":null,"index":0}],"created":1709644040,"id":"chatcmpl-xxxxxxxxxxxxx","model":"gpt-35-turbo","object":"chat.completion.chunk","system_fingerprint":null}<改行>
<改行>
data: {"choices":[{"content_filter_results":{},"delta":{},"finish_reason":"stop","index":0}],"created":1709644040,"id":"chatcmpl-xxxxxxxxxxxxx","model":"gpt-35-turbo","object":"chat.completion.chunk","system_fingerprint":null}<改行>
<改行>
data: [DONE]<改行>
<改行>

data:<スペース> に続き、JSON フォーマットで、応答が返ってきています。最初に、choices[0].delta.role で、ロールが送信され、以降で、choices[0].delta.content で回答の断片が送信されます。最後に、[DONE] で終了となります。

Server-Sent Events の詳細な仕様は、以下にあります。

Azure OpenAI client library を使うことで、これらのイベントを解析することなく、GetChatCompletionsStreaming の戻り値の StreamingResponse<StreamingChatCompletionsUpdate> を await foreach で展開し、StreamingChatCompletionsUpdate オブジェクトで取得できます。

サンプル コードは、以下に掲載しています。

以上、参考までに。

コメント (0)

コメントの投稿