数据STUDIO

DeepSeek Harness 上手实操:一切皆插件

Image

2026 年 8 月 13 日,DeepSeek 开源了它的第一个 Agent 框架 dsh(DeepSeek Harness)。

Image

项目出来之后热度涨得很快:上线 8 天 Star 破 10 万,现在已经超过 20 万。很多人第一反应也很自然:DeepSeek 这是也要做一个 Claude Code?

我一开始也是这么理解的。

但把项目装下来、翻完架构,再自己跑了一遍插件之后,我反而觉得,“DeepSeek 版 Claude Code”这个说法把它说小了。

dsh 确实可以拿来做编码 Agent,但它真正想解决的不是“怎么再做一个编码助手”,而是更底下一层的问题:

一个 Agent 到底应该由什么组成?这些东西能不能拆开、替换,再重新组合?

Image

在 dsh 里,模型不是中心。工具、任务规划、调度、沙箱、存储、Agent Loop,甚至模型本身,都只是 Harness 里的一个部件。

这也是为什么它最核心的那句 slogan 不是“更强的 Coding Agent”,而是:

Everything is a plugin。

这篇就顺着这句话往下拆:dsh 到底是什么、它和 Claude Code 是什么关系、“一切皆插件”具体落到了哪、自己写一个插件是什么体验,以及这么高的热度下面,现在到底适不适合真拿来干活。

Image

我的结论先放前面:

值得装,值得研究,尤其值得读它的架构;但目前还不建议直接接生产。

01它是什么:Agent = Model + Harness

先把定位说清楚。

DeepSeek Harness 不是模型,也不只是又一个 Claude Code,它首先是一个 Agent 框架。

它把 Agent 写成一个很简单的公式:

Agent = Model + Harness

Model 就是我们熟悉的 Claude、DeepSeek、GPT、Qwen 这些模型。

Harness 则是模型之外那一大堆真正让 Agent“能干活”的东西:工具、任务规划、调度、沙箱、存储、循环、会话,以及各种执行能力。

很多现有编码工具,其实已经把这两层揉得非常紧。比如 Claude Code,本质上就是 Anthropic 把自己的模型、工具链和 Agent 交互一起做成了一个完整产品。

dsh 走的是另一条路。

它把 Model 从中心位置拿下来,变成一个可以替换的组件。模型可以换,工具可以换,Session 可以换,Sandbox 可以换,理论上连 Agent Loop 都可以换。

这也是它和 Claude Code 最根本的区别。

有人做过实测(阿里云开发者社区的一整夜实测):dsh 自带的 provider 可以直接把 Claude Code 和 Codex 当子代理收进来——它会解析安装在 PATH 里的二进制,让它们去执行具体子任务。

这个关系其实很有意思。

因为它说明一种合法配置完全可以是:

外面跑 dsh,里面让 Claude Code 或 Codex 干某一段任务。

所以“我要不要从 Claude Code 换到 DeepSeek Harness”这个问题,多少有点问偏了。

Claude Code 更像一个已经打磨好的成品工具;dsh 更像把 Agent 背后的 Harness 层单独拆出来,交给开发者自己组装。

甚至可以说,一个偏“产品”,一个偏“底层框架”。

这也解释了为什么 dsh 第一眼看起来没那么顺手,但继续往架构里看,会越来越有意思。

DSH Desktop 支持多家模型服务商。

目前能接入 DeepSeek、OpenAI、Anthropic、Google Gemini,以及 xAI、Kimi、MiniMax、智谱 GLM 等主流服务。

图片

手里有多个模型账户时,这个设置比较方便。写代码用一种模型,处理长文档换另一种,费用或速度有变化,也能随时调整。

02一切皆插件:这句话在 npm 层是 116 个包

Image

“Everything is a plugin” 这种话我一般不会第一时间全信。

插件化大家都会说,最后到底是“支持几个扩展点”,还是“系统本身就是由插件拼起来的”,差别其实很大。

最简单的办法就是装一遍。

我在本机安装 @deepseek-ai/dsh,装完之后 node_modules 大约 288MB,里面躺着 116 个独立的 @deepseek-ai/dsh-* 包。

