☰
DeepSeek Harness 技术解读:用 Cordis 插件化 Agent 运行时,从时空可组合性到一切皆插件
2026/9/29 3:39:10 网站建设 项目流程

1. 为什么我要拆 DeepSeek Harness 的插件运行时

DeepSeek Harness 是 DeepSeek 开源的一个 Agent 运行时底座,命令名dsh,MIT 许可证,目前是 v0.1 开发者预览版。它和 Claude Code、Codex 那类开箱即用的成品不一样,官方自己都提示不推荐直接上生产。它真正有意思的地方在于设计取向:一切皆插件。模型、工具、技能、会话、沙箱、存储、主循环、调度、UI,全部被实现成插件,可以自由替换和重组。这套插件体系建立在 Cordis 元框架之上,Cordis 只干两件事——插件的加载/卸载,以及依赖关系管理。

如果你正在自建 Agent 运行时,或者被传统框架“插上去就拔不下来”的问题折磨过,这篇就是写给你的。我会从 Cordis 插件机制切入,把“时空可组合性”这个听起来很学术的概念,落到可复制的插件注册骨架、settings.json配置片段、启动验证和热加载检查动作上。你跟着做,能跑出一个最小可用的插件化运行时骨架。

先说清楚 Harness 是什么。Agent 的能力,一半在模型,一半在包裹模型的 Harness。公式很直白:Agent = Model + Harness。模型负责思考和推理,Harness 负责实际执行——工具调用、任务规划、调度、状态管理、权限控制、错误重试、安全护栏、评测、运行时监控。Harness 回答的是 Prompt 和 Context 都回答不了的问题:怎么让模型在真实环境里持续稳定地完成任务,出错时还能自我修复。

2. 时空可组合性到底在解决什么问题

传统插件系统的通病是“插上去拔不下来”。卸载一个插件往往要重启整个宿主进程。在 Agent 场景下这更致命:未来的自进化 Agent 可能自己生成工具、装进运行时、发现问题再自己替换。如果每次改动都要重启,积累的上下文、缓存、状态全崩。

DeepSeek Harness 把 effect(效应)和 coeffect(余效应)从编译期静态注解下沉成了运行时机制,为动态组合建立形式化基础。动态组合有两个正交维度:

维度要求静态对应物对应概念
时间维 temporal组件移除时,对共享环境的修改必须完整、安全地反转词法作用域 RAIIeffect(可逆运行时变换 + 左逆)
空间维 spatial组件能以结构化方式声明、发现、解析依赖模块导入解析coeffect(响应式依赖解析)

时间维落地成可回滚 Effect:每一次对 Context 的修改都必须配一个显式的逆函数。加载插件时按顺序叠加成撤销链,卸载时按 LIFO(后进先出)反向执行,状态精确恢复。逆操作在执行时根据当时状态动态生成,也就是带见证的效应。

空间维落地成反应式 Coeffect:组件把依赖声明为规格说明,Context 每次变化按 activating / deactivating / neutral 三种通知通知组件。依赖不可用不是错误,组件保持非活动状态等待,依赖满足后再自动进入 ACTIVE。

两者通过统一上下文范式合并成单一上下文类型 Γ∞,用 coeffect 上的观察等价赋予 effect 独立性——用空间维的可观测等价,解锁时间维的独立性。这就是“时空可组合”这个名字的由来。

注意:这套机制的价值不在学术名词,而在于它让“运行时安全地加载、卸载、替换组件”从口号变成了有形式化保证的工程能力。

3. 前置准备:拿到可用的模型端点

在写插件之前,得先让运行时能连上模型。DeepSeek Harness 是模型无关的,支持 OpenAI / Anthropic 兼容端点、本地模型(比如 Ollama)以及约 40 家模型提供方,不绑定 DeepSeek 自身模型。

我这边习惯用一个兼容端点来统一管理模型调用,省得每个 provider 都配一遍。你可以到 TaoToken 控制台创建一个 API Key,地址是 https://taotoken.net/api-keys ,注册入口在 https://taotoken.net/?utm_source=taotoken_aicg_blog_end&utm_medium=csdn&utm_campaign=rewrite&utm_content= 。拿到 Key 之后,接入文档在 https://taotoken.net/doc ,里面有各语言和各框架的对接示例,照着填就行。

这一步别跳过,因为后面插件注册骨架里的llm提供方要引用这个端点。Key 建议单独放环境变量,不要硬编码进settings.json提交到仓库。

4. 可复制的 Cordis 插件注册骨架

Cordis 的插件模型很轻:一个插件就是一个带apply函数的对象,apply接收ctx(上下文)和可选的config。加载时 Cordis 调用apply,卸载时执行你在apply里注册的清理逻辑。关键在于:所有对 Context 的修改都要通过ctx提供的 API 完成,这样运行时才能自动生成逆函数、叠加撤销链。

下面是一个最小插件骨架,我把它拆成“声明依赖 + 注册能力 + 返回清理”三段:

// plugins/hello-tool/index.js export const name = 'hello-tool' export const inject = ['llm'] // 空间维:声明依赖,llm 不可用时本插件保持非活动 export function apply(ctx, config) { // 时间维:每次修改 Context 都通过 ctx API,运行时自动记录逆操作 const disposeTool = ctx.tool.register({ name: 'hello', description: '返回一句问候,用于验证插件是否生效', parameters: { type: 'object', properties: { who: { type: 'string' } } }, async execute({ who }) { return `hello, ${who ?? 'world'}` }, }) // 注册一个可观测事件,方便回放时定位 const disposeHook = ctx.on('tool:call', (payload) => { ctx.logger.info('[hello-tool] tool called', payload.name) }) // 返回清理函数,卸载时按 LIFO 反向执行 return () => { disposeHook() disposeTool() } }

几个容易踩的点。第一,inject是空间可组合性的入口,写进去的依赖没满足时插件不会报错,而是挂起等待,依赖就绪后自动激活。第二,apply返回的函数就是时间可组合性的落点,卸载时 Cordis 会调用它,你在这里把注册的东西全部撤销。第三,不要在apply外面持有全局可变状态,否则逆函数没法精确恢复。

如果你想让 Agent 在运行时自己写插件,可以用cordis_define/cordis_run这两个工具,Agent 能检查、挂载、修改自己的运行时。这是自进化的铺路石,但预览版阶段建议先在沙箱里试。

5. settings.json 配置片段与启动验证

插件写好了,得让运行时知道去哪加载。settings.json里主要配三块:模型提供方、插件目录、预设模式。

{ "llm": { "provider": "openai-compatible", "baseURL": "https://taotoken.net/api", "apiKeyEnv": "TAOTOKEN_API_KEY", "model": "deepseek-chat" }, "plugins": { "dirs": ["./plugins"], "entries": ["hello-tool"] }, "preset": "standard", "log": { "appendOnly": true, "path": "./.dsh/sessions" } }

apiKeyEnv指向环境变量名,启动前先导出:

export TAOTOKEN_API_KEY="你的Key" dsh --config ./settings.json --check

--check会做一次启动自检,输出插件加载顺序、依赖解析结果和 Context 变更链。看到类似下面的输出就说明骨架通了:

[core] preset=standard [plugin] llm provider=openai-compatible model=deepseek-chat [plugin] hello-tool status=ACTIVE deps=[llm] [context] effect chain length=3, revertible=true

四种预设模式——标准、PTC(程序化工具调用)、极简、创造——各自加载不同的插件集。验证阶段用standard就行,等骨架稳定了再切PTC试程序化工具调用。

6. 热加载与常见错误排查

热加载是这套设计最直观的收益。改完插件代码,不用重启进程,直接触发重载:

dsh plugin reload hello-tool

预期结果是旧实例的清理函数按 LIFO 执行,Context 恢复到加载前状态,然后新实例重新apply。你可以用dsh plugin list看状态,dsh session replay <id>回放事件流,确认“模型可见 ⟺ 已记录”。

下面是我实测下来最容易撞的几个坑:

插件一直处于非活动状态。八成是inject里声明的依赖没就绪。先dsh plugin list看依赖树,确认llm提供方是否 ACTIVE。依赖不可用不是错误,是设计如此,别去改代码强行激活。

卸载后状态没恢复干净。检查你是不是在apply里直接改了全局对象,而不是走ctxAPI。绕过 Context 的修改运行时生成不了逆函数,撤销链就断了。

热加载后旧事件还在触发。说明清理函数没把事件监听注销。ctx.on的返回值一定要在清理函数里调用,否则监听器会叠加。

模型端点连不上。先确认baseURL是https://taotoken.net/api,再确认环境变量名和apiKeyEnv一致。接入细节可以对照 https://taotoken.net/doc ,里面有完整的错误码说明。

回放时轨迹对不上。追加式日志保证模型可见的都已记录,如果对不上,通常是插件在apply之外异步改了状态。把异步逻辑收进ctx管理的生命周期里。

7. 下一步:从骨架到可用的运行时

骨架跑通之后,你可以按需替换组件。想换模型提供方,改llm插件的配置就行,主循环不用动。想加权限控制,写一个拦截tool:call的插件,在事件流里做校验。想接本地模型,把 provider 换成 Ollama 的兼容端点。

长期做编码类 Agent 的话,可以考虑 Coding Plan,地址是 https://taotoken.net/coding-plan ,配合插件化的运行时,模型和工具都能按项目切换。想先验证模型行为,直接开模型对话 https://taotoken.net/chat 试几轮,确认输出符合预期再写进插件。

这套东西目前是 v0.1 预览版,别急着上生产。但它的设计思路值得每个自建 Agent 运行时的开发者研究一遍:把主循环做成插件、把副作用做成可逆、把依赖做成响应式,这三件事一旦落地,运行时的可维护性会上一个台阶。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询