为什么 DeepSeek Harness 说“一切皆插件”?理解DPH的设计框架和思想 - Cordis

为什么 DeepSeek Harness 说“一切皆插件”?从 Cordis 看懂 Agent 系统的可组合设计
当我们讨论一个 AI Agent,最先想到的往往是模型、提示词和工具调用。
但真正把 Agent 做成产品以后,问题很快就会变多:模型怎样替换?工具怎样注册?提示词由谁组装?会话如何恢复?插件卸载后,事件监听和资源占用能不能被正确清理?
DeepSeek Harness 的架构设计,并不是把这些能力全部塞进一个越来越大的 Agent 类,而是把它们都放进插件系统。
支撑这套设计的底层框架,叫作 Cordis。
Cordis 的核心价值不是“提供很多插件”,而是规定插件如何发现服务、声明依赖、交换事件,以及如何安全退出。
理解 Cordis,基本就拿到了理解 DeepSeek Harness 架构的一把钥匙。

一、Cordis 到底是什么?
Cordis 是 DeepSeek Harness 使用的插件运行时。
在 Harness 中,模型适配器、工具注册表、会话日志、系统提示词服务,甚至驱动一次完整对话的 Agent Loop,本身都是插件。它们被挂载到同一个 Context,通过服务和事件协作。
这意味着系统里没有一个必须不断打补丁的“特权核心”。如果要增加能力,通常不是去修改 Agent Loop,而是安装一个新插件,让它在已经公开的服务或事件位置上工作。
从使用者的角度看,Cordis 主要解决五个问题:
- 一个插件是什么?
- 插件怎样找到其他能力?
- 插件怎样声明依赖?
- 插件之间怎样通信?
- 插件卸载时怎样清理资源?
这五个问题,正好对应 Cordis 的五个核心概念。
二、插件:系统中的能力单元
在 Cordis 中,插件可以是带有 apply(ctx) 的函数,也可以是一个 Service 子类。Cordis 负责把它挂载到当前 Context,并管理它的生命周期。
这看起来只是一个形式约定,但它改变了系统的组织方式。
传统做法可能会把模型调用、工具执行、日志记录和提示词组装写进同一条固定流程。这样做在早期很直接,但每增加一种模型、工具策略或运行环境,都可能继续扩大这条流程。
Cordis 则要求每项能力先回答两个问题:
- 我向系统提供什么服务或事件监听?
- 我依赖系统中的哪些服务?
于是,模型适配器只需要负责模型能力,工具插件只需要负责工具注册,Session 服务只需要负责追加式事件日志。系统的组合关系由插件声明,而不是由一个中心类硬编码。
三、Context:插件共享的服务目录
Context 可以理解为 Cordis 中的服务访问入口。
服务通过稳定的 key 挂载在 Context 上,例如 Harness 中的:
ctx.agents:管理正在运行的 Agent;ctx.sessions:管理 Session 和追加式事件日志;ctx.llm:提供与具体模型厂商无关的模型调用能力;ctx.tools:注册工具并调度工具调用;ctx.systemPrompt:组装系统提示词和工具 schema;ctx.agentLoop:驱动 Turn 与 Step。
消费者依赖的是服务 key 和公开接口,而不是某个具体实现。
例如,Agent Loop 调用的是 ctx.llm。至于背后使用 DeepSeek 适配器、其他模型适配器,还是测试时的回放实现,由当前配置决定。只要它们提供同一项服务,Agent Loop 就不需要跟着修改。
这也是 Harness 所说的可替换能力设计:接口定义、具体 Provider 和能力消费者可以分开演进。
四、inject:用依赖关系决定加载时机
插件需要别的服务时,会通过 inject 显式声明。
Cordis 不要求开发者手工维护一份“先加载 A,再加载 B,最后加载 C”的启动顺序。一个插件声明依赖后,会等到所需服务已经存在再进入工作状态。
当前 Harness 的 Agent Loop 就声明了对 agents、sessions、llm、tools 和 systemPrompt 的依赖。
这条规则带来两个直接好处。
第一,依赖关系能从插件本身读出来,不需要到一个全局启动文件里寻找答案。
第二,配置可以替换服务实现,只要最终满足同一个服务需求,依赖它的插件就能继续工作。
所以在 Cordis 体系里,加载顺序不是一份人工编排的清单,而是服务依赖计算出来的结果。
五、类型化事件:让插件参与正在发生的流程
服务适合提供稳定能力,事件则适合让插件观察或介入一次正在发生的操作。
Cordis 提供四种事件分发模式:
| 模式 | 适合做什么 | 关键特点 |
|---|---|---|
emit |
广播通知 | 监听~器观察事件,不返回处理结果 |
waterfall |
包裹或拦截流程 | 监听~器调用 next() 才会继续传递,也可以短路 |
parallel |
并行完成多项异步工作 | 等待所有监听~器完成 |
serial |
按顺序完成异步检查 | 依次等待监听~器,并保留顺序语义 |
其中最值得注意的是 waterfall。
它不是普通广播,而是一条可以允许插件包裹的调用链。监听~器收到 next():调用它,表示把控制权交给后续监听~器;不调用,则表示在当前位置结束这条链。
Harness 把这种机制用在多个关键位置,例如:
agent/pre-step:进入一次 Step 前调整或拒绝输入;agent/request:包裹模型请求;llm/stream:介入模型流式输出;tools/pre-execute:在工具执行前应用策略。
所以插件不只能“收到通知”,还可以在公开位置上增加权限、重试、观测或其他策略,而不需要修改默认循环。
六、Effect:注册必须可以撤销
插件系统最容易被忽略的问题,是卸载。
一个插件加载时可能注册工具、添加提示词片段、监听事件、启动定时器,或者占用其他资源。如果卸载时只删除插件对象,却没有撤销这些注册,系统就会出现重复监听、旧配置残留或资源泄漏。
Cordis 的规则很明确:注册是一种可逆副作用。
插件通过 ctx.effect()、ctx.on() 或返回 disposer 的注册方法安装能力。插件卸载时,Cordis 会沿生命周期撤销这些副作用。
这使热重载不再只是“重新执行一次插件代码”。正确的过程是:先清理旧插件留下的注册,再挂载新版本。
在 Harness 中,工具、提示词片段、模型适配器和 Provider 都遵循这项原则。一个工具插件离开以后,它注册的工具也应该同时消失。


