Skip to content

Latest commit

 

History

History
105 lines (74 loc) · 6.44 KB

File metadata and controls

105 lines (74 loc) · 6.44 KB

01 一张图看懂 Chatbox

本章先建立地图,再进入实现。核心问题是:Chatbox 是桌面应用、Web 应用、移动应用,还是一个 AI Agent Harness?答案是:它用一套共享的领域模型,把这四种身份叠在一起。

一、产品边界:聊天 UI 只是最外层

Chatbox Community Edition 的公开定位是多模型桌面客户端,但源码显示它至少包含六条能力线:

能力线 解决的问题 主要代码区域
对话 创建会话、发送消息、流式渲染、继续/重新生成 renderer/stores/session/routes/session/
模型 用统一接口接入不同 API 协议和供应商 shared/providers/shared/models/
Agent 让模型调用搜索、文件、代码、MCP、Skills renderer/packages/model-calls/stores/session/tools-builder.ts
数据 会话、设置、图片、blob、知识库持久化 renderer/storage/main/store-node.ts
平台 Desktop/Web/Mobile 共用业务逻辑 renderer/platform/src/main/、Capacitor
交付 自动更新、安装包、签名、CI、错误上报 main/app-updater.ts.github/、Sentry

因此,“发送一条消息”不是一个 React onClick 直接 fetch,而是一条跨层流水线:UI 产生领域动作,Session Store 组装上下文,Provider 把统一模型请求翻译成具体协议,AI SDK 把响应变成流,Stream Processor 把流重新组织为可持久化的 Message content parts。

二、总架构

flowchart TB
  UI[React UI / TanStack Router]
  Store[Zustand Session Store\n会话 CRUD / 生成编排]
  Agent[Agent Harness\nContext + Tools + Sandbox]
  Model[ModelInterface\n统一 chatStream]
  Provider[Provider Registry\nOpenAI / Claude / Gemini / ...]
  Stream[Stream Chunk Processor\ntext / reasoning / tool]
  Persist[Platform + Storage\nIndexedDB / SQLite / Electron IPC]
  Main[Electron Main\n窗口 / 文件 / 沙箱 / OAuth / 更新]
  UI --> Store
  Store --> Agent
  Agent --> Model
  Model --> Provider
  Provider -->|HTTP / OAuth / API Key| Remote[模型服务]
  Remote --> Provider --> Model
  Model --> Stream --> Store --> UI
  Store --> Persist
  Persist --> Main
  Main --> Persist
Loading

这张图有两个容易被忽略的事实:

  1. Renderer 才是 AI 业务编排中心。Main 负责操作系统能力和安全边界,不负责决定 prompt、模型、工具组合。
  2. Storage 不是单一数据库。配置、会话、图片和大 blob 使用不同存储介质,再由 Platform 接口统一给业务层使用。

三、四个源码边界

1. Main:可信的操作系统适配层

src/main/main.ts 创建 BrowserWindow、注册 IPC、处理深链、快捷键、托盘、更新、OAuth 回调和本地文件能力。它还初始化知识库、Session Attachment RAG、MCP stdio transport、Skills 和 Sandbox 的 handlers。

Main 的原则是:能直接触碰系统的事情留在这里。例如读取用户真实文件、运行受控命令、创建本地预览服务、访问 app.getPath('userData'),都不应该从 Renderer 直接做。

2. Preload:窄而显式的桥

src/preload/index.ts 使用 contextBridge.exposeInMainWorld('electronAPI', electronHandler) 暴露有限 API。它不把完整 ipcRenderer 放到网页里,而是把 invoke、监听器、窗口事件、更新事件、MCP 传输事件封装成类型化对象。

3. Renderer:产品状态与 Agent 运行时

Renderer 负责页面、路由、状态、会话、模型调用、工具构建、流式更新、设置和跨平台业务逻辑。这里的 platform 实例把 Desktop、Web、Mobile 的差异收敛到统一接口。

4. Shared:跨运行时的领域契约

src/shared/types/models/providers/context/services/ 里放的是可以被多个运行时复用的模型、消息、设置、Provider、上下文转换和错误定义。Shared 不是“杂物目录”,而是防止 Desktop、Web、Mobile 各自发明一套消息语义的边界。

四、为什么要把 Agent 放在客户端

Chatbox 的 Agent 能力不是一个独立服务器上的 workflow,而是运行在用户本机的客户端里。这样做带来三种产品能力:

  • 文件和代码可以在本地工作目录上执行,数据不必先上传云端。
  • 用户可以配置自己的 API Host、API Key、OAuth 和本地模型。
  • UI 可以把工具调用、审批、暂停、继续和产物预览做成同一个交互闭环。

代价也很清楚:必须处理 Electron 安全、跨平台路径、原生模块、权限边界、崩溃恢复和长时间流式状态。这也是后续章节反复出现“边界”和“状态”的原因。

五、关键设计取舍

取舍 选择 结果
模型接口 自有 ModelInterface + AI SDK 上层不依赖单一供应商;底层可以适配不同协议
工具来源 运行时按能力构建 ToolSet 普通聊天不被 Agent 工具污染;弱模型可以被门控
会话结构 Session + Thread + Message Fork 支持历史线程与重新生成分支,而不把历史压扁成线性日志
平台差异 Platform 接口 业务逻辑可共享;实现细节隔离在 desktop/web/mobile
长结果 blob 外置 + 消息短预览 避免单条消息和上下文被工具输出撑爆
高风险操作 持久化 pause + 显式审批 重启后仍能恢复,不把安全判断放在临时 Promise 里

六、读源码的抓手

不要从最厚的 React 组件开始。推荐按下面的链路走:

  1. src/renderer/stores/session/orchestration.ts:一次生成的总编排。
  2. src/renderer/stores/session/agent-harness.ts:系统 prompt 和 ToolSet 的构建。
  3. src/shared/models/types.ts:统一模型协议。
  4. src/shared/providers/index.ts:Provider 到 Model 的实例化。
  5. src/renderer/stores/session/stream-chunk-processor.ts:流事件到消息部件的归一化。
  6. src/shared/types/session.ts:最终被保存和渲染的状态形状。

这一条路径就是 Chatbox 的“脊柱”。设置页、MCP、RAG、Sandbox 都是接在这条脊柱上的侧枝。

小结

Chatbox 的核心不是某个组件,而是一个跨平台的客户端 Agent Runtime:Renderer 负责决策和状态,Provider 负责模型协议适配,Main 负责系统能力,Shared 负责领域契约,Storage 负责让长生命周期状态可恢复。下一章从启动过程和 IPC 开始,解释这些边界在程序启动时如何实际接上。