前阵子我想找个能自己加工具的 AI 客户端。试过几个,基本都卡在同样的地方:能用的工具是平台定好的;记忆存在平台的数据库里,导不出来;想把团队自己的脚本接进去,也没有入口。
后来装了 DeepSeek Harness(下面简称 DSH)。一开始没抱太大期望,直到翻开它的安装目录,看到 190 多个包,一层层点进去,发现真正写 agent 循环的只有 dsh-agent-loop 一个,其他都是可替换的零件。它的 README 里有一句话:新行为写进插件,不要改内核。
就为这句话,我认真看了两周,写了 5 个插件,都开源发到了 npm。这篇文章先说这 5 个插件,再讲 DSH 的结构和它的边界。
我写的 5 个插件
- dsh-memory-git:把长期记忆存成 Markdown,放进用户自己的 Git 仓库。5 个工具,写、搜、读、同步、自检,78 个测试。
- dsh-git:右侧栏的 Git 面板,分支、提交图、暂存、diff、push/pull 都在里面。20 个测试。
- dsh-ai-chat-mcp:把 DSH 变成 MCP 服务端,Claude Desktop、Cursor、n8n 都能调它的会话能力,12 个工具。
- dsh-automation:macOS 的桌面和浏览器自动化,6 个工具,从截图录屏到鼠标键盘和 CDP 浏览器。
- dsh-computer-use:Windows 版,功能对齐,底层换成常驻 PowerShell 工作进程和 UI Automation。
五个插件都在 GitHub 上,MIT 协议,装之前不用改配置,在 DSH 的设置里填包名就行。
印象最深的是 memory-git 的同步。pull --rebase 冲突会停在中间状态,用户下一次写入就莫名其妙失败,查了很久才改成失败自动回滚。自动化插件那边也折腾了不少时间,我一直在 bash 里测截图,怎么都报权限错,后来才反应过来 bash 本身在沙箱里,插件跑在 Host 进程,权限其实没问题。
这些插件是怎么接进去的
上面 5 个插件用到了不同的扩展点。写它们的过程中我把 DSH 的结构摸了一遍,这部分讲清楚它为什么能这样扩展。
DSH 的外壳是一个桌面应用,但真正跑逻辑的是它内部的插件系统,底座是 Cordis。Cordis 是一个 TypeScript 插件框架,提供依赖注入、作用域服务和生命周期管理。它的核心循环只有一个包,其他能力都是挂在扩展点上的插件。
这样一来,写插件基本不用碰内核。我的 5 个插件都是独立的 npm 包,只依赖 Node 内置模块,改完发版,用户装上就能用。
再往里看,它大量使用了同一个模式,官方叫 service 的拆分,我习惯叫它三层抽象:
- Definition 定义能力契约,比如沙箱的 confine(argv, policy)
- Provider 是真正的实现,macOS 用 Seatbelt,Linux 用 bwrap 或者 Landlock
- Consumer 是调用方,比如 bash 工具
这个拆分在实际写代码时差别很大。比如沙箱,今天是 sandbox-exec,换一套实现,bash 工具不用改。它内部到处是这种结构,所以才敢说「新行为写进插件」。
写插件时能用的扩展点,我数下来是四类。
第一类是 Host 插件,也就是给模型加工具。这是最常用的一类,插件跑在 Host 进程里,通过 ctx.tools.register 把工具注册出去。参数是原生 JSON Schema,返回值自己决定怎么渲染,没有 SDK,也没有编译步骤。memory-git、ai-chat-mcp 和两个自动化插件的主体都是这一类。我第一个 hello world 只写了十几行。
js
export const inject = ['tools']
export function apply(ctx) {
ctx.effect(() => ctx.tools.register({
name: 'hello',
description: 'Say hello',
parameters: { type: 'object', properties: {}, additionalProperties: false },
output: {
schema: { type: 'string' },
render: (value) => [{ type: 'text', text: value }],
},
execute: async () => 'hello from my plugin',
}))
}
第二类是 Client 插件,也就是给界面加东西。这里它给的是注入,不是替换:客户端预先声明好一批 slot,插件只能往这些 slot 里塞组件。dsh-git 的面板就塞在右侧栏的标签页和会话标题栏上。这意味着你可以在它现有的界面里加你需要的东西,但不能改它的形态。
第三类是 Skills。Skills 是给模型看的操作手册,不会一次性全塞进上下文,平时只显示一行摘要,用到的时候才加载全文。这个设计挺重要,不然插件一多,光工具描述就能把上下文吃满。
第四类是 MCP。DSH 本身能当 MCP 客户端,接外部的 MCP 服务,工具名会变成 mcp__服务器名__工具名。反过来,它也可以当 MCP 服务端,dsh-ai-chat-mcp 就是干这个的。
另外还有三样东西,不算扩展点,但决定了插件能不能安全地跑起来:沙箱、审批、子代理。沙箱分三档,read-only、workspace-write、danger-full-access,每次调用传策略,后端不可用时直接报错,不会放行。审批是敏感操作先问人。子代理是把大任务拆给独立上下文的子会话。这三样我写插件时都要考虑,尤其是涉及文件写入和网络请求的时候。
它的边界
不过用了两周,我也摸到了它的边界。能力面确实能无限扩展,但另外几层不行。
能力面几乎没上限,因为插件是完整权限的 Node 代码,能起服务、跑原生命令、编译原生 helper、调任意 API。上下文面也能扩展,Skills 按需加载、MCP 接外部、子代理隔离,这些都能缓解「东西太多装不下」的问题。
但交互面是有限的。你只能往官方声明的 slot 里注入,核心的布局、输入框、流式渲染协议都改不了。分发面也不成立,插件是宿主的附属物,想把它做成独立产品,得自建壳、签名、上架。实时链路目前也不支持,它的交互是回合制的请求响应。
还有三条工程上的硬边界。
一是信任。Host 插件跑在非沙箱进程里,能力没有上限,意味着信任也没有上限。装一个第三方插件,本质上是在跑别人写的代码。我现在装插件之前都会看一眼它注册了什么工具、有没有往外发请求。
二是工具过载。工具描述占上下文,工具越多,模型选错的概率越高。这也是为什么它要做 Skills 按需加载。
三是单进程耦合。插件崩溃或者卡住,会拖累宿主。另外原生能力还受系统权限限制,比如 macOS 的辅助功能和屏幕录制。
我自己的感觉是,它更像 VS Code,不像 Chromium。它能在自己的形态里长出各种工具,但长不出形态之外的产品。
关于「自进化」
这个词我一开始也不太信,觉得有点玄。用了两周,我的理解是:它说的不是模型自己改自己,而是能力能沉淀下来。
后来我把它拆成三层。
第一层是工具沉淀。今天写死的操作,明天可以变成插件。我的 5 个插件就是 5 次沉淀,以后换机器、换项目,装上就能用。
第二层是经验沉淀。dsh-memory-git 让偏好和踩过的坑跨会话留下来。这次写插件的命名规范、发布流程、测试怎么跑,下次可以直接复用。
第三层是流程沉淀。Skills 可以存操作手册,子代理可以把大任务拆开,workflow 可以把多步串起来。流程本身也能被固化。
这个过程里我确实大量用 DSH 来写 DSH 插件:让它起子代理分工写模块、跑测试、修 CI,最后我来 review 和发版。但我不想把它说得多神奇,实际用下来,AI 写出来的代码还是要一段段看,测试该挂还是会挂,尤其涉及系统权限和边界的部分,得自己验证。它的价值在于把重复劳动压缩了,不是替你做判断。
还有一点我比较在意:这些能力最后都落在文件里,插件在我的仓库里,记忆在我的 Git 里,技能是 Markdown。就算哪天不用 DSH 了,这些东西还在。这一点比「自进化」这个词本身更重要。
想自己写一个的话
写第一个插件其实只要三步。
package.json 里声明 bundle 和版本准入:
json
{
"name": "@you/dsh-hello",
"version": "0.1.0",
"type": "module",
"exports": { ".": "./index.js" },
"files": ["index.js", "cordis.patch.yml", "README.md"],
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } },
"engines": { "node": ">=20", "dsh": ">=0.2.1-rc.3 <1.0.0" }
}
engines.dsh 是版本准入,不匹配的话插件页会直接提示不兼容,不会装上去才炸。
然后写 cordis.patch.yml:
yaml
- insert:
- id: hello
name: '@you/dsh-hello'
最后是 index.js,就是前面那段 ctx.tools.register。
写完 npm publish,用户在插件页填包名就能装。发布时有两个小坑:npm 开了 2FA 的话,发布需要 bypass 2FA 的 token;另外国内默认 registry 是 npmmirror,它是只读镜像,发布要显式指定官方源。
最后
用了两周,我觉得 DSH 目前最适合两种人:一种是想让 AI 接入自己工具链的开发者,另一种是愿意花时间把重复工作沉淀成工具的人。如果只是想开箱即用、不想折腾配置,那它可能不是最省事的选择。
它现在还在 rc 阶段,插件数量不多,好处是现在写的插件反而容易被看见。
5 个插件的仓库都在这里,欢迎 star 或者提 issue:
- github.com/auraflame-y...
- github.com/auraflame-y...
- github.com/auraflame-y...
- github.com/auraflame-y...
- github.com/auraflame-y...
官方文档:deepseek-harness.github.io/deepseek-ha...
如果你也在写 DSH 插件,欢迎交流,尤其是 Windows 桌面自动化和插件分发这两块,我还在踩坑。