一个社区作者读源码后给出过另一组更细的数据:359MB、555 个 package manifest、255 个顶层依赖目录,其中 196 个带 @deepseek-ai 作用域。

他有句话形容得很准:

这不像“一个带插件的框架”,更像“一个恰好能启动的小型包仓库”。

比如:

dsh-llm
dsh-session
dsh-tools
dsh-sandbox
dsh-subagent
dsh-agent-loop
...

几乎每项能力都单独拆成了包。

再跑:

dsh --profile web --dump-config

它会把当前环境真正装配起来的插件树整个打印出来。

我本机跑出来是 503 行,前面一部分大概这样:

- id: llm
name: '@deepseek-ai/dsh-llm'
- id: session
name: '@deepseek-ai/dsh-session'
- id: agent
name: '@deepseek-ai/dsh-agent'
- id: agent-default-model
name: '@deepseek-ai/dsh-agent-default-model'
config:
provider: deepseek-official
model: deepseek-v4-flash

模型适配器、会话存储、Agent、默认模型,都是一排 id + 包名。

到这里,“一切皆插件”就不是一句宣传话了。

它是真的在把整个系统尽可能拆成插件。

当然还内置了大量的插件,我们可以尽情的筛选安装,好用的插件的确能提高效率。

图片

这也是所谓“无特权内核”最直观的一层意思:

没有哪个模块天然就是不能碰的核心。

你想扩展 dsh,通常不是先 fork 核心,然后在里面打 patch;而是往现有插件树里再挂一个插件。

听起来只是开发方式不同,但对 Agent 这种还在快速变化的系统来说,差别其实很大。

因为今天你想换模型,明天可能想换 Sandbox,过两周又想换掉某一段 Agent Loop。如果每次都要去改一个巨大的核心仓库,最后很容易变成“能跑,但没人敢再动”。

03真正撑住插件体系的,是 Cordis

当然,光把代码拆成一百多个 npm 包,不代表架构就一定好。

如果这些包背后还是大量全局状态、事件监听和互相引用,那只会从“一个大项目难维护”变成“一百多个小项目一起难维护”。

dsh 这套插件体系真正关键的东西,在它底下的元框架 Cordis。

Cordis 对应一篇论文:《A Programming Paradigm for Spatiotemporal Composability》。

Image

名字有点学术,但核心可以压成两个方向:

时间上的可逆,空间上的依赖响应。

先看时间。

Cordis 有一个很关键的概念叫 revertible effects,可逆效应。

Image

简单理解就是:

插件往系统里加了什么,就应该知道以后怎么完整撤回来。

比如一个插件注册工具、监听事件、创建计时器。

传统做法通常是先:

register()
addListener()
setInterval()

卸载时再手动:

unregister()
removeListener()
clearInterval()

问题是,只要有一个地方忘了清理,就会留下脏状态。

Cordis 的思路是让运行时去追踪这些 effect。插件被卸载时,相关 effect 一起 unwind。

所以“装”和“卸”不再是两个互不相关的动作。

再看空间。

Cordis 里另一个概念叫 reactive coeffects。

名字有点绕,但落到 dsh 插件代码里其实很直接:

export const inject = ['tools']

这句话就是在说:

我这个插件依赖 tools。

等 tools 准备好以后,再加载我。

也就是说,插件不是假设“整个世界早就准备好了”,而是明确声明自己依赖哪些能力。

这两件事合起来之后,dsh 才敢把系统拆得这么散。

对工程师来说,最实际的好处就是一句话:

换东西,不一定要 fork 核心。

换模型适配器、换文件系统 provider、换执行环境,甚至换 Agent Loop,都可以围着插件和依赖关系来做。

这才是 dsh 比“支持几个 Plugin API”更值得看的地方。

04seam:换一个执行环境,不用把所有工具重写一遍

Cordis 之外,还有一个我觉得很实用的机制:seam(能力缝)。

它把能力拆成三个角色:

  • Service Definition:声明接口
  • Service Provider:实现接口
  • Consumer:使用接口

