メインコンテンツまでスキップ

データフロー

このドキュメントでは、メッセージ送信、ツール呼び出し、状態管理、キャッシュ戦略など、Elftia 内のレイヤー間でデータがどのように流れるかを説明します。

メッセージ送信の完全フロー

ユーザーが入力欄にテキストを入力してから AI の応答が完全にレンダリングされるまでの、完全なデータフローです。

sequenceDiagram
participant U as User Input
participant UI as UnifiedInput
participant UCC as UnifiedChatContext
participant CBC as ChatBackendContext
participant IPC as IPC Layer
participant CR as CompletionRouter
participant CS as CompletionService
participant ED as EngineDispatcher
participant Engine as Engine (Chat/SDK/TinyElf/CLI)
participant LLM as LLM API
participant DB as DbClient (Worker)
participant Store as chatStore (Zustand)
participant Render as MessageRenderer

U->>UI: Type message, press Enter
UI->>UCC: sendMessage(content, attachments)
UCC->>UCC: Create user message, update local state
UCC->>CBC: Send message via IPC
CBC->>IPC: window.api.completion.chatInSession(params)
IPC->>CR: secureHandle validates token
CR->>CS: completion.chatInSession(sessionId, messages, config)
CS->>ED: engineDispatcher.chat(session)
ED->>Engine: Route to appropriate engine
Engine->>LLM: HTTP request (SSE stream)

loop SSE streaming response
LLM-->>Engine: data chunk
Engine-->>CR: IPC event (stream:delta)
CR-->>IPC: mainWindow.send('stream:delta', chunk)
IPC-->>CBC: onStreamDelta callback
CBC-->>Store: setStreamingState({content})
Store-->>Render: Re-render StreamingMessage
end

Engine-->>CR: stream complete
CR->>DB: Persist assistant message
CR-->>IPC: stream:end event
IPC-->>CBC: onStreamEnd callback
CBC-->>UCC: Update message list
UCC-->>Store: clearStreamingState, addMessage
Store-->>Render: Render final message

主要ステップの説明

  1. ユーザー入力UnifiedInput コンポーネントが入力内容、添付ファイル、モデル選択を受け取る
  2. UnifiedChatContextuser メッセージオブジェクトを作成し、ローカル状態を楽観的に更新する
  3. IPC 呼び出し — Preload 経由で公開された window.api.completion.chatInSession() でメインプロセスに送信される
  4. CompletionRouter — トークンを検証し、パラメータを解析して CompletionService を呼び出す
  5. EngineDispatcher — Agent の engineType に基づいて適切なエンジンにルーティングする
  6. LLM API — エンジンが HTTP リクエストを行い、SSE ストリーミングレスポンスを受信する
  7. ストリーミングプッシュ — 各デルタが IPC イベント経由でフロントエンドに送信され、Zustand ストアがリアルタイムで更新される
  8. 永続化 — 完了後、バックエンドがアシスタントメッセージをデータベースに書き込む
  9. 状態同期 — フロントエンドがストリーミング状態をクリアし、最終メッセージを表示する

Agent ツール呼び出しフロー

LLM がツール呼び出し(tool_use)を返した場合の処理フローです。

flowchart TB
LLM[LLM returns tool_use] --> Parse[Parse tool_call]
Parse --> FW{ExecutionFirewall<br/>path check}

FW -->|deny| Block[Return denied result to LLM]
FW -->|pass| Guardian{GuardianAgent<br/>AI safety review}

Guardian -->|risk: high/critical| PermGate{ChannelPermissionGate<br/>human confirmation}
Guardian -->|risk: low/none| Execute[Execute tool]
Guardian -->|monitor mode| LogOnly[Log and execute]

PermGate -->|user denies| Block
PermGate -->|user approves| Execute

Execute --> Result[Tool execution result]
Result --> Audit[AuditLogger records]
Result --> BackToLLM[Result returned to LLM]
BackToLLM --> LLM

LogOnly --> Execute
{/* Tool call pipeline in TinyElf Agent Loop */}
interface ToolCallPipeline {
firewall: ExecutionFirewall; // 1. Deterministic check (zero LLM overhead)
guardian: GuardianAgent; // 2. AI review (mode-dependent)
permissionGate: ChannelPermissionGate; // 3. Human confirmation (Channel sources only)
executor: ToolExecutor; // 4. Execute
auditLogger: AuditLogger; // 5. Audit
}

状態管理の階層

フロントエンドの状態管理は、それぞれ明確な責務を持つ 3 つのレイヤーに分かれています。

graph TB
subgraph "Layer 1: Zustand Store (core state)"
CS[chatStore — sessions/messages/streaming/branches]
SS[settingsStore — settings state]
end

subgraph "Layer 2: React Context (domain state)"
CDC[ChatDataContext — data cache wrapper]
UCC2[UnifiedChatContext — unified chat API]
TC[ThemeContext — theme]
AC[AuthContext — authentication]
EC[ElfiContext — Elfi assistant]
end

subgraph "Layer 3: Feature Context (feature state)"
CTC[ChatTabsContext — multi-tab]
WIC[WorldInfoHighlightContext — WI highlights]
MSC[MessageSelectionContext — multi-select]
end

CS --> CDC
CDC --> UCC2
UCC2 --> CTC

レイヤーの責務

