DeepSeek Harness 插件入门

本文是面向新手的插件入门:不预设任何框架知识,从"是什么"讲到"能干什么"、"怎么装"、"怎么学"。所有结论实测于 DSH 0.1.2-rc.1;想看拆到零件级的深度版,见姊妹篇《以学习为目的:我用一个桌宠插件,把 DeepSeek Harness 的插件系统拆到了零件级》。

一、插件是什么?

一句话:插件就是一个 JS 文件(或 npm 包),它告诉 harness"我是谁、我依赖什么、我要干什么",然后被装进正在运行的 harness 进程里,获得和内核平起平坐的能力。

打个比方:harness 是一部手机,官方出厂装好了电话、相机、浏览器(agent 循环、工具、会话管理)。插件就是 App——你自己写的、别人发布的,装上就多一个能力,卸掉就恢复原样,而且 App 崩了不影响手机系统。

官方自己的几十个能力包(dsh-skill、dsh-terminal-bash、dsh-webhook-github……)全部都是用同一套插件机制写出来的。没有"内核特权代码"和"插件代码"的区别——你写的插件和官方内核在架构地位上完全平等。

这带来一个初学者最容易忽略的事实:你不需要任何特殊许可。没有"签名""审核""白名单"——一个 file:// 路径指向你硬盘上的 JS 文件,它就是合法的插件。门槛低到"一个文件",天花板高到"订阅智能体的每一次心跳"。


二、用来干嘛?(三类用途)

你想做的事

用插件

让 harness 看到外部世界

订阅内部事件(智能体每调一次工具、每回一条消息都产生事件)、接 webhook(比如 GitHub PR 触发评审)、接 MCP 外部工具

让 harness 多一双手

注册新工具给模型用——比如一个"查公司内部数据库"的工具、一个"发钉钉消息"的工具

让 harness 多一个器官

提供新服务挂到总线上,供其他插件消费;或者改掉现有行为(换人设、换沙箱策略、换界面)

真实例子(官方包里现成的):dsh-webhook-github 收到 PR 事件自动让智能体评审代码;dsh-schedule 让智能体定时干活;本文作者写的 dsh-pet-state 让桌面小猫实时看到智能体在干嘛。

如果非要提炼一个总纲:插件回答的是"harness 和世界之间缺一层胶水"的问题。世界是多样的(你的公司 API、你的手机、你的桌面、你的代码仓库),harness 的内核不可能内置所有对接——于是它把对接面做成了标准插槽,插件就是你自己造的插头。


三、插件长什么样?(最小形态就 8 行)

// my-plugin/index.js
export const name = "my-plugin";       // ① 我是谁
export const inject = [];              // ② 我依赖哪些服务(可以先空着)
export function apply(ctx) {           // ③ 我要干什么
  console.log("我装进 harness 了!");
}

就这三样,业内叫"三件套":

  • name:身份证。框架靠它识别插件、管理装卸与热更新。
  • inject:购物清单——"我需要 sessions 服务、tools 服务",框架会在启动你之前把这些服务备好递到你手上。你不用管它们从哪来、加载顺序谁先谁后。
  • apply(ctx):干活的入口。ctx 是一根"总线",上面挂着所有服务(ctx.sessions 会话管理、ctx.tools 工具注册表、ctx.skills 技能表……)和所有事件。

一个重要的心智校准:apply 不是"被调用的函数",而是"装配动作"。你在里面干的事只有一类——往 ctx 总线上挂东西(挂监听器、挂服务、挂工具),挂完就退场,之后的世界是事件驱动的。如果你的 apply 里写了个死循环,你写的不是插件,是抢占宿主线程的寄生虫。

一个最小但能说明问题的 demo

8 行骨架只证明了"能装上",还没证明"有用"。下面这个 demo 只多 7 行,却把插件最有魔力的能力——实时看见智能体的心跳——变成了可验证的事实:

装上(下一节的两行 YAML),然后正常和智能体聊一句,再看 /tmp/heartbeat.log:

