版本基准: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 CodeCodex CLIDSH
本质一个固定的 agent 应用一个固定的 agent 应用一层启动器 + 一堆可替换插件
扩展方式插件 / MCPAGENTS.md + MCPprofile:有序的插件 bundle 栈 + 你自己的补丁层
入口CLI + IDECLI + IDE + 云CLI / Web / headless / SDK / ACP / 飞书
换界面改不了改不了换一个 bundle 就是另一个前端(Web、TUI、桌面)
配置粒度全局 + 项目全局 + 项目全局 + profile + 项目 + 命令行 --patch

一句话记法:模型是脑子,DSH 是身体,profile 是这次装成什么形状的身体。

它现在有六个入口,共用一个 dsh 命令:

入口命令适合谁
Web GUIdsh web想要图形界面、看会话列表、点按钮的人
终端dsh tui习惯终端、不想开浏览器的人
一次性任务dsh headless "跑测试"脚本 / CI 里只要一个结果
SDK / ACPdsh --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

两个细节值得记住:

  1. token 在 URL 里,每次重启都会变。别收藏旧链接,重启后去日志或 dsh-web-url.txt(如果你像我一样写了重启脚本)里拿新的。
  2. 工作区默认就是「你执行命令时所在的目录」。想换工作区,先 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_*管后台作业长命令不阻塞对话

设计上有两个克制的地方,很关键:

  1. 没有「万能 exec」。想跑命令就是 pwsh,它被单独计数、单独超时、单独审计。
  2. 写文件前必须读过(默认策略)。这条看起来烦,实际上挡住了大量「模型凭想象覆盖你的文件」。

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. 十个坑(都是实测撞的)

  1. 启动会写 profile。dsh web 启动时会重写 ~/.dsh/profiles/web/cordis.yml;如果你的进程对 ~/.dsh 没有写权限,会直接 EPERM 退出。排查顺序:先看权限,再看版本。
  2. token 地址每次重启都变。收藏旧链接 = 401。把「取新 URL」写进你的重启脚本。
  3. 受限沙箱里 Chromium 起不来。要自动化浏览器(比如发文章),启动 Edge/Chrome 那一步需要完整权限,脚本本身不需要。
  4. Job 对象会杀子进程。想让脚本在宿主退出后继续活,必须用 WMI/计划任务脱离父进程,否则永远死在第一步。
  5. 会话文件是多帧 zstd。只解第一帧会得到一行,误判成文件损坏。
  6. ~/.dsh 不在工作区里。默认沙箱下读得到、写不了;要改配置得显式提权。
  7. cordis.yml 不是给你改的。它每次启动被重写,改它等于白改;改 cordis.patch.yml。
  8. bundle 顺序即优先级。把前端 bundle 放到 base 前面,会得到一个没有工具的空壳界面。
  9. 别在会话里贴密钥。会话是明文事件流,session.v4.jsonl.zstd 解压就能看;用 env_key / 环境变量。
  10. 审批弹窗永远先看「范围」。同意一次 和 本会话不再问 是两个量级的授权,别顺手点第二个。

12. 边界:这些事别让它干

  • 生产数据库、线上服务器:沙箱再严也挡不住你自己点「同意」。
  • git push --force:写进 AGENTS.md 的禁区。
  • 删目录:让它 git clean -n 列出来,你自己删。
  • 读别人仓库的 AGENTS.md 之前就跑起来:那是可执行指令,不是文档。2026 年这类供应链手法已经不是理论。
  • 把 token 写进配置:环境变量 + 定期轮换。
  • danger-full-access + never 在没有隔离的本机上:这个组合只属于一次性容器。

最后

DSH 的学习曲线不在命令,在心智模型:profile 决定装什么,AGENTS.md 决定脾气,沙箱决定边界,会话决定可复盘。这四件事想清楚,剩下的都是熟练度问题。

一个判断标准:如果你发现自己在反复跟它说同样的话,那句话该进 AGENTS.md 了;如果你发现自己反复手动做同一件事,那件事该写成 schedule 或者插件了。

最后留个问题:你现在的工作流里,哪一步是「每天都做、但从来没自动化」的?评论区聊聊,我挑几个写成下一篇。

Logo

更多推荐