# 機能設計書 23-ストリーミング応答

## 概要

本ドキュメントは、Liam DBシステムにおけるストリーミング応答機能の設計を記述するものである。本機能は、AIエージェントからの応答をリアルタイムにユーザーに配信し、対話的なユーザー体験を実現する。

### 本機能の処理概要

ストリーミング応答機能は、LLM（Large Language Model）からの出力をチャンク単位でクライアントに配信し、ユーザーが応答の生成過程をリアルタイムで確認できるようにする。Server-Sent Events（SSE）プロトコルを使用し、サーバーからクライアントへの一方向ストリーミングを実現する。

**業務上の目的・背景**：LLMの応答生成には時間がかかるため、従来のリクエスト/レスポンス方式では長時間待機が発生し、ユーザー体験が悪化する。ストリーミング応答により、生成途中の内容をリアルタイムで表示することで、待ち時間の体感を短縮し、対話的なAIアシスタント体験を提供する。

**機能の利用シーン**：デザインセッション詳細画面でユーザーがメッセージを送信した後、AIエージェント（Lead Agent、PM Agent、DB Agent、QA Agent）からの応答がリアルタイムで画面に表示される。思考過程（Reasoning）やツール呼び出しの状況も逐次表示される。

**主要な処理内容**：
1. LLM応答のチャンク単位でのストリーミング送信
2. SSE形式でのイベント配信（messages, end, error, analyzed_requirements）
3. クライアント側でのチャンクの蓄積とメッセージ再構築
4. Reasoning（思考過程）の抽出と表示
5. ツール呼び出し情報の抽出と表示
6. 接続断時のリトライと再接続処理

**関連システム・外部連携**：LangGraphのカスタムイベント機能（dispatchCustomEvent）を使用してLLMストリームを配信する。Next.js APIルートでSSEエンドポイントを提供する。

**権限による制御**：ストリーミングエンドポイントへのアクセスには認証済みユーザーであることが必要。

## 関連画面

| 画面No | 画面名 | 関連種別 | 関連する操作・処理 |
|--------|--------|----------|------------------|
| 14 | デザインセッション詳細画面 | 主画面 | AIエージェントのリアルタイムストリーミング応答表示 |

## 機能種別

データ連携 / リアルタイム通信

## 入力仕様

### 入力パラメータ

| パラメータ名 | 型 | 必須 | 説明 | バリデーション |
|-------------|-----|-----|------|---------------|
| userInput | string | Yes（startの場合） | ユーザーの入力メッセージ | - |
| designSessionId | string | Yes | デザインセッションの識別子 | UUID形式 |

### 入力データソース

- クライアントからのPOSTリクエスト（/api/chat/stream, /api/chat/replay）
- LangGraphワークフローからのカスタムイベント

## 出力仕様

### 出力データ

| 項目名 | 型 | 説明 |
|--------|-----|------|
| event | string | SSEイベント種別（messages, end, error, analyzed_requirements） |
| data | string | JSON形式のペイロード（メッセージチャンク等） |

### 出力先

- SSE（Server-Sent Events）ストリーム
- クライアント側のReact状態（messages, analyzedRequirements）

## 処理フロー

### 処理シーケンス

```
1. クライアント: fetch()でストリームエンドポイントに接続
   └─ /api/chat/stream または /api/chat/replay
2. サーバー: LangGraphワークフローを開始
   └─ streamEvents()でイベントをイテレート
3. サーバー: dispatchCustomEventでLLMチャンクを配信
   └─ SSE形式でクライアントに送信
4. クライアント: parseSse()でSSEをパース
   └─ handleStreamEvent()でイベント種別に応じた処理
5. クライアント: MessageTupleManagerでチャンクを蓄積
   └─ coerceMessageLikeToMessage()でメッセージに変換
6. クライアント: React状態を更新してUIに反映
7. サーバー: ワークフロー完了時にENDイベントを送信
8. クライアント: 接続をクローズし完了処理
```

### フローチャート

```mermaid
flowchart TD
    A[ユーザー入力] --> B[fetch /api/chat/stream]
    B --> C{接続成功?}
    C -->|Yes| D[SSEストリーム開始]
    C -->|No| E[エラー処理]
    D --> F[イベント受信ループ]
    F --> G{イベント種別}
    G -->|messages| H[メッセージ処理]
    G -->|analyzed_requirements| I[要件更新]
    G -->|error| J[エラー表示]
    G -->|end| K[完了処理]
    H --> F
    I --> F
    J --> L[再接続判定]
    K --> M[ストリーム終了]
    L --> N{リトライ可能?}
    N -->|Yes| O[replay呼び出し]
    N -->|No| P[エラー終了]
    O --> B
```

