资讯动态

DeepSeek Harness 从零上手:从认识到写出第一个插件

发布时间:2026/9/5 9:46:06 来源:尧图企业网站定制
这篇文章与其说是教程不如说是我的踩坑记录。从认识 DeepSeek Harness到安装、到运行、到写出第一个能用的工具插件每一步都来自真机实测。为了防止你跟我一样绕弯我尽量把当时我卡住的那个点讲出来。全文按照我的理解顺序分三段先弄明白它是什么再把它用起来最后动手真正动手写一个插件。第一部分 · 先搞懂DeepSeek Harness 到底是什么1. 一个简单比喻我发现理解 DeepSeek Harness 只要记住一句话为方便DeepSeek Harness下面都用 dsh 简称Agent Model Harness智能体 模型 运行框架拆开说是Model模型负责想——写代码、回话、推理。这是智能体的那份智力。Harness运行框架负责干——让模型能看懂环境、能调工具、能一直干活。这是它的身体。我在网页版 ChatGPT 上用模型时其实只有模型没有harness。它在封闭的对话框里既看不到本地文件也跑不了本地命令。dsh 多出来的就是这层身体。它让 AI 能在我真实的电脑上动手读文件、执行命令、调用工具、一步步把事推完。官方英文定位是A harness keeps agents working in real-world environments——让智能体在真实环境里持续干活。记这条主线就够了模型是大脑harness 是身体而身体由一堆插件拼起来这就引出这一核心点。2. 一切皆插件不是口号官方口号是Everything is a plugin。头回听我以为只是宣传后来才发现它是架构事实。dsh 建立在Cordis插件系统上。在这个体系里下面这些全部是插件模型models——用哪家模型、怎么调工具tools——读文件、跑命令、搜网页这些模型能用的手脚技能skills——可复用的能力包会话sessions——对话状态怎么存、怎么续沙箱sandboxes——模型能摸文件系统的哪些地方存储storage——数据放哪Agent Loop——智能体循环怎么转调度scheduling——后台任务怎么排UI——连图形界面本身都是我原来以为扩展能力是去改 dsh 源码其实完全不用换模型就是换插件加能力就是加插件想把几个能力拼成新模式就改配置全程不碰源码。Cordis 内核管插件挂载、卸载和依赖。插件之间靠services服务和events事件协作。举个具体的我写一个工具插件只要声明我依赖 tools 服务Cordis 会等 tools 就绪了再加载我的插件。这个细节后面写插件会直接用到。3. 工具是规范值 投影工具是模型在真实环境干活的手。dsh 里每个工具的定义抽象得很干净分两层schema声明入参和返回值的具体形状我叫它规范值render投影把返回值转成模型真正看到的内容我一开始不太理解为什么拆两层。后来想明白了——给程序看的权威结果和给模型看的展示内容是两回事。我的工具可以先返回一个结构化对象程序拿着它精确判断render再把它压成一段文本给模型理解。想换个展示样式完全不用动计算逻辑。这个两层设计贯穿 dsh 整个工具执行管线写插件那节我会亲手碰到它。4. 会话可追踪另一个主张是Every run is traceable。模型看到的一切都会记进一个 append-only只追加会话日志系统提示词、推理过程、每次工具调用和结果、子代理调度、每次上下文注入逐条记。在Trajectory轨迹视图里能按来源把这几样拆开检查。继续对话Resume、分叉fork、搜索search、重放replay全都是基于同一条事件流。这意味着我能随时回放模型每一步为什么这么干、到底看了什么。这套可追踪不是炫技是我敢让它动本地文件的底气。5. 权限和沙箱让 AI 在我电脑上跑命令头一个想的就是安全。dsh 用沙箱管这块。沙箱只管文件系统效果三档由松到严模式允许什么我什么时候用danger-full-access不设限制需要全局操作时workspace-write工作区目录 临时区可写日常干活read-only只读禁止写入只看不动沙箱之外还有审批策略超范围的操作是问我一下还是直接拒绝ask/never。界面上的权限档位本质就是沙箱 审批的组合。我的实践很简单日常 workspace-write只查不改 read-onlyfull access 尽量别碰。6. 有几种跑法dsh 给不同场景配了几套模式Standard——完整工具集文件编辑、shell、文件/网页搜索、技能、规划、目标、子代理、工作流Code——能力不变但工具通过 Code Mode SDK 暴露模型可以用一个 TypeScript 程序把多步操作编排起来Minimal——只有 bash 文件编辑器做模型基准测试用的Creator——检查运行时、在内存里测插件、组合新模式是插件开发者的场地日常用 Standard 就够想折腾插件就上 Creator。第二部分 · 怎么上手动用概念捋过一遍开始动手。这里我尽量把每一步和前面的概念对一下让你知道界面上那个按钮背后到底是什么。7. 装好跑起来先有 Node.jsdsh 的运行底座LTS 22 及以上然后一条命令npx deepseek-ai/dsh web第一次会下载依赖。看到下面这行就是起来了DeepSeek Harness http://127.0.0.1:3080/浏览器打开http://127.0.0.1:3080/主界面出来就算环境就绪。想先装好再启动也行npm install -g deepseek-ai/dsh然后dsh web一个意思。8. 把模型接上dsh 自己不产模型得告诉它用哪家、密钥在哪。去 platform.deepseek.com 注册建一个 API 密钥sk-开头只显示一次当时我立即就复制了回到 dsh右上角设置 → 模型在 DeepSeek 提供方卡片点编辑填密钥保存回主界面对话区顶部能看到模型名比如 DeepSeek-V4-Flash就成了想用更多模型dsh 两种接法添加提供方从内置列表挑OpenAI、OpenRouter、通义千问、月之暗面、智谱等二十多家填密钥即可添加自定义提供方填一个 OpenAI 兼容的服务地址适合本地模型Ollama / vLLM、第三方中转、公司内网网关一句提醒API 密钥就是钱袋子别截图、别提交进 git万一泄露了赶紧去平台吊销重建。9. 工作区与会话工作区就是一个项目目录的持久化记录agent 读文件、改代码、跑命令都以它为根。不选工作区输入框是锁着的——因为 agent 没有地盘没法干活。添加工作区点对话区顶部的选择工作区或侧边栏工作区面板的添加在系统目录选择器里选项目根目录别选 C 盘、用户主目录这种又大又全的确认后工作区进侧边栏输入框解锁会话则是一个独立对话。点左上角新会话就能开一个每个会话有自己的上下文互不干扰。一个工作区可以挂多个会话。10. 第一次任务第一跳指令我建议从总结式开始——只读、安全、立刻见效列出当前工作区目录下的文件并简要说明这个项目是做什么的点发送dsh 的干活过程大致是这样读——调工具读目录、开文件想——模型推理得出结论干——有必要就执行命令、写文件超出权限会先问你交差——给结果对话区会一条条滚动展示工具调用。切到轨迹视图能看到它每一步读了什么、做了什么前面说的可追踪这时候就落地在界面上了。跑完这条你就算会用了。剩下的切换模型、加附件、设目标、子代理、后台任务、工作流都是绕这个循环增强概念没变。第三部分 · 真正动手写第一个插件会开只是会开车自己写插件等于造零件。既然前面懂了一切皆插件那就亲手往 harness 这只身体上加一只手。下面是从零写一个真实能用工具插件的全过程。11. 一个 dsh 插件长什么样在 Cordis 里插件就是一个导出apply函数的模块。dsh 加载时拿上下文对象ctx调它我通过ctx去注册能力。最小形态// greet-tool.js —— 最小插件骨架纯 JS / ESM export const name my-greet-tool // 可选诊断日志标注用 export function apply(ctx) { // 插件主体挂载时调用 console.log([my-greet-tool] apply() ran) }Cordis 接受三种插件形态函数最常见、对象、类想公开服务时用。加工具用函数就够了。两个关键地方**inject**声明依赖的服务。export const inject [tools]等于说等工具注册表就绪了再调我这个 apply纯 JSESM能零配置跑.ts写法要额外参数.js不用插件目录里放一个package.json{ name: dsh-my-tool, version: 0.1.0, private: true, type: module, main: greet-tool.js, dsh: { bundle: { patch: ./bundle.patch.yml } } }这里dsh.bundle是 bundle 清单只有打算装成 bundle那条路才需要它。12. 注册一个工具defineTool光有apply还不够得把工具注册给模型用。dsh 提供了defineTool来规范化工具定义再注册进ctx.tools。完整代码// greet-tool.js —— 一个最小的 dsh 自定义工具插件 import { defineTool } from deepseek-ai/dsh-tools export const name my-greet-tool export const inject [tools] export function apply(ctx) { console.log([my-greet-tool] apply() ran, registering greet tool) return ctx.tools.register(defineTool({ name: greet, // 工具名模型用它发起调用 description: Greet someone by name., // 模型看它决定何时调用 parameters: { // 入参 JSON Schemaexecute 前会校验 name: { type: string, required: true, description: The name to greet }, }, output: { // 输出契约schema 规范值 render 投影 schema: { type: string }, render: (_args, value) [{ type: text, text: value }], }, async execute(args) { // 真正干活 return Hello, ${args.name}! }, })) }defineTool替我做三件事把参数转成 JSON Schema、execute 前校验 args、做冲突检测。注意output里的schema render双层——这就是前面概念 3 讲的分层现在落到代码里了。这套defineTool({ name, description, parameters, output, execute })ctx.tools.register(...)我实测跑通了在 dsh 0.1.0-rc.6 真实启动后apply()确实执行到了注册那一步日志见 14 节。13. 把插件挂进 dsh有三条路先分清两个概念——boot profiledsh --profile web启动的只有web和headless和agent-presetstandard/code/minimal/cordis四份 YAML。加工具 往 web profile 插件树顶层插一个插件。路 A--patch覆盖层写一个insert-greet.patch.yml- insert: - id: my-greet-tool name: file:///D:/dsh-practice/my-tool/greet-tool.jsWindows 下这里有个坑路径必须file:///前缀且盘符小写。我一开始写裸的D:/...或大写file:///D:/...一律报ERR_UNSUPPORTED_ESM_URL_SCHEME。还有一个更关键的坑模块解析用--patch挂file:///路径的插件时dsh 不会帮你解析插件里的裸包名 import。也就是说greet-tool.js里的import { defineTool } from deepseek-ai/dsh-toolsNode 会从my-tool/目录往上找node_modules找不到就中断报ERR_MODULE_NOT_FOUND。全局装的 dsh 不会背这个三包名解析的锅。两种解法的实测结论在插件目录里装一份 dsh-toolsmy-tool/下执行npm i deepseek-ai/dsh-tools然后得用绝对路径 importfile:///c:/...。干净但要在插件里配依赖。改挂载方式走 bundle路 C而不用file:///路径插件会被装进 profile 的 node_modules依赖自然能解析。真 boot 后stdout 出现下面这行标记说明插件加载成功了。这是我验证的核心证据实测输出见 14 节[my-greet-tool] apply() ran, registering greet tool dsh web: http://127.0.0.1:3080路 Bprofile 用户层cordis.patch.yml内容跟路 A 一样只是写进$DSH_HOME/profiles/web/cordis.patch.yml初始是空的[]。boot 行为和路 A 相同。这条路藏在 profile 目录下官方文档只在层序图里顺带提了一句不显眼。路 Cdsh plugin add装成 bundlenode node_modules/deepseek-ai/dsh/lib/bin.js plugin --profile web add D:/dsh-practice/my-tool前提是环境里有 pnpm。注意没声明dsh.bundle的话只会装成普通依赖、不进层栈补上 bundle 清单再remove add一次。bundle 里的插件行要按包名引用- insert: - id: my-greet-tool name: dsh-my-tool14. 验证它到底装上没有这里有个最容易被坑的地方dsh --dump-config不代表加载了。dump 是 boot-free 的只证明配置树里有条目不证明真的加载。要看加载得看 boot 的 stdout。我把验证拆成三层**--dump-config**配置树里出现插件图层只证明有条目加载日志最硬boot stdout 出现标记行[my-greet-tool] apply() ran, registering greet tool真实调用让模型真正触发 greet看返回结果验证①--dump-config看配置树前提有坑dsh 首次运行会在 home 目录写 profile 并建 symlinkWindows 上EPERM很可能挡路。解法是显式把DSH_HOME指到自己有权限的目录# Windows PowerShell把 DSH_HOME 指到自己可写的目录 $env:DSH_HOMED:\dsh-practice\home dsh --profile web --patch D:/dsh-practice/my-tool/insert-greet.patch.yml --dump-config输出末尾会出现 patch 图层实测确认# D:\dsh-practice\my-tool\insert-greet.patch.yml - id: my-greet-tool name: file:///D:/dsh-practice/my-tool/greet-tool.js到这一步只证明配置树有条目插件根本没执行。验证②真实 boot 看加载日志这才是决定性的。bootdsh 0.1.0-rc.6web profiledsh --profile web --patch D:/dsh-practice/my-tool/insert-greet.patch.ymlstdout 出现下面两行就说明插件apply()真执行了、走到了ctx.tools.register(...)Web UI 也正常起来[my-greet-tool] apply() ran, registering greet tool dsh web: http://127.0.0.1:3080这行apply() ran, registering greet tool就是插件真的被加载的实锤。是不是能自己起个最小 Cordis 环境来验证我试过不行。ToolRegistry构造时依赖reflect去找父级 Context裸new Cordis Context()拿不到ctx.tools。完整的 tools 服务是由dsh-agent通过 dsh 自身插件树装配好再注入的。所以验证只能走 dsh 本体 profile boot另起炉灶拼 Cordis 既非官方路径也根本跑不通。验证③真实调用配好模型API key后在会话里让它用 greet 工具向某个名字打招呼模型会调 greet你会看到类似Hello, ${name}!的返回。这层要模型参与不想配模型的话前面验证②已经足够证明注册成功。下面这组截图是我在本机真实跑通的记录dsh 0.1.0-rc.6web profile用 OpenAI 兼容的内网网关接 Qwen3.6-35B-A3B为了不动我电脑上正式环境端口 3080 那个验证实例用的是独立的DSH_HOME快照 独立端口配模型、挂插件全是隔离的。启动后的首页——右侧已选好工作区ai-uview-pro、标准模式、模型 Qwen3.6-35B-A3B输入提示词让它调 greet对话结果——模型完整走了一遍读提示 → Think决定调 greet→ 工具调用 → 读到结果 → 输出。留意消息流里那个闪电图标行Tool call · greet · Alice工具确实被模型选中并执行了点开工具行看详情——这一张最能说明插件定义的效果。能看到入参IN{name: Alice}正是我们parameters里声明的 schema和返回值OUTHello, Alice!正是execute的返回右上角还有个Inspect按钮。前面概念 3 的规范值 投影在界面上就是这个样子隔离验证的小窍门正式跑时用一个独立DSH_HOME比如dsh-test/home配独立端口--port 3081把模型密钥、插件都放这个沙盒里截图演示完不用了直接删目录就行不会弄脏日常用的 dsh。15. Windows 踩坑清单下面都是我实机踩过的按阻断程度排坑现象解法file:///前缀且盘符小写裸D:/...或file:///D:/...报ERR_UNSUPPORTED_ESM_URL_SCHEME用file:///d:/...小写盘符插件裸包名解析失败import deepseek-ai/dsh-tools报ERR_MODULE_NOT_FOUND插件目录装依赖或用 bundle 挂载路 CWindows symlinkEPERM首次运行写~/.dsh报EPERM: symlink设$env:DSH_HOME到有权限的目录Node 版本太老报缺createZstdDecompress/stripTypeScriptTypes升到^22.19.0 || 24.0.0pnpm 缺失dsh plugin add装 bundle 失败先装 pnpm无dsh.bundle装了也不进层栈补package.json的dsh.bundle.ts插件npm 安装版直接跑报错用纯.js或加NODE_OPTIONS--dump-config误判以为 dump 有条目 已加载dump 是 boot-free看 boot stdout模型调用无密钥让它调 greet 报 401/空结果自定义提供方走apiKeyEnv从环境变量读密钥如OPENAI_API_KEY启动前先$env:OPENAI_API_KEY...16. 绕了一圈回到开头写到这第三部分其实和第二部分的概念对上了第 2 节一切皆插件→ 我写的 greet 工具和 dsh 内置的 shell、文件编辑工具在这套架构里是平级的都是注册进ctx.tools的一个条目第 3 节规范值 投影→ 就是我代码里的output.schema和output.render第 6 节Creator 模式→ 就是给插件开发者准备的测试场想动手的话先最小实现把greet-tool.js和package.json抄下来看配置树设好$env:DSH_HOME跑dsh --profile web --patch ... --dump-config确认 patch 图层进树真实 boot 复现dsh --profile web --patch ...抓 stdout 里[my-greet-tool] apply() ran那行稳定后再装 bundle走路 Cdsh plugin add依赖解析更省心等把第一个工具跑起来你大概就有数了——dsh 那批社区插件就是这么来的。顺着前面那条主线读文件、用工具、写插件都是同一件事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价