这套设计本身并不陌生。

真正有意思的是,dsh 里文件系统和子进程 provider 可以共享同一个执行世界。

举个比较具体的例子:

如果你把 Sandbox provider 指到一个远程环境,那么 Bash、PTY、LSP 这些能力可以跟着一起迁过去。

不用为了“远程执行”再分别维护:

Remote Bash
Remote PTY
Remote LSP

这一整套平行实现。

这类设计平时看文档不一定有感觉,但做过复杂 Agent 之后会很容易意识到它的价值。

Agent 一旦从“本地帮我改两行代码”走向远程 Sandbox、容器、集群,执行环境怎么统一搬走会变成一个很实际的问题。

05自己写一个插件,其实没有想象中复杂

只看架构很容易越看越抽象。

所以我实际跑了一下它的工具插件。

一个最小插件大概长这样:

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

export const name = 'my-tool'
export const inject = ['tools']

export function apply(ctx: Context) {
  ctx.tools.register(defineTool({
name: 'count_words',
description: 'Count words, lines and characters in a string.',
parameters: {
text: { type: 'string', required: true },
unit: { type: 'string', enum: ['word', 'line', 'char'] },
    },
output: {
schema: { type: 'object', properties: { count: { type: 'number' } } },
render: (_args, value) => [{ type: 'text', text: `${value.count}` }],
    },
async execute(args, exec) {
const unit = args.unit ?? 'word'
const count = unit === 'line'
        ? args.text.split(/\r?\n/).length
        : args.text.trim().split(/\s+/).filter(Boolean).length
return { count, unit }
    },
  }))
}

代码不长,但里面几处设计很能说明 dsh 的思路。

第一,parameters 不是一份单纯写给模型看的 JSON 描述。

它同时干三件事:

推断 TypeScript 类型、在 execute 之前校验模型生成的参数、让模型知道这个工具应该怎么调用。

所以到了:

async execute(args, exec)

这里时,args 已经不是模型随便吐出来的一坨 JSON。

它前面有 schema 兜着。

第二:

ctx.tools.register(...)

不是简单把工具塞进一个全局 registry。

这个注册本身就是前面说的 effect。

插件卸载,工具注册也会跟着撤销。

第三,PTC mode(原 Code Mode)里可以直接调:

await tools.count_words({...})

参数和返回值类型仍然来自同一份 schema。

而且调用走的还是正常执行管线,包括权限策略,并不是“为了方便代码调用,再偷偷开一条旁路”。

这个设计我比较喜欢。

因为很多框架一开始说“工具统一”,最后模型调用一套、代码调用一套、测试又一套,时间一长就会慢慢长成三个系统。

dsh 至少在这里是想把它们压回同一个执行入口。

06真正把插件挂进去,只需要一个 patch

有了 apply(ctx) 还不够。

dsh 还得知道插件文件放在哪、什么时候加载。

官方教程的最小闭环其实不复杂,就三步。

第一步,保存插件文件。

比如:

scratch-plugin/src/my-tool.ts

里面就是刚才那段代码。

这句很关键:

export const inject = ['tools']

它告诉 Cordis:

等 tools 服务准备好之后,再加载这个插件。

所以进入 apply() 时,正常情况下 ctx.tools 已经可用,不需要自己到处判空。

第二步,写一个挂载点 cordis.yml。

- insert:
- id: my-tool
name: '/绝对路径/scratch-plugin/src/my-tool.ts'

本质上就是告诉 dsh:

把这个插件插进当前插件树。

第三步,用 --patch 启动。

npm 安装方式:

dsh web --patch ./scratch-plugin/cordis.yml

如果从源码跑:

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

起来之后,再执行:

dsh --profile web --dump-config

插件树末尾就能看到:

id: my-tool

到这里才算真的把插件挂进去。

接下来有两种用法。

一种是正常 Agent 工具调用。

工具描述进入模型可见的上下文,模型自己判断什么时候调用。官方教程里的例子是让模型用 greet 工具问候 Ada,然后拿到:

Hello, Ada!

这条路需要一个真的能用的模型 Key。

