開発解説

ローカルLLM APIのストリーミングを扱う方法|途中・完了・失敗を分ける

ローカルLLM APIのストリーミングを扱う方法 — 途中・完了・失敗を分ける
目次

ストリーミングを使うと、モデルが回答を最後まで生成する前から画面へ文字を表示できる。

ただし実装では、「小さなJSONが順番に返ってくる」とだけ覚えると、途中で切れた回答を完成扱いしてしまうことがある。

重要なのは、受信途中のデータと、正常に確定した最終回答を分けることだ。

LM StudioのOpenAI互換Responses APIでは、ストリーミング時に作成、テキストの差分、完了などを示すイベントが順番に返る仕組みが案内されている。実際のイベントにはcreated、テキスト差分を示すイベント、completedなどがある。

一方、Ollamaなど別のAPIでは形式が同じとは限らない。使用しているAPIの公式資料を基準にする。

受信中は表示用バッファーに蓄積する。正常完了を確認して確定保存し、失敗や中止を完成扱いしない。
途中の表示と回答の確定を分ける。図を拡大して見る

クライアント内部で3つの状態を分ける

最低限、次を別に持つ。

1. 受信した生のイベント・データ
2. 画面表示へ使う途中の文字列バッファー
3. 正常終了後に確定したメッセージ

画面表示用のバッファーは、受信するたびに更新してよい。

ただし、まだ生成途中の内容を、そのまま保存済みの会話履歴や次のリクエストへ渡す確定メッセージとして扱うとは限らない。

1回届いたデータだけで文章の完成を判断しない

テキストの差分は文章の途中で届く。文字列やツール呼び出しなど、複数回の受信を集めて初めて意味が完成するデータもある。

受信単位ごとに「これは完成した1回答」と判定せず、イベントの種類とAPI仕様に従って蓄積する。

UTF-8の文字列処理も、利用するHTTPライブラリやSSE処理の仕組みに合わせて行い、バイト列の途中を誤って文字列化しない。

正常終了・エラー・切断・キャンセルを分ける

「データが来なくなった」だけでは、何が起きたのか分からない。

少なくとも次を分ける。

  • APIが正常終了を通知した
  • サーバーがエラーを返した
  • ネットワーク接続が途中で切れた
  • 利用者が生成をキャンセルした

これらをすべて同じ「完了」として扱うと、途中までの文章を成功した回答として保存してしまう。

JavaScriptやTypeScriptでAbortControllerなどを使って利用者側から停止する場合も、正常終了とは別の状態を持つ。

保存する前に完了状態を確認する

画面には生成途中の文章を表示していても、会話履歴へ確定保存する処理は終了状態を確認してから行う。

たとえば内部状態を次のように分けられる。

receiving   受信中
completed   正常終了
failed      エラー・通信切断
cancelled   利用者が中止

状態名は自由だが、「停止した」という1状態だけにまとめない。

最初に正常終了と意図的なキャンセルを1回ずつ試す

まず短い入力で、最後まで正常に生成できるケースを1回保存する。

次に、少し長い生成を始めて途中で利用者側からキャンセルする。

その2回について、

画面表示:
途中バッファー:
保存された会話:
終了状態:
サーバーログ:

を比べる。

正常終了と途中キャンセルを区別できれば、その後に再接続、ツール呼び出しのストリーミング、より複雑なUIを追加しやすい。

最初の成功条件は文字が滑らかに表示されることではなく、途中・成功・失敗・キャンセルを正しく区別して保存できることだ。

まずcurlで通常のAPIリクエストを確認したい場合は、ローカルLLM APIへcurlで最初のリクエストを送る方法へ戻る。TypeScriptでクライアントを作る場合は、TypeScriptからローカルLLM APIを呼ぶ方法も確認できる。

まとめ

  • ストリーミングで届く途中のデータを、そのまま最終回答として保存しない
  • 受信するイベントやデータ形式はAPIごとに確認し、提供元が違っても同じ形式だと決めつけない
  • 正常終了、サーバー側のエラー、通信切断、利用者によるキャンセルを別の状態として扱う
  • 画面へ表示する途中の文字列と、会話履歴へ保存する確定済みメッセージを分ける

公式サイトで次に進む

この記事で明示的に扱うソフトだけを表示します。配布・手順は各公式ページで確認してください。

local-gen.jpがファイルを配布するものではなく、互換性を保証するものでもありません。

同じテーマから

この記事で扱ったデータ

モデル・GPU・実行ソフトの確認済みデータを、記事とは別に確かめられます。

サイト内検索