生命不息,折腾不止。这一篇把「人工守着点」变成「写完睡觉、明早收结果」——不开浏览器、不起服务,一条命令让 dsh 的 Agent 自己把活干完。

前面几篇我们一直在 Web 界面里点来点去玩 dsh(DeepSeek Harness),今天聊点真正能落地的:无头模式(headless)。它干的事特别朴素——跑一个任务、把最后一段回答打到终端、然后退出。就这三个动作,却正好是自动化(cron、CI、shell 脚本)要的形状。上个月就有不少兄弟问我「能不能让它半夜自己干活」,答案全在这一篇。

一、先想明白:dsh 不是「聊天终端」,它是个 profile 启动器

很多人以为 dsh 命令是个类似终端聊天(TUI)的东西,其实不是。官方对它的定位很明确:dsh 是一个 profile 启动器,它本身不管交互,只负责把你交给某个「profile」(配置组合)去跑。它有四个入口:

命令 干什么
dsh --profile <name> 启动 $DSH_HOME/profiles/<name> 这个 profile
dsh --profile headless "任务" 跑一次性任务,执行完退出
dsh web --profile web 的硬编码别名(就是大家天天用的 Web 界面)
dsh plugin --profile <name> <参数> 管理 profile 的插件依赖(转发给 pnpm)

注意第 2 个。headless 和 web 一样,都是内置保留的 profile 名,第一次用会自动从随附模板初始化——web = base + web-app,headless = base + headless。

关键差别在于:headless 这个 bundle 不挂 ApiProxy、HTTP 服务、Web runtime,也不带浏览器客户端。翻译成人话就是——它一个监听端口都不开,没有东西会被意外暴露到网络上,启动开销也小一圈。而文件编辑、命令执行、AGENTS.md/CLAUDE.md 上下文加载(有 65,536 字节的预算上限)、工作区写沙箱这些,跟 web 模式完全一致。

还有一个容易踩的坑:老教程里的 dsh run 子命令已经移除了,一次性任务统一改成 dsh --profile headless "任务"。别照着旧资料抄 dsh run。

二、跑起来:第一条 headless 命令

前置环境跟装 dsh 时一样:Node.js ^22.19.0 或 >=24.0.0。零安装直接:

1
npx @deepseek-ai/dsh --profile headless "把当前目录的测试跑一遍并汇总结果"

引号里那一串就是任务本身,是位置参数,不是 flag。它背后发生的事是这样的:

  1. 建一个全新的持久化 Session;
  2. 把你这句话当成一条用户消息提交进去;
  3. 等 Agent 跑到「安静下来」(quiescence);
  4. flush 会话、把最后一段非空的助手文本打到 stdout;
  5. 退出。

所谓「一次性」,指的是一个任务,而不是「一次模型调用」——Agent 在内部可以自己决定读多少文件、调多少工具、甚至派几个子 Agent,这中间可能有几十次调用。所以任务文本写得好不好,直接决定结果靠不靠谱。反例:「帮我修测试」太含糊;正例:「tests/orders 里的测试挂了,找出根因、修生产代码、别动测试文件本身」。

来看几个能直接抄的真实用例:

1
2
3
4
5
6
7
8
# 让 Agent 总结当前仓库,只读不写
dsh --profile headless "Inspect this repository. Do not edit files. Run read-only checks and report failures with file evidence."

# 审查某个文件的鉴权逻辑,给可执行建议
dsh --profile headless "审查 src/server.ts 的鉴权逻辑,给 3 条可执行建议"

# 把结果存到文件
dsh --profile headless "Summarize this repository and identify its main packages." > /tmp/out.txt

三、退出码是自动化的「契约」,得看懂

headless 模式之所以适合塞进脚本,靠的就是退出码。官方给的契约很清晰:

退出码 含义
0 最终 turn 的结束原因是 completed(任务正常完成)
1 没到 completed,或者 runner 本身挂了
130 收到第一个 SIGINT(Ctrl+C)后优雅关闭

