一、一句话定位
如果 nanobot 是「一个讲得清的分层引擎」,那 DeepSeek Harness 就是「一个谁都能改的插件底座」。它的全部秘密写在两个字里:Cordis——一个 vendor 进来的插件元框架。
官方给过一个极端的判断:连「循环(loop)」本身都是个可卸载的插件。在 Harness 里没有特权内核,模型、工具、UI、编排、文件系统,包括 agent-loop,全都是挂在共享 ctx 上的插件。
二、Cordis 的五个核心概念
读懂 Harness,先读懂 Cordis 的五件事:
- 插件是实现
Service的对象。可以是一个带inject和apply(ctx)的函数,也可以是Service子类;生命周期由 Cordis 挂载到当前上下文。 - 上下文是服务的容器。一个服务占据稳定的
ctx.<key>(如ctx.tools、ctx.llm、ctx.sessions);其他插件通过 key 查找,而非导入具体实现。 - 通过
inject声明依赖。插件声明所需服务后,会等它们就绪才启动;加载顺序由依赖表达,而非手动编排。 - 类型化事件用于通信。服务通过
emit/waterfall/parallel/serial分发——分别应对观察、环绕包装、并行扇出、按序执行。 - 注册是可逆的副作用。
ctx.effect()或ctx.on()安装的提示词片段、工具 schema、监听器,在 reload / teardown 时按预期撤销。
关键心智:你从不直接
import一个具体实现,只向ctx要一个能力。换实现 = 换插件,消费方一行不动。
三、agent-loop:一个「只是插件」的驱动器
capability-seams 文档里把 ctx.agentLoop 的角色标成 bundle,附注写得直白:
唯一的具体循环插件;扩展包依赖 dsh-agent 的事件和服务,而不依赖此包。
也就是说,Harness 默认只提供一个循环实现(ReactLoopAgent),但架构上它和「工具」「LLM」平级,随时可被替换。
3.1 主循环:kick → turn → step
agent.ts 里的驱动器入口极干净:
// packages/core/agent-loop/src/agent.ts
private async kick(): Promise<void> {
try { while (await this.turn()) {} }
catch { /* 失败在驱动器边界被收容 */ }
}
turn()发射turn/start→ 跑若干step()→ 自然停止时发射turn/stopping(串行终检点)→turn/end;step()经agent/request瀑布流构造请求、llm.stream逐 chunk 写assistant/chunk事件,再executeToolCalls;- 整个过程被一个 Phase 状态机(idle / maintenance / running)管着,输入分
followup/steer/inject/cancel四类。
3.2 生命周期熔断:三路 abort 融合
最见功力的是「卸载不能泄漏 agent」。index.ts 把三路 abort 用 AbortSignal.any 融合(fused)成一束:
- 调用方主动取消;
- owner fiber 卸载(插件被卸);
- 工厂 teardown。
任意一路触发,其余两路也被一并点燃,保证正在跑的 agent 一定会被干净回收。这是生产级运行时才在意、教学型引擎常常忽略的细节。
四、小结
Harness 的第一课:它不是一个「框架里有循环」,而是「循环也只是框架里的一个插件」。理解了 Cordis 的服务容器与依赖注入,你就拿到了打开整个代码库的钥匙——后面读工具、读会话、读沙箱,都是在 ctx.<key> 之间穿行。
下一篇我们顺着 ctx.tools 往下,看它的工具执行流水线如何在「不改循环」的前提下,把钩子、权限、沙箱、并发编排全塞进去。