この記事の音声は準備中です。
音声の一覧・再生リストへストリーミングと Agent の UX 実装パターン
この記事の目的
「Agent は本質的に遅い」という前提に立ち、体感品質を決める 4 つの実装パターン — トークンストリーミング・進捗の可視化・中断と軌道修正・非同期化 — を使い分けられるようになります。
対象読者
- ユーザーに直接応答する Agent(チャット・アシスタント)のフロントエンド/バックエンドを実装するエンジニア
- 「Agent が黙り込む」「止められない」という UX 課題に取り組むチーム
前提知識
- Agent ループ — 応答までに複数の LLM 呼び出しとツール実行が挟まる構造
- Human-in-the-Loop 設計 — 承認・介入の設計(本記事はその実装面)
本文
概要: レイテンシは消せない。不安だけ消せる
Agent の応答時間は、単発の LLM 呼び出しと桁が違います(数十秒〜数分)。この遅さの多く — 複数回の推論、ツールの実行、外部 API の待ち — は本質的で、最適化しても消えません。UX 実装の目標は、待ち時間を消すことではなく、「動いているか分からない不安」を消すことです。無応答の 30 秒は、ユーザーにとって故障と区別がつきません。
詳細: パターン 1 — トークンストリーミング
最終応答のテキストを、生成と同時に逐次表示します。主要な LLM API はサーバー送信イベント(SSE: Server-Sent Events)形式のストリーミングをサポートしており、最初のトークンが出るまでの時間(TTFT最初のトークンが出るまでの時間。ストリーミング表示の体感と全体レイテンシを左右する主要指標の一つ。)が体感を支配します。
- 長い応答では必ずストリーミングにします。全文完成を待つ実装は、体感を数倍悪化させるうえ、HTTP タイムアウトの原因にもなります
- ストリーミング中の部分テキストは未確定として扱います(途中でツール呼び出しに切り替わる・エラーで中断されることがあります)
詳細: パターン 2 — 進捗の可視化
ループの中間ステップ(いま何をしているか)をユーザーの語彙で表示します。
- やること: ステップの要約表示(「経費データを検索しています…」「3 件見つかりました。規定と照合しています…」)
- やらないこと: 生のツール入出力のダンプ。情報過多で読めないだけでなく、内部 API のレスポンスに含まれる秘密情報・個人情報を画面に流出させる事故につながります
- 実装上は、ツール呼び出しイベントを「ユーザー向けの一文」に変換する層を 1 枚挟みます(ツール名からの固定文言でも、モデルに生成させる方式でも可)
詳細: パターン 3 — 中断と軌道修正
長く走る Agent には、ユーザーがいつでも止められる・向きを変えられる手段が必要です。
- キャンセル: 停止ボタンは必須です。UI 上で止まって見えるだけで裏のループが走り続ける実装は、コストと副作用の両面で危険です。キャンセルをループの停止条件(Agent ループ)まで貫通させます
- 軌道修正: 実行中の追加指示(「その方針でなく〜で」)を次のイテレーションの入力に混ぜ込めると、キャンセル → 再指示のやり直しコストが消えます
- 中断時の状態保全: 中断はエラーではありません。途中経過を保存し(メモリと状態管理 のチェックポイント長時間タスクの「ここまで完了した」を再開可能な形で永続化したもの。ツール実行の完了やフェーズ境界などの意味のある単位で切る。信頼性とトークンコスト制御の両方の仕組み。)、再開・引き継ぎができる形で止めます
詳細: パターン 4 — 同期チャット型か、非同期ジョブ型か
| 形態 | 向くタスク | 実装の要点 |
|---|---|---|
| 同期チャット型 | 数秒〜1 分程度。対話的に方向修正したいタスク | ストリーミング + 進捗表示 + キャンセル |
| 非同期ジョブ型 | 数分〜時間単位。調査・一括処理・長い生成 | 依頼を受け付けて即応答 → バックグラウンド実行 → 完了通知。進捗の照会画面 |
数分を超えるタスクを同期チャットで待たせるのは設計ミスです。「受け付けました。終わったら通知します」に切り替え、承認待ち(Human-in-the-Loop 設計)も通知で運びます。
実装例: 非同期ツールと実行中の軌道修正
2026-09-10 確認の OpenAI Responses API では、Astra の function / custom tool に async: true を付けると、アプリがツールを実行している間もモデルが独立した作業を続けられます。結果は元の call_id と結び付けます。WebSocket の実行中の指示変更(mid-turn steering)では、完了済みの作業を引き継ぐ継続処理へ追加指示を渡せます。
画面の「停止」「条件変更」は次の状態管理につなぎます。
| 起きたこと | アプリ側の処理 | 表示すること |
|---|---|---|
| ツールが実行中 | 呼出し ID と実行状態を保存し、同じ操作を重複起動しない | 確認中の対象と進捗 |
| 条件が変わった | 対象・引数・承認の有効性を再評価する | 変更を受け付けた条件と残る作業 |
| 停止後に結果が届いた | 元の実行に記録し、新しい実行への採用可否を判定する | 停止済みでも完了した外部操作があればその事実 |
推論の停止は、送信済みメールや外部 API の更新を巻き戻しません。キャンセル不能な操作は状態を照会し、必要なら補償処理へ進みます。非同期ツール API の採用だけでジョブの永続化・再起動後の復旧が得られるわけではありません。
実務での注意点
アンチパターン
- スピナーだけで数十秒待たせる → ユーザーは故障と判断して離脱・再送信し、再送信が二重実行を生む → 進捗の可視化(パターン 2)と、処理中の再送信ガードを入れる
- 生のツールログをそのまま流す → 読めない上に、内部データ・秘密情報の画面流出になる → ユーザー語彙への変換層を挟み、表示内容を出力としてレビュー対象にする
- キャンセルが効かない(見た目だけ止まる) → 裏でループが走り続け、コストと副作用が発生し続ける → キャンセルをループ停止条件・実行中ツールの中断まで貫通させ、テストで確認する
チェックリスト
- 最終応答がストリーミング表示される
- 実行中に「いま何をしているか」がユーザーの語彙で見える
- 進捗表示に秘密情報・内部データが混入しない設計になっている
- 停止ボタンがあり、裏のループまで実際に止まることをテストした
- 数分を超えるタスクが非同期ジョブ型(通知ベース)になっている
- 中断時に途中経過が保全され、再開または報告ができる
関連トピック
- Agent ループ — キャンセルを貫通させる停止条件
- Human-in-the-Loop 設計 — 承認 UI と通知の設計
- メモリと状態管理 — 中断時の状態保全
- レイテンシ最適化 — 裏側の実レイテンシを下げる技術
- AI プロダクトの UX パターン — 本記事の実装の上位にある体験設計のパターン言語
- チャットを超える UI — テキストボックス以外の入出力形態(バックグラウンド実行ほか)
参考資料
-
Async tool calling — 本文の仕様例(アクセス日: 2026-09-10)
-
Mid-turn steering — 本文の仕様例(アクセス日: 2026-09-10)
-
Streaming Messages(Anthropic docs) — SSE イベントの仕様と実装例(アクセス日: 2026-07-05)