レイヤー技術特性ユースケース
Layer 1Zustand高頻度更新、精密なサブスクリプション、Provider ネスト不要ストリーミングメッセージ、ブランチ切り替え、セッション一覧
Layer 2React Context中頻度更新、API メソッドの提供、依存性注入チャット操作(送信/再生成)、認証、テーマ
Layer 3React Context低頻度更新、機能の分離マルチタブ、キーワードハイライト、メッセージ選択

状態移行の方向

ChatDataContext (deprecated) ──migrating──> chatStore (Zustand)

v
UnifiedChatContext (unified API layer)

ChatDataContext は Zustand ストアへの移行途中にある旧来の Context ラッパーです。新規コードは chatStore または UnifiedChatContext を直接使用してください。

メッセージのブランチ

チャットメッセージはツリー構造を使用し、ブランチ(再生成/編集で新規ブランチを作成)をサポートします。

graph TB
M1[User: Hello] --> M2a[Assistant: Hello! v1]
M1 --> M2b[Assistant: Hi! v2]
M2a --> M3[User: Help me write some code]
M3 --> M4a[Assistant: Sure v1]
M3 --> M4b[Assistant: Of course v2]
{/* Branch data structure */}
interface BranchInfo {
id: string;
parentMessageId: string;
children: string[];
currentIndex: number;
}

{/* Branch operations */}
interface BranchOperations {
switchBranch(messageId: string, index: number): void;
getActivePath(rootId: string): Message[];
regenerate(messageId: string): void;
editMessage(messageId: string, newContent: string): void;
}

ブランチ切り替えのロジック

  1. ユーザーがブランチナビゲーション矢印をクリックする
  2. switchBranch(parentMessageId, newIndex)BranchInfo.currentIndex を更新する
  3. getActivePath() がルートから末端までのアクティブなメッセージパスを再計算する
  4. メッセージ一覧が再レンダリングされる

キャッシュ戦略

フロントエンドキャッシュ

キャッシュ技術TTL目的
メッセージキャッシュZustand messageCacheセッション存続期間メッセージの再読み込みを回避
UI 状態IndexedDB (frontendCache)永続下書き、折りたたみ状態、スクロール位置
セッション一覧Zustand sessions更新時に再取得サイドバーのセッション一覧
プロバイダー一覧Zustand providers更新時に再取得モデルセレクター

バックエンドキャッシュ

キャッシュ場所TTL目的
MCP ツール一覧CacheService5 分MCP ツールの繰り返し列挙を回避
Transformer チェーンTransformerService10 分コンパイル済み変換チェーン
プロバイダーインデックスLLMConfigServiceO(1) MapID によるプロバイダーの高速検索
API キークールダウンApiKeyPoolService60 秒〜15 分(指数バックオフ)429/529 エラー後のクールダウン
PromptGuardian 結果PromptGuardianSHA-256 キーレビュー済みプロンプトのキャッシュ結果
GuardianAgent 結果GuardianAgentSHA-256 キーレビュー済みツール呼び出しのキャッシュ結果

セッション保護

アクティブな会話中に WebSocket のプロジェクト更新がサイドバーを更新してチャットメッセージをクリアしないよう防ぎます。

{/* Session protection flow */}
interface SessionProtection {
activeSessions: Set<string>;
processingSessions: Set<string>;

markActive(sessionId: string): void; // Mark when user sends a message
shouldSkipRefresh(): boolean; // activeSessions.size > 0
markInactive(sessionId: string): void; // Remove after conversation completes
}

マルチキーラウンドロビン(ApiKeyPoolService)

バックエンドは LLM プロバイダーごとに複数の API キーの設定をサポートし、セッションアフィニティ付きの加重ラウンドロビンを使用します。

flowchart LR
Req[Request] --> Check{Session bound?}
Check -->|yes| BoundKey[Use bound key]
Check -->|no| RR[Weighted round-robin selects key]
RR --> Bind[Bind to session]
Bind --> Call[API call]
BoundKey --> Call
Call --> OK{Success?}
OK -->|429/529| Cool[Cool down this key]
Cool --> Retry[Switch to next key and retry]
OK -->|success| Done[Return result]
{/* Simplified ApiKeyPoolService */}
class ApiKeyPoolService {
private sessionBindings: Map<string, string>;
private cooldowns: Map<string, { until: number; backoff: number }>;

resolveApiKeyForRequest(
providerId: string,
sessionId?: string,
): Promise<{ keyId: string; apiKey: string }>;

markKeyError(keyId: string, statusCode: number): void;
}

関連ファイル

ファイル説明
packages/renderer/src/shared/state/chatStore.tsZustand チャット状態ストア + 統合チャット API(送信/再生成/編集、データキャッシュ。旧 UnifiedChatContext / ChatDataContext はここに統合済み)
packages/renderer/src/features/chat/hooks/useSessionProtection.tsセッション保護
packages/renderer/src/shared/utils/frontendCache.tsフロントエンド IndexedDB キャッシュ
packages/desktop/app/main/services/capabilities/llm/completion/CompletionService.tsLLM 補完サービス
packages/desktop/app/main/services/capabilities/llm/completion/ApiKeyPoolService.tsマルチキーラウンドロビン
packages/desktop/app/main/services/agent-core/engine/EngineDispatcher.tsエンジンディスパッチ
packages/desktop/app/main/services/routers/CompletionRouter.ts補完 IPC ルーター
packages/desktop/app/main/services/platform/security/ExecutionFirewall.tsパスファイアウォール
packages/desktop/app/main/services/platform/security/GuardianAgent.tsAI ツールレビュー
packages/desktop/app/main/services/infra/cache/CacheService.tsバックエンドキャッシュサービス