新系列《 DeepSeek Harness 》第 2 篇 · 为什么"一切皆插件"不是口号,是工程范式
上一篇讲了 DeepSeek Harness 是什么。今天深入它的心脏——Cordis,那个让"一切皆插件"真正成立的底层框架。
Cordis 有五个核心概念。把这五个概念理解透,你就理解了整个 DeepSeek Harness 的设计哲学,也理解了为什么它能做到"改任何行为都不用动源码"。
概念 1 :插件( Plugin )——一切皆插件的"一切"
在 Cordis 里,插件是实现 Service 的对象。
它可以是一个带可选 inject 和 apply(ctx) 字段的函数,也可以是一个 Service 子类,生命周期由 Cordis 挂载到当前上下文。
关键理解:在 DeepSeek Harness 里,模型的每一部分都是插件:
这跟传统框架有本质区别。传统框架里,核心循环是"特权代码",你在外面打补丁绕来绕去。 Cordis 里没有特权内核——循环本身也是插件,可以被替换、被扩展、被叠加。
官方文档原话:
不存在需要打补丁的特权内核:扩展 dsh 的方式是把插件挂载到其他插件旁边。
这就是"一切皆插件"的真正含义:不是"什么都能加插件",而是"没有什么是核心"。
概念 2 :上下文( Context )——服务的容器,解耦的关键
上下文是 Cordis 解耦的基石。
每个服务占据一个稳定的 ctx.<key>:ctx.tools、ctx.llm、ctx.sessions、ctx.agents。
插件之间不直接 import 对方的实现,而是通过 key 从上下文查找服务:
// 不这样(直接依赖具体实现):
import{BashTool}from"./bash-tool"
// 而是这样(通过上下文查找服务):
ctx.tools.register(myTool)
这一层间接带来巨大的灵活性:只要接口不变,实现随便换。今天 bash 是本地执行,明天换成远程沙箱执行——对使用方(模型、其他插件)完全透明,因为它只认识 ctx.tools,不认识具体实现。
这就是文档里说的"能力 seam"(接缝):一个可替换能力 = 声明接口( Service Definition )+ 实现( Provider )+ 使用方( Consumer )。替换一个 Provider ,整个产品行为就变了,而调用方一行代码都不用改。
概念 3 :注入( inject )——依赖声明,而不是依赖注入
传统的依赖注入( DI )是框架"塞"给你依赖。 Cordis 的 inject 是声明式的:
插件声明所需的服务后,会等待这些服务就绪才启动。
exportconstmyPlugin={
inject:["tools","llm"],// 我依赖这两个服务
apply(ctx){
// 到这里时,ctx.tools 和 ctx.llm 一定已就绪
},
}
这解决了一个真实的工程难题:插件之间的启动顺序。
传统框架里,启动顺序靠手动编排("先启动 A ,再启动 B"),插件一多就乱。 Cordis 里,顺序由依赖关系自动推导——A 依赖 B ,就等 B 就绪再启动 A 。加载顺序通过服务依赖表达,而非手动编排启动序列。
好处是:你新增一个插件,永远不用担心"它该在谁之后启动"。声明依赖, Cordis 自己搞定。
概念 4 :类型化事件( Events )——四种分发模式
插件之间怎么通信?不是直接调函数(耦合),而是发事件(解耦)。
Cordis 的事件是类型化的( TypeScript 声明合并),并且有四种分发模式,每种语义不同:
| 模式 | 是否 await | 分发顺序 | 有返回值? | 用途 |
|---|---|---|---|---|
emit |
否 | 按注册顺序 | 否 | 观察、通知 |
waterfall |
否 | 按注册顺序 | 是 | 环绕中间件(可短路、可替换) |
parallel |
是 | 并行 | 否 | 并行扇出 |
serial |
是 | 按注册顺序 | 是 | 按序处理并传递结果 |
最值得注意的是 waterfall(瀑布式)——它是环绕中间件:
ctx.waterfall("agent/request",async(request,next)=>{
// 前置处理:改写请求
request.model="deepseek-chat"
// 委托给下游
constresult=awaitnext()
// 后置处理:包装结果
returnresult
})
监听器收到 (...args, next),调用 next() 执行下游,不调用则短路(直接返回,阻止后续)。
这正是权限系统的实现方式:策略监听器拥有决策权时,不调用 next() 直接返回拒绝——下游工具永远不会执行。
DeepSeek Harness 的事件就是扩展点。 想拦截模型请求?监听 agent/request。想在工具执行前做检查?监听 tools/pre-execute。想观察流式输出?监听 llm/stream。
概念 5 :可逆副作用( Effects )——卸载即撤销
这是 Cordis 最优雅的设计,也是"一切皆插件"能成立的安全保证。
注册是可逆的副作用。提示词片段、工具 schema 、适配器、提供方和监听器通过
ctx.effect()或ctx.on()安装, reload 和 teardown 时会按预期撤销。
apply(ctx){
// 注册一个副作用,拿到 disposer(释放函数)
constdispose=ctx.effect(()=>{
ctx.tools.register(myTool)
return()=>{// 返回清理函数
ctx.tools.unregister(myTool)
}
})
// 插件卸载时,dispose() 自动被调用
}
每个注册都对应一个 disposer 。 插件卸载时,它注册的一切——工具、监听器、适配器、提示词片段——全部自动撤销,不留任何垃圾。
这解决了插件系统最大的噩梦:热插拔之后的状态残留。在 Cordis 里,加载和卸载是完美对称的:装上去注册,卸下来撤销。你甚至可以动态替换一个插件——旧的撤销,新的挂上, Agent 继续跑。
五个概念,一条主线
把这五个概念串起来,你会看到一条清晰的主线:
插件(谁)→ 上下文(在哪)→ 注入(依赖谁)→ 事件(怎么通信)→ 副作用(怎么卸载)
这套设计的最终效果:你可以像搭积木一样组装 Agent ,每块积木都能随时替换、随时拿掉,而整个系统不会崩。
这就是"万物皆插件"从口号变成工程范式的全部秘密。
一句话总结
Cordis 的五个核心概念——插件、上下文、注入、事件、副作用——构成了一个"无特权内核"的插件系统:任何行为都可以替换,任何注册都可以撤销。
下一篇预告: 5 分钟跑起 DeepSeek Harness ,并写出你的第一个插件。
夜雨聆风