数据流
本文档描述 Elftia 中数据在各层之间的流转方式,包括消息发送、工具调用、状态管理和缓存策略。
消息发送全流程
从用户在输入框输入到 AI 回复渲染完成的完整数据流:
sequenceDiagram
participant U as 用户输入
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: 键入消息, 按 Enter
UI->>UCC: sendMessage(content, attachments)
UCC->>UCC: 创建 user message, 更新本地状态
UCC->>CBC: 通过 IPC 发送消息
CBC->>IPC: window.api.completion.chatInSession(params)
IPC->>CR: secureHandle 验证 token
CR->>CS: completion.chatInSession(sessionId, messages, config)
CS->>ED: engineDispatcher.chat(session)
ED->>Engine: 路由到对应引擎
Engine->>LLM: HTTP 请求 (SSE stream)
loop SSE 流式响应
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: 重渲染 StreamingMessage
end
Engine-->>CR: stream 完成
CR->>DB: 持久化 assistant message
CR-->>IPC: stream:end event
IPC-->>CBC: onStreamEnd callback
CBC-->>UCC: 更新消息列表
UCC-->>Store: clearStreamingState, addMessage
Store-->>Render: 渲染最终消息
关键步骤说明
- 用户输入 —
UnifiedInput组件捕获输入、附件、模型选择 - UnifiedChatContext — 创建
user消息对象,乐观更新本地状态 - IPC 调用 — 通过 Preload 暴露的
window.api.completion.chatInSession()发送到主进程 - CompletionRouter — 验证 token,解析参数,调用
CompletionService - EngineDispatcher — 根据 Agent 的
engineType路由到对应引擎 - LLM API — 引擎发起 HTTP 请求,接收 SSE 流式响应
- 流式推送 — 每个 delta 通过 IPC 事件推送到前端,Zustand store 实时更新
- 持久化 — 完成后后端将 assistant 消息写入数据库
- 状态同步 — 前端清除流式状态,显示最终消息
Agent 工具调用流
当 LLM 返回工具调用(tool_use)时的处理流程:
flowchart TB
LLM[LLM 返回 tool_use] --> Parse[解析 tool_call]
Parse --> FW{ExecutionFirewall<br/>路径检查}
FW -->|拒绝| Block[返回 denied 结果给 LLM]
FW -->|通过| Guardian{GuardianAgent<br/>AI 安全审查}
Guardian -->|risk: high/critical| PermGate{ChannelPermissionGate<br/>人工确认}
Guardian -->|risk: low/none| Execute[执行工具]
Guardian -->|monitor 模式| LogOnly[记录日志后执行]
PermGate -->|用户拒绝| Block
PermGate -->|用户批准| Execute
Execute --> Result[工具执行结果]
Result --> Audit[AuditLogger 记录]
Result --> BackToLLM[结果返回给 LLM]
BackToLLM --> LLM
LogOnly --> Execute
{/* TinyElf Agent Loop 中的工具调用管线 */}
interface ToolCallPipeline {
firewall: ExecutionFirewall; // 1. 确定性检查 (零 LLM 开销)
guardian: GuardianAgent; // 2. AI 审查 (按模式决定)
permissionGate: ChannelPermissionGate; // 3. 人工确认 (仅 Channel 来源)
executor: ToolExecutor; // 4. 执行
auditLogger: AuditLogger; // 5. 审计
}
状态管理层次
前端状态管理分为三层,各层有明确的职责边界:
graph TB
subgraph "Layer 1: Zustand Store (核心状态)"
CS[chatStore — 会话/消息/流式/分支]
SS[settingsStore — 设置状态]
end
subgraph "Layer 2: React Context (领域状态)"
CDC[ChatDataContext — 数据缓存 wrapper]
UCC2[UnifiedChatContext — 统一聊天 API]
TC[ThemeContext — 主题]
AC[AuthContext — 认证]
EC[ElfiContext — Elfi 助手]
end
subgraph "Layer 3: Feature Context (功能状态)"
CTC[ChatTabsContext — 多标签]
WIC[WorldInfoHighlightContext — WI 高亮]
MSC[MessageSelectionContext — 消息多选]
end
CS --> CDC
CDC --> UCC2
UCC2 --> CTC
各层职责
| 层级 | 技术 | 特征 | 适用场景 |
|---|---|---|---|
| Layer 1 | Zustand | 高频更新、精确订阅、无 Provider 嵌套 | 流式消息、分支切换、会话列表 |
| Layer 2 | React Context | 中频更新、提供 API 方法、依赖注入 | 聊天操作(发送/重新生成)、认证、主题 |
| Layer 3 | React Context | 低频更新、功能隔离 | 多标签、关键词高亮、消息选择 |
状态迁移方向
ChatDataContext (deprecated) ──迁移──> chatStore (Zustand)
│
v
UnifiedChatContext (统一 API 层)
ChatDataContext 是历史遗留的 Context wrapper,正在逐步迁移到 Zustand store。新代码应直接使用 chatStore 或 UnifiedChatContext。
消息分支机制
聊天消息使用树结构支持分支(regenerate/edit 创建新分支):
graph TB
M1[User: 你好] --> M2a[Assistant: 你好! v1]
M1 --> M2b[Assistant: 嗨! v2]
M2a --> M3[User: 帮我写代码]
M3 --> M4a[Assistant: 好的 v1]
M3 --> M4b[Assistant: 当然 v2]
{/* 分支数据结构 */}
interface BranchInfo {
id: string;
parentMessageId: string;
children: string[];
currentIndex: number;
}
{/* 分支操作 */}
interface BranchOperations {
switchBranch(messageId: string, index: number): void;
getActivePath(rootId: string): Message[];
regenerate(messageId: string): void;
editMessage(messageId: string, newContent: string): void;
}
分支切换逻辑
- 用户点击分支导航箭头
switchBranch(parentMessageId, newIndex)更新BranchInfo.currentIndexgetActivePath()重新计算从根到叶的活动消息路径- 消息列表重新渲染
缓存策略
前端缓存
| 缓存 | 技术 | TTL | 用途 |
|---|---|---|---|
| 消息缓存 | Zustand messageCache | 会话生命周期 | 避免重复加载消息 |
| UI 状态 | IndexedDB (frontendCache) | 持久 | 草稿、折叠状态、滚动位置 |
| 会话列表 | Zustand sessions | 刷新时更新 | 侧边栏会话列表 |
| 提供商列表 | Zustand providers | 刷新时更新 | 模型选择器 |
后端缓存
| 缓存 | 位置 | TTL | 用途 |
|---|---|---|---|
| MCP 工具列表 | CacheService | 5 分钟 | 避免重复列举 MCP 工具 |
| Transformer 链 | TransformerService | 10 分钟 | 编译后的转换链 |
| 提供商索引 | LLMConfigService | O(1) Map | 按 ID 快速查找提供商 |
| API Key 冷却 | ApiKeyPoolService | 60s-15min 指数退避 | 429/529 错误后冷却 |
| PromptGuardian 结果 | PromptGuardian | SHA-256 key | 已审查提示的缓存结果 |
| GuardianAgent 结果 | GuardianAgent | SHA-256 key | 已审查工具调用的缓存结果 |
会话保护机制
防止 WebSocket 项目更新在活动对话期间刷新侧边栏并清除聊天消息:
{/* 会话保护流程 */}
interface SessionProtection {
activeSessions: Set<string>;
processingSessions: Set<string>;
markActive(sessionId: string): void; // 用户发消息时标记
shouldSkipRefresh(): boolean; // activeSessions.size > 0
markInactive(sessionId: string): void; // 对话完成后移除
}
多密钥轮询 (ApiKeyPoolService)
后端支持每个 LLM 提供商配置多个 API Key,使用加权轮询 + 会话亲和策略:
flowchart LR
Req[请求] --> Check{会话已绑定?}
Check -->|是| BoundKey[使用绑定的 Key]
Check -->|否| RR[加权轮询选择 Key]
RR --> Bind[绑定到会话]
Bind --> Call[API 调用]
BoundKey --> Call
Call --> OK{成功?}
OK -->|429/529| Cool[冷却该 Key]
Cool --> Retry[切换到下一个 Key 重试]
OK -->|成功| Done[返回结果]
{/* 简化的 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.ts | Zustand 聊天状态 Store + 统一聊天 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.ts | LLM 补全服务 |
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.ts | AI 工具审查 |
packages/desktop/app/main/services/infra/cache/CacheService.ts | 后端缓存服务 |