技术博客
LLM 大语言模型AI Agent理解与问答推理优化论文解读

为什么 DeepSeek Harness 说“一切皆插件”?理解DPH的设计框架和思想 - Cordis

NOTA62026-08-17 18:54
为什么 DeepSeek Harness 说“一切皆插件”?理解DPH的设计框架和思想 - Cordis

为什么 DeepSeek Harness 说“一切皆插件”?从 Cordis 看懂 Agent 系统的可组合设计


当我们讨论一个 AI Agent,最先想到的往往是模型、提示词和工具调用。

但真正把 Agent 做成产品以后,问题很快就会变多:模型怎样替换?工具怎样注册?提示词由谁组装?会话如何恢复?插件卸载后,事件监听和资源占用能不能被正确清理?

DeepSeek Harness 的架构设计,并不是把这些能力全部塞进一个越来越大的 Agent 类,而是把它们都放进插件系统。

支撑这套设计的底层框架,叫作 Cordis


Cordis 的核心价值不是“提供很多插件”,而是规定插件如何发现服务、声明依赖、交换事件,以及如何安全退出。

理解 Cordis,基本就拿到了理解 DeepSeek Harness 架构的一把钥匙。



一、Cordis 到底是什么?

Cordis 是 DeepSeek Harness 使用的插件运行时。

在 Harness 中,模型适配器、工具注册表、会话日志、系统提示词服务,甚至驱动一次完整对话的 Agent Loop,本身都是插件。它们被挂载到同一个 Context,通过服务和事件协作。

这意味着系统里没有一个必须不断打补丁的“特权核心”。如果要增加能力,通常不是去修改 Agent Loop,而是安装一个新插件,让它在已经公开的服务或事件位置上工作。

从使用者的角度看,Cordis 主要解决五个问题:

  1. 一个插件是什么?
  2. 插件怎样找到其他能力?
  3. 插件怎样声明依赖?
  4. 插件之间怎样通信?
  5. 插件卸载时怎样清理资源?

这五个问题,正好对应 Cordis 的五个核心概念。


二、插件:系统中的能力单元

在 Cordis 中,插件可以是带有 apply(ctx) 的函数,也可以是一个 Service 子类。Cordis 负责把它挂载到当前 Context,并管理它的生命周期。

这看起来只是一个形式约定,但它改变了系统的组织方式。

传统做法可能会把模型调用、工具执行、日志记录和提示词组装写进同一条固定流程。这样做在早期很直接,但每增加一种模型、工具策略或运行环境,都可能继续扩大这条流程。

Cordis 则要求每项能力先回答两个问题:

  • 我向系统提供什么服务或事件监听?
  • 我依赖系统中的哪些服务?

于是,模型适配器只需要负责模型能力,工具插件只需要负责工具注册,Session 服务只需要负责追加式事件日志。系统的组合关系由插件声明,而不是由一个中心类硬编码。


三、Context:插件共享的服务目录

Context 可以理解为 Cordis 中的服务访问入口。

服务通过稳定的 key 挂载在 Context 上,例如 Harness 中的:

  • ctx.agents:管理正在运行的 Agent;
  • ctx.sessions:管理 Session 和追加式事件日志;
  • ctx.llm:提供与具体模型厂商无关的模型调用能力;
  • ctx.tools:注册工具并调度工具调用;
  • ctx.systemPrompt:组装系统提示词和工具 schema;
  • ctx.agentLoop:驱动 Turn 与 Step。

消费者依赖的是服务 key 和公开接口,而不是某个具体实现。

例如,Agent Loop 调用的是 ctx.llm。至于背后使用 DeepSeek 适配器、其他模型适配器,还是测试时的回放实现,由当前配置决定。只要它们提供同一项服务,Agent Loop 就不需要跟着修改。

这也是 Harness 所说的可替换能力设计:接口定义、具体 Provider 和能力消费者可以分开演进。


四、inject:用依赖关系决定加载时机

插件需要别的服务时,会通过 inject 显式声明。

Cordis 不要求开发者手工维护一份“先加载 A,再加载 B,最后加载 C”的启动顺序。一个插件声明依赖后,会等到所需服务已经存在再进入工作状态。

当前 Harness 的 Agent Loop 就声明了对 agentssessionsllmtools 和 systemPrompt 的依赖。

这条规则带来两个直接好处。

第一,依赖关系能从插件本身读出来,不需要到一个全局启动文件里寻找答案。

第二,配置可以替换服务实现,只要最终满足同一个服务需求,依赖它的插件就能继续工作。

所以在 Cordis 体系里,加载顺序不是一份人工编排的清单,而是服务依赖计算出来的结果。


五、类型化事件:让插件参与正在发生的流程

服务适合提供稳定能力,事件则适合让插件观察或介入一次正在发生的操作。

Cordis 提供四种事件分发模式:

模式 适合做什么 关键特点
emit 广播通知 监听~器观察事件,不返回处理结果
waterfall 包裹或拦截流程 监听~器调用 next() 才会继续传递,也可以短路
parallel 并行完成多项异步工作 等待所有监听~器完成
serial 按顺序完成异步检查 依次等待监听~器,并保留顺序语义

其中最值得注意的是 waterfall

它不是普通广播,而是一条可以允许插件包裹的调用链。监听~器收到 next():调用它,表示把控制权交给后续监听~器;不调用,则表示在当前位置结束这条链。

Harness 把这种机制用在多个关键位置,例如:

  • agent/pre-step:进入一次 Step 前调整或拒绝输入;
  • agent/request:包裹模型请求;
  • llm/stream:介入模型流式输出;
  • tools/pre-execute:在工具执行前应用策略。

