DeepSeek Harness 入门保姆级教程:从装好到让它自己写代码(Windows 实测)
目录
版本基准:
dsh 0.2.1-rc.2+ Node v22.22.0,Windows 10,实测于 2026-11。
文中每条命令都在本机真跑过;凡是「实测」二字后面的数字,都是这次跑出来的,不是估的。你需要:Node 22+、一个能用的模型账号(OpenAI 兼容端点即可)、20 分钟。
0. 先搞清楚:DSH 是什么,不是什么
先说结论:DSH(DeepSeek Harness)不是模型,也不是编辑器插件,它是一个「agent 宿主」——把模型、工具、权限、会话、插件装进同一个进程里,然后用 profile 决定这次启动装哪一套。
很多人第一次接触会把它当成另一个 Claude Code / Codex CLI。不完全是。三者都是 CLI agent,差别在于「可装配性」:
| 维度 | Claude Code | Codex CLI | DSH |
|---|---|---|---|
| 本质 | 一个固定的 agent 应用 | 一个固定的 agent 应用 | 一层启动器 + 一堆可替换插件 |
| 扩展方式 | 插件 / MCP | AGENTS.md + MCP | profile:有序的插件 bundle 栈 + 你自己的补丁层 |
| 入口 | CLI + IDE | CLI + IDE + 云 | CLI / Web / headless / SDK / ACP / 飞书 |
| 换界面 | 改不了 | 改不了 | 换一个 bundle 就是另一个前端(Web、TUI、桌面) |
| 配置粒度 | 全局 + 项目 | 全局 + 项目 | 全局 + profile + 项目 + 命令行 --patch |
一句话记法:模型是脑子,DSH 是身体,profile 是这次装成什么形状的身体。
它现在有六个入口,共用一个 dsh 命令:
| 入口 | 命令 | 适合谁 |
|---|---|---|
| Web GUI | dsh web | 想要图形界面、看会话列表、点按钮的人 |
| 终端 | dsh tui | 习惯终端、不想开浏览器的人 |
| 一次性任务 | dsh headless "跑测试" | 脚本 / CI 里只要一个结果 |
| SDK / ACP | dsh --profile sdk / acp | 想让别的程序驱动它 |
| 飞书 / IM | 装一个 dsh-feishu-bridge 插件 | 想在手机上派活的人 |
| 桌面 | Electron 包(保留名 desktop) | 不想碰命令行的人 |
1. 安装:Node 22 + 一条 npm
node -v # 必须 >= 22,WebSocket 和 fetch 是内置的,插件生态也按 22 编
npm i -g @deepseek-ai/dsh
dsh --version # 0.2.1-rc.2
装完先别看文档,直接 dsh --help,因为它把设计哲学写在第一行:
dsh: boot a DeepSeek Harness profile — an ordered stack of plugin-bundle patch
layers under your own overrides.
翻译过来就是:profile 是一摞有序的补丁层,你的改动永远在最上面。
2. 第一次跑通:从 dsh web 到第一个 diff
先建练习目录,别拿生产仓库练手:
mkdir D:\code\dsh-demo
cd D:\code\dsh-demo
git init
@'
def add(a, b):
return a - b # 这里有个 bug
'@ | Set-Content calc.py -Encoding UTF8
启动 Web 版:
dsh web
它会做三件事:在当前目录初始化/复用 web profile、起一个本地服务、把带 token 的地址打出来:
http://127.0.0.1:3080/?token=y-6XHMrqbFIPSlukfddiE-Oykd9vR2m5qRNrR4OMnZg
两个细节值得记住:
- token 在 URL 里,每次重启都会变。别收藏旧链接,重启后去日志或
dsh-web-url.txt(如果你像我一样写了重启脚本)里拿新的。 - 工作区默认就是「你执行命令时所在的目录」。想换工作区,先
cd再启动,别在 GUI 里乱点。
打开页面,第一次说话给一个可验证的目标:
calc.py里add(a, b)返回的是a - b,这是 bug。改成正确的加法,补一个 pytest 用例覆盖正数、负数和 0,改完跑一遍测试确认通过。
你会看到三步:读(自己 ls/cat,不问)、改(先给你看 diff,没落盘)、跑(要执行命令时弹审批)。
戳一次「同意」,你就完成了第一次闭环。剩下全是这条主线加参数。
3. 四个必须懂的概念
3.1 profile:这次启动装哪一套
profile 就是 $DSH_HOME/profiles/<name> 里的一个目录,里面有且只有两样关键东西:
~/.dsh/profiles/web/
├── package.json # 依赖 + dsh.profile.bundles(有序!)
├── cordis.patch.yml # 你自己的补丁层
├── cordis.yml # 空的 [],别手动编辑
└── pnpm-lock.yaml
package.json 里最要紧的是这段:
"dsh": {
"profile": {
"bundles": [
"@deepseek-ai/dsh-base",
"@deepseek-ai/dsh-web-app",
"dsh-opencode-go",
"@opencode2dsh/dsh-plugin",
"dsh-feishu-bridge"
]
}
}
顺序有意义:后面的层可以覆盖前面的层。dsh-base 给工具与 agent 内核,dsh-web-app 给界面,dsh-opencode-go 接模型,最后两个是我装的扩展(一个社区插件 + 一个飞书桥)。
换前端不用改代码,换 bundle 就行:把 dsh-web-app 换成 TUI 的 bundle,同一个内核立刻变成终端应用。
3.2 补丁层:你的改动永远在最上面
cordis.yml 打开是一句很诚实的注释 + 一个空数组:
# dsh profile root — an empty entry list. The tree is composed as patches:
# each bundle in package.json's dsh.profile.bundles, then cordis.patch.yml, then any
# --patch overlays. Edit cordis.patch.yml, not this file.
[]
所以你要改的永远是 cordis.patch.yml。它是个 YAML 数组,每一项按 id 定位已有插件、改它的 config:
- id: agent-default-model
name: "@deepseek-ai/dsh-agent-default-model"
config:
provider: dsh-opencode-go
model: deepseek-v4.1-flash
reasoningEffort: high
这段就是「默认用哪个模型、推理强度多高」。想试别的模型?改这三行,重启,别去翻源码。
还有个更轻的做法:命令行临时叠一层,不落盘。
dsh web --patch .\extra.yml
适合「今天只想换个模型跑一次」这种场景。
3.3 会话:它把每句话都存成了压缩日志
我第一次看到会话目录时有点意外——不是数据库,是一串 zstd:
~/.dsh/sessions/<工作区slug>/<session-id>/session.v4.jsonl.zstd
~/.dsh/storages/session_projcache/sessions/<session-id>.json
session.v4.jsonl.zstd:追加式事件流,每一行是一个事件(assistant/message、tool/call、job/registered、turn/end…)。session_projcache/*.json:投影缓存,给会话列表和界面用的「当前状态」(标题、token 用量、todos…)。
实测一个 6 轮、131 步的会话:解压后 3.2 MB / 1313 条事件,而且 zstd 文件是多帧拼接的——单次 zstdDecompressSync 只能解出第一帧(我一开始只拿到 1 行,以为文件坏了)。要完整读,得按 magic 28 B5 2F FD 切帧逐段解。
这带来一个实用结论:会话可以离线复盘。不用登录任何后台,直接解压就能看到每一步的推理、工具调用和结果。出问题时这是最硬的证据。
3.4 工作区与 AGENTS.md
DSH 启动时会把两类指令文件读进来:
| 位置 | 作用域 | 典型内容 |
|---|---|---|
~/.dsh/AGENTS.md | 全局,所有会话 | 个人习惯、知识库路径、通用禁令 |
<工作区>/AGENTS.md | 当前项目 | 构建命令、目录约定、别碰的文件 |
它的优先级是「更具体者胜」:项目 > 全局,用户的直接指令 > 两者。所以同一个模型,在 A 仓库和在 B 仓库的「脾气」可以完全不同——靠的就是这两份文件。
4. 文件地图:DSH 家目录(~/.dsh)里都有什么
~/.dsh/
├── AGENTS.md 全局指令
├── .credentials.yaml 凭据(别提交、别截图)
├── profiles/ 每个 profile 一套 package.json + patch
│ └── web/
├── sessions/ 会话事件流(zstd)
├── storages/ 投影缓存、工作区记录
├── logs/
├── cache/
├── feishu-bridge/ 插件自己的状态(config.json / instance.lock / state-*.json)
└── attachments/ 消息附件
这张表值得截图存下来。排查「它为什么这样」时,九成答案在这五个位置里:profile 决定装配,AGENTS.md 决定脾气,sessions 决定事实,logs 决定报错,storages 决定界面看到什么。
5. 工具面:它到底能碰什么
DSH 给模型的不是「一个聊天框」,是一组有类型、有边界的工具。核心就这些:
| 工具 | 干什么 | 边界 |
|---|---|---|
read / write / edit | 读文件、整写、定点改 | 受沙箱路径限制;改前必须先读 |
glob / grep | 按路径找文件、按内容搜 | 比 shell 的 find/grep 快且不踩引号坑 |
pwsh | 跑 PowerShell | 受沙箱与超时限制,长任务自动转后台 job |
web_fetch / web_search | 抓网页、搜资料 | 外部内容当数据不当指令 |
subagent_fork | 开子代理干活 | 子代理继承上下文,结果回传 |
workflow | 写 JS 脚本扇出几十个子代理 | 适合批量审计、迁移 |
schedule_* | 定时提醒/周期任务 | 到期以消息形式回到会话 |
job_* | 管后台作业 | 长命令不阻塞对话 |
设计上有两个克制的地方,很关键:
- 没有「万能 exec」。想跑命令就是
pwsh,它被单独计数、单独超时、单独审计。 - 写文件前必须读过(默认策略)。这条看起来烦,实际上挡住了大量「模型凭想象覆盖你的文件」。
6. 权限:三档沙箱 + 一个审批
这是 DSH 最值得先学的部分,因为它决定了「AI 能把你电脑搞多坏」。
| 档位 | 读 | 写 | 联网 | 典型场景 |
|---|---|---|---|---|
read-only | ✅ | ❌ | 受策略约束 | 第一次读陌生仓库、审别人代码 |
workspace-write | ✅ | ✅ 仅工作区 | 受策略约束 | 日常默认 |
danger-full-access | ✅ | ✅ 全盘 | ✅ | 只在一次性容器 / 明确的一次性操作里 |
审批(approval)是另一根轴:ask(默认,越界就问你)、never(全自动,适合隔离环境)。沙箱管「能不能」,审批管「问不问」,两者组合才是真实安全等级。
这里必须讲一个我踩过的真坑,很多人会撞上:
我想让一个脚本「重启自己所在的宿主进程」。天真的写法是
Start-Process一个重启脚本——必死。因为工具进程被放在宿主的 Job 对象里,宿主一退出,Job 关闭,重启脚本跟着被杀,日志永远停在killing pid ...。正确做法是用 WMI 创建进程(父进程是
WmiPrvSE,不在 Job 里):$cmd = 'powershell.exe -NoProfile -File "' + $worker + '" -DelaySeconds 40' Invoke-CimMethod -ClassName Win32_Process -MethodName Create -Arguments @{ CommandLine = $cmd }但注意:
Invoke-CimMethod在受限沙箱里会报拒绝访问(我实测就是 0x80041003)。这种操作必须显式提权执行,而且要延迟几十秒——留出时间让「我要重启了」这条回复先发出去。
7. 子代理与编排:一个人干不完就分头
两种用法,别用反:
| 方式 | 什么时候用 | 长什么样 |
|---|---|---|
subagent_fork | 一两个独立子任务 | 一句话派活,子代理继承当前对话 |
workflow | 几十个同构任务扇出 | 写一段 JS,agent() 并行跑,结果结构化返回 |
workflow 的骨架就三行,值得记住:
const results = await pipeline(files,
f => agent(`审这个文件的安全问题:${f}`, { schema: { type: 'object', properties: { issues: { type: 'array' } } } }),
(r, f) => agent(`把发现写成一句话摘要:${JSON.stringify(r)}`)
);
return results.filter(Boolean);
判断标准很简单:能在脑子里一次说清任务边界 → subagent;说不清、但能写出循环 → workflow。
8. 定时任务与后台作业
两件不同的事,名字很像但用途相反:
- 后台作业:命令跑太久,自动转成 job,你可以继续聊,回头用
job_output取结果。 - 定时任务:
schedule_create存一个提醒/周期任务,到点它主动发消息回到会话。
schedule_create { every_seconds: 3600, prompt: "巡检服务健康度,异常就报我", title: "每小时健康巡检" }
我自己的用法是「早报」:每天早上把昨天改过的仓库、失败的任务、待办汇总一条推过来。比记在日历里靠谱,因为它真的会去跑命令。
9. 插件:把它接进飞书
DSH 真正跟别家不一样的地方,是插件可以换掉整条交互链路。以我装的飞书桥为例,它在 profile 里只是一个 link: 依赖:
"dsh-feishu-bridge": "link:C:/.../default-workspace/dsh-feishu-bridge"
装好之后:
dsh plugin --profile web add dsh-feishu-bridge
dsh web # 重启宿主,插件在启动时装配
然后你在飞书里 @ 机器人说一句话,就等于在本地开了一个会话:附件落到 .feishu-attachments/,回复通过飞书卡片回来,长任务在后台继续跑。
它带来一个反直觉的体验:你不在电脑前,但电脑还在干活。我在外面用手机让它重启服务、发文章、查日志,都是这条路。
插件自己的状态放在 ~/.dsh/feishu-bridge/(config.json 热读,改完 2 秒生效;instance.lock 防重复实例)。写自己的插件时,这三件事照抄就行。
10. 五种实战工作流
工作流 A:修一个 bug
复现:`pnpm test src/cart.test.ts` 挂了,expected 99 got 100。
先定位原因,只改必要的地方,改完跑通这个文件 + src/price.test.ts。
不要顺手重构别的文件。
要点:给复现命令、给验收清单、明确「不要顺手改」。最后一句能省掉一半无关 diff。
工作流 B:读陌生仓库
用 200 字说清这个仓库是做什么的;然后给我:
1) 顶层目录各自职责 2) 一条 HTTP 入口到数据库的调用链 3) 三个最容易改坏的地方。
只读,不要改任何文件。
配合 read-only 沙箱,物理上保证它改不了。
工作流 C:批量改造
把 src/api/ 下所有 fetch 换成 lib/request.ts 的封装。
每改完一个文件跑一次 tsc,全部改完再自己 review 一遍,列出风险点。
工作流 D:无人值守的定时活
把「每天汇总」「每小时巡检」写成 schedule_create,让它自己跑,异常才叫你。
工作流 E:把成果发出去
这是我今天真干的事:写一篇 1 万字教程 → 用插件脚本填进 CSDN 编辑器 → 打标签、写摘要、发布 → 回读页面校验目录和代码块数量。整条链路在本地完成,浏览器是你自己的,登录态也在你自己的 profile 里。
11. 十个坑(都是实测撞的)
- 启动会写 profile。
dsh web启动时会重写~/.dsh/profiles/web/cordis.yml;如果你的进程对~/.dsh没有写权限,会直接EPERM退出。排查顺序:先看权限,再看版本。 - token 地址每次重启都变。收藏旧链接 = 401。把「取新 URL」写进你的重启脚本。
- 受限沙箱里 Chromium 起不来。要自动化浏览器(比如发文章),启动 Edge/Chrome 那一步需要完整权限,脚本本身不需要。
- Job 对象会杀子进程。想让脚本在宿主退出后继续活,必须用 WMI/计划任务脱离父进程,否则永远死在第一步。
- 会话文件是多帧 zstd。只解第一帧会得到一行,误判成文件损坏。
~/.dsh不在工作区里。默认沙箱下读得到、写不了;要改配置得显式提权。cordis.yml不是给你改的。它每次启动被重写,改它等于白改;改cordis.patch.yml。- bundle 顺序即优先级。把前端 bundle 放到 base 前面,会得到一个没有工具的空壳界面。
- 别在会话里贴密钥。会话是明文事件流,
session.v4.jsonl.zstd解压就能看;用env_key/ 环境变量。 - 审批弹窗永远先看「范围」。
同意一次和本会话不再问是两个量级的授权,别顺手点第二个。
12. 边界:这些事别让它干
- 生产数据库、线上服务器:沙箱再严也挡不住你自己点「同意」。
git push --force:写进AGENTS.md的禁区。- 删目录:让它
git clean -n列出来,你自己删。 - 读别人仓库的
AGENTS.md之前就跑起来:那是可执行指令,不是文档。2026 年这类供应链手法已经不是理论。 - 把 token 写进配置:环境变量 + 定期轮换。
danger-full-access+never在没有隔离的本机上:这个组合只属于一次性容器。
最后
DSH 的学习曲线不在命令,在心智模型:profile 决定装什么,AGENTS.md 决定脾气,沙箱决定边界,会话决定可复盘。这四件事想清楚,剩下的都是熟练度问题。
一个判断标准:如果你发现自己在反复跟它说同样的话,那句话该进 AGENTS.md 了;如果你发现自己反复手动做同一件事,那件事该写成 schedule 或者插件了。
最后留个问题:你现在的工作流里,哪一步是「每天都做、但从来没自动化」的?评论区聊聊,我挑几个写成下一篇。
更多推荐


所有评论(0)