dshkit

DeepSeek Harness 无头运行(以及接进 CI)

headless profile 跑一次会话、输出结果、退出,不起服务端。怎么调用、为什么退出码重要、以及怎么让 CI 的 profile 和你手动用的那个彻底分开。

更新于 2026-08-132 min
一句话结论

dsh --profile headless "任务" 执行单次会话、输出结果、终止。它在 dsh-base 上叠 dsh-headless,所以没有网页服务端。无效命令、配置错误和启动失败一律非零退出——这正是它能被脚本安全包起来的原因。

被拿去演示的总是那个交互式浏览器 agent,但 harness 真正开始产生价值,是在 headless profile——agent 从「一个你手动开着用的东西」变成「一个你排期跑的东西」的那一刻。

调用方式

dsh --profile headless "总结一下测试套件为什么在挂"

它执行一次会话、输出结果、终止。不起服务端、不绑端口、不等输入。

架构上它和交互式 agent 是同一个底座、换了顶层:headlessdsh-base 上叠 dsh-headless,而 web 叠的是 dsh-web-appdsh-base 里的一切——模型适配器、工具、持久化、沙箱与审批策略、凭证、遥测—— 两种情况下都在。你换的是套在 agent 外面的壳,不是 agent 本身。

退出码就是契约

这是让 headless 可脚本化的关键性质:无效命令、属于别的模式的选项、配置错误和启动失败,一律非零退出

这条保证比听上去值钱。自动化 agent 里你怕的不是崩溃,而是「悄悄成功但什么都没干」。一个放错位置的 flag 会让 launcher 非零退出,意味着坏掉的流水线在第一次运行就暴露,而不是第十次。

if ! dsh --profile headless "$JOB"; then
  echo "harness 执行失败" >&2
  exit 1
fi

参数顺序,再说一遍

launcher 先解析自己的 flag,遇到第一个不认识的 token,之后全部算 app 的参数。把 --profile 放在任何给 app 的东西前面:

dsh --profile headless --some-app-flag "任务"

在 shell 脚本里这比交互式更要命,因为你会插值变量,而一次空展开就能让那条分界线位移。给变量加引号。

给 CI 单独一个 profile

别让 CI 指向你手动在用的那个 profile。profile 很便宜而且互相隔离,插件又是装进 profile 自己的 node_modules,所以单独一个 profile 白送你一套独立作用域的插件集。

好处是叠加的:

  • 受限的插件集:CI 没道理持有这个任务用不到的能力。
  • 更严的审批策略:审批和沙箱策略是 dsh-base 的行,CI profile 可以把它们 patch 得更紧,完全不动 你交互用的那套。
  • 可复现:你本地的 profile 会随着你折腾而漂移;一个 cordis.patch.yml 进了版本控制的 CI profile 不会。
  • 爆炸半径:搞坏本地 profile 的实验,不会搞坏流水线。

像建其它 profile 一样建好它,然后确认它实际组装出了什么:

dsh --profile ci --dump-config

CI 里的凭证

自定义服务商通过 apiKeyEnv 指定的环境变量读 key,这和 CI 注入密钥的方式严丝合缝——在流水线里把密钥 设成环境变量,按名字引用即可。

存在 $DSH_HOME/.credentials.yaml 里的目录内服务商 key 反而更别扭,因为那要求这个文件存在于 runner 上。自动化场景优先走自定义服务商这条路,让凭证从 CI 的密钥库来,永远不落盘。

记住运行中的进程持有它启动时那份环境。在流水线里这通常不成问题——每一步都是全新启动——但也意味着, 在某些 CI 系统里,上一步 export 的密钥在后面的步骤里看不见。

headless 解决不了的事

它也不能替你决定「这个 agent 被允许干什么」。沙箱和审批策略来自 dsh-base 而且可 patch——这是把 双刃剑。一个持有仓库写权限、审批策略又很宽松的自动化 agent,是一项部署决策,不是便利设置。有意识地 决定它,并把编码这项决策的 patch 放进版本控制,让 reviewer 看得见。

常见问题

web profile 和 headless profile 有什么区别?

两者都叠 dsh-base。web 加 dsh-web-app 提供浏览器能力,headless 加 dsh-headless 提供无服务端的单次执行。

headless 模式会检查退出码吗?

会。无效命令、属于别的模式的选项、配置错误和启动失败一律非零退出,所以配错的流水线会大声失败,而不是悄悄成功。

CI 里能用另一套插件吗?

能,而且应该。插件按 profile 装进各自的 node_modules,所以 CI profile 可以持有一套刻意受限的插件集,完全不影响你交互用的 profile。

接着看