从零手写 DeepSeek Harness 插件:保姆级教程,看完你也能给 Agent 造"工具"
如果这篇文章对你有帮助,欢迎点赞、收藏、关注,你的支持是我持续输出的最大动力。
源码可私信获取。

一、先说个离谱的开局:5 万星的项目,README 却啥也没说
2026 年 8 月 13 日晚,DeepSeek 一口气放了两颗炸弹:
- DeepSeek V4 Pro 正式版全量上线,Agent 能力大幅提升;
- DeepSeek Harness(以下简称 dsh)开发者预览版 v0.1 以 MIT 协议开源。
博客发布当天,仓库 star 数约 5 万,一度冲上 GitHub 趋势榜第一;本文成文时该仓库 star 已涨到 13 万+,fork 13.1k。下文的截图即取自当前版本仓库,数据以你打开仓库时的实时数字为准。
一夜之间,GitHub 上这个仓库冲到了 5 万多星,有的统计口径甚至说一天破了 7 万——这是开源历史上最快涨星的项目之一。
我抱着好奇心点进仓库,整个人直接看傻了:README 总共一千七百多字,没截图、没功能列表,连"这玩意儿到底能干啥"都没讲明白,就甩了一句英文口号:
Everything is a Plugin(一切皆插件)。
合着现在开源项目都走"老板画饼"路线是吧?口号喊得震天响,具体内容全靠用户自己悟。开周会都没这么抽象——至少老板还会画个饼的形状,这连饼皮都不给你看。
但等你折腾完一圈就会明白:这句话不是营销,是整个项目设计哲学的全部。

图:deepseek-ai/deepseek-harness 仓库主页,当前 star 数 13.1k+,MIT 协议。

图:DeepSeek 官网 Harness 页面首屏,明确写着"开发者预览版:一切皆插件",并提供一键使用 / 源码安装两种入口。
二、先搞清楚:Harness 到底是什么
2.1 一个公式看懂它
DeepSeek 官方给出了极其清晰的公式:
Agent = Model(模型 / 灵魂) + Harness(躯壳 / 执行引擎)
没有 Harness,再强的模型也只是一个"昂贵的自动补全"——它能回答问题,但不能动手干活。
有了 Harness,模型才能:
- 感知环境:文件系统、终端、网页
- 调用工具:编辑文件、执行命令、搜索网页
- 维持状态:会话、记忆、子 Agent 调度
- 持续执行:多步真实任务闭环
一句话:模型负责"想",Harness 负责"干"。
2.2 它填补了 DeepSeek 的最大短板
DeepSeek V4 系列模型性能强、价格低、权重开源,但Agent 任务的可靠性一直偏弱。这个短板本质不是模型问题,而是运行框架问题——工具调用协议、循环控制、错误重试、状态管理、沙箱隔离,这些都得靠框架层解决。
Harness 就是 DeepSeek 对标 Claude Code / Codex 给出的开源答案。
三、核心设计:"一切皆插件"到底有多彻底
3.1 底层是 Cordis 元框架
Harness 的底层不是 Express、不是 Fastify、不是任何你熟悉的 Web 框架,而是基于一个叫 Cordis 的元框架。
Cordis 只负责三件事:
- 插件的挂载(mount)与卸载(unmount)
- 插件之间的依赖关系管理
- 通过**服务(Service)和事件(Event)**让插件互相通信
关键点:Cordis 内核本身不包含任何 Agent 能力,所有能力全部住在插件里。
3.2 哪些东西是插件?几乎所有东西
| 插件层 | 具体能力 |
|---|---|
| Models | 大模型后端本身,可换任意 OpenAI 兼容模型 |
| Tools | 文件编辑、Shell 执行、文件搜索、网页搜索 |
| Skills | 可复用的技能包,Agent 可按需调用 |
| Sessions | 会话状态、运行历史管理 |
| Sandboxes | 隔离执行环境(Docker / 本地进程等) |
| Storage | 状态存储、文件系统抽象 |
| Loops & Scheduling | Agent 控制循环、子 Agent 调度、任务编排 |
| UI | 连用户界面都是插件,可换 Web UI / CLI / 其他 |
整个框架由约 220 个小型 npm 包组成 monorepo,官方内置了一百多个插件。
仓库的 docs 目录就是官方文档(含中文),从用户指南到插件开发教程一应俱全,结构如下:

