用 headless 模式跑通自动化编排流水线

约 30 分钟

在 headless 模式串联自定义工具与 LLM 适配器,完成一次端到端自动化任务并产出结果。

上一课你已经看到,session 把每次执行以只追加的 SessionEvent 日志落盘,headless profile 则以无服务器的一次性运行器支撑 CI 与脚本化编排——两者配合,自动化才有迹可循、可复现、可审计。这一课就把这套底座真正用起来:不做新开发,只做装配。

你已经有两个插件:lesson-2-4 用 defineTool 写的工具,lesson-4-1 写的自定义 LLM 适配器。headless 模式把它们接成一条流水线:模型走你的适配器,动作走你的工具,跑一次,拿结果。

dsh-headless 是无服务器的一次性 runner(one-shot runner)。它不启动浏览器,不挂常驻服务;任务结束,进程就退出。官方教程把 headless 命令行与 Web UI、Python SDK 并列,是三种接入方式之一。你要交付一条命令:成功返回,输出里能看到端到端任务的结果。

核心概念

profile 模板:headless 启动时的配置分层

先看生活类比。点外卖时,你选一个套餐,套餐里固定几样东西;你在备注栏加料;骑手出发前还能再贴一张手写纸条。最终送到的,是这几层叠加的结果。headless 启动也是这个逻辑。

技术定义:运行中的 dsh 是启动时按有序层级组合出的插件树。profile 是存放在 Harness home 的命名组合:列出要叠加的 bundles,容纳安装的树外插件,保存你自己的 cordis.patch.yml。web 与 headless 随发行版作为模板提供;headless 模板叠加 dsh-headless,即无服务器的一次性 runner。

层级按顺序应用到空条目列表:profile 列出的每个 bundle → profile 的 cordis.patch.yml → home 级 cordis.patch.yml → 任何 --patch 覆盖层。补丁按行 id 定位:替换整行配置,或插入新行。想覆盖模板里某一行,就把它写进更高层。

在 headless 模式跑通一次端到端任务

  1. 1

    确认安装里带 web 与 headless 两个 profile 模板。两者都以 dsh-base 为第一层:模型适配器、工具、持久化、沙箱与审批策略、设置、凭据、遥测都在这一层;headless 模板再叠加 dsh-headless,得到无服务器的一次性 runner。

  2. 2

    把你的工具插件与适配器插件打包成 bundle。bundle 是 Cordis 配置行与所挂载代码的分发格式;在 package.json 的 dsh 字段里声明 dsh.bundle,指向该 bundle 的补丁文件。

  3. 3

    让 profile 叠加上这两个 bundle。profile 用 dsh.profile 字段列出要叠加的 bundles;再把工具与适配器需要的配置行写进 profile 的 cordis.patch.yml。补丁按行 id 替换整行或插入新行。

  4. 4

    启动 headless 一次性运行,把任务作为参数传入。任务结束进程即退出,输出通过命令行返回,执行轨迹写入仅追加的会话日志。命令沿用 dsh web 的入口约定;若你的版本子命令不同,以 --help 为准。

    sh
    dsh headless "调用自定义工具完成一次巡检,并汇报结果"
  5. 5

    核对退出码与输出。确认命令成功返回;在命令行输出里找到工具返回值与模型最终回答;再打开会话轨迹核对工具执行记录。非零退出时,先看错误输出,再打印实际启动的插件树——文档说明任何打印出的行都可以用你自己的补丁替换。

你应该看到

headless 命令成功返回;命令行输出包含工具调用结果与适配器驱动的模型最终回答;会话轨迹中可见端到端执行记录;全程未启动服务器或浏览器。

遇到问题:headless 跑起来了,但模型看不到你的工具或适配器;补丁像没生效,或报错说某行配置不存在。

可能原因

  • 补丁按行 id 定位。行 id 写错、大小写或命名空间不符,补丁就不会命中任何行。
  • 层级顺序问题。profile 的 cordis.patch.yml 会被 home 级补丁和 --patch 覆盖层压过,低层补丁可能被高层整行替换。
  • 版本差异。开发者预览中字段名或模板内容可能已经变化。

排查步骤

  1. 打印实际启动的插件树,对照输出核对行 id。文档说明:任何打印出的行都可以用你自己的补丁替换。
  2. 把补丁放进更高层级,例如 --patch 覆盖层,确认补丁是否生效。
  3. 钉住包版本,核对当前文档与 --help,按实际字段名重写补丁。
自检:headless 启动时,配置层级按什么顺序应用?要覆盖模板里某一行配置,最稳的做法是什么?

顺序是:profile 列出的每个 bundle → profile 的 cordis.patch.yml → home 级 cordis.patch.yml → 任何 --patch 覆盖层。覆盖某一行时,先打印实际启动的插件树核对行 id,再把补丁放进最后的 --patch 层,或在 cordis.patch.yml 里按行 id 替换整行。