# 客户端封装器

<cite>
**本文档引用的文件**
- [src/lib/sdk/client.ts](file://src/lib/sdk/client.ts)
- [src/app/api/chat/route.ts](file://src/app/api/chat/route.ts)
- [src/lib/im/conversation-engine.ts](file://src/lib/im/conversation-engine.ts)
- [src/lib/sdk/system-prompt.ts](file://src/lib/sdk/system-prompt.ts)
- [src/lib/workspace-fs.ts](file://src/lib/workspace-fs.ts)
- [src/hooks/use-chat.ts](file://src/hooks/use-chat.ts)
- [src/app/api/upload/route.ts](file://src/app/api/upload/route.ts)
- [src/lib/db.ts](file://src/lib/db.ts)
- [src/lib/types.ts](file://src/lib/types.ts)
</cite>

## 目录
1. [简介](#简介)
2. [项目结构](#项目结构)
3. [核心组件](#核心组件)
4. [架构总览](#架构总览)
5. [详细组件分析](#详细组件分析)
6. [依赖关系分析](#依赖关系分析)
7. [性能考量](#性能考量)
8. [故障排查指南](#故障排查指南)
9. [结论](#结论)
10. [附录](#附录)

## 简介
本文件面向“客户端封装器”的设计与实现，重点围绕 createForgeQuery 函数展开，系统性阐述其查询选项构建、环境变量配置与 SDK 实例创建过程；详解 ForgeQueryOptions 接口的关键参数（如 sessionId、model、workspaceId、abortController 等）；说明附件处理机制（ForgeAttachment 类型与多模态内容构建流程）；并给出会话管理策略（首次消息与后续消息的差异处理）。最后提供完整的 API 使用示例与最佳实践建议。

## 项目结构
客户端封装器位于后端 SDK 层，负责将前端请求转换为可执行的 SDK 查询，并通过流式事件回传给前端。整体涉及以下模块：
- SDK 客户端封装：构建查询选项、系统提示词、MCP 服务器、权限桥接与环境变量
- 聊天 API：接收请求、保存消息、调用 SDK、恢复失败重试、生成 SSE 流
- 即时消息（IM）引擎：轻量化会话、权限处理、流式消费与附件检测
- 工作空间与提示词：加载 .claude/ 配置、内存与规则上下文
- 前端 Hook：流式渲染、权限决策、消息持久化

```mermaid
graph TB
subgraph "前端"
FE_UI["聊天界面<br/>use-chat.ts"]
end
subgraph "后端 API"
API_CHAT["/api/chat<br/>chat/route.ts"]
API_UPLOAD["/api/upload<br/>upload/route.ts"]
end
subgraph "SDK 封装层"
SDK_CLIENT["createForgeQuery()<br/>client.ts"]
SYS_PROMPT["系统提示词构建<br/>system-prompt.ts"]
WS_FS["工作空间文件系统<br/>workspace-fs.ts"]
end
subgraph "IM 引擎"
IM_ENGINE["ConversationEngine<br/>conversation-engine.ts"]
end
subgraph "数据库"
DB["SQLite 存储<br/>db.ts"]
end
FE_UI --> API_CHAT
FE_UI --> API_UPLOAD
API_CHAT --> SDK_CLIENT
API_CHAT --> DB
SDK_CLIENT --> SYS_PROMPT
SDK_CLIENT --> WS_FS
IM_ENGINE --> SDK_CLIENT
IM_ENGINE --> DB
```

图表来源
- [src/app/api/chat/route.ts:74-342](file://src/app/api/chat/route.ts#L74-L342)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/sdk/system-prompt.ts:16-553](file://src/lib/sdk/system-prompt.ts#L16-L553)
- [src/lib/workspace-fs.ts:32-42](file://src/lib/workspace-fs.ts#L32-L42)
- [src/lib/im/conversation-engine.ts:30-231](file://src/lib/im/conversation-engine.ts#L30-L231)
- [src/lib/db.ts:18-43](file://src/lib/db.ts#L18-L43)

章节来源
- [src/app/api/chat/route.ts:74-342](file://src/app/api/chat/route.ts#L74-L342)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/sdk/system-prompt.ts:16-553](file://src/lib/sdk/system-prompt.ts#L16-L553)
- [src/lib/workspace-fs.ts:32-42](file://src/lib/workspace-fs.ts#L32-L42)
- [src/lib/im/conversation-engine.ts:30-231](file://src/lib/im/conversation-engine.ts#L30-L231)
- [src/lib/db.ts:18-43](file://src/lib/db.ts#L18-L43)

## 核心组件
- createForgeQuery：核心封装函数，负责解析模型提供商、构建系统提示词、加载 agents/MCP、设置权限模式、构造 SDK 环境变量与查询选项，并返回可迭代的 Query 实例。
- ForgeQueryOptions：查询选项接口，包含 prompt、sessionId、model、workspaceId、abortController、canUseTool、bypassPermissions、customSystemPrompt、resumeSession、extraMcpServers、thinkingMode、providerType、skipMcpServers、skipAgents、useImPrompt、skipPersistSession、attachments 等。
- ForgeAttachment：附件类型，包含 name、serverPath、mimeType、tier（image/pdf/text/binary）。
- 系统提示词构建：根据 workspaceId 动态拼接基础提示词、环境信息、工作空间配置、记忆与规则上下文。
- 会话管理：区分首次消息（持久化 sessionId）与后续消息（resume 会话），并在失败时进行回退重试。
- 附件处理：将上传文件映射为 ForgeAttachment，按 tier 分类构建多模态内容块（图像/base64 文档/文本注入/二进制占位）。

章节来源
- [src/lib/sdk/client.ts:219-244](file://src/lib/sdk/client.ts#L219-L244)
- [src/lib/sdk/client.ts:246-255](file://src/lib/sdk/client.ts#L246-L255)
- [src/lib/sdk/client.ts:257-300](file://src/lib/sdk/client.ts#L257-L300)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/app/api/chat/route.ts:82-96](file://src/app/api/chat/route.ts#L82-L96)
- [src/app/api/upload/route.ts:60-90](file://src/app/api/upload/route.ts#L60-L90)

## 架构总览
下图展示从聊天 API 到 SDK 封装再到 IM 引擎的整体交互流程，以及失败回退与附件处理路径。

```mermaid
sequenceDiagram
participant FE as "前端 UI<br/>use-chat.ts"
participant API as "聊天 API<br/>chat/route.ts"
participant SDK as "SDK 封装<br/>createForgeQuery()"
participant DB as "数据库<br/>db.ts"
participant IM as "IM 引擎<br/>conversation-engine.ts"
FE->>API : POST /api/chat
API->>DB : 读取会话/设置源
API->>API : 构建权限模式/思维模式
API->>SDK : createForgeQuery(options)
SDK-->>API : Query 可迭代对象
API->>API : drainQuery() 消费流
API->>DB : 保存用户/助手消息
API-->>FE : SSE 流事件
Note over API,SDK : 失败回退
API->>API : 若 resume 失败则重建历史提示
API->>SDK : 再次 createForgeQuery(历史提示)
SDK-->>API : Query
API->>API : drainQuery() 继续消费
API->>DB : 保存助手消息
API-->>FE : SSE 完成事件
Note over API,IM : IM 场景
IM->>SDK : createForgeQuery(跳过 MCP, 使用 IM 提示)
SDK-->>IM : Query
IM->>IM : consumeStream() 实时预览
IM->>DB : 保存消息
```

图表来源
- [src/app/api/chat/route.ts:162-238](file://src/app/api/chat/route.ts#L162-L238)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/db.ts:18-43](file://src/lib/db.ts#L18-L43)
- [src/lib/im/conversation-engine.ts:164-207](file://src/lib/im/conversation-engine.ts#L164-L207)

章节来源
- [src/app/api/chat/route.ts:162-238](file://src/app/api/chat/route.ts#L162-L238)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/db.ts:18-43](file://src/lib/db.ts#L18-L43)
- [src/lib/im/conversation-engine.ts:164-207](file://src/lib/im/conversation-engine.ts#L164-L207)

## 详细组件分析

### createForgeQuery 设计与实现
- 输入参数：ForgeQueryOptions
  - prompt：用户输入文本
  - sessionId：会话标识符（首次消息持久化，后续消息用于恢复）
  - model：模型名称
  - workspaceId：工作空间标识，决定 .claude/ 上下文与项目路径
  - abortController：可选中止控制器
  - canUseTool：权限回调（用于 IM 或 confirm 模式）
  - bypassPermissions：绕过权限模式
  - customSystemPrompt：自定义系统提示词
  - resumeSession：是否恢复会话
  - extraMcpServers：额外 MCP 服务器配置
  - thinkingMode：统一思维模式（off/auto/max）
  - providerType：提供商类型，用于映射思维模式
  - skipMcpServers：跳过 MCP 服务器（IM 轻量化）
  - skipAgents：跳过 agents 文件加载
  - useImPrompt：使用 IM 简化提示词
  - skipPersistSession：跳过会话持久化
  - attachments：附件数组（ForgeAttachment[]）

- 关键步骤
  1) 解析提供商：resolveProvider(model) 获取 apiKey、baseUrl、authType
  2) 构建系统提示词：buildSystemPrompt 或 buildImSystemPrompt（基于 workspaceId 加载 .claude/ 配置、环境信息、记忆与规则）
  3) 加载 agents：loadAgentsFromFiles（可跳过）
  4) 加载 MCP 服务器：从 DB、浏览器 MCP 与 extraMcpServers 合并（IM 可跳过）
  5) 构建 SDK 环境变量：buildSdkEnv（复制进程环境、扩展 PATH、注入凭据、移除 CLAUDECODE）
  6) 设置 SDK 选项：model、cwd、systemPrompt、env、sessionId/resume、includePartialMessages、agentProgressSummaries、betas、settingSources、pathToClaudeCodeExecutable
  7) 思维模式映射：mapThinkingMode（统一 off/auto/max 映射到各提供商兼容参数）
  8) 权限处理：bypassPermissions 或包装 canUseTool（对 .claude/ 写入自动放行，限制 settingSources 以避免绕过权限桥）
  9) 附件处理：若存在附件，构建多模态内容块（image/document/text/binary），否则使用字符串 prompt
  10) 返回 Query 实例

- 会话管理策略
  - 首次消息：设置 sessionId 并持久化（persistSession 默认开启）
  - 后续消息：设置 resume（不带 sessionId），避免冲突
  - 失败回退：当 drainQuery 抛出异常或产出零块时，重建历史提示并使用新 sessionId 再试一次

- 附件处理机制
  - 附件类型 ForgeAttachment：name、serverPath、mimeType、tier
  - 多模态内容构建：按 tier 分类
    - image/pdf：base64 内容块
    - text：读取内容并注入为 tagged text
    - binary：仅记录文件名
  - 上传分类：/api/upload 将文件按 MIME 与扩展名归类为 image/pdf/text/binary tier

```mermaid
flowchart TD
Start(["进入 createForgeQuery"]) --> Resolve["解析提供商<br/>resolveProvider(model)"]
Resolve --> SysPrompt["构建系统提示词<br/>buildSystemPrompt/buildImSystemPrompt"]
SysPrompt --> Agents["加载 agents可跳过"]
Agents --> MCP["加载 MCP 服务器可跳过"]
MCP --> Env["构建 SDK 环境变量<br/>buildSdkEnv"]
Env --> Options["组装 SDK 选项<br/>sessionId/resume/settingSources/thinking"]
Options --> Perm["权限处理<br/>bypassPermissions/canUseTool"]
Perm --> Attach{"是否有附件？"}
Attach --> |否| QueryStr["返回 query({ prompt, options })"]
Attach --> |是| Blocks["构建多模态内容块<br/>buildMultimodalContent"]
Blocks --> QueryIter["返回 query({ prompt: AsyncIterable, options })"]
QueryStr --> End(["结束"])
QueryIter --> End
```

图表来源
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/sdk/client.ts:546-597](file://src/lib/sdk/client.ts#L546-L597)

章节来源
- [src/lib/sdk/client.ts:219-244](file://src/lib/sdk/client.ts#L219-L244)
- [src/lib/sdk/client.ts:246-255](file://src/lib/sdk/client.ts#L246-L255)
- [src/lib/sdk/client.ts:257-300](file://src/lib/sdk/client.ts#L257-L300)
- [src/lib/sdk/client.ts:378-537](file://src/lib/sdk/client.ts#L378-L537)
- [src/lib/sdk/client.ts:546-597](file://src/lib/sdk/client.ts#L546-L597)

### ForgeQueryOptions 参数详解
- prompt：用户输入文本
- sessionId：会话 ID（首次消息持久化，后续消息恢复）
- model：模型名称
- workspaceId：工作空间 ID，决定 .claude/ 路径与项目路径
- abortController：可选中止控制器
- canUseTool：权限回调（用于 IM 或 confirm 模式）
- bypassPermissions：绕过权限模式（危险，谨慎使用）
- customSystemPrompt：自定义系统提示词（覆盖默认）
- resumeSession：是否恢复会话
- extraMcpServers：额外 MCP 服务器配置
- thinkingMode：统一思维模式（off/auto/max）
- providerType：提供商类型，用于映射思维模式
- skipMcpServers：跳过 MCP 服务器（IM 轻量化）
- skipAgents：跳过 agents 文件加载
- useImPrompt：使用 IM 简化提示词
- skipPersistSession：跳过会话持久化
- attachments：附件数组（ForgeAttachment[]）

章节来源
- [src/lib/sdk/client.ts:219-244](file://src/lib/sdk/client.ts#L219-L244)

### ForgeAttachment 类型与多模态内容构建
- ForgeAttachment 字段
  - name：原始文件名
  - serverPath：服务端绝对路径
  - mimeType：MIME 类型
  - tier：分层类别（image/pdf/text/binary）
- 多模态内容构建规则
  - image/pdf：读取二进制数据，base64 编码，作为 SDK 图像/文档内容块
  - text：读取 UTF-8 内容，截断至约 100k 字符，注入为 tagged text
  - binary：仅记录文件名与 MIME 类型
- 上传分类逻辑
  - 根据 MIME 类型与扩展名判断 tier（image/pdf/text），并暴露安全的 /api/upload 路径

章节来源
- [src/lib/sdk/client.ts:246-255](file://src/lib/sdk/client.ts#L246-L255)
- [src/lib/sdk/client.ts:546-597](file://src/lib/sdk/client.ts#L546-L597)
- [src/app/api/upload/route.ts:60-90](file://src/app/api/upload/route.ts#L60-L90)

### 会话管理策略（首次消息 vs 后续消息）
- 首次消息
  - 设置 sessionId 并持久化（persistSession 默认开启）
  - 使用字符串 prompt 或多模态内容块
- 后续消息
  - 设置 resume（不带 sessionId），继续之前的对话
  - 若 resume 失败（抛错或产出零块），回退到重建历史提示并使用新 sessionId 再试
- IM 场景
  - 跳过 MCP 服务器，使用 IM 简化提示词
  - 若“会话 ID 已被占用”，自动重试使用全新 sessionId

章节来源
- [src/lib/sdk/client.ts:413-423](file://src/lib/sdk/client.ts#L413-L423)
- [src/app/api/chat/route.ts:171-207](file://src/app/api/chat/route.ts#L171-L207)
- [src/lib/im/conversation-engine.ts:164-207](file://src/lib/im/conversation-engine.ts#L164-L207)

### 前端集成与最佳实践
- 前端 Hook use-chat
  - 乐观渲染用户消息与占位助手消息
  - SSE 流式消费，支持 text_delta、thinking_delta、agent_content 等事件
  - 支持权限决策（允许/拒绝/会话级允许）
  - 会话缓存：跨会话切换时保留流进度
- 最佳实践
  - 优先使用 sessionId 持久化首次消息
  - IM 请求启用 skipMcpServers 与 useImPrompt 以降低延迟
  - 附件上传后通过 /api/upload 获取安全访问路径
  - 在 confirm 权限模式下提供清晰的 canUseTool 回调
  - 对于长对话，合理设置 thinkingMode（auto/max）以平衡性能与推理深度

章节来源
- [src/hooks/use-chat.ts:75-696](file://src/hooks/use-chat.ts#L75-L696)
- [src/app/api/upload/route.ts:60-90](file://src/app/api/upload/route.ts#L60-L90)

## 依赖关系分析
- 组件耦合
  - chat/route.ts 依赖 createForgeQuery、MessageMapper、permission-bridge、workspace-fs、db
  - conversation-engine.ts 依赖 createForgeQuery、permission-bridge、workspace-fs、db
  - client.ts 依赖 provider、workspace-fs、agents-loader、mcp-loader、browser-tools、system-prompt
- 外部依赖
  - @anthropic-ai/claude-agent-sdk：Query、Options、ContentBlockParam
  - better-sqlite3：本地数据库存储
  - Node.js 进程环境：PATH、HOME、CLAUDECODE、ANON_CREDENTIALS

```mermaid
graph LR
API["chat/route.ts"] --> CLIENT["client.ts"]
API --> DB["db.ts"]
API --> WS["workspace-fs.ts"]
API --> SYS["system-prompt.ts"]
IM["conversation-engine.ts"] --> CLIENT
IM --> DB
IM --> WS
CLIENT --> PROVIDER["provider.ts"]
CLIENT --> WS
CLIENT --> AGENTS["agents-loader.ts"]
CLIENT --> MCP["mcp-loader.ts"]
CLIENT --> BROWSER["browser-tools.ts"]
CLIENT --> SYS
CLIENT --> SDK["@anthropic-ai/claude-agent-sdk"]
DB --> SQLITE["better-sqlite3"]
```

图表来源
- [src/app/api/chat/route.ts:1-16](file://src/app/api/chat/route.ts#L1-L16)
- [src/lib/sdk/client.ts:14-27](file://src/lib/sdk/client.ts#L14-L27)
- [src/lib/workspace-fs.ts:1-5](file://src/lib/workspace-fs.ts#L1-L5)
- [src/lib/sdk/system-prompt.ts:1-10](file://src/lib/sdk/system-prompt.ts#L1-L10)

章节来源
- [src/app/api/chat/route.ts:1-16](file://src/app/api/chat/route.ts#L1-L16)
- [src/lib/sdk/client.ts:14-27](file://src/lib/sdk/client.ts#L14-L27)

## 性能考量
- IM 轻量化：skipMcpServers 与 useImPrompt 显著减少子进程启动与提示词长度，提升响应速度
- 附件处理：大文件读取与 base64 编码可能带来内存压力，建议控制单次附件大小与数量
- 思维模式：max 会增加推理成本，建议在复杂任务或长对话中使用
- 数据库：WAL 模式与外键约束保证一致性，但频繁写入需注意磁盘 I/O
- 流式传输：TransformStream 与 SSE 保证实时性，注意客户端断开时及时中止

## 故障排查指南
- “会话 ID 已在使用”
  - 现象：resume 时 SDK 抛错或产出零块
  - 处理：自动重试使用全新 sessionId 的查询
- “权限被拒绝”
  - 现象：.claude/ 写入被阻断
  - 处理：bypassPermissions 模式下自动放行；confirm 模式下通过 canUseTool 回调授权
- “附件读取失败”
  - 现象：构建多模态内容时警告
  - 处理：检查 serverPath 是否存在、可读；二进制文件仅记录文件名
- “SSE 断开”
  - 现象：客户端断开导致流中断
  - 处理：前端使用 AbortController 中止；后端在 finally 中关闭 writer

章节来源
- [src/app/api/chat/route.ts:187-207](file://src/app/api/chat/route.ts#L187-L207)
- [src/lib/sdk/client.ts:451-498](file://src/lib/sdk/client.ts#L451-L498)
- [src/lib/sdk/client.ts:585-588](file://src/lib/sdk/client.ts#L585-L588)
- [src/hooks/use-chat.ts:646-666](file://src/hooks/use-chat.ts#L646-L666)

## 结论
createForgeQuery 将模型提供商解析、系统提示词构建、agents/MCP 加载、权限与环境变量配置、多模态附件处理与会话管理策略整合为统一入口，既满足桌面聊天的完整能力，又能在 IM 等场景下保持低开销与高响应。通过合理的参数选择与错误回退机制，可在复杂任务与多模态交互中稳定运行。

## 附录

### API 使用示例（概念性说明）
- 桌面聊天
  - 步骤：发送 POST /api/chat，携带 sessionId、message（或 attachments）、permissionMode、thinkingMode
  - 行为：首次消息持久化 sessionId；后续消息恢复；失败自动回退；SSE 流式输出
- 即时消息（IM）
  - 步骤：ConversationEngine.processMessage，自动保存用户消息，构建 canUseTool，调用 createForgeQuery（跳过 MCP，使用 IM 提示）
  - 行为：实时预览文本增量；工具调用与文件附件检测；最终保存助手消息
- 附件上传
  - 步骤：POST /api/upload，返回安全访问路径与 tier 分类
  - 行为：按 MIME 与扩展名分类 image/pdf/text/binary

章节来源
- [src/app/api/chat/route.ts:74-342](file://src/app/api/chat/route.ts#L74-L342)
- [src/lib/im/conversation-engine.ts:30-231](file://src/lib/im/conversation-engine.ts#L30-L231)
- [src/app/api/upload/route.ts:60-90](file://src/app/api/upload/route.ts#L60-L90)