有两个坑要专门提:

  • 退出码 0 只证明「Agent 跑完了」,不证明你要的文件真的存在、部署真的成功、测试真的过了。外部效果得自己另外验证。
  • SIGTERM 会被当成正常的停止请求,任何情况下都退出 0。如果你用 systemd、CI 的超时机制去杀它,超时杀掉和「正常完成」在退出码上分不出来,得另外看任务输出/状态来判断。

写个最朴素的 shell 包装器,靠退出码决定脚本走向:

1
2
3
4
5
6
7
8
#!/usr/bin/env bash
set -euo pipefail
if dsh --profile headless "run the test suite and report any failures"; then
echo "Agent 任务完成。"
else
echo "Agent 任务没跑完。" >&2
exit 1
fi

四、让它半夜自己干活:环境变量 + cron

headless 从环境变量里读凭据和配置,这正是它「流水线友好」的原因——没有交互式弹窗、没有 keyring、不会把配置文件误提交进仓库。常用的几个:

1
2
3
4
5
6
7
export DSH_HOME=/custom/path          # harness 主目录,默认 ~/.dsh
export DEEPSEEK_API_KEY=sk-xxx # 凭据,从 CI secret 注入,别硬编码进脚本
export DEEPSEEK_BASE_URL=https://你的端点/v1 # 指向任意 OpenAI 兼容接口
export DSH_MODEL=deepseek-v4-flash # 钉死模型,别让默认模型被悄悄换掉
export DSH_PERMISSION_MODE=workspace-write # 覆盖进程级权限预设
export DSH_TOOLS_MODE=native # native | code | both
export DSH_TELEMETRY_DISABLED=1 # 硬关遥测

(这些变量名是社区教程里的通用约定,具体以官方文档为准。)其中 DSH_MODEL 钉死模型这件事,我建议当成非协商项——不然哪天服务商改了默认模型,你半夜那条 cron 的行为和账单就悄悄变了。

DEEPSEEK_BASE_URL 指向任何 OpenAI 兼容接口都能跑,这正是 dsh「多模型对接 + 价格便宜」的卖点。想换成 Claude/GPT 这类国外模型,把 BASE_URL 指到中转站 ai.aklibk.com 就行(国内直连、免绑卡、人民币按量付费),前几篇《自定义 Provider》有完整步骤。

一条夜里跑代码审查的 cron,长这样:

1
2
3
4
0 2 * * * cd /srv/my-app && \
DEEPSEEK_API_KEY=sk-xxx DSH_MODEL=deepseek-v4-flash \
dsh --profile headless "review yesterday's commits and post a summary" \
>> /var/log/dsh-nightly.log 2>&1

几个要点:cd 到正确的目录再启动——headless 和 web 一样,把「当前工作目录」当成默认的工作区根,而且任务里没法事后把 Session 挪到更安全的仓库去;日志就落到上一篇讲的会话 JSONL 里(~/.dsh/sessions/),等于自动留了一份可追溯的「账本」,出问题几周后都翻得出来。

五、插件是分 profile 的,headless 里要单独装

这是最容易被忽略的一个坑:你在 web 界面里装的插件,headless profile 里不会自动有。插件是 profile 作用域的,得单独给 headless 装:

1
dsh plugin --profile headless add @deepseek-ai/dsh-subagent-codex

机制跟 Web UI 一模一样,同一个插件市场、同一套版本解析。所以下次你发现「插件在 web 里好用、cron 里没生效」,先别怀疑人生,dsh plugin --profile headless add <包名> 补一遍再说。

六、写在最后

无头模式是 dsh 里过了演示阶段之后最有用的一个模式——它把「人」从循环里拿掉,换成退出码和日志文件,于是 Agent 从「玩具」变成了能进 cron、进 CI、进脚本的「零件」。但也要清醒:它是一次性的、没有中途纠偏的机会,含糊的任务 + 没人在场 = 凌晨三点自信满满地跑偏。所以任务文本写得越具体、模型钉得越死,它越像个靠谱的夜班同事。

生命不息,折腾不止。下一篇我把这个系列收个口——《DeepSeek Harness 速查表:模型、插件、沙箱、权限、Creator、Trajectory、无头自动化一条命令清单》——把这一路所有命令和配置串成一张能收藏的 cheat sheet,想不起来的时候翻一眼就够。