2026-09-14T03:12:01.412Z  user/message        ← 你开口
2026-09-14T03:12:01.473Z  turn/start
2026-09-14T03:12:01.483Z  step/start
2026-09-14T03:12:08.826Z  reasoning-chunks    ← 它在想
2026-09-14T03:12:19.995Z  tool/call           ← 它动手了
2026-09-14T03:12:20.141Z  tool/result
2026-09-14T03:12:54.338Z  text-chunks         ← 它在回答你
2026-09-14T03:13:56.029Z  turn/end            ← 一轮结束

这个 demo 说明问题的点在于:你写的 15 行代码,此刻运行在 harness 进程内部,和 agent 循环共享同一个事件总线——不需要轮询、不需要猜、不需要任何"接口对接",智能体的每一次思考、每一次调用工具,都主动流经了你写的函数。这就是插件和"从外部观察"的本质区别。

两个小讲究:日志写文件而不是 console.log,是因为 web profile 的 stdout 你看不到,文件是最直观的验证通道;appendFileSync 而非打缓冲,是因为 demo 追求"保存即见",真实插件应该用节流+原子写(第七节的桌宠会示范)。


四、怎么安装?

这是 DSH 插件系统最友好的地方——不用打包、不用发布。

方式一:本地文件(学插件就用这个)

在 profile 的补丁文件 ~/.dsh/profiles/web/cordis.patch.yml 里加两行:

- insert:
    - id: my-plugin
      name: file:///你电脑上/my-plugin/index.js

保存,约 2~5 秒后插件就活在运行中的 harness 里了。零安装命令,一个 file:// 路径直指你硬盘上的 JS 文件。

方式二:npm 包(用别人写好的插件)

dsh plugin --profile web add @deepseek-ai/dsh-webhook-github

然后在同一个 patch 文件里 name 写包名即可。

方式三:什么都不装,只改配置

很多定制根本不需要写 JS——patch 文件本身就能改配置:换系统提示词、改端口、开关某个功能。可以理解为"插件系统提供了一个纯配置的档位"。

卸载 = 删掉那两行 YAML。热更新 = 改了插件代码后,把 URL 加个版本号 index.js?v=2 再递增(这是实测出来的最大的坑:不改 URL 的话 harness 会用缓存的旧代码——loader 只在模块 URL 变化时才重新加载,内容变化它不管)。


五、具体能干什么?(能力清单)

进入 apply(ctx) 之后,你手上有这些牌:

能力

怎么做

例子

订阅智能体的一切动态

ctx.on("session/event", (session, event) => {...}, { global: true })

每次工具调用、每条消息、每次思考都流经这里

注册新工具给模型用

用 defineTool 挂到 ctx.tools

让模型能"查天气""操作你的智能家居"

注册新命令给用户用

类似地挂到用户命令层

类似斜杠命令

提供新服务

ctx.你的服务名 = ...

别的插件可以

按野心从小到大,可以分成四级,这也是作者推荐的学习纵深:

  1. 观察者(只读):订阅事件流,看清智能体的每一次心跳。桌宠就是这一级。
  2. 造工具:用 defineTool 给模型加一双手。
  3. 拧旋钮:连代码都不写,用 patch 改配置改变 harness 行为。
  4. 造地基:提供新服务让别的插件消费——到这一级你在给生态写代码。

一个直观的能力天花板参照:桌宠插件——订阅全局事件流、跑状态机、写文件、定时器、可热重载——总共 160 行,其中真正核心的逻辑不到 60 行。

框架同时也在替你兜底:插件监听器抛错只记一条警告、会话照常跑;插件的文件写入照样过沙箱审批。扩展性从不以稳定性为抵押——这是你敢随便写、随便试的底气。


六、它和 Skill 是什么关系?

Skill(技能)是 DSH 的另一个扩展机制,经常和插件放在一起比。一句话分清:

Skill 改变模型"知道什么"(一份指令文档),Plugin 改变运行时"能做什么/看得到什么"(一段真代码)。

Skill

Plugin

本质

Markdown + frontmatter

JS 模块

进入哪里

模型的上下文(按需加载的指令)

harness 进程(真实运行的代码)

适合

部署流程、代码规范、回复格式

webhook、新工具、事件监听、新服务

触发

模型按目录自主判断,或用户 /name

随 harness 启动,事件驱动

