01 · 项目全景与分层架构¶
全仓库面貌与五层架构:既看清整个 monorepo 的构成,也看清系统运行时每一层承担什么。
1. 项目全景¶
DeepSeek Harness(dsh) 是 DeepSeek AI 开源的一款 Agent 运行时(agent harness):它把"大模型对话 + 工具调用 + 会话管理 + 权限策略 + 沙箱执行"组装成一个可配置、可替换、可组合的运行时产品。通常的 agent 框架把主循环、工具注册、会话存储写死在框架里,只留少量扩展点;dsh 的定位反过来——它介于"框架"与"应用"之间:既提供完整可运行的 CLI / Web 产品,又提供可编程的插件生态。
三个标志性设计决策
- 无特权内核(no privileged core)。常规框架里总有几个"核心模块"是不可替换的;dsh 连模型适配器、工具注册表、会话日志、甚至 Agent 主循环本身都做成插件。扩展方式 = 在旁边再挂一个插件,而非修改内核。
- 事件溯源会话(event-sourced session)。常规会话存储"最终状态",dsh 存"发生了什么":会话是追加式事件日志,模型可见的历史由日志
deriveMessages()投影得出,绝不另存一份副本。"模型可见 ⟺ 已记录"是始终成立的性质。 - 框架层完全自持(vendored Cordis)。常规项目把插件框架当作 npm 依赖引入;dsh 把底层插件框架 Cordis 源码直接 vendored 进仓库并改名
@deepseek-ai/*,附 18 项本地加固,因此整个框架层可审计、可补丁、可发布。
版本与格式策略
基线为上游 dsh-v0.1.7-rc.1(developer preview,MIT 协议)。演进策略是"地基优先":会话格式版本保持 SESSION_FORMAT_VERSION,released 旧版本经相邻迁移链读入(session-format-v0-to-v1 / -v1-to-v2 / -v2-to-v3 / -v3-to-v4 + session-format-catalog:读路径 decode→migrate→encodeCurrent,写后继 generation、不动旧代),仅未发布/未知版本双向 fail loud;session-query 检索域 SQLite 使用单调 SCHEMA_VERSION。
2. 分层架构¶
仓库是 pnpm monorepo(Node ^22.19 || >=24,全 ESM)。自上而下分五层(各层之间只通过接口约定连接,这是它和"按目录分层的单体框架"的本质区别):
| 层 | 位置 | 职责 | 关键事实 |
|---|---|---|---|
| 应用层 | apps/cli、apps/web |
CLI bin(dsh)与 Web GUI |
CLI 源码经 node --import tsx/esm 启动;bin 是"瘦自执行组合" |
| 组合层 | packages/bundle、preset、examples |
profile / bundle / preset 三种组合原语 | bundle = 可分发补丁层;preset = 会话级 agent 组合;覆盖所有运行形态 |
| 能力层 | packages/<group>/<pkg> |
50 个包组、265 个包级 package.json(workspace glob packages/*/*),全部能力扩展口 |
每包遵循 Service Definition / Provider / Consumer 三段式 |
| 框架层 | vendor/ |
vendored Cordis + 生态(cosmokit、schemastery、loader、include、hmr 等) | 全部改名 @deepseek-ai/*;18 项本地修改有详单与测试 |
| 外部 SDK | python/、packages/sdk |
Python SDK 与 TypeScript JSON-RPC SDK,跨进程驱动运行时 | 协议:stdio 换行分隔 JSON-RPC;Python 侧有官方 deepseek_harness 包 |
图 1:五层架构总览(应用 / 组合 / 能力 / 框架 / 外部 SDK)
flowchart TD
subgraph L1["应用层 · apps"]
A1["dsh CLI bin<br/>瘦自执行组合"]
A2["Web GUI 宿主"]
end
subgraph L2["组合层 · bundle / preset / examples"]
B1["profile 运行形态"]
B2["bundle 可分发补丁层"]
B3["preset 会话级 agent 组合"]
end
subgraph L3["能力层 · 50 包组 / 265 包 · 全部为能力扩展口"]
C1["core:session / tools / agent / agent-loop"]
C2["llm 适配 · shell · fs · sandbox"]
C3["subagents · web · skills · persistence"]
end
subgraph L4["框架层 · vendor/(vendored Cordis + 生态)"]
D1["@deepseek-ai/cordis 插件框架(18 项本地加固)"]
D2["cosmokit · schemastery · loader · include · hmr"]
end
subgraph L5["外部 SDK 层"]
E1["python/ deepseek_harness"]
E2["packages/sdk TS JSON-RPC"]
end
A1 --> L2
A2 --> L2
L2 --> L3
L3 --> L4
E1 -.stdio JSON-RPC 子进程.-> A1
E2 -.stdio JSON-RPC 子进程.-> A1
注:分层为本报告的学习归纳视角;上游架构文档(docs/architecture.md)以"核心包表 + 事件 + 能力扩展口 + 扩展点"组织,并无五层说法。图 1~15 均为此归纳视角下的示意。注意:能力层向上提供"扩展口",组合层用 profile / bundle / patch 挑选具体 Provider,因此"换一个 Provider 就换掉整个产品"。所有图表需联网加载 Mermaid 渲染。
图 1 逐节点走读(每个节点一句话,说明「谁 / 职责 / 连向谁」):
- A1
dshCLI bin:应用层入口,是"瘦自执行组合"——本体不含任何业务逻辑,只负责node --import tsx/esm加载并组合一组插件,然后启动运行时。 - A2 Web GUI 宿主:应用层的第二个形态,承载浏览器前端,同样只是一层薄宿主,把能力组合交给组合层。
- B1 profile 运行形态:组合层原语,声明"以什么形态跑"(交互式 CLI / 服务器等)。
- B2 bundle 可分发补丁层:可打包分发的一层补丁,叠加在能力层之上改变具体 Provider 选择。
- B3 preset 会话级 agent 组合:面向单个会话的 agent 组合,把主循环、工具、权限排布成一套 preset。
- C1 core:session / tools / agent / agent-loop:能力层核心——事件溯源会话、工具注册表、agent 接口、主循环实现。
- C2 llm 适配 · shell · fs · sandbox:LLM 提供方适配与执行世界(子进程、文件系统、沙箱)的能力扩展口。
- C3 subagents · web · skills · persistence:子代理、web 传输、技能、持久化等更高层能力口。
- D1
@deepseek-ai/cordis插件框架:vendored 的框架层核心,自带 18 项本地加固,所有插件都运行在它之上。 - D2 cosmokit · schemastery · loader · include · hmr:框架层的配套生态(配置、类型、加载器、include 展开、热重载)。
- E1
python/ deepseek_harness:外部 Python SDK,通过 stdio JSON-RPC 子进程驱动dshCLI。 - E2
packages/sdkTS JSON-RPC:外部 TypeScript SDK,同样的跨进程驱动方式。
分层流向:A1/A2 → L2(应用层从组合层选形态)→ L2 → L3(组合层决定能力层装哪些 Provider)→ L3 → L4(全部能力跑在 vendored Cordis 框架上);E1/E2 -.驱动.-> A1(外部 SDK 通过 stdio JSON-RPC 子进程拉起 CLI)。
核心分层约束(最重要的一条架构纪律)
扩展插件只依赖 Service Definition,绝不依赖具体 Provider。 例如 UI、hook、工具插件只依赖 dsh-agent(接口),从不依赖 dsh-agent-loop(实现),所以主循环可整体替换。这是"换一个 Provider 就把整个产品换掉"的根本原因——文件系统/子进程/终端/LSP 共享一个执行世界,把 FS 与子进程 Provider 指向远程沙箱,Bash、PTY、LSP 全部随之迁移。