从源码视角看 DeepSeek Harness:Cordis 插件系统是如何工作的

从源码视角看 DeepSeek Harness:Cordis 插件系统是如何工作的

🔭 一句话总结:Harness 的灵魂是 Cordis 插件系统——它用”服务注入 + 事件总线”的组合,让所有 Agent 能力在配置层面自由拼装,而无需改一行源码。


先搞清楚几个概念

在深入源码之前,先理清 DeepSeek 给出的几个关键定义:

Model + Harness = Agent

这是 DeepSeek 内部对 Agent 的公式化定义:

  • Model(大模型):负责”思考”,做推理、决策、规划
  • Harness(运行框架):负责”执行”,做工具调用、上下文管理、任务调度

Cordis 是什么

Cordis 是 Harness 的底层插件元框架,名字来自拉丁语,意为”心脏/核心”。

它的职责极简:

  1. 加载 / 卸载插件
  2. 管理插件间依赖
  3. 提供服务注册与发现
  4. 事件总线的通信机制

Cordis 不包含任何业务逻辑,所有具体能力都是插件。


Cordis 核心机制:服务与事件

服务(Service)

插件通过 ctx.provide(key, factory) 注册服务,其他插件通过 ctx.get(key) 获取服务。

// 注册一个服务
ctx.provide('fileSystem', async (path: string) => {
  return await fs.readFile(path, 'utf-8');
});

// 获取这个服务
const content = await ctx.get('fileSystem')('/path/to/file');

服务支持依赖注入——声明式地声明自己依赖哪些其他服务:

ctx.provide('codeSearch', async (deps) => {
  const fs = await deps.get('fileSystem');
  const llm = await deps.get('model');
  // 使用 fs 和 llm 实现代码搜索
}, ['fileSystem', 'model']);

事件(Event)

插件可以发布事件订阅事件

// 订阅事件
ctx.on('agent:task:start', (task) => {
  console.log(`Task started: ${task.id}`);
});

// 发布事件
ctx.emit('agent:task:start', { id: 'task-123', type: 'code_review' });

这让插件之间的耦合完全基于事件协议,而非直接调用——插件可以完全不知道彼此的存在,只需遵循同一套事件规范。


插件结构:每个 Cordis 插件由哪些部分组成

interface CordisPlugin {
  name: string;           // 插件唯一标识
  version: string;        // 版本号

  metadata?: {
    description?: string;
    author?: string;
    tags?: string[];
  };

  dependencies?: string[];  // 声明依赖的其他插件

  // 核心生命周期
  register(ctx: CordisContext): Promise<void>;  // 注册服务和事件
  init(ctx: CordisContext): Promise<void>;      // 初始化逻辑
  destroy?(ctx: CordisContext): Promise<void>;  // 清理资源
}

register vs init 的区别

  • register:这个阶段插件注册自己的服务,其他插件可以在此阶段依赖它。所有插件的 register 完成后,服务才能被正确解析。
  • init:所有插件 register 完成后调用,此时可以安全地调用其他插件注册的服务。

配置驱动的插件拼装

Harness 的插件组合不是在代码里写死的,而是通过配置文件声明:

{
  "mode": "standard",
  "plugins": {
    "model": "@deepseek-ai/model-deepseek-v4",
    "tools": [
      "file-system",
      "terminal",
      "browser",
      "code-editor"
    ],
    "skills": ["code-review", "doc-generator"],
    "ui": "web"
  }
}

切换模式时,只需要改变配置:

{
  "mode": "minimal",
  "plugins": {
    "model": "@deepseek-ai/model-deepseek-v4",
    "tools": ["shell", "file-edit"]
  }
}

这就是”配置层面自由拼装“的真正含义——改配置表,不用改代码。


Loop 插件:Agent 的循环控制逻辑

Agent 运行时的”是否继续循环”由 Loop 插件控制,这是最有技术含量的插件之一。

interface LoopPlugin {
  // 决定是否继续下一轮
  shouldContinue(ctx: CordisContext, state: AgentState): Promise<boolean>;

  // 决定最大轮次
  maxIterations?: number;

  // 循环结束时的回调
  onComplete?(ctx: CordisContext, result: TaskResult): Promise<void>;
}

Harness 默认提供的 Loop 策略:

策略 说明
maxIterations 达到 N 轮后强制退出
converged 模型输出收敛(连续两轮输出相同)时退出
toolNotFound 所需工具不存在时退出
costLimit 达到预设成本上限时退出
humanApproval 每轮需要人类确认后继续

创造模式下,你可以实现自己的 Loop 策略,注册为新插件。


Sandbox 插件:安全隔离的艺术

Agent 执行 shell 命令、运行测试时可能产生危险操作——Sandbox 插件负责隔离执行

