Cordis 微内核架构¶
"Everything is a Plugin." —— DSH 设计基石
要读懂 DeepSeek Harness 的源码,第一道门槛不是 Prompt 工程,也不是 Tool Calling,而是 Cordis 插件微内核。DSH 没有采用传统框架中层层固化的面向对象继承体系,而是将所有功能解耦为挂载在 Cordis Context 树上的微插件。
核心文件速查¶
| 路径 | 职责 | 重要度 |
|---|---|---|
vendor/cordis/ |
官方 vendored 的 Cordis 微内核实现 | ⭐⭐⭐ |
packages/core/scope/src/index.ts |
作用域化上下文绑定原语 (scopeOf, scopeTarget) |
⭐⭐⭐ |
packages/boot/app-boot/src/index.ts |
根 Cordis 实例初始化与 Profile 启动序列 | ⭐⭐⭐ |
packages/core/agent/src/index.ts |
Agent 在 Cordis Context 上的扩展声明 |
⭐⭐ |
核心概念:Context、Service 与 Plugin¶
在 Cordis 中,系统运行时由一棵 层次化的 Context 树 驱动。每个插件(Plugin)都是一个独立的加载单元,挂载到某个 Context 节点上。
┌──────────────────────────────┐
│ Root Context │
│ (ctx: Context, fiber: root)│
└──────────────┬───────────────┘
│
┌────────────────────┼───────────────────┐
│ │ │
┌────────▼─────────┐ ┌────────▼─────────┐ ┌───────▼──────────┐
│ dsh-base ctx │ │ dsh-session ctx│ │ dsh-tools ctx │
│ Provides: │ │ Provides: │ │ Provides: │
│ ctx.llm │ │ ctx.sessions │ │ ctx.tools │
└────────┬─────────┘ └────────┬─────────┘ └───────┬──────────┘
│ │ │
└────────────────────┼───────────────────┘
│
┌──────────────▼───────────────┐
│ Agent-Scoped Context │
│ (Child ctx with agentId) │
└──────────────────────────────┘
1. 服务提供与 TypeScript 类型注入¶
当一个插件实现某个系统能力时,它继承自 Service 并注册到 Context 上:
// packages/core/session/src/index.ts
import { Context, Service } from '@deepseek-ai/cordis'
// 1. 利用 TypeScript 模块扩展(Module Augmentation)扩展全局 Context 接口
declare module '@deepseek-ai/cordis' {
interface Context {
sessions: SessionStore
}
interface Events {
'session/event'(event: SessionEvent): void
'session/flush'(): Promise<void>
}
}
// 2. 继承 Service 并在构造函数中绑定服务名
export class SessionStore extends Service {
constructor(ctx: Context) {
// 注册服务名 'sessions',当此插件挂载时,ctx.sessions 将自动可用
super(ctx, 'sessions', true)
}
// 服务实现...
}
2. 依赖注入与生命周期控制¶
其他插件如果依赖 ctx.sessions,无需手动通过构造函数传参,只需使用 ctx.inject 或在插件定义中声明依赖:
export const name = 'my-custom-plugin'
export const inject = ['sessions', 'tools'] // 声明必需的服务
export function apply(ctx: Context) {
// 当 sessions 和 tools 服务全部就绪时,apply 才会执行
ctx.on('session/event', (event) => {
console.log('Observed session event:', event.type)
})
}
3. 可逆的副作用与自动垃圾回收 (Disposal)¶
Cordis 最具革命性的特性在于 副作用的可逆性(Reversibility)。
在普通 Node.js 程序中,插件动态卸载非常容易造成内存泄漏(如未清理的 EventEmitter 监听器、定时器、全局句柄)。
在 Cordis 中,所有通过 ctx.on()、ctx.setInterval()、ctx.plugin() 注册的行为,都会被当前 Context 的 Fiber 严格追踪。当插件卸载时(ctx.scope.dispose()):
- 该 Context 上绑定的所有事件监听器自动注销;
- 该 Context 启动的所有定时器与子任务自动终止;
- 继承该 Context 的所有子插件递归卸载。
Agent Scope:多作用域隔离机制¶
DSH 中往往同时存在多个活跃 Agent(如主 Agent、后台子任务 Subagent、Fresh-Agent Ralph 等)。不同的 Agent 可能需要使用不同的系统提示词片段、不同的工具集或不同的沙箱模式。
DSH 在 packages/core/scope 中实现了一套极其精妙的 作用域解析算法:
// packages/core/scope/src/index.ts
export function scopeOf(ctx: Context): Agent | undefined {
// 向上沿着 Context 层次树递归寻找最近绑定的 Agent 实例
let current: Context | undefined = ctx
while (current) {
if (current[scopeTarget]) {
return current[scopeTarget]
}
current = current.parent
}
return undefined
}
- 全局服务(Root Scope):如持久化数据库连接、LLM Provider 注册中心、Web 服务器;
- Agent 局部服务(Agent Scope):每个 Agent 分配一个由
ctx.isolate()派生出的子 Context,挂载该 Agent 专属的工具权限与 Prompt 片段; - 当工具流水线执行时,
ctx.tools会先查找 Agent 本地注册的私有工具,再回退到全局公共工具。
源码精读与核心断言¶
让我们看 packages/core/agent-loop/src/index.ts 如何与 Cordis 生命周期深度结合:
// packages/core/agent-loop/src/index.ts:31
const INACTIVE_STATES: ReadonlySet<FiberState> = new Set([
FiberState.UNLOADING,
FiberState.DISPOSED,
FiberState.FAILED,
])
class FactoryOwnership {
private accepting = true
private readonly teardown = new AbortController()
constructor(private readonly fiber: Context['fiber']) {
// 监听当前 Fiber 的销毁事件,确保 Agent Loop 随插件优雅关闭
fiber.on('dispose', () => {
this.accepting = false
this.teardown.abort(new HarnessError('agent loop is being unloaded'))
})
}
}
本章思考与自测¶
- 思考题:如果一个插件通过全局
process.on('SIGINT', handler)注册了信号监听,Cordis 的自动 dispose 能否自动清理它?DSH 是如何规范第三方扩展开发的? - 自测题:
declare module '@deepseek-ai/cordis'在 DSH 中扮演了什么角色?为什么它能保证整个 monorepo 中 50+ 个子包在编写插件代码时拥有 100% 准确的智能提示和类型校验?