DeepSeek Harness(DSH)入门

大家对"本地 Agent"这词儿应该不陌生, 用过 Claude Code、Codex或者其他类似的工具。DSH (DeepSeek Harness) 是同一个赛道的新玩家,但它的野心很直白:Everything is a Plugin,一切皆插件。工具、模型、界面、权限,几乎全挂在插件体系上——意思是你不用跪求官方加功能,自己就能给它"接条胳膊"。

这篇文章聊三件事:它到底是个啥、怎么在本地调起来、怎么写出你的第一个自定义插件。

01 先搞清楚:DSH 到底是什么玩意儿

DSH 是 DeepSeek 开源的本地 AI Agent 运行框架,官方仓库 https://github.com/deepseek-ai/deepseek-harness,npm 包名 @deepseek-ai/dsh,MIT 协议开源(连许可证都这么大方)。理解它最省事儿的公式是:

Agent = 大模型 + Harness

模型负责"想",Harness 负责"干"。

说人话:大模型只负责往外蹦字儿,它没长手。Harness 就是那双手:让 AI 读写本地文件、执行系统命令、调工具、按流程把活儿干完。DSH 把这俩焊一块儿,再把整个过程可视化、可控制——你终于能从"它说它改了"变成"我看到它改了"。

和网页版 AI 聊天工具比,差别不是功能多寡,是"问它"和"让它干"的本质区别:

对比项 网页版 AI DSH
文件访问 只能上传附件(传一次烦一次) 直接读写工作区目录,项目文件说改就改
命令执行 想都别想 Shell 命令、跑测试、装依赖,全给你安排
结果落点 烂在聊天记录里 直接写进本地项目,落地即用
过程透明度 中间过程是黑盒,出问题全靠猜 工具调用树全程可见、可追溯
安全机制 本地审批?不存在的 沙箱 + 敏感操作审批
扩展能力 功能焊死 插件随便长

DSH 底层基于 Cordis 插件框架(cordis 4.0.2 那版)。所谓"一切皆插件",就是说读文件、跑命令、搜网页、换模型、改界面,在 DSH 里全是可插拔模块。
官方目前给它贴了 Developer Preview(开发者预览版)的标签,插件 API 还可能给你来个不兼容更新——但反过来说:现在正是偷师它插件机制的好时候。

02 十分钟跑起来:一条命令就行

DSH 基于 TypeScript,跑在 Node.js 上。环境要求:Node.js 20.19+ 或 22+官方 quick-start 原话是 ^20.19 || >=22,所以 20.19 其实就能跑,别被"Node 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

启动后还差两步配置,DSH 才肯干活 —— 它比你家猫还挑。

第一步,喂模型。 DSH 自己不产模型,得配大模型 API 密钥。界面右上角"设置 → 模型"里,把 DeepSeek 开放平台申请的 API 密钥(sk- 开头那一串)粘进去;它也兼容所有 OpenAI 协议接口,智谱、通义千问、本地 Ollama 等 20+ 家厂商都能接,路子野得很。

第二步,选工作区。 工作区就是你让 AI 动土的项目目录,也是所有文件操作的边界。没选之前,输入框是锁死的——它不是在摆谱,是在保护你。点"选择工作区",把启动 DSH 时所在的项目目录加进来选中即可。

然后就能开始第一次对话了。

03 三个核心概念

下面仨是 DSH 的设计核心,搞懂它们才算"会用"而不是"会点"。

工作区:AI 的"自留地"

工作区划定了 AI 能碰哪些文件。添加工作区只是记个目录路径,不会复制或移动你文件,删工作区也不会删本地文件(松口气)。最佳实践:选项目根目录,别拿 C 盘、用户目录这种"全宇宙范围"当工作区——范围越大越慢,翻车概率也越高。

会话:独立的对话上下文

每个会话是独立对话,上下文互不串门;会话归属对应工作区,方便按项目翻旧账。它还支持分叉、归档、搜索,适合多方案对比和事后复盘——毕竟谁还没改崩过几次呢。

工具调用树:可观测的执行过程

这是 DSH 跟"黑盒 AI"最大的区别:AI 每走一步,界面上就留一行,串起来就是完整执行链路。常见工具包括 Think(内部碎碎念)、Bash/Pwsh(跑命令)、Glob(按模式找文件)、Read(读文件)、Write/Edit(写文件)等,每一行都能点开看参数和结果——像极了你妈查你浏览器历史。