所以插件不只能“收到通知”,还可以在公开位置上增加权限、重试、观测或其他策略,而不需要修改默认循环。


六、Effect:注册必须可以撤销

插件系统最容易被忽略的问题,是卸载。

一个插件加载时可能注册工具、添加提示词片段、监听事件、启动定时器,或者占用其他资源。如果卸载时只删除插件对象,却没有撤销这些注册,系统就会出现重复监听、旧配置残留或资源泄漏。

Cordis 的规则很明确:注册是一种可逆副作用。

插件通过 ctx.effect()ctx.on() 或返回 disposer 的注册方法安装能力。插件卸载时,Cordis 会沿生命周期撤销这些副作用。

这使热重载不再只是“重新执行一次插件代码”。正确的过程是:先清理旧插件留下的注册,再挂载新版本。

在 Harness 中,工具、提示词片段、模型适配器和 Provider 都遵循这项原则。一个工具插件离开以后,它注册的工具也应该同时消失。





七、一次工具调用里,五个概念怎样连起来?

假设我们安装了一个只读的仓库统计工具插件:

  1. 它以插件形式挂载到 Cordis;

  2. 它通过 inject 声明需要 tools 服务;

  3. Cordis 等待 ctx.tools 可用,再启动插件;

  4. 插件向工具服务注册名称、参数 schema 和执行函数;

  5. Agent Loop 在每个 Step 组装系统提示词与工具 schema;

  6. 模型返回 Tool Call 后,ctx.tools 找到对应实现并执行;

  7. Tool Result 被写入 Session Event Log,下一次模型请求可以从日志恢复这段历史;

  8. 插件卸载时,注册返回的 disposer 撤销这个工具。

Plugin、Context、inject、Event 和 Effect 不是五个彼此独立的术语。它们共同定义了一项能力怎样进入系统、参与运行,以及离开系统。


八、Cordis 事件和 Session Event的不同

这是理解DP Harness 架构时很容易混淆的一点。

Cordis 事件主要服务于进程内协作。agent/*llm/streamtools/* 等事件让插件观察或拦截正在运行的流程,它们本身不等于持久化记录。

Session Event 则是追加到会话日志中的事实,例如 turn/startstep/startuser/messageassistant/messagetool/call 和 tool/result。它们可以用于恢复模型历史、重放 UI,以及继续一个中断的会话。

Harness 在这里增加了一条重要规则:模型能够看到的内容,必须能从 Session Log 重建。

因此,如果一个插件要给模型增加新的上下文,需要在发起请求时临时拼接一段字符串。它还需要让这项输入成为可恢复的会话事实,否则恢复会话以后,模型看到的上下文就会发生变化。

Cordis 提供运行时扩展能力,Session Event Log 提供持久化事实。二者配合,才让插件化和可恢复性同时成立。


九、Cordis 如何塑造 Harness?

理解这套基础机制后,再看 DeepSeek Harness,会发现很多设计都不是孤立决定。

1. 新能力优先做成插件

模型、工具、提示词、上下文和策略都通过已有服务或事件扩展。只有默认循环本身的职责变化,才需要修改 Agent Loop。

2. 能力可以替换,但接口保持稳定

消费者依赖服务定义,Provider 负责具体实现。替换模型或运行环境时,依赖方不必感知每个实现细节。

3. Turn 和 Step 有清楚的边界

一个 Step 包含一次模型请求及其工具调用;一个 Turn 可以包含零个或多个 Step。插件通过生命周期事件参与这些阶段,而 Session Log 记录需要持久化的事实。

4. 插件作用域决定资源归属

Harness 可以为 Agent 创建自己的 Context 作用域。注册在这个作用域里的工具、提示词和监听~器属于该 Agent;Agent 被释放时,这些注册可以一起撤销。

最终得到的不是一个固定用途的编程助手,而是一套可以通过配置组合 Agent 产品的运行框架。


十、学习 Cordis,建议从哪里开始?

如果你正在阅读 DeepSeek Harness,可以按下面的顺序学习:

  1. 先理解 Plugin、Context、Service、Event 和 Effect,不急着看完整 Agent Loop;
  2. 写一个只依赖单个服务的小插件,观察它何时加载和卸载;
  3. 分别尝试 emit 与 waterfall,特别注意 next() 的语义;
  4. 追踪一个工具从注册、进入提示词、被模型调用,到结果写入 Session Log 的完整过程;
  5. 最后再阅读 Turn、Step、权限、沙箱和持久化,它们会自然落到前面的基础概念上。

当你能回答“一个工具由谁注册、为什么此时可用、谁能拦截它、卸载时谁负责清理”时,就已经真正理解 Cordis 在 Harness 中的作用了。


结语

Agent 框架的难点,通常不是完成一次模型调用,而是让许多能力可以长期组合、替换、观察和清理。

Cordis 没有替 Harness 决定模型该怎样回答,也没有替工具决定业务逻辑。它提供的是更底层的运行规则:能力以插件进入系统,通过 Context 发现服务,用 inject 声明依赖,以事件参与流程,并通过 Effect 安全退出。

这正是 DeepSeek Harness 能够坚持“一切皆插件”的原因。

理解 Cordis 以后,我们看到的不再是一堆散落的包,而是一张由服务依赖、生命周期和事件连接起来的运行图。

点赞收藏
// 评论1
0 / 500
NOTA62026-08-18 05:40

Cordis 特别适合做 Agent Runtime 的eval ground。生产 Runtime 通常追求稳定,而 Eval Runtime 天生追求频繁变化、隔离、重组、重复运行。 Cordis 正好把这些变化本身变成了正常状态,而不是异常情况。我想,这是dp为什么把cordis作为harness基础框架的原因,是因为构建测试评估runtime环境,做对比试验效率极高。