从零到发布:给 DeepSeek Harness 写你的第一个插件(实战踩坑全记录)
August 15, 2026 · View on GitHub
本文是我从"装好 dsh"到"把 dsh-report-studio 发布进社区目录"的完整复盘。 读完你就能避开我踩过的每一个坑,直接开始写自己的插件。
为什么现在值得做
DeepSeek Harness(简称 dsh)是 DeepSeek 开源的首个 Agent 框架,"一切皆插件": 模型、工具、会话、技能、UI 全部是 Cordis 插件。目前处于开发者预览期, 核心仓库暂不接受外部 PR,官方把贡献路径指向生态:
- 发布插件(仓库打
dsh-plugintopic 即可被发现) - 写教程、答社区问题、报 issue
社区已经有 awesome 目录(900+ 公开插件)。空白仍然很多——比如"会话→工作交付物" (日报/周报/交接文档)一个插件都没有,所以我做了 dsh-report-studio。
起步:两种加载方式
方式一(正式安装,需要 pnpm)
dsh plugin --profile <你的profile> add <包名或本地路径>
它转发给 profile 目录里的 pnpm。没有 pnpm 会直接报错——先装:
npm install -g pnpm
方式二(本地开发,--patch 覆盖层,不需要 pnpm)
按官方教程建一个覆盖层文件:
# my-report.yml
- insert:
- id: report-studio
name: '/绝对路径/dsh-report-studio/index.js'
dsh --profile headless --patch ./my-report.yml "任务"
最小插件骨架(免构建,纯 ESM)
// index.js
export const name = 'my-plugin'
export const inject = ['tools']
export function apply(ctx) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: { type: 'string', required: true, description: 'The name to greet' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return 'Hello, ' + args.name + '!'
},
}))
}
几个关键点:
inject: ['tools']让 Cordis 等工具注册表就绪后再执行apply。defineTool从@deepseek-ai/dsh-tools导入;parameters推断并校验参数,output.schema校验返回值,output.render把返回值变成模型看到的内容。- object 输出 schema 必须写
additionalProperties: true,否则注册直接失败。 字符串参数用enum: ['a','b']约束取值;可选参数直接省略required。
打包成可安装的 bundle
profile 清单里的两个概念(bundle 与 profile)由两份 manifest 区分:
// package.json
{
"name": "dsh-report-studio",
"type": "module",
"main": "index.js",
"files": ["index.js", "lib", "skills", "templates", "cordis.patch.yml"],
"dsh": { "bundle": { "patch": "./cordis.patch.yml" } }
}
# cordis.patch.yml
- insert:
- id: report-studio
name: dsh-report-studio
dsh plugin --profile web add github:你的账号/你的仓库 即装即用。
在工具里读当前会话(官方能力,零私货)
工具执行时通过 exec.agent.session 拿到当前会话:
async execute(args, exec) {
const session = exec.agent?.session
if (!session) throw new Error('report tools require an agent-bound session')
for (const event of session.events) {
// 事件信封:{ seq, time, type, data }
}
}
会话是事件溯源日志,常用事件:
| 事件 | data 内容 |
|---|---|
turn/start / turn/end | 回合边界与结束原因(kind: completed/blocked/error/aborted/max-tokens) |
step/start / step/end | 模型步骤边界 |
user/message | 用户消息(注意 source.kind 区分 direct / plugin 注入) |
assistant/message | 助手消息 + usage(inputTokens/outputTokens/cacheReadTokens/cacheWriteTokens/reasoningTokens) |
tool/call / tool/result | 工具调用与结果;raw arguments 是 JSON 字符串,需要自行 parse |
todo/write | 任务清单快照(最新一次为准) |
跨会话查询还有官方 ctx.sessionQuery 服务(web 默认不挂载,别当硬依赖)。
注册运行时 skill
const skills = ctx.get('skills') // 可选服务用 ctx.get,不要写进 inject
if (skills) {
skills.register({
name: 'work-report', // kebab-case
description: '一句话路由说明',
content: readFileSync(SKILL_FILE, 'utf8'),
source: 'runtime',
provider: 'dsh-report-studio',
})
}
skill 的 Markdown 是模型视角的说明书:何时用、分几步、每步调哪个工具、 硬规则(比如"保存前所有 [[待写:…]] 必须清零")。它按需注入,不占常驻 token。
我踩过的坑(全部实测)
-
--patch绝对路径插件的模块解析:插件里的裸导入(如@deepseek-ai/dsh-tools) 从插件自己的目录向上找 node_modules,找不到就直接启动失败。 官方教程之所以能跑,是因为 scratch-plugin 建在仓库 checkout 里。 本地开发时,给插件目录软链依赖即可:mkdir -p node_modules ln -s ~/.dsh/profiles/node_modules/@deepseek-ai node_modules/@deepseek-ai(正式
dsh plugin add安装不受影响,pnpm 会从 profile 解析 peer 依赖。) -
headless bundle 装不进新建 profile:
@deepseek-ai/dsh-headless依赖的@deepseek-ai/dsh-code-runtime-worker不在 npm 上。 想跑 e2e 就用默认 headless profile + --patch,别新造 profile。 -
pnpm 缺失:
dsh plugin报 "pnpm not found on PATH" 就是没装; 国内网络用镜像npm i -g pnpm --registry=https://registry.npmmirror.com。 -
Node 26 的
node --test tests/行为变了:目录参数会被当模块加载; 直接node --test让它自动发现*.test.js。 -
占位符校验要真校验:我在 report_save 里拒绝残留
[[待写:…]]的报告, e2e 时这个守卫真的拦截了一次模型输出——守卫不是摆设,要测到它生效。 -
DSH 会话日志是多帧 zstd:想读历史会话(做跨会话周报之类)时, Node 内置的
zstdDecompressSync只解压第一帧,而 DSH 的 session.jsonl.zstd 是拼接帧容器——同步解压出来只有一行 header,事件全丢。 正确姿势:先按帧结构扫描(magic 0xFD2FB528 + 帧头描述符 + 块头循环), 再逐帧解压拼接。算法可以直接抄官方 dsh-session-persistence-jsonl 的 format.ts(scanZstdFrames),MIT 协议。
测试与发布清单
- 单测:纯函数(事件提取/模板渲染/哈希/路径安全)用 node:test 全覆盖, 哈希断言用已知向量(sha256('abc'))。
- e2e:默认 headless profile + --patch 跑一次真实任务,验证产物文件与凭据哈希。
- 发布:
gh repo create 你的名字/你的仓库 --public --source . --push, 然后gh repo edit --add-topic dsh-plugin。 - 收录:给 awesome-deepseek-harness 提 PR(中英双语各加一条, 没有合适类目就提案新类目,我的 PR 就是这么干的:新类目"输出与交付")。
一个可照抄的完整例子
dsh-report-studio: 会话 → 日报/周报/交接文档/公众号文章 + 可验证凭据(报告与产物 SHA-256)。 结构一目了然:index.js(宿主插件)+ lib/(纯函数)+ templates/(Markdown 模板)
- skills/(SKILL.md)+ tests/。MIT,欢迎抄、欢迎 PR。
在开源生态里,官方包与社区包同样重要——这是 Harness 官方白纸黑字的态度。 现在空白还多,动手吧。