DeepSeek Harness Headless 模式:从 CLI 和 CI 跑一次性 Agent 任务
用 dsh --profile headless 运行 DeepSeek Harness headless 模式,理解退出码与 SIGTERM 行为,并把一次性 agent 任务脚本化接入 CI。
dsh --profile headless "你的任务" 从终端跑一个单次 agent 任务,打印最终回答然后退出——没有 Web UI,没有监听端口,不需要浏览器。这正是脚本、cron 任务和 CI 流水线该用的模式。本文讲清楚这条命令、它的退出码、它和 web profile 在底层的差异,以及如果你后面需要脱离 shell 脚本,它和 SDK 之间是什么关系。
命令
dsh --profile headless "summarize the open pull requests in this repository"
引号里的字符串是一个位置参数——任务文本本身,不是一个 flag。在底层,dsh 会新建一个持久化会话,把它跑完,把 agent 的最终回答打印到 stdout,然后退出。
headless 模式到底砍掉了什么
headless 和 web 都是保留 profile 名——第一次使用时会自动用内置模板初始化(web = base + web-app bundle,headless = base + headless bundle)。实际的差异在于:headless bundle 不会挂载 web profile 会带上的 API 代理、HTTP host、web 服务器、web 运行时或浏览器 client。这意味着:
- 完全没有监听端口——没有任何东西可能意外暴露到网络上。
- 没有 Web UI 可点——你得到的是一次纯粹的终端交互。
- 更小的启动开销,因为一整层面向 web 的插件根本不会加载。
除此之外——工具执行、AGENTS.md/CLAUDE.md 上下文加载(受限于 65,536 字节的预算)、默认的 workspace-write 沙箱——行为都和 web profile 下一致。
退出码
| 退出码 | 含义 |
|---|---|
0 | 任务达到 completed 状态 |
1 | 任务未成功完成 |
130 | 进程收到 SIGINT(Ctrl+C) |
SIGTERM——编排系统(systemd、进程管理器、CI runner 的超时机制)通常发送的信号——在任何场景下都被视为正常的停止请求,退出码总是 0。插件树最多有 5 秒的优雅关闭窗口 来完成清理;第二次收到信号会强制立即退出。如果你在用有自己超时逻辑的编排层来包装 headless 运行,这一点很重要:仅凭退出码,一次由 SIGTERM 触发的超时看起来和任务成功完成没有区别,所以如果你需要区分"我们把它杀掉了"和"它自己跑完了",请单独检查任务输出/状态。
把 headless 脚本化
一个检查退出码的最简 shell 包装:
#!/usr/bin/env bash
set -euo pipefail
if dsh --profile headless "run the test suite and report any failures"; then
echo "Agent task completed."
else
echo "Agent task did not complete." >&2
exit 1
fi
对一批输入循环执行 headless——比如对本地检出的多个仓库分别跑同一个任务:
#!/usr/bin/env bash
set -euo pipefail
for repo in ./repos/*/; do
echo "== $repo =="
(cd "$repo" && dsh --profile headless "review the diff and flag anything risky")
done
因为每一次调用都是一个全新的、完整的会话(调用之间没有需要保持热启动的服务进程),如果你的 CI runner 支持,跨不同工作目录并行跑这个模式在机制上是安全的——唯一需要留意的约束是你所配置模型 provider 的 API 速率限制,而不是 dsh 本身。
headless 的 profile 设置
和任何非保留 profile 一样,headless 在第一次使用时会用默认 bundle 模板自动引导。如果你想要一个挂载不同插件的 headless profile——比如给 CI 用的精简工具集——可以创建一个独立的自定义 profile 名(而不是 headless 本身),再往里装:
dsh plugin --profile ci-headless add github:owner/repo
dsh --profile ci-headless "run the linter and summarize violations"
用这种方式启动的任何 profile,在 I/O 行为上都和 headless 完全一样——没有端口、一个任务、跑完打印退出——因为这个行为来自挂载了哪些 bundle(更准确地说,是没有挂载面向 web 的 bundle),而不是来自 headless 这个特定名字。
结合 --patch 和 --dump-config
有两个 launcher 层面的 flag 值得和 headless 运行一起了解,因为它们对任何 profile 都适用,也可以叠加在一次 headless 调用之上:
# 为这次运行额外叠加一层配置覆盖
dsh --profile headless --patch ./ci-overrides.yml "run the linter"
# 查看一次 headless 运行实际会用到的、完全合并后的配置,但不实际运行
dsh --profile headless --dump-config
--patch 和 --dump-config 是 launcher 自身的 flag,不是应用专属的 flag,所以必须写在任务字符串之前——launcher 遇到的第一个它不认识的 token,就被当作应用自己参数的起点。当你要排查一个 CI 任务和本地 headless profile 行为不一致的问题时,--dump-config 特别有用:在两个环境里各跑一次,diff 输出就能精确看出到底是哪一层配置不同。
模型配置照样适用
headless 模式读的是和其他 profile 一样的共享模型配置——$DSH_HOME/settings.yaml 和 $DSH_HOME/.credentials.yaml——因为 provider/模型配置存在 profile 之外。如果你还没配置 provider,先通过 Web UI 配置好(见 配置你的 DeepSeek API Key 与模型),或者在 CI 调用之前用环境变量设置凭据:
export DEEPSEEK_API_KEY=your-key-here
dsh --profile headless "your task"
headless 与 SDK 的关系
如果用 shell 脚本调用 dsh --profile headless 开始让你觉得受限——你需要结构化输出、对多轮交互的编程式控制,或者你正在其上构建更大的自动化系统——下一步可以考虑 SDK,它通过 stdio JSON-RPC 协议驱动 dsh,而不是解析终端输出:
- TypeScript SDK(
dsh-sdk-client)——用于基于 Node.js 的自动化。 - Python SDK(
deepseek-harness-sdk,pip install deepseek-harness-sdk)——内置打包好的运行时,不需要系统级 Node.js,但平台限定在 Linux x64/arm64 和 macOS 14+(arm64)。
两者对接的是同一套底层协议;headless 模式是 CLI 原生、零额外依赖的入口,而 SDK 适合你需要把 agent 嵌入一个更大的程序中,而不是当作子进程调用的场景。
FAQ
headless 模式会打开任何网络端口吗?
不会。headless bundle 明确不挂载 API 代理、HTTP host、web 服务器、web 运行时或浏览器 client——完全没有任何东西在监听。
我的 CI 应该检查哪个退出码来判断成功?
0 表示任务达到了 completed 状态。任何其他退出码(1 表示任务未完成,130 表示 Ctrl+C)都应该视为失败。外部超时触发的 SIGTERM 关闭同样会返回 0,所以如果你需要区分"我们把它杀掉了"和"它确实跑完了",请检查任务输出/状态,而不是只依赖退出码。
我能并行跑多个 headless 任务吗?
每一次 dsh --profile headless 调用都是一个独立的进程和会话,所以跨不同工作目录并行运行几个实例,从机制上讲通常没问题——你更可能碰到的约束是所配置模型 provider 的 API 速率限制,而不是 dsh 本身的限制。
headless 模式和 --dump-config 这类 CLI 参考工具是一回事吗?
不是——--dump-config/--dump-default-config 和 --patch 是 launcher 层面的 flag,不管哪个 profile 都能用,用于查看或覆盖配置,而不是运行一个任务。完整的 flag 参考见我们的 CLI 速查表。
做自动化时该用 headless 模式还是 Python/TypeScript SDK?
headless 模式是最简单的入口——一条 shell 命令、一个任务、纯文本输出——很适合 CI 步骤和 cron 任务。当你需要超出解析 stdout 的结构化/编程式控制时,再考虑 SDK:详见 DeepSeek Harness Python SDK。
Next steps
- DeepSeek Harness CLI 速查表 —— 每一个 launcher flag 和环境变量。
- DeepSeek Harness Python SDK —— 超越 shell 脚本的编程式控制。
- 配置你的 DeepSeek API Key 与模型 —— 在 headless/CI 运行前配置好凭据。
- DeepSeek Harness 快速上手 —— 如果你还没安装 dsh。