图:官方仓库 docs 目录结构,docs/user/guide 是用户指南入口,docs/user/develop 下按 basic / framework / practice 三层组织插件开发教程。
docs/user/guide/:用户指南(安装、配置、四种模式);docs/user/develop/:插件开发教程(基础 / 框架 / 实战三层);docs/cookbook/:常见场景实操配方;docs/cordis-api/、docs/cordis-tutorial/:Cordis 元框架的 API 文档与教程。
想深入学习,优先啃官方 docs,比任何二手教程都靠谱。
3.3 插件化的实际意义
- 想换沙箱? 挂载一个新的 sandbox 插件,不用改源码;
- 想加自定义工具? 写一个 tool 插件,配置里启用就行;
- 想换模型后端? 换 model 插件,其他全部不动;
- 想做自己的 Agent 产品? 基于 Harness 薄内核组装插件,不用从零造运行时。
3.4 一张图看懂 Harness 的整体架构
从这张图可以直观看到:所有箭头都指向 Cordis 内核,但内核本身不干活,活全在插件里。 这就是"薄内核 + 可组合零件"的设计哲学。
这和 Claude Code、Codex 那种"控制循环 + 工具集 + UI 焊死在一起"的单体架构形成本质区别。
打个比方:
- Claude Code / Codex 是成品家电——买回家就能用,但拆不开、改不了;
- Harness 是乐高底座——底子给你铺好了,想拼坦克拼城堡全看你自己。
四、安装:两条路,按需自选不踩坑
先把两条安装路线的全局脉络看清楚,再动手不迟:
4.1 前置条件
- Node.js:官方声明的版本范围是
^22.19.0 || >=24.0.0,不确定直接用 Node 24。 - 验证方法:终端执行
node -v,能看到版本号就说明装好了。
4.2 路线 A:npx 一键启动(懒人首选)
普通用户直接选这个,终端敲一行命令:
npx @deepseek-ai/dsh web
中途会弹一个确认,问你要不要继续安装,输入 y 就完事。
装完自动启动本地服务,地址是 http://127.0.0.1:3080,浏览器打开就能进界面。
4.3 路线 B:源码编译安装(硬核玩家)
想改源码、研究内部逻辑的选这个:
# 如果没装 corepack,先安装:npm install -g corepack
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
corepack enable
pnpm install
pnpm run build
注意:pnpm run build 一定不要省。 我第一次只安装依赖就启动,插件日志虽然出现了,Web 页面却缺少构建产物,白白排查了半天。
4.4 装完先做三步基础配置
进页面后第一件事三连:
- 填 API Key:没有的去官网生成一个,这相当于入场门票;
- 切中文:左下角 Settings → Language,别硬扛英文;
- 加工作区:选个本地目录作为要折腾的项目文件夹,再选模型(要速度选 Flash,要效果选 Pro)。
配置页长这样,照着填就行:

图:官方仓库 docs 目录自带的模型配置页截图,对应 Settings → Providers / 模型配置。
如果你想接第三方模型(比如国产其他大模型或本地 Ollama),可以配置自定义提供方:

图:自定义提供方表单,需要填 Provider ID、显示名称、API 地址、API 协议和 API 密钥。
4.4.1 配置自定义提供方:把任意 OpenAI 兼容模型接进来
Harness 最爽的一点就是模型不绑定 DeepSeek。只要对方提供 OpenAI 兼容接口,就能在 Settings 里加一个自定义提供方:
- 打开 Settings → Providers,点"添加自定义提供方";
- 填 Provider ID(自己起个英文名,如
my-ollama); - 填显示名称(界面上显示的名字,如
Ollama 本地模型); - 填 API 地址(如
http://localhost:11434/v1); - 选择 API 协议(OpenAI 兼容选
openai); - 填 API 密钥(本地模型随便填,远程服务填真实 Key);
- 点击"获取可用模型",勾选你要用的模型,保存。
坑 6 预警:有的厂商能拉出一百多个模型,别全选,挑常用的几个就行,不然下拉菜单长得能拖到屏幕底。
关于 API Key 的一个细节:DeepSeek 官网生成的 Key 是 **Write-only(只写)**的,创建后只显示一次,关闭弹窗就再也看不到了。所以创建后第一时间复制保存。丢失了只能删掉重建,没有找回通道。
4.5 进阶:聊聊 Profile(配置文件)
先理解一个概念:Profile 就是 Harness 的“配置命名空间”。你可以在同一个电脑上配置多套完全不同的 Agent 环境,互不干扰。
比如:
# 默认 Web Profile
dsh web
# 用 --profile 指定一套独立配置
dsh web --profile web2 --port 8080
指定 Profile 后,--port 也要同步指定,否则可能端口冲突。每次启动用 --profile 参数选择,相当于给 Agent 开了不同的“工作台”。
4.6 工作区与项目加载
Harness 以**工作区(Workspace)**为单位组织任务。简单理解:每个工作区绑定一个本地文件夹,Agent 只能在这个文件夹及配置好的沙箱里活动。
- 新建会话前先选工作区;
- 会话中
/workspace命令可以切换当前工作区; - 想让 Agent 操作某个项目,先把项目文件夹加进工作区。
这样既安全又不容易把 Agent 搞晕——它永远知道自己该在哪个目录下干活。
五、四种模式怎么选?给你翻译成人话
官方给了四种 Agent 模式,新手看着头大:
| 模式 | 定位 | 人话翻译 |
|---|---|---|
| Standard(标准) | 完整编码 Agent,工具最全 | 全能选手,写代码、改文件、跑命令、搜网页啥都能干,新手直接选这个 |
| Code(代码) | 工具通过 Code SDK 暴露 | 效率狂魔专属,把多步操作写成一个程序一次执行,省 token |
| Minimal(极简) | 只有 Bash + 文件编辑两个工具 | 极简主义狂喜,也是官方发 Agent 跑分时用的模式 |
| Creator(创造) | 构建自定义预设 | 高阶玩家玩法,能自己写插件、折腾运行时 |
说直白点,这四个模式就像奶茶点单:
- 标准是正常杯全糖;
- Code是浓缩快取;
- 极简是纯水无糖;
- 创造是自己 DIY 小料随便加。
硬核提醒:以后看任何大模型的 Agent 跑分,先问一句"在什么模式下测的?"Minimal 模式分数高,不代表复杂生产任务表现好。
5.1 模式背后的工具集差异
四种模式本质是预置工具集不同:
| 模式 | 预置工具 | 适合场景 |
|---|---|---|
| Standard | 文件读写、Shell、搜索、子 Agent 调度等全量工具 | 日常编码、文档、调研 |
| Code | 通过 Code SDK 暴露的程序化工具 | 批量任务、自动化流水线 |
| Minimal | 仅 Bash + 文件编辑两个工具 | 跑分、极简环境验证 |
| Creator | 自定义预设 | 自己组装工具链 |
注意:Windows 平台目前对 Minimal 模式的 JSON-RPC Composition 支持不完整,如果你在 Windows 上跑 Minimal 模式遇到莫名其妙的 JSON-RPC 报错,换 Standard 模式通常就解决了。
六、插件开发实战:从零手写你的第一个工具
理解了"一切皆插件",下面进入本文核心:手写一个最小插件,完整跑通"加载插件 → 注册工具 → Agent 调用 → 返回结果"的闭环。
我们的目标是做一个 greet 工具:Agent 调用它并传入名字,插件返回:
你好,Datawhale!你的第一个 Harness 插件已经运行。
6.1 准备源码环境
开发原始 TypeScript 插件,需要进入 Harness 源码仓库(这也是官方入门文档采用的方式):
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
corepack enable
pnpm install
pnpm run build
本文实测使用 Node.js v24.19.0。 记住:pnpm run build 不要省,否则 Web 页面会缺少构建产物。
6.2 创建插件文件
在仓库根目录执行:
mkdir -p scratch-plugin/src
新建 scratch-plugin/src/greet-tool.ts:
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'greet-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'greet',
description: 'Greet someone by name.',
parameters: {
name: {
type: 'string',
required: true,
description: 'The name to greet',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
return `你好,${args.name}!你的第一个 Harness 插件已经运行。`
},
}))
console.log('[greet-tool] loaded; tool name: greet')
}
6.3 逐行拆解:这个插件到底在干嘛
先不用研究所有类型,这个插件其实只有四个部分:
| 组成 | 作用 |
|---|---|
name | 插件名称,用于识别和挂载 |
inject | 声明需要 Harness 的 tools 服务(依赖注入) |
apply(ctx) | 插件加载入口,Cordis 挂载时调用 |
ctx.tools.register(...) | 注册一个模型可以调用的工具 |
其中 defineTool 内部的五个字段:
name:工具名,模型调用时用这个名字;description:工具描述,告诉模型"这个工具是干嘛的、什么时候该用";parameters:参数定义,告诉模型该传什么、哪些必填;output:约定结果的类型和显示方式(schema定义结构,render定义展示);execute:真正执行代码的地方,模型把参数传进来,这里干活并返回结果。
为什么 description 这么重要?
因为模型是靠"读"来选择工具的。它不会遍历你的代码,只看你给它的工具描述。所以:
- 描述写得模糊,模型就不知道该在什么时候调用;
- 参数定义写得笼统,模型就会传错参数类型;
- 一个好的工具描述 = 明确的触发场景 + 清晰的参数约束。
这是"提示词工程"在插件开发里的体现——你写给模型看的元数据,和写给人看的代码一样重要。
6.3.1 进阶示例:一个读写文件的小工具
greet 只展示了"输入 → 返回"的最小闭环,实际项目里你更可能写带副作用的工具——比如读写文件。下面这个例子注册两个工具:read_file 和 append_file,让 Agent 能读文件并在末尾追加内容。
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
import { readFile, appendFile } from 'node:fs/promises'
export const name = 'file-ops'
export const inject = ['tools']
export function apply(ctx: Context) {
// 工具一:读文件
ctx.tools.register(defineTool({
name: 'read_file',
description: '读取指定路径的文件内容。当用户需要查看某个文本文件时使用。',
parameters: {
path: {
type: 'string',
required: true,
description: '文件的绝对路径或相对工作区的路径',
},
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
try {
return await readFile(args.path, 'utf-8')
} catch (e) {
return `读取失败:${(e as Error).message}`
}
},
}))
// 工具二:追加写文件
ctx.tools.register(defineTool({
name: 'append_file',
description: '向文件末尾追加内容。注意:不会覆盖已有内容,适合写日志、记笔记。',
parameters: {
path: { type: 'string', required: true, description: '目标文件路径' },
content: { type: 'string', required: true, description: '要追加的内容' },
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args) {
try {
await appendFile(args.path, args.content + '\n', 'utf-8')
return `已追加 ${args.content.length} 个字符到 ${args.path}`
} catch (e) {
return `写入失败:${(e as Error).message}`
}
},
}))
}
这个例子展示了两个实战细节:
- 一个插件注册多个工具——只要在
apply里多调几次ctx.tools.register就行; - 异常要兜底——文件不存在、权限不足时,把错误转成字符串返回给模型,而不是让插件直接抛异常。记住:工具的错误消息也会被模型读到,写清楚错误原因,模型才知道怎么自我修正。
6.4 把插件插入 Harness
先执行 pwd 拿到当前仓库的绝对路径,然后新建 scratch-plugin/cordis.yml:
- insert:
- id: greet-tool
name: '/Users/yourname/deepseek-harness/scratch-plugin/src/greet-tool.ts'
把 name 换成你机器上的绝对路径。
插件最好放在 Harness 源码仓库内。这个示例依赖仓库里的 @deepseek-ai/cordis 和 @deepseek-ai/dsh-tools,放到另一个目录可能出现 Cannot find module 报错。
6.5 启动并检查
pnpm dsh web --patch ./scratch-plugin/cordis.yml
如果 3080 端口已经被占用:
pnpm dsh web --patch ./scratch-plugin/cordis.yml --port 3082
看到下面两行,说明插件和 Web 服务都已就绪:
[greet-tool] loaded; tool name: greet
dsh web: http://127.0.0.1:3082
进入"设置 → 插件 → 插件列表",搜索 greet-tool.ts,状态应为"已启用"。
补充一个常见困惑:如果你把插件写好后用
npx方式启动(npx @deepseek-ai/dsh web),插件默认不会加载——因为 npx 启动的是发布包,不会去读你scratch-plugin/cordis.yml里的本地路径。开发本地插件请用仓库内的pnpm dsh,而不是 npx。
6.5.1 常见启动报错速查
开发阶段最常碰到的三个报错,先记住解法:
| 报错信息 | 原因 | 解法 |
|---|---|---|
MISSING_CREDENTIAL | 没有配置 API Key 对应的凭据 | 在设置里填 API Key,或 dsh credentials set 变量名 写入 |
UNKNOWN_MODEL | 模型 ID 与当前配置的模型名不匹配 | 去 Providers 检查模型 ID 是否填对 |
Cannot find module '@deepseek-ai/cordis' | 插件文件放在了仓库外 | 把插件放回仓库目录,或配置好 node_modules 路径 |
这三个坑基本覆盖了 90% 的启动失败场景。
6.6 让 Agent 调用它
选择工作区,新建一个标准模式会话,输入:
请调用 greet 工具问候 Datawhale。
展开工具调用,可以看到输入和输出:
IN { "name": "Datawhale" }
OUT 你好,Datawhale!你的第一个 Harness 插件已经运行。
至此,插件最小闭环已经跑通:加载插件 → 注册工具 → 模型调用 → 返回结果。
6.6.1 会话与 Session ID:调试必备知识
你可能注意到界面里每个会话都有一串 ID,这叫 Session ID。它的用处很大:
- 恢复:中途断网/崩溃,用
/resume或指定 Session ID 找回上次会话; - 分叉:从历史某步开新分支尝试不同方案(对应第八节的可观测性设计);
- 定位问题:报错时把 Session ID 贴出来,方便排查历史工具调用。
也就是说,Harness 的会话不是“用完即走”的聊天记录,而是可审计、可追溯、可恢复的运行轨迹。
把整个闭环画成图,就是下面这条链路:
七、插件和工具的区别:别搞混了
开发之前先厘清一对关键概念,很多人在这上面栽跟头:
- 插件(Plugin) = 一个
apply(ctx)模块,是框架的安装/生命周期单元。它决定"什么时候加载、依赖什么、卸载时清理什么"。 - 工具(Tool) = 插件通过
ctx.tools.register(...)注册的能力,是模型可见、可调用的函数。它决定"Agent 能做什么"。
打个比方:
插件是安装的应用程序,工具是应用暴露的函数 / API 端点。 一个应用可以暴露 0 个、1 个或多个函数。
理解了这层关系,你就明白了为什么 Harness 官方说"一个插件可以注册多个工具,但工具不能脱离插件独立存在"。
7.1 常用命令速查表
开发过程中你会反复用到这些命令,先存下来:
| 命令 | 作用 |
|---|---|
dsh web | 启动 Web 界面(默认端口 3080) |
dsh web --port 8080 | 指定端口启动 |
dsh web --profile web2 | 用指定 Profile 启动 |
dsh --dump-config | 导出当前配置 |
dsh plugin --profile web add <包名> | 给指定 Profile 安装插件 |
dsh plugin --profile web remove <包名> | 卸载插件 |
dsh plugin --profile web update | 更新所有插件 |
dsh credentials set 变量名 | 写入凭据(API Key 等) |
pnpm dsh web --patch ./cordis.yml | 开发时挂载本地插件 |
小技巧:记不住参数没关系,所有子命令后面加 --help 都能看到说明,比如 dsh plugin --help。
八、Harness 的可观测性:为什么它调试起来"不像玄学"
插件开发绕不开调试。Harness 的第二大设计原则就是可观测性——这是很多 Agent 框架严重缺失的部分,也是它工程化的底气。
8.1 只追加的事件日志
模型看到的一切全部记录在只追加(append-only)的会话日志中:
- 系统提示词
- 模型推理过程
- 每一次工具调用及返回结果
- 子 Agent 调度记录
- 每一次上下文注入
8.2 Trajectory 视图
可以按来源(source)检查这些记录,清晰看到 Agent 每一步做了什么、为什么这么做。
8.3 可恢复、可分叉、可搜索、可重放
因为日志是单一事件流,所以任意一次 Agent 运行都可以:
- 恢复(Resume):中断后从断点继续;
- 分叉(Fork):从某个历史节点分出一条新路径,尝试不同方案;
- 搜索(Search):在历史运行中查找特定操作;
- 重放(Replay):用相同历史重新运行,对比结果。
这对调试插件意味着什么? 插件跑偏了、参数传错了、返回结果不对——不再是"凭感觉猜",而是把事件日志拉出来,看模型当时到底看到了什么、调用了什么、返回了什么。这是从"玄学调试"到"工程化调试"的关键一步。
8.4 调试插件时的实操技巧
结合上面的设计,调试一个插件时按下面顺序查,效率最高:
- 看日志:用
dsh web --verbose启动,或直接看会话的 Trajectory 视图,定位是哪一步出错; - 看参数:展开工具调用的 IN 参数,确认模型传参是否符合你的
parameters定义; - 看返回:确认
execute的返回值结构,是否满足output.schema; - 改描述:如果模型压根不调用你的工具,多半是
description写得不清楚,模型不知道什么时候该用; - 加日志:在
execute里console.log打印中间变量,再重启 web 服务。
这套流程能把插件调试时间缩短一大半。
九、直接安装大佬做好的插件:以 DSH Vision Toolkit 为例
9.1 安装插件
如果终端里已经有 dsh 命令:
dsh plugin --profile web add @dsh-external/dsh-vision-toolkit
如果一直用 npx,可以写成:
npx @deepseek-ai/dsh@0.1.0-rc.6 \
plugin --profile web add @dsh-external/dsh-vision-toolkit
安装到 web Profile 后,检查配置里是否已经出现它:
dsh --profile web --dump-config | grep vision-toolkit
然后重启正在运行的 Harness Web 服务。 插件的宿主代码和浏览器代码都在启动时加载,只刷新页面通常不够。
9.2 配置视觉模型
Vision Toolkit 要求 Python 3.11 或更高版本。第一次使用 managed 运行时还需要联网安装它锁定的 Python 依赖。
打开 Harness 的"设置 → 视觉工具",配置:
- 一个兼容 OpenAI 接口的视觉模型地址;
- 对应的视觉模型名称;
- 一个 DSH Credential 引用,例如
VISION_API_KEY。
密钥可以通过命令写入 Harness 的凭据系统:
dsh credentials set VISION_API_KEY
在设置页面点击"测试连接"。注意:远程图片问答、定位和 OCR 需要视觉服务 Key;裁剪、颜色分析、像素对比等本地工具不需要。
9.3 在会话中使用
把图片复制进当前工作区,例如 ./screenshot.png,在会话中先加载插件附带的 Skill:
/vision-tools
然后直接描述任务:
请用 vision_glance 分析 ./screenshot.png,告诉我页面上出现了什么错误。
也可以做更具体的操作:
请用 vision_ground 定位截图里的发送按钮,并生成带标注的预览图。
请比较 reference.png 和 actual.png,告诉我差异最大的区域。
插件会按需向当前 Agent 暴露对应的 vision_* 工具。生成的裁剪图、热力图和报告会保存在工作区的 .dsh-vision-toolkit/artifacts 目录中。
9.4 更新或卸载
dsh plugin --profile web update
dsh plugin --profile web remove @dsh-external/dsh-vision-toolkit
操作后记得重新启动 Web Profile。
9.5 进阶:用 Python SDK 直接写 Agent 逻辑
除了在 Web 界面里用,Harness 还提供了 Python SDK(deepseek-harness-sdk),适合想用 Python 写测试脚本或自动化流程的同学。
先安装:
pip install deepseek-harness-sdk
然后连接本地的 dsh 服务:
from deepseek_harness_sdk import HarnessClient
client = HarnessClient(base_url="http://127.0.0.1:3080")
# 列出当前会话
sessions = client.list_sessions()
print(sessions)
# 创建一个新会话并发送消息
session = client.create_session(profile="web")
response = session.send("用 Python 帮我写一个快速排序")
print(response.text)
几个要点:
- SDK 连接的还是本地 dsh 服务,所以先要把
dsh web跑起来; - 每个
session对应 Web 里的一个会话,天然支持上下文续聊; - 适合在 CI/CD 里跑 Agent 回归、批量任务,或者给内部工具接上 Harness 能力。
这相当于给 Python 开发者开了一扇后门:不打开浏览器也能用 Agent。
十、安装第三方插件前的安全自查清单
Harness 插件运行在宿主进程里,属于可信代码。不要因为安装命令只有一行,就跳过源码和权限检查。动手前至少确认四件事:
- 仓库是否公开?许可证和维护者是否清楚?
- 安装脚本会下载什么?是否会运行额外程序?
- 插件需要哪些目录、网络和凭据权限?
- 是否说明了支持的 Harness 版本、卸载方式和测试方法?
10.1 为什么要这么谨慎?
插件是在宿主进程里运行的。这意味着:
- 它能读写你工作区里的所有文件;
- 它能调用你配置的所有凭据(API Key、Token);
- 它能发起任意网络请求;
- 它能在你机器上执行任意代码。
所以装第三方插件,本质上等于把部分机器权限交出去。不要因为是“官方生态”就放松警惕。 尤其是从 npm 上装包时,先花 5 分钟看一下它的源码和 package.json 里的脚本字段。
十一、与 Claude Code / Codex 的本质差异
写插件之前,值得先把 Harness 放在行业坐标系里看清楚。它和目前最火的两个 Agent 产品差异极大:
| 维度 | DeepSeek Harness | Claude Code | Codex |
|---|---|---|---|
| 架构 | 插件化薄内核(Cordis) | 单体架构 | 单体架构 |
| 开源 | MIT 完全开源 | 闭源 | 闭源 |
| 模型绑定 | 模型是插件,可换任意模型 | 绑定 Anthropic 模型 | 绑定 OpenAI 模型 |
| 运行模式 | 4 种预设,可自定义 | 单一编码 Agent 模式 | 单一编码 Agent 模式 |
| 可观测性 | 只追加事件日志,可恢复/分叉/重放 | 有日志,可重放能力弱 | 有运行记录 |
| UI | UI 是插件,可替换 | 固定 CLI/TUI | 固定 CLI/TUI |
| 沙箱 | 沙箱是插件,可换 | 内置沙箱 | 内置沙箱 |
| 模型层 | 模型可替换,可接任意 OpenAI 兼容后端(含本地 Ollama) | 模型锁定 | 模型锁定 |
| 当前状态 | v0.1 开发者预览,官方警告破坏性变更 | 相对成熟 | 相对成熟 |
核心差异一句话:Claude Code 和 Codex 是产品,Harness 是平台/内核。前者给你一个成品 Agent,后者给你一堆零件让你组装自己的 Agent。定位不同,不是直接替代关系。
十二、常见坑与踩坑实录
把我(和社区)踩过的坑整理成清单,能帮你省下大量排查时间:
坑 1:pnpm run build 没跑,Web 页面空白
只 pnpm install 就启动,插件日志虽然会出现,但 Web 页面缺少构建产物。构建步骤不能省。
坑 2:插件放在仓库外,报 Cannot find module
示例插件依赖仓库里的 @deepseek-ai/cordis 和 @deepseek-ai/dsh-tools,放到另一个目录会找不到模块。插件最好放在 Harness 源码仓库内。
坑 3:Node 版本不对
官方要求 ^22.19.0 || >=24.0.0。用 Node 20 会有一堆兼容性报错。不确定就直接上 Node 24。
坑 4:端口被占用
默认 3080 被占用时,加 --port 3082 换一个,别慌。
坑 5:装完插件界面没反应
只刷新页面通常不够。插件的宿主代码和浏览器代码都在启动时加载,需要重启 Web Profile。
坑 6:模型列表过长
自定义提供商"获取可用模型"时,有的厂商能拉出一百多个模型。别全选,挑常用的几个就行,不然下拉菜单长得能拖到屏幕底。
坑 7:插件权限
Harness 插件运行在宿主进程里,属于可信代码。装第三方插件前务必检查源码和权限(见上一节自查清单)。
坑 8:MISSING_CREDENTIAL 报错
启动时提示 MISSING_CREDENTIAL,说明配置引用的凭据不存在或没填。用 dsh credentials set 变量名 写入对应的 API Key 即可。
坑 9:UNKNOWN_MODEL 报错
提示 UNKNOWN_MODEL,通常是模型 ID 与配置不一致。去设置里核对:你选的模型名,是否在模型列表里真实存在?有时厂商改名了,旧 ID 就会失效。
坑 10:danger-full-access 安全警告
部分高权限插件(或使用 --danger-full-access 模式)会弹风险提示。确认风险可控再继续,不然就在沙箱里跑。
十三、总结:把这条链路刻进脑子
自己写插件时,最小结构是:
apply(ctx) → 注册工具 → execute(args) → 返回结构化结果
使用现成插件时,流程是:
plugin add → 重启 Profile → 配置凭据 → 加载 Skill → 调用工具
前者让你理解 Harness,后者让 Harness 真正变得有用。
十四、高频问题 FAQ
Q1:Harness 一定要配 DeepSeek 的模型吗?
不需要。模型是插件,默认预置 DeepSeek,但你可以配置任何 OpenAI 兼容接口,包括本地 Ollama。详见 4.4.1 节。
Q2:Windows 能用吗?
能。官方支持 Windows / macOS / Linux。注意两点:一是 Minimal 模式的 JSON-RPC Composition 在 Windows 上支持不完整,报错就换 Standard 模式;二是部分依赖 Python 的插件(如 Vision Toolkit)需要额外装 Python 3.11+。
Q3:dsh web 和 pnpm dsh web 有什么区别?
dsh 是全局安装的命令;pnpm dsh 是仓库内开发模式。开发本地插件必须用 pnpm dsh(配 --patch),因为 npx/全局安装的 dsh 不会加载你本地新建的插件源码。
Q4:插件装完为什么界面没反应?
插件宿主代码和浏览器代码都在启动时加载。改完插件、装完新插件,必须重启 Web Profile(或重启 dsh web),只刷新页面没用。
Q5:MISSING_CREDENTIAL / UNKNOWN_MODEL 怎么解决?
两个都是配置问题。前者用 dsh credentials set 变量名 补凭据;后者去设置里核对模型 ID 是否真实存在、是否填错。
Q6:能不能只用 Harness 跑 CLI,不打开浏览器?
可以。dsh web 是 Web 模式,还有 CLI 模式可以直接在终端里跑 Agent;Python SDK 也能在脚本里驱动本地 dsh 服务(见 9.5 节)。
Q7:插件写坏了会不会把系统搞崩?
插件运行在宿主进程里,写坏的插件可能导致服务崩溃——但配置和工作区数据都在磁盘上,重启服务即可恢复。开发时建议用独立 Profile 试插件,别直接在正式配置上折腾。
十五、写在最后
最后回看那个"离谱"的 README,你会发现:
"一切皆插件"不是一句空话。 模型是插件,功能是插件,界面是插件,连调度循环本身都是插件。DeepSeek 把 Agent 运行时的每一块骨头都拆成了可替换的积木,这既是它对 Claude Code / Codex 单体架构的釜底抽薪,也是它给 Agent 生态的"安卓时刻"投名状。
GitHub:https://github.com/deepseek-ai/deepseek-harness
官方页面:https://deepseek.com/harness/
下一步:把官方仓库拉下来,试试把 packages/llm/ 换成一个本地 Ollama 模型,看看"模型也是插件"到底是怎么换的——那又是另一篇实战了。觉得有用的话,点个赞让我知道,下篇安排。源码可私信获取。

395

被折叠的 条评论
为什么被折叠?



