# 理解 DeepSeek Harness:一篇插件机制的科普读物 > 这是概念篇,讲"为什么这样设计"。代码与命令细节请看同目录的《dsh-plugin-guide.md》(技术参考篇)。 > > 阅读本文不需要编程背景,但读完后你应该能明白:一个插件在 dsh 里是什么、靠什么活下去、如何被别人使用。 ## 一、先搞清楚:Agent Harness 是什么 大语言模型很聪明,但它像一颗**泡在营养液里的大脑**——没有手,摸不到文件;没有腿,跑不了命令;没有本子,记不住昨天做了什么。 **Agent Harness(智能体运行框架)就是给这颗大脑配上的"身体和工位"**:一个能读文件、执行命令、调用 API、记录对话历史的工作环境。模型负责思考,harness 负责让思考落地。 DeepSeek Harness(简称 dsh)就是 DeepSeek 开源的这样一套"工位"。你在终端或网页里和它对话、让它改代码查资料时,背后支撑这一切的就是它。 ## 二、dsh 最特别的决定:没有"内核" 想象两种商场: **传统商场**:有一栋不可动摇的主楼(内核),商户(插件)只能在旁边的铺位营业。你想改造主楼的大堂?对不起,那是开发商的地盘,得动整栋楼的图纸。 **dsh 商场**:**没有主楼**。保安室、配电房、广播站、连"商场管理办公室"本身,全都是一间间普通的铺位。每一间都可以退租、可以换成隔壁竞品、可以在隔壁再开一家。 这就是 dsh 的口号——**一切皆插件**: - 对接模型的代码?插件。 - 工具清单?插件注册出来的。 - 聊天记录的存储?插件。 - 甚至"智能体循环"本身(模型思考 → 调工具 → 再思考的那个循环)?也是插件。 为什么这么执着?因为**"每一部分都能被换掉"意味着每一部分都能被定制**。举例:把"文件系统"插件换成指向远程沙箱的版本,那么所有依赖文件系统的东西——Bash 命令、终端、代码分析——会自动跟着搬进沙箱,一行都不用重写。能力像插头一样换了个插座,电器们毫无感知。 ## 三、那么,"插件"到底是什么? 剥掉所有术语,一个插件就是一份**三句话的自我介绍**: ```ts export const name = 'hello-plugin' // 我叫什么 export const inject = ['tools'] // 我需要什么(可以不填) export function apply(ctx) { ... } // 入驻后我要做什么 ``` 把它想象成**入驻商场的租户**: 1. **签合同时报名字**(`name`); 2. **在合同里写明"我需要水电和网络"**(`inject`)——商场保证这些就位之前,不会让你进场装修; 3. **进场后开始布置**(`apply`)——挂招牌、摆货架,也就是把你的能力"注册"出去; 4. **退租时一切自动恢复原样**——这一点下一节细讲,它是整个设计的魔法所在。 插件可以简单到一个函数,也可以复杂到一个类。形态不重要,重要的是这份"自我介绍"的契约。 ## 四、两个基石概念:服务与上下文 ### 服务:插在墙上的标准插座 插件之间怎么合作?不是互相抄微信,而是通过**服务**。 dsh 里每个能力都占一个公开的、有名字的位置——像一个标准的**墙面插座**: - `ctx.tools` —— 工具登记处 - `ctx.llm` —— 模型接线总机 - `ctx.fs` —— 文件系统接口 - `ctx.sessions` —— 会话档案室 想用工具?你不需要认识"工具部"的任何一个人,只要找到 `tools` 这个插座。**依赖名字,而非依赖具体的人**——这是解耦的第一课:插座后面接的是谁,随时可以换。 ### 上下文(ctx):租户的钥匙牌 每个插件入驻时,商场发给它一块**钥匙牌**(`ctx`)。用这块牌子能做的一切操作——注册工具、监听事件、占用资源——都被记账。为什么要记账?为了下一节的主角。 ## 五、魔法所在:可逆的副作用 传统插件系统最常见的烂尾方式:插件装了又卸,留下几个再也杀不掉的定时器、几个没人认领的监听器,系统越跑越脏,最后只能重启。 dsh 的解法朴素而彻底:**你在钥匙牌(ctx)上做的每一个注册,都会被记账;退租时按账单逐条撤销。** - 注册了一个工具?卸载时自动注销。 - 监听了一个事件?自动移除。 - 起了一个定时器?用 `ctx.effect(() => 返回清理函数)` 告诉商场怎么关掉它。 带来的直接后果是:**插件可以随时加载、随时卸载、随时热替换**——改一行配置,旧的退场、新的进场,不留残渣。上一节说"一切皆可替换",而"可逆的副作用"就是让"替换"敢真的发生而不留后患的地基。 ## 六、插件之间怎么说话:事件 插座(服务)解决"找到对方",但很多场景不适合一对一喊话: - 我只是想**知道**发生了什么("有工具刚执行完"); - 我想**拦截**一件事("这次文件写入,先问过用户没有?"); - 很多插件都想**加工**同一份数据("请求发出去之前,每人加一点自己的料")。 dsh 用**事件**覆盖这些场景,几种模式各像一种生活场景: | 模式 | 生活类比 | 特点 | |---|---|---| | `emit` | 小区广播 | 谁想听谁听,说完就完 | | `bail` | 举手认领 | 排队问"谁能处理?",第一个举手的定案 | | `waterfall` | 流水线 | 每站加工一下**必须传给下一站**;不传,货就被扣下(= 拦截) | | `serial` | 依次签收 | 按顺序过手,每步等上一步 | 其中 `waterfall` 值得多看一眼,因为**"拦截"就是这么实现的**:想做一个权限网关?在流水线上占一站,检查到危险操作就不把货传下去(不调用 `next()`),整个请求到此为止。想做一个观察者?加工完原样传下去即可。一套机制,两种角色。 另一个容易混淆的点:dsh 里有两类"事件"。**实时事件**(如 `agent/*`、`tools/*`)像广播,听过就过了;**会话事件**(如每轮对话、每次工具调用的记录)像**台账**,一笔笔记进日志,回放、恢复、摘要全都从这本台账重建。原则一句话:**凡是模型看过的东西,必须能从台账里查回来。** ## 七、插件怎么"给模型装上手":工具 模型终究要通过某种方式操作世界,这个界面就是**工具**。一个工具是一张标准格式的"操作卡": - **说明书**(description):写给模型看的——这张卡是干嘛的; - **申请表**(parameters):模型按格式填参数; - **执行器**(execute):真正干活的部分; - **回执**(output):干完之后返回什么。 这里藏着 dsh 一个很克制的设计:**回执必须是规范的数据,而不是一段话**。比如"搜索文件"工具返回的是结构化的匹配列表,而不是一段"我帮你找到了三个文件,它们分别是……"的散文。原因:散文是给人看的,数据是给程序用的;从数据生成散文容易,反过来就要靠猜。人类可读的展示(UI 卡片)是独立的另一层,不混进模型的结果里。 还有两个理念值得一提: - **权限不写死在工具里**。"这个操作危不危险、要不要先问用户"属于流水线上的策略站(第六节的 waterfall),工具本身只管干活。工具换到别的环境,策略跟着环境走。 - **说明书自动生效**。工具注册后,它的说明书会自动汇入给模型的提示词——模型"天生"就知道自己有哪些卡可用,不需要额外接线。 ## 八、插件怎么组合成一整套系统 单个插件好理解了,那一个跑起来的 dsh 是怎么拼出来的?答案是**分层穿搭**: 1. **组合包(bundle)**是一袋配好的料:一个 npm 包,里面除了代码还附一张**配置单**(声明"我包含哪些插件、各带什么参数")。它是最小的分发单位——你写好一个袋子,别人拎走就能用。 2. **profile**是一套完整穿搭:按顺序把 `dsh-base`(基础款:模型对接、工具、存储……)和各个组合包一层层穿上,再套上用户自己的 `cordis.patch.yml`(个人偏好层)。 3. **--patch overlay**是出门前临时加的外套:不改任何文件,命令行上指定一下就生效——本地开发插件时天天穿它。 叠加规则简单粗暴:**后穿的覆盖先穿的,而且是整件替换、不做缝缝补补**。这带来一个舒服的生态关系:插件作者提供合理的默认值,用户永远可以在自己的层里整行覆盖,谁也不用求谁改代码。 想看自己身上到底穿了什么?一条 `--dump-config` 命令展开整套配置树,每一层、每一行都清清楚楚。 ## 九、插件怎么到达用户手里:分发与信任 三种主要途径,本质区别在**"用户拿到的是成品还是原料"**: - **npm 发布 / tarball**:成品。作者预先构建好,用户一条命令安装,无需任何额外授权。 - **GitHub 安装**:原料。拉下来的是源码,需要在用户机器上现场构建——这意味着用户必须**显式授权**"允许这个包在安装时执行构建脚本"。 这个授权设计不是流程刁难,而是一次诚实的风险告知:允许构建脚本,等于允许陌生代码在你机器上、且在任何沙箱之外执行。所以官方建议:只授权给看过源码的包,并锁定到具体 commit。**信任应该是一个显式的动作,而不是默认发生的事。** ## 十、把整张地图串起来 最后用一段话把所有概念连成线: > 模型是大脑,**harness** 是工位。工位上的一切设备都是**插件**——它们靠**服务**(标准插座)找到彼此,靠**依赖声明**决定开工顺序,靠**事件**(广播与流水线)协作与拦截,靠**可逆的副作用**做到即插即拔不留渣。模型通过**工具**(操作卡)驱动这些设备,操作卡的说明书自动汇入模型的视野。一个个插件打成**组合包**分发,用户按层叠穿成自己的 **profile**——于是,一套完全属于你自己的智能体工位就成型了。 如果这张地图让你产生了"我想试试"的念头,接下来: 1. **动手写第一个插件**:上游仓库 `docs/user/develop/basic/index.zh.md`(半小时教程,一个文件跑起来); 2. **查阅具体契约**:回到本目录的《dsh-plugin-guide.md》,那里有本文每个概念的精确语法和完整约定。