interface SandboxPlugin {
  // 执行命令
  exec(cmd: string, env?: Record<string, string>): Promise<ExecResult>;

  // 文件系统隔离
  mount(hostPath: string, sandboxPath: string): void;

  // 网络隔离控制
  setNetworkAccess(allowed: boolean): void;

  // 资源限制
  setResourceLimits(cpu: number, memory: number): void;
}

DeepSeek 官方的 Sandbox 实现支持:

  • 进程级隔离:命令在独立进程运行,超时自动 kill
  • 文件系统白名单:只允许访问特定目录
  • 网络访问控制:可完全禁用网络或仅允许特定域名
  • 资源配额:CPU 时间、内存上限

Scheduler 插件:多 Agent 任务调度

多 Agent 协作时,谁来执行哪一步由 Scheduler 插件决定:

interface SchedulerPlugin {
  // 决定下一个由哪个 Agent 执行
  selectNextAgent(agents: Agent[], task: Task): Promise<Agent>;

  // 决定任务并行还是串行
  planExecution(tasks: Task[]): Promise<ExecutionPlan>;
}

内置的调度策略:

  • round-robin:轮询分配
  • capability-based:根据 Agent 能力标签匹配任务
  • priority-based:按优先级排队
  • dependency-based:根据任务依赖关系构建 DAG

源码目录结构(基于项目 README 推断)

deepseek-harness/
├── packages/
│   ├── cordis/               # 插件元框架核心
│   │   ├── src/
│   │   │   ├── context.ts    # CordisContext(服务+事件核心)
│   │   │   ├── plugin.ts     # 插件接口定义
│   │   │   ├── loader.ts     # 插件加载器
│   │   │   └── events.ts     # 事件总线实现
│   │   └── package.json
│   │
│   ├── dsh/                  # CLI 主入口
│   │   ├── src/
│   │   │   ├── commands/
│   │   │   │   ├── web.ts    # npx @deepseek-ai/dsh web
│   │   │   │   ├── tui.ts    # npx @deepseek-ai/dsh tui
│   │   │   │   └── headless.ts
│   │   │   └── index.ts
│   │   └── package.json
│   │
│   ├── plugins/             # 官方插件集
│   │   ├── model-deepseek-v4/
│   │   ├── model-claude/
│   │   ├── tool-file-system/
│   │   ├── tool-terminal/
│   │   ├── tool-browser/
│   │   ├── tool-code-editor/
│   │   ├── skill-code-review/
│   │   ├── skill-doc-generator/
│   │   ├── sandbox-default/
│   │   ├── loop-default/
│   │   └── scheduler-default/
│   │
│   └── ui/                  # 交互界面插件
│       ├── ui-web/
│       ├── ui-tui/
│       └── ui-headless/
│
├── README.md
├── LICENSE (MIT)
└── package.json

插件开发:写一个自定义 Model 插件

假设你想让 Harness 接入 Claude 模型,而不是默认的 DeepSeek-V4:

// model-claude.ts
import { CordisPlugin, CordisContext } from '@deepseek-ai/cordis';

export const claudeModelPlugin: CordisPlugin = {
  name: 'model-claude',
  version: '0.1.0',

  async register(ctx: CordisContext) {
    ctx.provide('model', async (deps) => {
      const { Anthropic } = await import('@anthropic-ai/sdk');
      const client = new Anthropic({ apiKey: process.env.ANTHROPIC_API_KEY });

      return {
        name: 'claude-3-5-sonnet',

        async complete(prompt: string, options?: any) {
          const response = await client.messages.create({
            model: 'claude-3-5-sonnet-20241022',
            max_tokens: options?.maxTokens ?? 4096,
            messages: [{ role: 'user', content: prompt }]
          });
          return response.content[0].text;
        }
      };
    });
  },
};

然后改配置:

{
  "mode": "standard",
  "plugins": {
    "model": "model-claude"  // 替换成你的插件
  }
}

整个框架无需修改,模型就这么换了。


Cordis 的设计启示

Cordis 插件系统的设计借鉴了现代后端框架的成熟经验:

  • Angular 的依赖注入:服务延迟实例化 + 声明式依赖
  • Node.js 的 EventEmitter:事件驱动解耦
  • Docker 的插件模型:插件是独立可替换的运行单元

这种设计让 Harness 在保持核心稳定的同时,获得了无限的可扩展性——这正是 MIT 协议 + 插件架构组合在一起的深意。


相关链接

  • GitHub:https://github.com/deepseek-ai/deepseek-harness
  • npm:@deepseek-ai/dsh
  • Cordis(框架源码):https://github.com/deepseek-ai/cordis

  • 🔭 本文基于项目 README、技术媒体报道及源码结构推断整理,正式 API 以官方文档为准。*

评论区

0 条评论

登录后可评论。

拾光·开源拾遗 121 阅读