前言
DeepSeek Harness(DSH)最近在中文 AI 圈火得有点快。它的设计思路里有一条特别值得关注:把「一切能力」都做成插件。这意味着只要你会写 JavaScript,就能给一个 AI Agent 加新工具、新面板、新接口——不需要 fork 整个项目。这篇文章就是顺着这条思路走一遍:从一个最朴素的「打招呼」插件开始,做到一个真正能用的「定时提醒」插件,并最终让它在 DSH 里跑起来。DSH 插件是什么
用一句话讲:DSH 插件就是一个返回 Cordis Plugin 对象的 JavaScript 函数,由 Cordis 组合系统装到 DSH 进程里,可以订阅事件、调用服务、注册工具、注册 UI 面板。- 插件既能跑在 Node 端(Host),也能跑在浏览器端(Client);
- 插件的「权限」是显式授予的——它不会默认接管你的系统。
准备:环境与工具
- DSH 本体(npx @deepseek-ai/dsh web)
不需要 fork 仓库,不需要懂 Rust,写普通 JS 就行。我用的是 pnpm,但 npm / yarn 也都能用。第一个插件:一个简单的「打招呼」
我们先写一个最朴素的 Host 插件,让它在 DSH 启动后向控制台打一声招呼。完整代码长这样:return {
apply(ctx) {
ctx.effect(() => {
console.log('[hello-dsh] plugin loaded');
return () => console.log('[hello-dsh] plugin unloaded');
});
},
};
把这串代码塞到一个 hello-dsh.js 文件里,然后用 cordis_define 工具把它注册成 Package,再用 cordis_run 启动。打开 DSH 的终端,你应该能看到 [hello-dsh] plugin loaded。如果没看到,别慌——先确认你的 DSH 是 dev 模式还是打包模式;打包模式下 console.log 会被吃掉,改成 ctx.logger.info 就行。第二个插件:定时提醒(实战)
打招呼只是热身。下面这个插件才是正文。它会做三件事:- 暴露一个 schedule_add 工具给 Agent(添加定时任务);
- 暴露一个 schedule_list 工具给 Agent(查看所有任务);
- 在任务到期时,触发 DSH 的事件总线,让前端弹一条提醒。
return {
inject: ['harness'],
apply(ctx) {
const tasks = [];
ctx.harness.handle('schedule_add', (req) => {
const id = crypto.randomUUID();
tasks.push({ id, ...req });
setTimeout(() => {
ctx.scope.emit('schedule:fire', { id, ...req });
}, req.delayMs ?? 0);
return { ok: true, id };
});
ctx.harness.handle('schedule_list', () => tasks);
},
};
把这段代码通过 cordis_define + cordis_run 加载之后,Agent 就能调 schedule_add 了。比如告诉它「5 分钟后提醒我喝水」,它会自己拼出 { delayMs: 5*60*1000, message: '喝水' } 这种参数传进来。下一步是让前端能「看到」任务触发。我们再加一个 Client 插件:return {
apply(ctx) {
ctx.host.on('schedule:fire', (payload) => {
new Notification(payload.message ?? '提醒', {
body: payload.message,
}).show();
});
},
};
发布与加载
- 本会话试用:cordis_define + cordis_run,改完用 cordis_run update 切换版本;
- 团队共享:把 Package 提交到一个 Git 仓库,团队成员用 cordis_run + URL 引用;
- 持久化:把插件放到 ${DSH_HOME}/.agent-presets/<id>/ 下,写成 preset,下次启动 DSH 自动加载。
我自己的惯例是先用本会话跑通,确认无坑后写成 preset——这样下次 DSH 升级不会丢。踩坑经验
写 DSH 插件有几个最容易翻车的地方,我列一下:- ctx.serviceName 不要访问未声明的 Service。要么 inject: [...],要么用 ctx.get('serviceName') + 检查 undefined;
- 副作用必须用 ctx.effect() / ctx.on()。直接 setInterval 会导致 stop / update 不掉;
- 不要在 Client 代码里用 TypeScript / JSX / import。DSH 不做编译;
- 数据不要用 JSON.stringify 序列化 Services / Events。它们是活对象,会丢失上下文。
写在最后
DSH 插件开发的核心体验是:把「我想要一个 AI 工具」这件事,缩短到一杯咖啡的时间。你写 30 行 JS,调一个 cordis_run,一个新的工具就出现在 Agent 的工具箱里了。如果你也有想法但不知道怎么落地,先从你最近手动重复过的那个动作开始——它大概率就能变成一个插件。