## ビジネスルール

### 業務ルール

| ルールNo | ルール名 | 内容 | 適用条件 |
|---------|---------|------|---------|
| BR-23-01 | 最大リトライ回数 | 接続断時は最大2回までリトライ | END イベントを受信せずに接続が切断された場合 |
| BR-23-02 | ワークフロー進行状態 | ストリーム開始時にworkflowInProgressフラグを設定 | start関数呼び出し時 |
| BR-23-03 | アボート処理 | ナビゲーション時に進行中のストリームをアボート | useNavigationGuard |

### 計算ロジック

- チャンクの蓄積: MessageTupleManagerがチャンクをIDベースで管理し、同一メッセージのチャンクを結合
- リトライカウント: retryCountRef.currentで管理、MAX_RETRIES（2）まで

## データベース操作仕様

### 操作別データベース影響一覧

| 操作 | 対象テーブル | 操作種別 | 概要 |
|-----|-------------|---------|------|
| - | - | - | ストリーミング機能自体はDB操作なし |

### テーブル別操作詳細

本機能はリアルタイム配信のみを行い、直接的なデータベース操作は行わない。

## エラー処理

### エラーケース一覧

| エラーコード | エラー種別 | 発生条件 | 対処方法 |
|------------|----------|---------|---------|
| network | ネットワークエラー | fetch失敗 | エラー表示、リトライ可能 |
| abort | アボート | ユーザー操作による中断 | ワークフロー停止、フラグ維持 |
| timeout | タイムアウト | リトライ上限到達 | CONNECTION_TIMEOUTエラー表示 |
| unknown | 不明なエラー | 予期しない例外 | GENERALエラー表示 |

### リトライ仕様

- MAX_RETRIES = 2
- ENDイベントを受信せずに接続が切断された場合、replay エンドポイントでリトライ
- アボート時はリトライせず、ワークフロー進行フラグは維持（再接続可能に）

## トランザクション仕様

ストリーミング機能はステートレスな通信処理であり、トランザクション制御は行わない。ワークフローの状態管理はLangGraphのチェックポイント機能が担当する。

## パフォーマンス要件

- チャンク配信遅延: 50ms以下を目標
- 接続タイムアウト: ブラウザ標準のfetchタイムアウトに依存
- メモリ使用: MessageTupleManagerでチャンクを効率的に管理

## セキュリティ考慮事項

- SSEエンドポイントは認証済みユーザーのみアクセス可能
- AbortControllerによる適切な接続管理
- エラーメッセージは機密情報を含まない形式

## 備考

ストリーミング応答機能は@langchain/langgraph-sdkのuseStreamを参考に実装されている。カスタムイベントにより、メッセージ以外の情報（analyzedRequirements等）も配信可能。

---

## コードリーディングガイド

本機能を理解するために参照すべきファイルと、推奨する読み解き順序を以下に示す。

### 推奨読解順序

#### Step 1: データ構造を理解する

SSEイベントの種別と、メッセージチャンクの管理方法を理解する。

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 1-1 | constants.ts | `frontend/internal-packages/agent/src/streaming/constants.ts` | SSE_EVENTSの定義（messages, end, error, analyzed_requirements） |
| 1-2 | MessageTupleManager.ts | `frontend/internal-packages/agent/src/streaming/core/MessageTupleManager.ts` | チャンクの蓄積とID管理 |

**読解のコツ**: SSE_EVENTSはサーバーとクライアントで共通利用される定数。

#### Step 2: サーバーサイドのストリーミング処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 2-1 | streamingLlmUtils.ts | `frontend/internal-packages/agent/src/utils/streamingLlmUtils.ts` | LLMストリームの処理とdispatchCustomEvent |
| 2-2 | handlers.ts | `frontend/internal-packages/agent/src/streaming/server/handlers.ts` | Reasoning、通常メッセージ、ToolCallの処理ハンドラ |

**主要処理フロー**:
- **21-46行目（streamingLlmUtils.ts）**: チャンクの蓄積とカスタムイベントディスパッチ
- **43行目**: dispatchCustomEvent(eventType, chunk)でクライアントに送信

