跳转至

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'))
    })
  }
}

本章思考与自测

  1. 思考题:如果一个插件通过全局 process.on('SIGINT', handler) 注册了信号监听,Cordis 的自动 dispose 能否自动清理它?DSH 是如何规范第三方扩展开发的?
  2. 自测题declare module '@deepseek-ai/cordis' 在 DSH 中扮演了什么角色?为什么它能保证整个 monorepo 中 50+ 个子包在编写插件代码时拥有 100% 准确的智能提示和类型校验?