想让模型学会你的部署流程 → Skill。想让 harness 收 GitHub webhook、多一个查数据库的工具 → Plugin。

有意思的是它们共享同一套地基:skill 的注册表本身就是某个插件(dsh-skill)提供的 ctx.skills 服务。所以学懂插件,skill 对你而言只是"内容",而不是另一套系统。


七、毕业实例:我刚刚做的桌面宠物

用这套知识实际造了个东西——一只能看到智能体在干活的桌面 ASCII 猫。它思考时头顶冒泡泡,跑 bash 时抡🔨,单轮调 15 次工具会流汗💦,工具报错炸毛❗,两分钟没人理就蜷起来💤。这就是第三节那个 15 行 demo 的完全体,总共约 160 行插件 + 160 行 Swift 渲染。

它由三个文件构成

① dsh-pet-plugin/index.js      插件(本文全部知识的应用)
② ~/.dsh/profiles/web/cordis.patch.yml   挂载(第四节的两行 YAML)
③ desktop-pet/Sources/main.swift  显示端(与 harness 零耦合,只读一个 JSON)

① 插件端:三件套 + 状态机 + 原子写

状态机每变一次,就把完整状态原子地写进 ~/.dsh/pet-state.json(250ms 节流 + tmp/rename 原子替换——读端是另一个进程,绝不能让它读到半个 JSON)。

② 挂载:第四节的两行 YAML 原样上场

- insert:
    - id: dsh-pet-state
      name: file:///…/dsh-pet-plugin/index.js?v=4

那个 ?v=4 就是第四节说的热更新坑——这是我第四次修改插件代码,每次递增一次。保存 YAML 后约 3 秒插件上线。

③ 显示端:另一个进程,只认识 JSON

Swift 写的猫窗口每 0.125 秒读一次 pet-state.json,按状态换 ASCII 帧(idle 眨眼、work 敲键盘、shock 炸毛……),按工具名举道具(bash🔨 write🖌️ web_search🔭)。它对 harness 一无所知——不知道 Cordis、不知道事件流,只认识那份 JSON 的字段契约。

这个设计里藏着的插件哲学

发布端与显示端解耦,靠一个 JSON 文件握手。 为什么不直接在插件里画窗口?因为插件的宿主是 harness 进程,显示端有自己的生命周期(退出、重启、多开);而一个磁盘上的 JSON 文件是最朴素的进程间契约——谁都能读,harness 升级不受影响,你甚至可以再写一个浏览器版、手机版猫,读同一个文件。

它顺便验证了插件的边界纪律:插件监听器抛错只警告不崩溃(有一次状态机写崩了,harness 和会话毫发无伤,猫坏了修猫就行);插件文件写入照样被沙箱审批拦截过一次(patch 文件在工作区外);热更新让猫的状态机迭代了四个版本,harness 一次都没重启。

从第三节的 15 行 demo 到这里的 160 行,多出来的每一行——状态机、节流、原子写、effect 清理、版本号——都不是插件知识,而是普通工程知识。这正是入门该建立的手感:插件的学习曲线短,门槛之后全是熟悉的工程世界。


八、动手前的最后一张图

把全文收进一张图:

你写的 JS 文件(name / inject / apply 三件套)
        │  一行 YAML(patch 文件里的 file:// 或包名)
        ▼
正在运行的 harness 进程
        │  框架按 inject 备好服务,调用 apply(ctx)
        ▼
ctx 总线 ──┬── 订阅事件:智能体的每次思考/调用/回复流经你
           ├── 注册工具:模型下一轮就能用你的工具
           ├── 挂新服务:别的插件可以依赖你
           └── ctx.effect:卸载时自动回收,不留僵尸

记住三个关键词就够了:三件套(你是谁/要什么/干什么)、总线(一切能力的交换所)、一行 YAML(从硬盘到进程的全部旅程)。第七节的猫就是这三个关键词乘以一点工程耐心。


实测环境:DSH 0.1.2-rc.1 / Node 24 / macOS 26(Apple Silicon)。深度续篇:《以学习为目的:我用一个桌宠插件,把 DeepSeek Harness 的插件系统拆到了零件级》;概念七问版:《DSH 插件入门:用七个问题和一只猫,建立你的心智模型》。