はじめに
ChatGPTは回答が一気に表示されるのではなく、少しずつ表示されます。これができたらユーザーは待機時間が短く感じらますよね。
今回はChatGPTのAPIを利用してどのように実装するのかを調査してみました。
環境
.NET 6 コンソールアプリ
OpenAIのモデル:gpt-3.5-turbo
ポイント
結果を順次受け取るようにする際のポイントをまとめました。
streamパラメータの設定
以下記事の通り、APIにリクエストを送る際のパラメータでstreamをtrueにする必要があります。
https://platform.openai.com/docs/api-reference/chat/create#chat/create-stream
結果の受け取り
上記のようにstreamを有効にした場合、結果は以下のようなJSONになります。
data: { "id": "...", "object": "...", "created": ..., "model": "gpt-3.5-turbo-0301", "choices": [ { "delta": { "content": "..." }, "index": 0, "finish_reason": null } ] } data: { "id": "...", "object": "...", "created": ..., "model": "gpt-3.5-turbo-0301", "choices": [ { "delta": { "content": "..." }, "index": 0, "finish_reason": null } ] } (省略) data: [DONE ]
上記はすべての結果をまとめて表示していますが、コードを実行した際にはdata:[JSON形式のデータ]という結果を繰り返し取得できます。このままではJSONに変換しにくいので「data:」の部分は削除すると扱いやすいです。
また、回答はcontent
の中にあるのでJSONの解析でこの値を取得するといいでしょう。
コード
では実際にコードを見てみましょう。C#のコンソールアプリでの例です。
using System; using System.Collections.Generic; using System.IO; using System.Net.Http; using System.Net.Http.Json; using System.Text.Json.Nodes; using System.Threading.Tasks; class Program { static async Task Main(string[] args) { string key = "[OpenAIのAPIキー]"; Console.WriteLine("質問を入力してください。"); string question = Console.ReadLine(); Console.WriteLine("--------------------------------------"); var client = new HttpClient(); var request = new HttpRequestMessage(HttpMethod.Post, "https://api.openai.com/v1/chat/completions"); request.Headers.Add("Authorization", $"Bearer {key}"); var content = JsonContent.Create(new { model = "gpt-3.5-turbo", messages= new List<message> { new message("user", question) }, stream = true }); request.Content = content; var response = await client.SendAsync(request, HttpCompletionOption.ResponseHeadersRead); using var streamReader = new StreamReader(await response.Content.ReadAsStreamAsync()); while (!streamReader.EndOfStream) { var line = await streamReader.ReadLineAsync(); if (string.IsNullOrEmpty(line)) continue; //冒頭の[data]を削除 line = line.Remove(0, 6); //[DONE]の場合は終了 if (line == "[DONE]") continue; var resultContent = JsonNode.Parse(line)?["choices"]?[0]?["delta"]?["content"]?.ToString(); if(resultContent != null) Console.Write(resultContent); //結果をコンソールに表示 } } } class message { public message(string _role, string _content) { role = _role; content = _content; } public string role { get; set; } public string content { get; set; } }
実行結果
以下のように結果が順次表示されました。