大数跨境

深入 DeepSeek Harness:Cordis 如何组合 Agent 的能力

深入 DeepSeek Harness:Cordis 如何组合 Agent 的能力 渔夫 AIDaily
2026-09-05
5

hi,我是渔夫。

周末愉快,今天抽空来了解下 DeepSeek Harness 里很关键的东西:Cordis。

来看看,它怎么把模型、工具、会话、权限这些能力组合成一个真正能持续扩展的 Agent 产品。

很多 Agent 项目最初都很简单,如图:

image.png

从图中,如果只是做一个 Demo,这个循环已经足够了。

但现实业务中,让 Agent 真正走向产品,问题会迅速复杂起来:

  • 不同用户可能使用不同模型

  • Web、CLI 和 SDK 需要共享同一套 Agent 能力

  • 有些 Agent 可以执行 Bash,有些只能读取文件

  • 工具执行前需要经过权限判断和用户审批

  • 本地执行环境可能需要切换成远程沙箱

  • 会话需要持久化、恢复、回放和分叉

  • 新功能需要接入现有流程,却不能不断修改 Agent Loop

这时候,我们面对的就不再是“如何调用一次模型”,而是如何组织一个不断增长、持续迭代的 AI Agent 产品。

今天,我们就正式聊聊 DeepSeek Harness 对这个问题解决方案的答案,就是 Cordis。

一、Cordis 到底是什么

从技术上说,Cordis 是 DeepSeek Harness 底层使用的插件运行时。

但如果只把它理解成“插件系统”,我觉得还是低估它的作用,Cordis 管理四类运行时机制:服务解析、依赖激活、事件编排和生命周期所有权。

在 DSH 中,模型适配器、工具注册表、会话日志、权限策略,甚至 Agent Loop 本身,都是挂载到 Cordis 上的插件的。

可以理解 Cordis 自身是一个精简的运行时,但它不承载具体的 Agent 业务逻辑。

模型、工具、会话和 Agent Loop 都通过插件接入,没有哪个业务模块拥有不可替换的特权地位。

Cordis 的核心价值,就是它把 Agent 产品拆成一组可以组合、替换、隔离和回收的能力。

二、Cordis 如何把能力组合成一个 Agent

先声明下,Cordis 并不是把几个插件直接拼成一个 Agent 对象,而是先组装出一套运行时能力,再由 Agent Loop 使用这些能力创建和驱动 Agent。

整个过程大致:

假设我们要启动一个能够读取代码,执行命令并回答问题的 Agent能力。

pnpm dsh --profile headless "分析这个项目的结构"

命令背后,系统需要准备模型调用、工具执行、提示词和会话管理等能力,再创建 Agent 来处理任务。

这时 DSH 定义要组合哪些能力,Cordis 负责加载插件并管理它们之间的依赖。

1、从启动命令到插件配置

从命令中的 headless 是一个 Profile,表示一次性命令行任务的运行方式。

它里面声明使用哪些 Bundle,Bundle 则包含插件配置及对应代码。

加载过程:

2、插件如何把工具交给 Agent 使用

假设我们希望 Agent 能执行 Bash 命令,先看工具接入涉及的两个部分:

  • Bash 工具插件:定义工具名称、参数,以及如何执行命令。

  • Tools 服务:统一管理已注册的工具,并提供执行入口。

Bash 工具插件加载后,会把工具定义注册到 Tools 服务,然后 Agent 运行时,通过这个服务查找并执行工具。

为啦方便理解,把过程分成两个阶段。

(1)启动时,先把工具注册进来。 Bash 工具插件告诉 Tools 服务:“我提供一个叫 bash 的工具,这是它的参数定义和执行函数。”此时只是登记工具,并没有执行命令。

(2)运行时,再根据模型的工具调用执行。 模型请求调用 bash 后,Agent Loop 将调用交给 Tools 服务。Tools 服务找到对应实现,经过执行前检查等处理后,调用 Bash 工具的执行函数。

那么,Tools 服务本身从哪里来?

Cordis 加载 dsh-tools 插件时,会创建工具服务,并将它以 tools 这个名称提供给其他插件。

因此,dsh-tools 是提供服务的插件,ctx.tools 是访问这个服务的方式。

3、依赖就绪后,Agent Loop 才能工作

上面步骤后,插件挂载后,并不一定立即运行,Cordis 会先检查它通过 inject 声明的服务依赖,只有这些服务全部就绪,才会激活插件。

例如,Agent Loop 声明了以下六项依赖:

static inject = [
  'agents',
  'sessions',
  'llm',
  'tools',
  'systemPrompt',
  'sessionProjections',
]

图中的箭头表示服务依赖,假如 ctx.tools 还没就绪,Agent Loop 就会保持等待。

等六项服务全部可用后,才进入初始化。需要注意的是,插件的激活顺序由依赖关系决定,而不是由 YAML 的行顺序决定。

初始化时,Agent Loop 会向 ctx.agents 注册一个 Agent Factory,也就是负责创建 Agent 实例的实现。

在 Headless 模式下,Headless Runner 负责接收命令行任务。

它通过 ctx.agents 请求创建 Agent,ctx.agents 再将创建工作委托给这个 Factory,实例创建完成后,Runner 将用户任务提交给 Agent。

这样,配置中的插件已经形成可用的服务啦,Agent 实例也已创建并收到任务。

剩下的它会使用这些服务组装上下文、调用模型,并在模型请求工具时执行对应工具。

Agent Loop 负责组织这个运行过程,模型适配、具体工具实现和会话持久化,则由相应插件承担。

三、扩展 Agent,为什么不必总改 Agent Loop

前面,Agent 已经能通过 Tools 服务调用 Bash了,如现在要为某些工具操作必须获得用户批准,才能执行。

简单拆解,实现需求涉及三个职责:判断是否需要审批、获取审批结果、根据结果决定是否继续。

1、在工具执行前,留出策略接入的位置

Tools 服务提供了 tools/pre-execute 事件,然后工具执行前,服务会通过这个事件调用已注册的策略监听器,取得执行决定。

这里的事件承担了明确的决策职责,这个策略插件可以返回 allowdeny 或 ask,Tools 服务负责处理决定。

需要区分两个层次:Cordis 提供事件分发机制,DSH 定义 tools/pre-execute 这个扩展点及其允许返回的决定。 业务策略再通过插件接入这个位置。

因此,已有扩展点能够表达的审批规则,可以在策略插件中实现,无需为此给 Agent Loop 增加新的判断分支,这样变得更加灵活很多。

2、Waterfall:继续委托,还是直接作出决定

tools/pre-execute 使用 waterfall(瀑布式事件):多个监听器依次处理同一次调用,每个监听器可以通过 next() 继续交给后续处理,也可以直接返回决定,结束这条处理链。

注:这是 waterfall 的机制示意,A、B 不代表 DSH 中固定存在的两个插件。

例如,一个监听器只记录调用信息,记录后应继续调用 next();一个负责审批判断的监听器,如果确定当前操作需要确认,就可以直接返回 ask

3、请求审批之后,还不能直接执行

当执行前决定为 ask 时,Tools 服务调用 ctx.approval.request(),审批服务根据配置和可用的审批渠道处理请求,再返回结果。

批准后,表示这次调用通过了审批,它仍然需要通过后续守卫和取消检查,避免将“用户同意”误当成绕过其他执行约束的许可。

四、一切皆插件,代价是什么

最后聊聊,一切插件化的好处,可以让 Agent 的能力可以独立组合和替换,但同时也会带来新的工程复杂度。

因为当模型、工具、会话、权限、Agent Loop 都被拆成插件之后,原来集中在一个流程里的逻辑,会分散到不同插件、服务和事件之间。

这时候,我们可能需要额外处理几类问题:

  • 插件之间的依赖关系怎么维护

  • 谁先加载,谁后激活

  • 一个事件经过了哪些插件

  • 某个插件失败后,问题怎么定位

  • 插件创建的连接、进程、沙箱由谁负责释放

  • 插件之间的接口发生变化后,如何保证兼容

也就是说,插件化并没有让复杂度消失。

它只是把原来写在 Agent Loop 里的代码复杂度,变成了插件之间的组合复杂度和协作复杂度


推荐阅读:

设计师和Vibe Coding的福音啊!这个网站把400多个Figma设计模版免费开放出来了!

我把微信公众号接入WorkBuddy,节省了很多时间!

参考资料:

Cordis 入门


【声明】内容源于网络
0
0
渔夫 AIDaily
一名不务正业的渔夫,是 ai 科技疯狂爱好者,我正在出海了,保持对世界的思考与好奇。
内容 395
粉丝 0
渔夫 AIDaily 一名不务正业的渔夫,是 ai 科技疯狂爱好者,我正在出海了,保持对世界的思考与好奇。
总阅读10.3k
粉丝0
内容395