七、一次工具调用里,五个概念怎样连起来?
假设我们安装了一个只读的仓库统计工具插件:
它以插件形式挂载到 Cordis;
它通过
inject声明需要tools服务;Cordis 等待
ctx.tools可用,再启动插件;插件向工具服务注册名称、参数 schema 和执行函数;
Agent Loop 在每个 Step 组装系统提示词与工具 schema;
模型返回 Tool Call 后,
ctx.tools找到对应实现并执行;Tool Result 被写入 Session Event Log,下一次模型请求可以从日志恢复这段历史;
插件卸载时,注册返回的 disposer 撤销这个工具。
Plugin、Context、inject、Event 和 Effect 不是五个彼此独立的术语。它们共同定义了一项能力怎样进入系统、参与运行,以及离开系统。
八、Cordis 事件和 Session Event的不同
这是理解DP Harness 架构时很容易混淆的一点。
Cordis 事件主要服务于进程内协作。agent/*、llm/stream、tools/* 等事件让插件观察或拦截正在运行的流程,它们本身不等于持久化记录。
Session Event 则是追加到会话日志中的事实,例如 turn/start、step/start、user/message、assistant/message、tool/call 和 tool/result。它们可以用于恢复模型历史、重放 UI,以及继续一个中断的会话。
Harness 在这里增加了一条重要规则:模型能够看到的内容,必须能从 Session Log 重建。
因此,如果一个插件要给模型增加新的上下文,需要在发起请求时临时拼接一段字符串。它还需要让这项输入成为可恢复的会话事实,否则恢复会话以后,模型看到的上下文就会发生变化。
Cordis 提供运行时扩展能力,Session Event Log 提供持久化事实。二者配合,才让插件化和可恢复性同时成立。
九、Cordis 如何塑造 Harness?
理解这套基础机制后,再看 DeepSeek Harness,会发现很多设计都不是孤立决定。
1. 新能力优先做成插件
模型、工具、提示词、上下文和策略都通过已有服务或事件扩展。只有默认循环本身的职责变化,才需要修改 Agent Loop。
2. 能力可以替换,但接口保持稳定
消费者依赖服务定义,Provider 负责具体实现。替换模型或运行环境时,依赖方不必感知每个实现细节。
3. Turn 和 Step 有清楚的边界
一个 Step 包含一次模型请求及其工具调用;一个 Turn 可以包含零个或多个 Step。插件通过生命周期事件参与这些阶段,而 Session Log 记录需要持久化的事实。
4. 插件作用域决定资源归属
Harness 可以为 Agent 创建自己的 Context 作用域。注册在这个作用域里的工具、提示词和监听~器属于该 Agent;Agent 被释放时,这些注册可以一起撤销。
最终得到的不是一个固定用途的编程助手,而是一套可以通过配置组合 Agent 产品的运行框架。
十、学习 Cordis,建议从哪里开始?
如果你正在阅读 DeepSeek Harness,可以按下面的顺序学习:
- 先理解 Plugin、Context、Service、Event 和 Effect,不急着看完整 Agent Loop;
- 写一个只依赖单个服务的小插件,观察它何时加载和卸载;
- 分别尝试
emit与waterfall,特别注意next()的语义; - 追踪一个工具从注册、进入提示词、被模型调用,到结果写入 Session Log 的完整过程;
- 最后再阅读 Turn、Step、权限、沙箱和持久化,它们会自然落到前面的基础概念上。
当你能回答“一个工具由谁注册、为什么此时可用、谁能拦截它、卸载时谁负责清理”时,就已经真正理解 Cordis 在 Harness 中的作用了。
结语
Agent 框架的难点,通常不是完成一次模型调用,而是让许多能力可以长期组合、替换、观察和清理。
Cordis 没有替 Harness 决定模型该怎样回答,也没有替工具决定业务逻辑。它提供的是更底层的运行规则:能力以插件进入系统,通过 Context 发现服务,用 inject 声明依赖,以事件参与流程,并通过 Effect 安全退出。
这正是 DeepSeek Harness 能够坚持“一切皆插件”的原因。
理解 Cordis 以后,我们看到的不再是一堆散落的包,而是一张由服务依赖、生命周期和事件连接起来的运行图。

Cordis 特别适合做 Agent Runtime 的eval ground。生产 Runtime 通常追求稳定,而 Eval Runtime 天生追求频繁变化、隔离、重组、重复运行。 Cordis 正好把这些变化本身变成了正常状态,而不是异常情况。我想,这是dp为什么把cordis作为harness基础框架的原因,是因为构建测试评估runtime环境,做对比试验效率极高。