DeepSeek Harness 的核心引擎:Cordis 插件框架深度解析
引言
在 AI Agent 的落地实践中,框架的灵活性与可扩展性往往决定了系统的上限。DeepSeek Harness(以下简称 DSH)之所以能够实现「一切皆插件」的架构设计,很大程度上归功于其底层运行的 Cordis 框架。
Cordis 最初诞生于第三方聊天机器人领域,随后被 DeepSeek 引入作为整个 Agent 运行时的基础设施。本文将深入剖析 Cordis 的设计哲学、核心机制,以及它在 DSH 中的实际应用。
插件系统为何需要框架
四个核心问题
当我们构建一个可扩展的系统时,模块化只能解决「代码如何组织」的问题,却无法应对以下挑战:
安装:新功能如何被安全地「接」入现有系统?
配置:同一功能在不同部署环境下需要不同参数,这些配置应当写在哪里?
卸载:功能下线时,它创建的定时器、事件监听器、网络连接等资源由谁负责清理?清理不干净就会导致内存泄漏。
协作:功能 A 依赖功能 B 的能力,但 B 可能尚未启动或后续会被替换,A 应当如何优雅应对?
Cordis 的设计者将「卸载」与「协作」这两件事从「插件作者的自觉」提升为「框架级保证」,从根本上消除了插件开发的隐患。
元框架的定位
Cordis 并不是一个面向终端的框架,而是一个元框架(meta-framework)。它规定了「副作用如何组合、依赖如何解析」,但不预设任何业务领域。这种设计使其既可以支撑聊天机器人,也能够驱动 Agent 运行时。
论文《A Programming Paradigm for Spatiotemporal Composability》将 Cordis 的机制形式化,提供了完整的定义、定理与证明,使其从「好用的工具」升格为「被理论验证的范式」。
Cordis 的五大核心机制
1. 插件:三种形态
Cordis 插件支持三种实现形态:
// 函数形态(最常见)
export function apply(ctx: Context) {}
// 对象形态
export const objectPlugin = {
name: 'object-plugin',
apply(ctx: Context) {},
}
// 类形态(需要对外提供服务时使用)
export class MyService extends Service {
constructor(ctx: Context) {
super(ctx, 'myService')
}
}
这三种形态在 DSH 中都有广泛应用:工具插件、适配器插件、面板插件无一例外。
2. 上下文(Context):插件树与层级管理
在 Cordis 中,ctx(上下文)是开发者打交道的主要对象。它既是操作入口,也是服务容器。
export function apply(ctx: Context) {
ctx.on('some/event', handler) // 监听事件(卸载时自动移除)
ctx.effect(() => { /* ... */ }) // 注册副作用(卸载时自动回滚)
ctx.plugin(childPlugin) // 挂载子插件(随父插件卸载)
ctx.get('someService') // 读取服务
ctx.provide('someValue', 42) // 提供服务
}
ctx.plugin(child) 并不是简单的注册操作,而是派生出一个子上下文。由此,插件不再平铺排列,而形成一棵层级树:
- 子上下文继承父上下文的所有能力
- 父插件卸载时,所有子插件递归卸载
- 子插件卸载时,不影响兄弟和父级
这种树形结构是 Cordis 生命周期语义的骨架。
3. Fiber:插件生命周期管理
Cordis 为每个已加载的插件实例维护一个 fiber(纤维),这是一个状态机:
- PENDING:已声明,但依赖服务尚未就绪
- LOADING / ACTIVE:加载中或已激活
- FAILED:加载异常或配置校验失败
- UNLOADING / DISPOSED:清理中或已拆除
无论插件因配置修改、热重载还是显式调用而卸载,清理工作都是自动完成的。在 DSH 中,cordis_inspect 工具巡检的就是每个 fiber 的状态。
4. Effect:可逆副作用
这是 Cordis 与传统依赖注入容器最本质的区别。
ctx.effect(() => {
const conn = createConnection()
return () => conn.close() // 清理函数
})
Effect 的主体在插件加载时执行,返回的清理函数在卸载时执行。无论插件因何种原因被卸载,Cordis 都会自动回滚所有注册的副作用——定时器取消、监听器移除、连接关闭,开发者无需手动处理。
这带来了几个关键特性:
- 热重载(HMR):插件可以在运行时安全替换
- 故障自动恢复:出错的插件被卸载后,相关资源自动清理
- 测试隔离:每个测试用例拥有独立的插件实例
在 DSH 中,「改配置不重启」正是基于这一机制:修改配置 → 旧插件卸载(所有 effect 回滚)→ 新插件加载,整个过程进程无需重启。
5. 服务与注入:响应式依赖
将能力挂载到 ctx 上供其他插件使用,这就是 Service(服务):
export class GreeterService extends Service {
constructor(ctx: Context) {
super(ctx, 'greeter') // 注册服务
}
greet(who: string) {
return `Hello, ${who}!`
}
}
消费方通过 inject 声明依赖:
export const inject = ['greeter']
export function apply(ctx: Context) {
console.log(ctx.greeter.greet('world'))
}
inject 的语义是:插件保持 PENDING 状态,直到所列服务全部就绪。配置文件的顺序无关紧要,启动顺序由依赖关系决定。
与传统 DI 容器的核心差异在于:
- 传统 DI 假设「服务一旦绑定就会一直存在」
- Cordis 的假设是「服务可以随时出现,也可以随时消失」
在 Agent 场景中,这是常态——LLM 被限流、MCP 服务器崩溃、文件 watcher 被系统杀死。Cordis 的处理是:提供方卸载时,所有依赖它的插件自动卸载;新提供方就绪后,依赖方自动重载。依赖方无需编写任何重连代码。
事件系统:解耦的通信
事件系统适合「喊一嗓子」而不关心谁在听的场景:
ctx.on('stats/report', (name, count) => {
// 监听,卸载时自动移除
})
ctx.emit('stats/report', 'tool_call', 42)
Cordis 支持多种分发模式,其中 DSH 最常用的是 waterfall(瀑布流)模式,本质是将 Koa/Express 的中间件机制搬进事件系统:
ctx.on('some/decision', async (input, next) => {
if (!hasPermission(input)) {
return { denied: true } // 不调 next() = 否决
}
return next() // 调 next() = 放行
})
多个互不相识的插件就这样组成一条决策链。在 DSH 中,工具执行管道(tools/pre-execute → tools/execute → tools/post-execute)就是一条 waterfall 链。
声明式配置:配置即程序
cordis.yml 体现了「配置即组合」的思想:
- id: greeter
name: './greeter.ts'
- id: consumer
name: './consumer.ts'
disabled: true # 保留但暂不挂载
其中 id 字段至关重要:
- 不带 id:每次配置读取都获得新 id,编辑配置会被视为「先删后加」
- 带 id:支持精准增量更新,改一行配置只会影响对应插件
此外,支持 Schema 校验确保插件在配置不完整时不会半启动,还支持 !!js 表达式在依赖就绪后求值。
在 DeepSeek Harness 中的实践
启动架构
DSH 的启动仅需约二十行代码:
async function boot(configPath, patches) {
const ctx = new Context()
ctx.provide('dshHomePath', dshHomePath)
await ctx.plugin(Loader)
await mountRootInclude(ctx, configPath, patches)
await ctx.get('loader')?.await()
return ctx
}
根 Context 只做引导工作,其余一切来自配置树。
Profile 与 Bundle 机制
DSH 引入了 Profile 概念,允许将应用拆分成可叠加的层。每个 Profile 包含 manifest 和用户自定义的 cordis.patch.yml。
Bundle(组合包) 是一个 npm 包,声明了要一次插入的多个插件。核心组合包 @deepseek-ai/dsh-base 正是通过这种方式,将数十个插件一次性注入空根。
这种设计使得部署方可以无需修改源码,只需在自己的 patch 层覆盖配置即可改变默认行为。
工具流水线
在 DSH 中,Agent 的每项能力本质上都是一个 Cordis 插件:
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet the named person.',
parameters: {
name: { type: 'string', required: true }
},
async execute(args) {
return `Hello, ${args.name}!`
}
}))
}
ctx.tools.register() 注册即为 effect,插件卸载时工具自动注销,无需手动处理。
自指工具集
DSH 最具特色的包是 @deepseek-ai/dsh-tool-cordis,它提供五个让 Agent 检查并改装自己运行时的工具:
- cordis_inspect:巡检服务状态、fiber 状态、工具注册表
- cordis_define:现场定义小插件包
- cordis_run:在沙箱中执行动态包
- cordis_stop / cordis_undefine:卸载动态包
这使得 Agent 可以检查自己的运行环境、现场编写并运行动态插件、用完再卸载,全程不动配置文件、不装 npm 包、不重启进程。这正是「可进化 Agent」的雏形。
扩展槽位一览
DSH 的扩展点不是固定的 API 列表,而是一张服务注册表。以下为主要槽位类别:
| 类别 | 槽位 | 用途 | 可替换提供方 |
|---|---|---|---|
| 执行 | shell | Bash / 沙箱 / PowerShell | bash-local, bash-sandbox, pwsh-local |
| 执行 | codeRuntime | 代码执行 | code-runtime-worker |
| 模型 | llm | LLM 适配器 | llm-deepseek, llm-pi-ai |
| 智能 | agents / agentLoop | Agent 注册与循环 | agent-loop |
| 数据 | sessions / storage | 会话与存储 | session-persistence-jsonl, storage-sqlite |
| 环境 | fs / web | 文件系统与网络 | fs-local, fs-sandbox, web-fetch-http |
| 治理 | tools / approval | 工具注册与审批管道 | - |
| 前端 | slots / theme | UI 槽位 | dsh-client-ui-* 系列 |
这种「Service Definition - Provider - Consumer」三层分离的设计,使得每项能力都是一条「接缝」,两侧可以独立演进。
插件生态的想象空间
基于 Cordis 的设计理念,DSH 插件生态已展现出丰富的可能性:
能力扩展:数据库查询、浏览器自动化、Docker 控制、MCP 客户端、Git 操作等工具插件
记忆系统:有界分层跨会话记忆、带审批的可审计记忆
多智能体协作:Agent 团队、跨会话消息、聊天记录导入
自进化能力:会话内热挂载/卸载持久化插件,从轨迹沉淀可回滚的运行时状态
更令人期待的是「会写插件的插件」——利用 ctx.loader.create() 在运行时动态挂载、更新、移除其他配置条目,Agent 可以生成自己的工具并形成闭环:生成 → 沙箱运行 → 观测结果 → 保留或卸载。
结语
Cordis 通过可逆副作用解决时间维度的组合问题,通过响应式依赖解决空间维度的组合问题,两个维度相互协同:coeffect 操作本身就是 effect,而 effect 可逆。
这种设计将「卸载」与「协作」从开发者的责任变为框架的结构保证,使得运行时动态增删组件不再是危险的冒险,而是安全可控的默认行为。对于构建可进化、可扩展的 AI Agent 系统而言,这无疑是一条值得深入探索的技术路径。