DSH 的安全机制也建立在这套可观测性上。权限分三档(官方沙箱术语是 read-only / workspace-write / danger-full-access):

  • read-only(只读):只能看不能改,最安全。
  • workspace-write:可读写工作区并执行命令,日常用这个最香。
  • danger-full-access:整机可操作,手贱党慎用。

超出权限的操作会弹审批窗口,而且每次审批只对当前这一步有效——别想着"批一次管一辈子",DSH 不惯这毛病。

顺手提一嘴:社区有人扒源码发现,官方默认预设表里其实没真 ship 一个叫 read-only 的预设。但"只读 / 工作区写 / 全开"这三档的概念你得门儿清,装懂容易,真出事就尴尬了。

04 本地调试:把每一步都摊开给你看

本地调试是 DSH 比远程方案舒服太多的地方。你不用猜 AI 干了啥,所有执行细节都摊在桌面上,像美剧CSI现场调查。

看工具调用树与性能统计

每次任务结束,工具调用树就是你的"审计日志":每一轮思考、每一步工具调用、命令参数、返回结果,全都能点开。底部还有一行统计:轮数、步数、模型思考耗时、工具执行耗时、缓存命中率、Token 消耗——查性能和成本问题基本靠它,比你记手账靠谱。

用 --dump-config 检查实际加载的插件树

DSH 的运行配置是多个 Bundle Patch 叠出来的一棵插件树。要是你装了插件却没生效,先别急着砸键盘,用这条命令确认插件真进配置里了:

dsh --profile web --dump-config

在输出里搜插件名,能搜到就说明 Bundle → Patch → Profile 组合成功了。

插件开发的本地调试:--patch

开发插件时,反复装到 Profile 太折腾。DSH 支持用 --patch 临时加载本地插件文件,改完代码刷一下就生效:

pnpm dsh web --patch ./my-plugin/cordis.yml

注意:本地 Patch 加载插件时,插件路径必须写绝对路径——因为插件解析是基于 Profile 环境的。写相对路径?它不会报错,但会静默失败,留你一个人对着黑屏怀疑人生。

推荐工作流:写代码 → --patch 调试 → 构建 → plugin add 正式安装 → dump-config 验证 → 正式运行。别跳步,跳步必踩坑。

05 自定义插件:写出你的第一个 Tool

这是 DSH 最值得玩的部分。
先记住一句话:在 DSH 里,插件就是一个导出了 apply() 方法的 TypeScript 模块。

import type { Context } from '@deepseek-ai/cordis'

export const name = 'my-plugin'

export function apply(ctx: Context) {
  // 在这里注册插件能力
}

DSH 加载插件时会调 apply(ctx),把 Context 对象塞给你。真正要啃的只有四个概念:

概念 作用
name 插件名称
inject 声明插件依赖的服务(如 'tools'),Cordis 等依赖就绪后才执行插件
apply 插件生命周期入口,DSH 加载时调它
ctx 插件体系核心,通过它注册工具、监听事件、访问服务

最常用的场景是给 Agent 加工具。
下面这个 text_stats 工具,让 AI 能数一段文本的字符数、行数、单词数——没错,就是个"数数"工具,但它是你长出的第一根手指:

import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'

export const name = 'text-stats-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(
    defineTool({
      name: 'text_stats',
      description: '统计一段文本的字符数、行数和单词数',
      parameters: {
        text: { type: 'string', required: true, description: '要分析的文本' },
      },
      output: {
        schema: { type: 'string' },
        render: (_args, value) => [{ type: 'text', text: value }],
      },
      async execute(args) {
        const text = args.text
        const characters = [...text].length
        const lines = text.length === 0 ? 0 : text.split(/\r?\n/).length
        const words = text.trim() ? text.trim().split(/\s+/).length : 0
        return JSON.stringify({ characters, lines, words })
      },
    }),
  )
}

defineTool 的结构很像 OpenAI 的 Function Calling:

  • parameters 告诉模型"这工具要吃啥参数",
  • execute 才是真干活的地点;

模型决定调用时,DSH 按你声明的 schema 校验参数再传进去。注册的 Tool 会自动并进 system prompt,插件卸载也自动注销——来去自如,不拖泥带水。

除了加工具,插件还能监听和拦截生命周期事件。比如给所有工具调用加道权限门,禁止执行 rm -rf /——毕竟 AI 一时上头,你可不想到时候对着空硬盘哭:

import type { Context } from '@deepseek-ai/cordis'