我本机没有 DeepSeek Key,所以没有继续跑模型对话。

另一种是 PTC mode。

工具可以直接变成:

await tools.count_words({ text, unit })

参数和返回值类型还是从同一个 schema 推出来,调用也继续走 dsh 正常的权限和执行管线。

更有意思的是卸载。

去掉 --patch 重启,或者从 cordis.yml 里删掉那一行,工具注册会跟插件一起 unwind。

不用再单独写一堆:

removeListener()
unregister()
clearTimer()

跑到这里,“可逆效应”才从一个论文术语变成真正能感受到的工程机制。

07我最认可的,反而是它的会话日志

插件系统看起来最抢眼,但继续往下看之后,我反而更喜欢 dsh 的另一条约束:

model-visible means logged。

翻成大白话就是:

只要模型看见过,就必须能从日志里重建出来。

dsh 的会话日志是 append-only。

模型请求里的 prompt 片段、每一次工具调用的输入输出、原始响应,都会进入日志。

Fork、回放、转录、Telemetry、持久化,也都围着这条流继续派生。

它还有一个 Trajectory 视图,可以继续往下看:

  • 当时到底激活了哪段 system prompt
  • Agent 调了什么工具
  • 工具输入是什么
  • 最后返回了什么

这条约束看起来没“一切皆插件”那么新鲜,但真正做过复杂 Agent 后,很容易知道它有多重要。

一个社区作者在前述 Medium 文章里提到,他曾经花两周排查一个生产 Agent 为什么干了一件蠢事,结果手上只有残缺的 Trace。

所以他看到 dsh 这条约束后,直接评价它是:

“这个项目里最好的想法。”

这个评价我基本认同。

很多 Agent 系统都是先把功能跑起来,等出问题以后再补日志、补 tracing、补 dashboard。

但真正难排查的,从来不是“这个函数有没有报错”,而是:

模型当时到底看见了什么,它为什么会做这个决定。

dsh 相当于把这个问题往前推了一层。

不是出了问题再想办法猜,而是一开始就规定:

模型能看到的东西,必须被记录。

这才是可观测性真正应该在的位置。

08四种模式,我更关注 Headless

dsh 目前预设了四种运行模式:

  • Standard:完整编码 Agent,带文件编辑、Shell、Skill、子代理等能力。
  • PTC:模型先出脚本再执行,更适合批量工具调用。
  • Minimal:只保留 Bash + 文件编辑,主要用于 benchmark。
  • Creative:自定义 Agent preset,用 Agent 造 Agent。

但这几个模式里,我更关注 Headless。

Claude Code / Codex 平时最常见的用法,还是人在终端里和 Agent 一来一回地交互。

这种体验很好,但不是所有任务都需要人一直盯着。

比如:

把某个 fetch() 迁移扩散到 src/api 下几十个文件。

或者逐模块扫描、逐模块修改、逐模块验证。

这类任务更适合程序直接驱动 Agent,而不是每处理一个文件都在终端里聊一轮。

阿里云的实测提到,官方提供了 Python SDK,可以通过 DeepSeekHarness 上下文管理器,再对每个模块调用一次:

harness.run()

来做逐文件、逐模块的无人值守处理。

这里需要说明一下:

dsh 本体仍然是 TypeScript / Node 技术栈。

Python SDK 只是它提供出来的批处理入口,不是说 dsh 本身变成了 Python 框架。

这个能力我觉得很实在。

因为如果 Agent 真要从“个人编码助手”往工程流水线里走,Headless 几乎迟早都会变成刚需。

09成本可能有优势

有博主实测还引用过一组第三方 benchmark。

30 个 Agent 任务、8 种 Harness 配置下:

  • Pi Agent 每成功一个任务大约 $0.028。
  • Claude Code 是 $0.195。

大约相差 7 倍。

Claude Code 的速度更快,中位时间约 122.7 秒。

这个数字当然很吸引眼球。

但我不太建议直接把它写成“dsh 比 Claude Code 便宜 7 倍”。

因为这毕竟是第三方实测,模型、任务、Harness 配置都会直接影响最后结果。

