微信扫码
添加专属顾问
DeepSeek Harness保姆级教程,照做即通「装→配→跑→写插件」全流程,0.1.2-alpha.1版本官方源码实操指南。 核心内容: 1. DeepSeek Harness的定义与架构(Agent=Model+Harness,插件化设计) 2. 环境准备要求(Node.js/pnpm版本、API key、DSH_HOME配置) 3. 安装启动双路径(npm快速入门与源码运行)
这份教程的目标只有一个:让你照做一遍就走通「装 → 配 → 跑 → 写插件 → 加载 → 批处理」的完整闭环。版本快照是官方源码 0.1.2-alpha.1(开发者预览,官方已明示未来会有破坏兼容性的变更)。
下面就跟云朵君一起开始吧~
DeepSeek Harness(简称 dsh)是 DeepSeek 开源的 agent harness(智能体框架),核心公式一句话:Agent = Model + Harness。模型是可替换的部件,Harness 负责模型之外的几乎所有东西——工具注册、任务规划、沙箱、会话存储、agent 循环。
它不是模型,也不是又一个「成品编码工具」:它构建在 Cordis 元框架之上,slogan 是 「一切皆插件」(Everything is a Plugin)。模型适配器、会话存储、工具集、沙箱、agent 循环本身,全都是可以热插拔的插件。想给它的某个 session 换一套能力,就「挂一个插件在旁边」,而不是去 fork 源码改核心。
我们后面所有操作,都会反复碰到这几个词:插件(plugin)、profile(具名组合)、bundle(分发包)、patch(改/插一行的配置文件)。
两点提前说明:
dsh 把 profile、凭据、session 等持久化内容放在 Harness home 里(默认相关目录可被环境变量 DSH_HOME 显式覆盖)。手动安装会用 ~/.dsh;用 Python SDK 时官方故意不读 ~/.dsh,要求你显式传 dsh_home,这一点后面会说。@deepseek-ai/dsh 及其同类插件包的依赖树比较大,受限环境(无 root / 沙箱)下 npm install 可能明显变慢,这是正常现象,不是装错。npx @deepseek-ai/dsh web
默认会在 http://127.0.0.1:3080 启动 Web UI,本机启动时还会用默认浏览器自动打开。只想起服务、不自动开浏览器就加参数:
npx @deepseek-ai/dsh web --no-open
git clone https://github.com/deepseek-ai/deepseek-harness.git
cd deepseek-harness
pnpm install
pnpm run build
pnpm dsh web
说明:pnpm run build 负责准备构建产物,pnpm dsh web 直接使用这些产物,不会再重新构建。
浏览器打开 http://127.0.0.1:3080 后:
$DSH_HOME/.credentials.yaml,页面只显示脱敏后的描述,不显示明文。dsh 的那个项目目录并选它。没选 workspace 之前,会话输入框是不可用的。好了,到此咱们就可以初步使用了,再进一步就是插件的使用和开发了~
一台跑起来的 dsh,本质上是一个「插件树」,启动时按固定的层叠顺序组装:
profile 中的每个 bundle(按声明顺序)
→ profile 自己的 cordis.patch.yml
→ home 级 $DSH_HOME/cordis.patch.yml
→ 命令行 --patch 覆盖层
三个概念:
cordis.patch.yml。内置模板:web、headless、sdk、sdk-minimal、acp。@deepseek-ai/dsh-base 提供模型接入、完整工具集、持久化会话、沙箱与权限策略;@deepseek-ai/dsh-web-app 追加浏览器应用;@deepseek-ai/dsh-headless 追加无 server 的一次性 runner。config,不是深度合并。看效果用这条命令:
dsh --profile web --dump-config
它会把你机器实际装配出来的插件树打印出来;--dump-default-config 打印的是默认树(不启动)。每一行都是「id + 包名 + config」,也就是「一切皆插件」最直观的现场——模型、工具、会话、agent 循环全是一排可以 patch 的插件行。
先建个 scratch 项目(假设你已经在仓库根目录,dsh 默认把运行目录当 workspace):
mkdir -p scratch-plugin/src
一个插件就是一个 TypeScript 模块,导出一个 apply(ctx) 函数:
// scratch-plugin/src/my-plugin.ts
import type { Context } from '@deepseek-ai/cordis'
export const name = 'hello-plugin'
export function apply(ctx: Context) {
console.log('[hello-plugin] plugin loaded!')
}
这就是完整的配置。dsh 加载插件时调用 apply 并把 ctx 传进来,你通过 ctx 注册能力。
插件有三种写法,功能场景不同:
// 对象形式
export default {
name: 'my-plugin',
inject: ['tools'],
apply(ctx: Context) { /* ... */ },
}
// 类形式:当你要把自己的服务提供给别的插件用时,通常用这种
import { Service, type Context } from '@deepseek-ai/cordis'
export default class MyService extends Service {
static inject = ['tools']
constructor(ctx: Context) {
super(ctx, 'myService')
}
}
函数形式在大多数场景够用;类形式主要用于「你提供了一个 service 给其他插件消费」。
如果插件要用某个 service(比如 tools 或 llm),在 inject 里声明:
export const name = 'my-tool-plugin'
export const inject = ['tools']
export function apply(ctx: Context) {
// 到这里 ctx.tools 一定已就绪
ctx.tools.register(/* ... */)
}
Cordis 会等 inject 里声明的依赖全部就绪再加载你的插件,所以 apply 里不用判空。
凡是经 ctx 注册的东西——事件监听、工具、定时器——插件卸载时都会自动清理:你不用手动 removeListener 或 clearInterval。需要显式释放心智资源(比如一个网络连接)时,用 ctx.effect 返回 disposer:
import type { Context } from '@deepseek-ai/cordis'
export function apply(ctx: Context) {
ctx.effect(() => {
const timer = setInterval(() => {
console.log('heartbeat')
}, 5000)
// 插件卸载时执行
return () => clearInterval(timer)
})
}
工具是「模型能看到的插件」。一个最小工具长这样:
// scratch-plugin/src/my-plugin.ts(覆盖前面 hello 版本)
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 `Hello, ${args.name}!`
},
}))
}
带异步 I/O 的经典例子是读文件(adding-a-tool),注意 exec.signal 被直接透传:
import { readFile } from 'node:fs/promises'
import type { Context } from '@deepseek-ai/cordis'
import { defineTool } from '@deepseek-ai/dsh-tools'
export const name = 'read-file-tool'
export const inject = ['tools']
export function apply(ctx: Context) {
ctx.tools.register(defineTool({
name: 'read_file',
description: 'Read a file from disk.',
parameters: {
path: { type: 'string', required: true, description: 'Absolute path' },
limit: { type: 'number' }, // 非必填
},
output: {
schema: { type: 'string' },
render: (_args, value) => [{ type: 'text', text: value }],
},
async execute(args, exec) {
return readFile(args.path, { encoding: 'utf8', signal: exec.signal })
},
}))
}
args 的 TypeScript 类型(execute 里直接拿到类型安全的参数);execute 运行之前,校验模型生成的参数(类型、必填、字面量约束等);官方在 tool authoring reference 里写死了五条规则:
execute 里拿到的参数一定已按 parameters schema 校验过。exec.signal:用它取消进行中的工作(上面的 readFile 就是直接透传)。output.render 分离:presentCall/presentResult 必须是 args(加结果)的纯函数,不能做 I/O。长任务不要走前台 execute,而是走 ctx.jobs.start(...)(后台 job)。工具注册是 effect 式的:插件卸载,工具自动注销,不留「还挂在那儿但没人管」的孤儿状态。
光有 apply(ctx) 不行,dsh 得知道插件文件在哪、什么时候加载。官方教程的最小闭环是三步,每一步都能复现:
第一步:落插件文件。 把上面的 greet-tool(或 read-file-tool)存成 scratch-plugin/src/my-plugin.ts。
第二步:写挂载点 cordis.yml。 一个 patch 文件往插件树里插一行,name 必须是绝对路径:
# scratch-plugin/cordis.yml
- insert:
- id: hello
name: '/绝对路径/到/deepseek-harness/scratch-plugin/src/my-plugin.ts'
第三步:用 --patch 叠加启动。 装的是 npm 包就跑:
dsh web --patch ./scratch-plugin/cordis.yml
从源码跑就是:
pnpm dsh web --patch ./scratch-plugin/cordis.yml
启动时终端会打印 [hello-plugin] plugin loaded!(如果你还留着 hello 版)或工具注册完成。接着在浏览器里问模型:
Use the greet tool to greet Ada.
模型调用 greet 后会拿到 Hello, Ada! 作为工具结果。
想确认插件树确实多了一行,用:
dsh --profile web --dump-config
拉到最后,会看到 id: hello(或你的插件 id)这一行。
卸载同样干净。 去掉 --patch 重启,或从 cordis.yml 删掉那一行,插件注册会随插件一起 unwind。不用手动清任何东西——这就是可逆 effect 最直观的一次兑现。
DSH_TOOLS_MODE 环境变量为进程选择 native、ptc 或 both(其他值会导致启动失败)。PTC 模式下,模型先出脚本再把脚本交给执行能力跑,更贴合「确定性一步一步执行」的批处理业务。对应地,注册过的工具也能被脚本直调,参数与返回类型都从同一个 schema 推出来,走的是正常执行管线(含权限策略)。
「工具」只是插件的一种。真正的扩展单元叫 seam(能力缝),一个可替换的能力由三个角色组成:
官方原话:只写一个角色不算 seam,加一个能力要把三个都设计全。("one role alone is not a seam; adding a capability means designing all three.")
Consumer 侧的写法值得记住,因为它体现了「硬依赖 vs 可选依赖」:
export const inject = ['tools', 'shell', 'systemPrompt', 'shellEnv']
// 已 inject 的 service,直接属性访问,缺了就起不来
const defaultMode = ctx.shell.sandboxMode
// 未 inject 的可选 service,可能返回 undefined,要自己判空
const sandboxPolicy = defaultMode === undefined ? undefined : ctx.get('sandboxPolicy')
inject 声明 + 直接属性访问 = 硬依赖;ctx.get(...) = 可选依赖。拦截与策略走事件(ctx.on / waterfall),直接能力调用走 service 方法,别混。
共享执行世界是这个设计最值钱的地方:文件系统和子进程 provider 共享同一个「执行世界」。你把沙箱 provider 指到远程环境,Bash、PTY、LSP 会一起跟着搬,不用给每个能力单独写一份 provider 分叉。
一个 agent 循环的基本单位:
循环内部跑一系列事件瀑布(agent/pre-step → agent/request → llm/stream → tools/pre-execute → tools/execute → tools/post-execute → tool/result → turn/end),其中 agent/* 和 tools/* 大多是 waterfall(监听器要调 next() 委托给下一个),agent/turn-stopping 是 serial(没有 next())。
对使用者来说,更常打交道的是一套内置 profile 入口:
内置 web、headless、sdk、sdk-minimal、acp 首次使用会从随附模板自动初始化;自定义 profile 才需要 dsh plugin 创建。给某个 session 组合一套不同的能力集,就 compose 一个 agent preset——preset 里的 service 行需要一个 isolate realm,保证这套能力不污染其它 session 的作用域。
dsh 相对交互式套装最实在的一个入口是 headless:无 GUI、无 server、一次一个任务、跑完退出。
dsh --profile headless "run the tests"
输出契约(官方文档明确写死):
dsh: reasoning: 前缀);dsh: :);dsh --profile headless --help 只打印帮助、不运行任何任务。它很适合脚本、CI 和一次性批处理:进程不监听端口,跑完自己就退。
由于官方尚未安全审计,headless 等于把未审计能力放在无人值守模式下跑,所以请先拿它做可回滚的迁移/实验任务,别让它去执行不可逆的批量写操作。
dsh 本体是 TypeScript/Node 栈,但官方提供了一个 Python SDK 作为批处理入口(注意:是入口,不是 dsh 本体)。
安装:
python -m pip install deepseek-harness-sdk
它会自动带上同版本的原生 runtime wheel 和 dsh 命令,正常 SDK 运行不需要系统 Node.js。最小用法:
from deepseek_harness import DeepSeekHarness
with DeepSeekHarness(
dsh_home="/absolute/path/to/isolated-dsh-home",
cwd="/absolute/path/to/workspace",
provider="deepseek-official",
model="deepseek-v4-flash",
) as harness:
result = harness.run("Inspect the repository and fix the failing tests.", session_id="example-001")
print(result.final_response)
几个关键点,缺了都会踩坑:
dsh_home 必须显式传(或设 DSH_HOME),SDK 故意不读 ~/.dsh;cwd 是 agent 的 workspace;provider/model 在初始化握手时发送;DeepSeekHarness 是懒启动的,复用一个 runtime 直到 close() 或退出 with 块;profile 默认 sdk,极简用 profile="sdk-minimal"(后者只组合持久 bash + str_replace_editor,系统提示词固定为 You are a helpful software engineer assistant.)。想给 Python 用的 run 装持久插件,走 profile 管理:
export DSH_HOME=/absolute/path/to/isolated-dsh-home
dsh --profile sdk --dump-default-config >/dev/null
dsh plugin --profile sdk add file:/absolute/path/to/my-plugin-bundle
命令级 patches=("/path/to/foo.patch.yml",) 则用于「本次 run 生效、不持久化」的临时变更。harness.run() 返回 RunResult(session_id, final_response, finish_reason, events, notifications),final_response 是该区间最后一个根会话 assistant 文本。
会话日志是 dsh 里一个架构级硬约束,而不是事后补的仪表盘:模型看到的一切,必须能从会话日志重建("Model-visible means logged")。
日志是 append-only 的。每一条 prompt 片段、每一次工具调用的输入输出、原始响应都在。fork、回放、转录、telemetry、持久化,全部从这一条日志流派生。官方的运行时不变式会断言它。这意味着:当你排查一个 agent 为什么干了蠢事时,trace 不会被「没记」或「记不全」卡死——不满足这条约束,这套系统根本起不来。
把上面所有步骤走完,你已经有了一套能跑通 dsh 的手感。最后给你几条真实的边界,都是有出处的:
0.1.2-alpha.1,官方明示「未来将出现破坏兼容性变更」。本文的命令与 API 都基于这个快照,投产前一定对照官方最新源码复核。落到结论就是:装起来玩、读它的架构、在可回滚的环境里做实验,是对的;现在直接接生产、或对它跑不可逆批处理,时机还没到。 它并不打算替代你手里已经能用的编码工具,而是把「agent 框架应该长什么样」这件事往前推了一档——想按它这条路深挖,官方仓库的 docs/architecture.md、docs/cordis-primer.md 和 docs/cookbook/ 就是下一步最该翻的入口
53AI,企业落地大模型首选服务商
产品:场景落地咨询+大模型应用平台+行业解决方案
承诺:免费POC验证,效果达标后再合作。零风险落地应用大模型,已交付160+中大型企业
2026-10-05
Hermes Agent:会自我进化的 AI 智能体
2026-10-05
拿下 45.2k Star,记忆准确率 94.6%!Vectorize 开源 Agent 记忆引擎
2026-10-04
高德开源 SkillClaw:让 Agent 的技能自己进化
2026-10-03
dots 如何接管我的 Codex 工作流
2026-09-30
Laya 开源:比Jev快4倍!421M 参数,33 毫秒完成 System 1 决策
2026-09-30
OpenAI 重磅发布 dots,我在 GitHub 找到了一个开源版
2026-09-29
基于 DeepSeek Harness,这个开源项目搭了一套可私有化的企业 Agent 平台
2026-09-28
GitHub 3.2万 Star,又一款开源的 Agent 记忆系统火出圈了
2026-07-23
2026-08-16
2026-07-25
2026-08-17
2026-07-15
2026-08-20
2026-07-09
2026-07-24
2026-07-20
2026-08-20
2026-09-23
2026-09-11
2026-09-03
2026-08-31
2026-08-23
2026-08-20
2026-08-19
2026-08-14
欢迎您使用【53AI 官方网站】(以下简称“本网站”或“我们”)。本《会员服务协议》(以下简称“本协议”)是您(以下简称“会员”或“用户”)与【深圳市博思协创网络科技有限公司】之间关于注册、登录及使用本网站会员服务所订立的法律协议。
在您注册或登录前,请务必审慎阅读、充分理解各条款内容,特别是免除或限制责任的条款、知识产权条款、争议解决条款等。此类条款将以加粗形式提示您注意。 当您通过微信公众号授权、手机验证码验证或其他方式成功登录本网站时,即视为您已完全理解并同意接受本协议的全部内容。
一、 定义
本网站:指由【深圳市博思协创网络科技有限公司】运营的,域名为【53ai.com】的网站及相关移动端页面。
会员服务:指本网站向注册会员提供的知识库文章查阅、内容检索及其他相关增值服务。
知识库内容:指本网站发布的包括但不限于文字、图表、数据、研究报告、行业分析等数字化内容资源。
二、 账号注册与登录
登录方式:本网站支持以下登录方式,您可根据实际情况选择:
微信公众号授权登录:您同意将您的微信OpenID信息授权给本网站,用于创建或关联会员账号。
手机验证码登录:您需提供真实有效的手机号码,并通过短信验证码完成身份验证与登录/注册。
账号安全:您的账号仅限您本人使用,禁止赠与、借用、租用、转让或售卖。因您保管不善导致的账号被盗、密码泄露等损失,由您自行承担。
实名认证:根据相关法律法规要求,我们可能要求您在特定功能下完成实名认证。如您拒绝提供,可能无法使用部分或全部服务。
未成年人保护:若您未满18周岁,请在法定监护人的陪同下阅读本协议,并在征得监护人同意后使用本服务。
三、 服务内容与规范
知识库查阅权限:会员登录后,有权按照其会员等级对应的权限范围,在线浏览、检索本网站知识库中的相关文章及内容。
服务变更:我们有权根据业务发展需要,调整、变更或终止部分服务内容,并将以网站公告、公众号消息等方式提前通知。
禁止行为:您在使用服务时不得实施以下行为:
利用技术手段批量爬取、下载、转存知识库内容;
将知识库内容用于商业目的或未经授权地向第三方传播;
干扰本网站正常运行或侵犯其他用户合法权益;
发布违法违规信息或从事违反公序良俗的活动。
四、 知识产权声明
权利归属:本网站知识库中的排版设计、软件代码等内容的知识产权均归【公司全称】或原权利人所有,受《中华人民共和国著作权法》等法律保护。
有限许可:本网站授予会员一项非独占、不可转让、不可转授权的普通许可,仅限于个人学习、研究之目的在线查阅知识库内容。
侵权追责:未经书面许可,任何单位或个人不得以任何形式复制、转载、摘编、镜像、汇编或以其他方式使用上述内容。一经发现,我们保留追究其法律责任的权利。
五、 个人信息保护
我们重视对您个人信息的保护。关于我们如何收集、使用、存储和保护您的个人信息,请单独阅读 《隐私政策》。
您通过微信公众号授权或手机号验证所提供的信息,我们将严格按照《个人信息保护法》的规定处理,仅用于身份识别、服务提供及安全验证等必要用途。
您可以随时通过网站设置或联系客服行使查阅、更正、删除个人信息及撤回授权同意的权利。
六、 免责声明
内容准确性:知识库内容仅供参考,不构成专业建议。我们不对其完整性、准确性、时效性作任何明示或暗示的保证,您应自行判断并承担使用风险。
不可抗力:因自然灾害、政策法规变化、网络故障、第三方平台接口异常(如微信接口维护、运营商短信通道故障)等不可抗力导致的服务中断或延迟,我们不承担违约责任。
第三方链接:本网站可能包含指向第三方网站的链接,该等网站的内容和服务不受我们控制,请您自行甄别风险。
七、 违约责任
如您违反本协议约定,我们有权视情节采取警告、限制功能、暂停服务、注销账号等措施,并保留要求赔偿损失的权利。
如因您的违约行为导致我们遭受行政处罚、第三方索赔或商誉损失,您应承担全部赔偿责任(包括但不限于罚款、赔偿金、律师费、公证费等)。
八、 法律适用与争议解决
本协议的订立、执行和解释均适用中华人民共和国大陆地区法律。
因本协议产生的或与本协议有关的任何争议,双方应友好协商解决;协商不成的,任何一方均可向【公司所在地】有管辖权的人民法院提起诉讼。
九、 其他
本协议构成双方就本服务达成的完整协议,取代此前任何口头或书面约定。
本协议任一条款被认定为无效或不可执行的,不影响其他条款的效力。
我们对本协议享有最终解释权,并在法律允许的范围内保留随时修改的权利。修改后的协议一经公布即生效,继续使用服务即视为同意修订内容。