From a03dc0e1e43ce680a0eec39579ac59efb779e9fe Mon Sep 17 00:00:00 2001 From: wuyang6 Date: Fri, 14 Aug 2026 10:18:13 +0800 Subject: [PATCH] docs: clarify DeepSeek Harness plugin composition --- ...eepseek-harness-architecture-evaluation.md | 71 +++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/content/posts/deepseek-harness-architecture-evaluation.md b/content/posts/deepseek-harness-architecture-evaluation.md index 2bede94..29eb93e 100644 --- a/content/posts/deepseek-harness-architecture-evaluation.md +++ b/content/posts/deepseek-harness-architecture-evaluation.md @@ -43,6 +43,77 @@ 这条链路的价值在于,扩展不必侵入一个巨大核心:插件可以提供服务、监听或包裹 waterfall,并在卸载时撤销注册。Cordis 预印本将其归纳为时间可组合性与空间可组合性:一方面追踪可逆 effect,另一方面根据依赖变化重新协调组件。但论文仍处于 active revision,适合作为设计解释,不应当作稳定标准。[Cordis 预印本](https://github.com/cordiverse/paper) +## Cordis 插件树到底怎样实现 + +“插件树”容易让人误以为是一棵类继承树。dsh 里实际叠加了三层机制: + +1. **配置层决定“挂载什么”。** Profile 先列出有序 Bundle;启动时再依次应用 Bundle patch、Profile 自身 patch、Harness home patch 和命令行 `--patch`。每个配置行用 `id` 作稳定身份,`name` 指向插件模块,还可带 `config`、`inject` 和 `disabled`。后层 patch 按 `id` 命中前层行时,替换整个 `config`,不做字段级深合并。[CLI 组合参考](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/apps/cli/reference/README.md) +2. **Cordis 运行时决定“何时激活”。** 根 `Context` 下的 Loader 把每行配置挂成一个插件实例,每个实例都有 Fiber 状态。插件通过 `apply(ctx, config)` 贡献能力,通过 `inject` 声明必需服务。因此 YAML 行的先后不决定加载顺序:依赖未就绪的插件保持 `PENDING`,服务出现后才激活。[Cordis 服务教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/03-services.zh.md) +3. **effect 层决定“怎样安全拆除”。** `ctx.tools.register(...)`、`ctx.on(...)`、服务注册和子插件都与当前插件生命周期绑定。配置删除、HMR 或服务提供方消失时,Cordis 会卸载相关 Fiber,反向撤销注册;服务恢复后,依赖它的插件再重新加载。[Cordis 生命周期教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/02-lifecycle-and-effects.zh.md) + +可以把一个简化后的运行时理解为: + +```text +Root Context +└─ Loader + ├─ tools provider Fiber → 提供 ctx.tools + ├─ greet-tool Fiber → inject: tools + ├─ llm provider Fiber → 提供 ctx.llm + ├─ agent-loop Fiber → 消费 agents / tools / llm + └─ surface Fiber → Web、ACP 或 SDK JSON-RPC +``` + +这棵“树”同时包含两种关系:Loader 建立的父子生命周期,以及 `inject` 建立的服务依赖图。Bundle 是生成配置行的发布层,并不会成为一个不可拆的运行时黑盒。 + +### 一个最小例子:向模型增加 `greet` 工具 + +下面是官方 Cordis 教程示例的精简版。插件不自己启动 Agent,只声明“我需要 `tools` 服务,并向它注册一个工具”: + +```ts +import type { Context } from '@deepseek-ai/cordis' +import { defineTool } from '@deepseek-ai/dsh-tools' + +export const inject = ['tools'] + +export function apply(ctx: Context) { + ctx.tools.register(defineTool({ + name: 'greet', + description: 'Greet the named person.', + parameters: { + name: { type: 'string', required: true }, + }, + output: { + schema: { type: 'string' }, + render: (_args, value) => [{ type: 'text', text: value }], + }, + execute: async ({ name }) => `Hello, ${name}!`, + })) +} +``` + +假设将它发布并安装为虚构示例包 `@acme/dsh-greet-tool`,可在 Profile 的 `cordis.patch.yml` 插入一行: + +```yaml +- insert: + - id: greet-tool + name: '@acme/dsh-greet-tool' +``` + +这个例子假定所选 Profile 已有 `tools`、LLM 和 agent loop 等基础行;它只演示新能力如何插入既有 Agent。完整过程是:Loader 看到 `greet-tool` 行 → 发现它需要 `tools`,Fiber 在缺失时保持 `PENDING` → `ctx.tools` 就绪后调用 `apply` 并进入 `ACTIVE` → `greet` 的 schema 进入模型工具列表 → 模型发出 `greet({"name":"Ada"})` 时经标准工具管线执行 → 返回 `Hello, Ada!`。删掉这行或 HMR 热替换包时,Loader 根据稳定 `id` 重挂变化节点;旧 Fiber 进入卸载并最终 `DISPOSED`,工具注册随 effect 自动撤销。官方完整例子还演示了如何监听 `tools/result` 事件。[进入 Harness 教程](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/docs/cordis-tutorial/07-into-the-harness.zh.md) + +### “接入协议”要先区分方向 + +| 接入面 | 连接方式 | 解决的问题 | +|---|---|---| +| Cordis 插件 | 进程内:TypeScript `apply(ctx, config)`、`inject`、Service、Event、effect | 增加或替换模型、工具、会话、策略、loop 和 UI 组件 | +| Profile / Bundle / patch | 部署时:YAML 配置行与稳定 `id` | 选哪些插件、如何覆盖默认值;它不是远程协议 | +| Web | 浏览器 → dsh:HTTP POST 一元调用;dsh → 浏览器:WebSocket 事件下行 | 产品 UI、会话展示和交互式操作;业务 Remote 由 Typert 生成严格描述 | +| ACP | 外部 Agent 客户端 ↔ dsh:Agent Client Protocol over newline-delimited JSON-RPC stdio | 标准化的 `session/new`、`session/prompt`、取消和一次性权限回答 | +| Python / TypeScript SDK | 评测器或程序 ↔ dsh:项目自有的逐行 JSON-RPC 2.0 stdio | 批量任务、自动化和 benchmark runner;方法为 `initialize`、`session/prompt`、`shutdown` 及事件/状态通知 | +| MCP client | dsh ↔ 外部工具服务器:MCP stdio 或 Streamable HTTP | 发现工具并注册为 `mcp____`;当前不桥接 Resources 和 Prompts | + +关键区别是:**Web 是浏览器产品面;ACP 和 SDK 是“从外部驱动整个 Agent”;MCP 是“把外部工具接给 Agent”;Cordis 则是“在进程内改变 Agent 由哪些部件构成”。** 它们解决的问题不同,不应放在同一层比较。ACP 只暴露基线自动化能力,而 SDK 线协议当前没有版本协商和单 prompt 取消,都不能等同于 Web 产品面的完整功能。[ACP 协议契约](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/acp/acp/README.md) [SDK 线协议](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/sdk/protocol/README.md) [Web Connection](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/client/connection/README.md) [MCP client](https://github.com/deepseek-ai/deepseek-harness/blob/47f943859bef60e4160492346772ded9b24f765a/packages/mcp/mcp-client/README.md) + ## 这套设计解决了什么 ### 运行时结构也可以替换