更值得看的其实不是“7 倍”本身,而是:

当 Model 和 Harness 被拆开后,你终于有机会针对不同任务重新组合成本、速度和能力。

交互编码可以继续用更强的模型。

大量批处理可以换另一套模型和 Harness 配置。

真正有价值的是这个自由度。

10真上手之后,坑也不少

说完优点,再说现在上手会真实撞到的问题。

1. 没有终端交互式 TUI。

dsh 当然有 CLI,前面的 --dump-config 就是在终端跑的。

但真正交互式使用主要还是 Web,Headless 又是另一条路线。

如果你已经习惯 Claude Code 那种一直待在终端里的工作方式,切过来会比较明显地不适应。

2. v0.1 还很早。

官方已经明确说了,未来会出现破坏兼容性的变更。

项目早期版本迭代也很快,社区实测里还有人遇到过会话偶发冻结、需要刷新页面的问题。

现在这个阶段,连 API 本身都还可能继续动。

3. 学习曲线确实不低。

想真正看懂它,至少得继续理解:

Cordis、Plugin、Profile、Bundle、Provider、Seam。

如果你的目标只是“装完马上让 Agent 帮我改代码”,Claude Code 这种成品工具明显更省事。

4. 模型要自己准备。

dsh 本身不提供模型。

DeepSeek、Claude 或其他 Provider 的 Key 都得自己配。

5. 安装比较重。

前面说了,我这里是 288MB、116+ 个 dsh 相关包。

插件拆得很细,代价就是依赖也多。

在受限环境里,安装体验并不一定舒服。我自己就在沙箱里撞到过 ~/.dsh 的 EPERM。

6. 本地 Runtime 目前还不算一等入口。

社区实测里提到,provider 目录已经收了大量云端供应商,但 Ollama / LM Studio / llama.cpp 这类常见本地 Runtime,并没有特别顺手的开箱入口。

不是完全不能跑。

可以走自定义 provider 或 OpenAI-compatible 配置,社区也已经有人用 DGX Spark + Ollama 挂 Qwen 27B 接进 dsh。

但现在还不是这种体验:

安装 -> 选择 Ollama -> 直接跑

如果你非常依赖本地模型,这一点目前还是得自己折腾。

11写在最后

所以看完这一圈,我对 dsh 的判断其实挺简单。

方向我很认可。

但现在不会拿它替掉手上的 Claude Code,更不会急着接进生产。

我觉得它真正值得看的,不是“DeepSeek 终于也做了一个 Coding Agent”。

而是它把 Agent 底下那层长期藏在产品内部的 Harness,单独拿出来重新设计了一遍。

模型可以换。

工具可以换。

文件系统可以换。

执行环境可以换。

Agent Loop 甚至也可以换。

再加上可逆 effect、seam、append-only session log 这些东西,至少在架构层面,它确实把“一个 Agent 框架应该怎么拆”这件事往前推了一步。

但另一方面,现在它仍然是很早期的版本。

开发者预览、破坏兼容变更、没有终端 TUI、本地 Runtime 支持不够顺手,生态也才刚开始长。

更重要的是,官方在 8 月 27 日的 release notes 里自己也提醒:

项目尚未完成安全审计,Sandbox、审批和权限控制不能被视为可靠的安全隔离。

这基本已经替我们回答了“能不能直接接生产”。

至少现在,我不会。

比较合适的用法反而是:

装下来,跑一遍插件,看看 Cordis,读读它的 architecture。

如果你自己正在做 Agent Framework、Coding Agent、长任务 Harness,里面不少设计都值得参考。

至于生产?

再等一等。

现在的 Claude Code 是一个已经可以拿来干活的产品。

DeepSeek Harness 更像一个刚把地基结构公开出来的新框架。

两者没必要二选一。

甚至以后真正有意思的场景,很可能就是:

Claude 干 Claude 擅长的事,DeepSeek 干 DeepSeek 擅长的事,外面再有一层 Harness 决定到底该叫谁来。

这可能才是 dsh 真正值得关注的地方。

Image