export const name = 'permission-gate'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.on('tools/pre-execute', async (exec, next) => {
    const cmd = exec.arguments?.command ?? ''
    if (exec.name === 'bash' && cmd.includes('rm -rf /')) {
      return { kind: 'deny', reason: '禁止 rm -rf /,别问,问就是不让' }
    }
    return next()
  })
}

⚠️ 这里有个新手最爱写错的点:tools/pre-execute 的决策只有三种——allow(调 next() 放行)、deny(reason)(拒绝)、ask(reason?)(转一次性人工审批)。拒绝必须返回 { kind: 'deny', reason: '...' }。那些写成 { allowed: false, reason: '...' } 的,框架根本不认,等于这道门形同虚设——你以为锁了门,其实只是把"请勿打扰"的牌子挂门把手上。

官方给的工具扩展点包括 tools/pre-execute、tools/execute、tools/post-execute、tools/result——权限控制、日志、重试、超时、审计,都能在这些环节插进去。

用脚手架快速创建插件项目

不想从零手写配置的话,社区有脚手架,一条命令生成插件项目(这玩意儿还真存在,不是我编的):

npx create-dsh-plugin@latest dsh-text-stats -t tool

生成后进目录装依赖,改 src/index.ts 里的代码,构建:

cd dsh-text-stats
pnpm install
pnpm run build

一个能发布的 DSH 插件包,三个关键文件:src/index.ts(插件逻辑)、package.json、cordis.patch.yml(把插件插进 DSH 插件树的声明)。

值得敲黑板的一点:package.json 里必须声明 dsh.bundle,否则即使装成功,插件也可能只是个普通依赖,不会自动变成 DSH 的组合层:

{
  "dsh": {
    "bundle": {
      "patch": "./cordis.patch.yml"
    }
  }
}

把插件安装到 Profile

Profile 可以理解成一套 DSH 运行环境,官方内置 web、headless 等。调试通过后,正式安装到 Web Profile:

dsh plugin --profile web add ./dsh-text-stats

安装后建议先 dsh --profile web --dump-config 确认插件进了插件树,再启动。然后在对话里直接对 AI 说"用 text_stats 工具统计这段文本",验验工具是否生效。

插件开发完,可以发到 npm(包名建议带 dsh 前缀),并在 GitHub 仓库加个 dsh-plugin 的 Topic,方便社区捞到你。

06 新手最容易踩的 5 个坑

1. 忘记声明 inject。 用了 ctx.tools 却没写 export const inject = ['tools'],插件会一直卡在 PENDING 状态装死。Cordis 按依赖决定加载顺序,不按你配置文件里的顺序——它说了算,不是你。

2. package.json 缺 dsh.bundle。 插件"装上了"却永远不生效,大概率是没声明 bundle patch,包只是个普通 npm 依赖,躺在那儿当吉祥物。

3. 本地 --patch 用了相对路径。 本地 Patch 加载插件要求绝对路径,相对路径会静默解析失败——不报错,但啥也不加载,专治各种不服。

4. 装完插件不 dump-config。 养成习惯,装完先 dump-config 确认插件真进了插件树再启动,能省下大把抓瞎的时间。

5. 同一个插件加载两次。 已经用 plugin add 装过 Bundle,又在 cordis.patch.yml 里手动 insert 同一插件,会喜提 duplicate loader entry id。正式 Bundle 装完就别手贱再手动 insert 了。

另外提醒一句:DSH 目前还是 Developer Preview,插件 API 可能来个破坏性更新。自己写插件时依赖要锁版本,别长期用 latest;升级前先做兼容测试,别拿生产环境练手。

结尾:先跑起来,再决定要不要跟它过日子

回到开头的问题:DSH 值不值得上手?我的建议分两步判断。

第一步,先跑通"它是什么":装好 Node,一条命令启动,选个工作区,让它帮你做件真实的、低风险的小事——比如给项目写 README、理理目录结构。如果这流程你觉得顺、可控、看得懂,再进第二步。

第二步,再玩"它还能干啥":从给 AI 加一个工具开始,理解万物皆插件的机制。你不必成为插件专家,只要搞懂 name、inject、apply、ctx 这四个词,DSH 对你来说就不再是固定功能的工具,而是个能按你需求自己生长的平台。

一个朴素的判断标准:如果一个工具让你愿意为它多装个环境、多读几页文档,那它通常值得留;如果只是图个新鲜,它迟早回收藏夹吃灰。DSH 目前最勾人的地方,是它第一次把"AI 到底干了啥"这件事,摊开给你看。

posted @ 2026-09-09 11:43  大卫小东(Sheldon)  阅读(81)  评论(0)    收藏  举报