#### Step 3: クライアントサイドのストリーム処理を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 3-1 | useStream.ts | `frontend/apps/app/components/SessionDetailPage/hooks/useStream/useStream.ts` | カスタムフック全体の構造 |
| 3-2 | parseSse.ts | `frontend/apps/app/components/SessionDetailPage/hooks/useStream/parseSse.ts` | SSEストリームのパース処理 |

**主要処理フロー**:
- **68-334行目（useStream.ts）**: useStreamフック全体
- **119-139行目**: handleMessageEventでチャンクをReact状態に反映
- **158-181行目**: handleStreamEventでイベント種別に応じた分岐
- **204-271行目**: runStreamAttemptでfetchとリトライロジック
- **300-323行目**: start関数でワークフロー開始

#### Step 4: UI表示との連携を理解する

| 順序 | ファイル | パス | 読解ポイント |
|-----|---------|------|-------------|
| 4-1 | SessionDetailPageClient.tsx | `frontend/apps/app/components/SessionDetailPage/SessionDetailPageClient.tsx` | useStreamの利用とUI更新 |

**主要処理フロー**:
- **95-100行目**: useStreamからmessages, isStreaming等を取得
- **177-182行目**: Chatコンポーネントへのメッセージ渡し

### プログラム呼び出し階層図

```
SessionDetailPageClient
    │
    └─ useStream (カスタムフック)
           │
           ├─ start
           │      └─ runStreamAttempt → fetch('/api/chat/stream')
           │
           ├─ replay
           │      └─ runStreamAttempt → fetch('/api/chat/replay')
           │
           ├─ processStreamEvents
           │      └─ parseSse → handleStreamEvent
           │                        ├─ handleMessageEvent
           │                        │      └─ MessageTupleManager.add
           │                        ├─ handleAnalyzedRequirementsEvent
           │                        └─ handleErrorEvent
           │
           └─ state管理
                  ├─ messages
                  ├─ analyzedRequirements
                  ├─ isStreaming
                  └─ error

[サーバーサイド]
API Route (/api/chat/stream)
    │
    └─ deepModeling (ワークフロー実行)
           │
           └─ streamEvents → dispatchCustomEvent
                  │
                  └─ streamLLMResponse
                         └─ チャンク送信
```

### データフロー図

```
[サーバー]                     [ネットワーク]              [クライアント]

LLM応答チャンク ──▶ dispatchCustomEvent ──▶ SSE ──▶ parseSse
       │                                              │
       │                                              ▼
       │                                      handleStreamEvent
       │                                              │
       │                                              ▼
       │                                      MessageTupleManager
       │                                              │
       │                                              ▼
       │                                      setMessages
       │                                              │
       └─────────────────────────────────────────────▶ UI更新
```

### 関連ファイル一覧

| ファイル | パス | 種別 | 役割 |
|---------|------|------|------|
| useStream.ts | `frontend/apps/app/components/SessionDetailPage/hooks/useStream/useStream.ts` | ソース | クライアント側ストリーム処理フック |
| parseSse.ts | `frontend/apps/app/components/SessionDetailPage/hooks/useStream/parseSse.ts` | ソース | SSEパーサー |
| constants.ts | `frontend/internal-packages/agent/src/streaming/constants.ts` | ソース | SSEイベント定数 |
| streamingLlmUtils.ts | `frontend/internal-packages/agent/src/utils/streamingLlmUtils.ts` | ソース | サーバー側ストリーム処理 |
| MessageTupleManager.ts | `frontend/internal-packages/agent/src/streaming/core/MessageTupleManager.ts` | ソース | チャンク管理 |
| handlers.ts | `frontend/internal-packages/agent/src/streaming/server/handlers.ts` | ソース | サーバー側イベントハンドラ |
| reasoningExtractor.ts | `frontend/internal-packages/agent/src/streaming/core/reasoningExtractor.ts` | ソース | Reasoning抽出 |
| extractToolCallsFromMessage.ts | `frontend/internal-packages/agent/src/streaming/core/extractToolCallsFromMessage.ts` | ソース | ツール呼び出し抽出 |
| SessionDetailPageClient.tsx | `frontend/apps/app/components/SessionDetailPage/SessionDetailPageClient.tsx` | ソース | ストリーム利用画面 |
| workflowStorage.ts | `frontend/apps/app/components/SessionDetailPage/utils/workflowStorage.ts` | ソース | ワークフロー進行状態管理 |
