材料收集完毕。基于官方架构文档(DeepSeek Harness Architecture)和你机器上的实际安装(v0.1.1-rc.2),下面是完整梳理。
DeepSeek Harness 系统架构与核心概念
一、设计哲学:万物皆插件(Everything-is-a-Plugin)
DSH 最核心的一句话:产品的每一部分都是插件——模型适配器、工具注册表、会话日志、沙箱策略、甚至 agent loop(智能体循环)本身,全部可以通过配置替换。
因此它没有需要打补丁的特权内核:扩展 DSH 的方式就是把插件挂载到其他插件旁边,所有注册都是"可逆副作用",插件卸载时自动撤销。
底层框架是 Cordis(以 vendor 方式引入的插件框架),五个核心概念:
| 概念 | 含义 |
|---|---|
| 插件 | 实现服务的对象,有 inject(声明依赖)+ apply(ctx)(挂载逻辑) |
| 上下文 | 服务的容器,ctx.<key> 如 ctx.tools、ctx.llm、ctx.sessions;按 key 查找而非 import 实现 |
| 依赖注入 | 通过 inject 声明依赖,加载顺序由依赖关系决定,非手动编排 |
| 类型化事件 | 4 种分发模式:emit(观察)、waterfall(环绕包装)、parallel(并行扇出)、serial(按序执行) |
| 可逆副作用 | 所有注册通过 ctx.effect()/ctx.on() 安装,reload/teardown 时按序撤销 |
二、分层装配:Profile + Bundle
运行中的 dsh 是一棵插件树,由启动时按序叠加的各层组合而成:
空条目列表
└─ Profile 列出的组合包(bundle)按顺序应用
└─ Profile 自己的 cordis.patch.yml
└─ Home 级 $DSH_HOME/cordis.patch.yml
└─ 命令行 --patch overlay
-
Profile(配置档):存放在 Harness home(
~/.dsh/profiles/)的具名组装。web、headless是随发行版交付的模板。每个 profile 有dsh.profile清单 + 自己的cordis.patch.yml(用户覆盖层)。 -
Bundle(组合包):Cordis 配置项+挂载代码的分发格式,在
package.json里通过dsh.bundle字段声明。
三层基础 bundle:
-
dsh-base:每个 profile 的第一层——模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测 -
dsh-web-app:在其上增加浏览器应用 -
dsh-headless:一次性运行器,完全不带服务器
查看你机器实际的配置树:
dsh --profile web --dump-config—— 打印出的任何条目都可以被你的 patch 替换。
三、六大核心子系统(packages/core)
一个轮次按同一条循环流经六个包:
| 包 | 职责 |
ctx 键 |
|---|---|---|
session |
仅追加的事件溯源日志 + 内存 store——唯一真源 | ctx.sessions |
system-prompt |
提示词段落 + 工具 schema 的组装 | ctx.systemPrompt |
tools |
作用域化工具注册表 + 受保护的执行流水线 | ctx.tools |
agent |
Agent 接口、活跃 agent 注册表、agent/* 事件 |
ctx.agents |
agent-loop |
实现该接口的默认驱动器 | ctx.agentLoop |
scope |
按 agent 划分作用域的注册原语(纯库,无 ctx 键) | — |
设计要点:扩展插件依赖 agent(需要发起 Agent 时),绝不直接依赖 agent-loop,因此循环保持可替换。
四、轮次流程(Turn Flow):Step 与 Turn
- Step(步骤) = 一次模型请求 + 它调用的工具
- Turn(轮次) = 零个或多个步骤,领取首条输入时打开,不再欠任何工作时关闭
turn/start
认领 next-step 输入 + 一条排队消息
组装提示词段落 + 工具 schema
→ agent/pre-step ← 决定模型看到什么(可改写/拒绝)
step/start
追加 entered 消息为 user/message
从日志派生模型历史
agent/request → llm/stream → assistant/chunk* → assistant/message
tool/call* → tools/pre-execute → tools/execute → tools/post-execute → tool/result*
step/end
工具欠另一次请求 或 有新输入到达 → 认领 → 下一步骤
→ agent/turn-stopping
turn/end
其中 agent/pre-step、agent/request、llm/stream 和三个 tools/* 事件是 waterfall(必须调用 next() 委托下去);agent/turn-stopping 是 serial。
五、会话日志:事件溯源(Event Sourcing)
核心不变量:「模型可见即已记录」——抵达模型请求的一切都必须能从日志重建。
- 会话日志是一份仅追加的
SessionEvent日志,12 种核心事件变体:turn/start、turn/end、step/start、step/end、user/message、assistant/chunk、assistant/message、tool/call、tool/result、steering/message、todo/write、request/header - 模型历史从日志派生(
deriveMessages()),不单独存储 - 原始
assistant/chunk保证 token 级回放保真 - fork、恢复、transcript、遥测、持久化全部派生自该事件流
- 扩展方式:通过 TS declaration merging 扩展
SessionEventMap(如 compaction 插件添加了compaction/*事件)
六、工具执行流水线
ctx.tools.execute() 的调用链:
tools/pre-execute(可重排的 allow/deny/ask waterfall)
→ 已注册的单调 ToolGuard(只能缩减权限,不能撤销)
→ tools/execute(环绕分派包装层,可替换 signal)
→ tools/post-execute(检查/替换结果)
→ finalizeContent(定义拥有的回调)
→ tools/result(不可变的权威结果)
关键语义:
- 参数经过一次无损 JSON 物化后被深度冻结
-
ToolGuard返回类型没有 allow 结果——undefined保留决策,返回 reason 只能拒绝,监听器顺序无法把拒绝变回允许(单调性) - 执行模式分
parallel(可与兄弟并行)和exclusive(独占形成屏障) -
deferContext()允许组合工具把嵌套分派的上下文转运回外层结果
七、LLM Seam(适配器缝)
ctx.llm 定义了消息/内容块/流式词汇表和适配器 seam。添加模型提供方 = 在 ctx.llm 上注册一个适配器。你机器上装的 dsh-llm-deepseek、dsh-llm-pi-ai 就是具体适配器,当前配置用的是 deepseek-official 提供方。
八、能力 Seam(Capability Seam)
一个 seam 是三项角色组成的可替换能力:
- Service Definition:声明接口
- Service Provider:实现它
- Consumer:使用它(通常是面向模型的工具)
"替换一个提供方就能改变整个产品"——文件系统与进程提供方共享同一个执行世界,把 fs 指向远程沙箱,Bash、PTY、LSP 就一起搬过去了,无需提供方专用 fork。子代理提供方(
ctx.subagent)在同一个接口之后也千差万别:从新建子 agent,到把轮次委派给另一个产品。
九、作用域(Scope)机制
ScopeKey 是不透明对象身份(已交付的 loop 用活跃 Agent 对象当 key);Scoped<T> 是编译期品牌标记。注册表用同一个上下文同时表达"每个 agent 的可见性"和"共享生命周期所有权",agent.ctx 的注册项 agent 专属、卸载时撤销、之后拒绝再注册。这解释了为什么子代理、workflow 里的工具集合可以按 agent 隔离。
十、事件是扩展点:三个事件域
| 事件域 | 性质 | 用途 |
|---|---|---|
| 会话事件 | 追加到日志、广播 session/event 的持久事实 |
需要 reload 后仍存在的事实 |
Agent 事件(agent/*) |
携带活跃 Agent:inbox、步骤、状态、请求、验证 |
观察/拦截进行中的工作 |
| 能力事件 | 无需 import 循环即可附加策略和适配器 |
fs/*、tools/*、telemetry/*
|
十一、新行为的归属位置(快速索引)
| 想做什么 | 挂哪里 |
|---|---|
| 添加模型提供方 |
ctx.llm 注册适配器 |
| 添加面向模型的能力 |
ctx.tools 注册,schema 自动进提示词 |
| 添加 shell 执行 |
ctx.shell 后端(本地后端经 ctx.subprocess spawn) |
| 限制启动的进程 |
ctx.sandbox 后端,消费方启动前包装 argv |
| 拦截请求/工具/轮次 |
agent/* 或 tools/* 事件 |
| 添加模型可见上下文 |
agent.inject(),落到下一次获准请求 |
| 添加 UI 集成 | 驱动 ctx.agents + 从 session/event 渲染 |
| fork 活跃会话 | ctx.sessions.fork(source, boundary?) |
| 管理同会话目标 |
ctx.goals,通过 agent/* 续跑 |
总结一句:DSH = Cordis 插件框架(装配层)+ 事件溯源会话(状态层)+ 可替换的 LLM/FS/Shell/Sandbox/Subagent 能力缝(能力层)+ Agent 接口与默认 Loop(执行层),最上层是 web/headless 等 profile(产品形态)。和 Codex/Trae 这类单体产品不同,你在 DSH 里改任何一个环节——哪怕是循环本身——都是在"叠 patch 层",而不是"打补丁"。