会话与无界面 headless 运行

约 20 分钟

解释会话如何保存执行轨迹,以及 headless 模式如何支持无界面一次性自动化运行。

上一课的收尾是在本地 Web UI 会话里验证流式回复:那个自定义 LLM 适配器挂在 ctx.llm 的接缝上,连到云托管模型端点,把流式响应转成 StreamChunk,认证错误与瞬时故障的重试路径各归各位。交互式运行这条路已经跑通——工具、适配器与会话都在浏览器这个驾驶舱里完成过验证。现在换一个场景:凌晨的 CI 流水线里没有人在屏幕前点按钮,任务却要照常执行,失败要能被追溯。你需要的不是又一个界面,而是一台能自动跑完任务并退出的运行器,外加一份事后能查证的运行记录。前者是 headless 运行器,后者是会话轨迹。焦点只有两个:会话如何保存执行轨迹,headless 如何支撑一次性自动化运行。两件事分开看,最后在流水线里合起来用。

核心概念

会话:只追加的执行轨迹

类比:会话像一个飞行记录仪。飞机不会只记录乘客的闲聊,而是持续把仪表读数、操作与时间戳写进黑匣子;无论复盘还是调查,都从这份只追加的记录还原现场。技术定义:session 是 core/session 插件提供的仅追加 SessionEvent 日志与内存存储,挂载在 ctx.sessions 上。一次运行中,系统提示词、推理内容、工具调用与结果、子 Agent 调度与注入的上下文,都按发生顺序追加进这条日志。日志只追加、不覆盖,因此保存的是完整执行轨迹,而不只是对话文本。Trajectory 视图按来源展示这些记录,让你看到模型每一步看到了什么、做了什么。查证一次失败,从这条轨迹开始,而不是靠回忆或截图。继续一个会话也不是新建文件,而是在同一条事件流上接着追加——Resume、fork、search、replay 都基于这条流。

轨迹是流水线的黑匣子,也是一份可操作的数据。检索:按来源或事件过滤记录,定位某一次工具调用。重放:用同一条事件流再跑一遍,验证改动后的行为是否如预期。分叉:从历史某一点复制一份轨迹,另起分支继续实验。检索、重放与分叉共用同一条事件流。你之前用 defineTool 开发的工具,每次被 agent 循环调用,调用与结果都会成为该会话日志中的事件。排查'模型改错了文件'这类事故时,你能精确看到是哪个上下文、哪次调用导致了失败。注意两个边界:一是轨迹能还原过程、提升可复现性,但不能保证结果正确;二是会话默认活在内存里,持久化、查询与遥测由独立子系统承担,需要时另行启用。这意味着轨迹可以当回归素材:重放它,看改动后的行为是否仍然成立。报告问题或复盘事故时,附上轨迹比贴一段对话更接近现场——它记录了当时的全部上下文。

核心概念

headless:无服务器的一次性运行器

类比:headless 像一台自动售货机。柜台需要店员全程在场,售货机不需要——投币、按键,它执行一次交易就待机,放在没有人的走廊照常工作。技术定义:headless 是随 dsh 一起发布的一个 profile 模板,与 web 模板并列;它由 dsh-headless 捆绑包组装,提供一次性运行器(one-shot runner),完全不启动服务器。进程接受任务、运行、退出,不留常驻服务、不占端口。因为无界面、无服务器,它适合嵌入脚本与 CI:把任务交给它,跑完取结果,成功或失败都留下会话轨迹供你回查。它与 web 共用同一套配置档案与插件生态,区别只在运行形态,而不是另一套产品。一次运行对应一个进程,参数化输入即可复用同一个 profile 跑不同任务。

headless 不是单独的程序,而是由 profile 组装出来的运行形态,这套机制你在配置档案一节已经见过,这里只补它和运行形态的关系。profile 列出它堆叠的 bundle、安装的插件,并持有你的 cordis.patch.yml;web 与 headless 就是随 dsh 提供的两个 profile 模板。dsh-base 是每个 profile 的第一层:模型适配器、工具、持久化、沙箱与审批策略都在其中。web 模板在其上追加浏览器应用,headless 模板在其上追加一次性运行器——两者共享基座,差别只在堆叠的 bundle。配置按固定顺序叠加:先按 profile 列出的顺序应用每个 bundle,再应用 profile 的 cordis.patch.yml,然后是用户主目录级补丁,最后是 --patch 覆盖层。你前面写的补丁逐层盖上去,就能定制 headless 的模型、工具与策略。补丁按行 id 定位并整体替换该行配置,或插入新行——这正是你覆盖 headless 默认行为的入口。

把这两件事放进一条流水线看:准备阶段用 profile 与补丁固定任务形态;执行阶段由 headless 一次性运行器接手,无人值守地跑完;收尾阶段靠会话轨迹留痕、检索与分叉,让失败可归因。CI 的每次触发都复现同一套准备与执行,差异只来自输入;这正是脚本化编排想要的可重复性。准备、执行、留痕三个阶段互不耦合:你可以单独更换模型或任务,不必改动整条流水线。没有图形会话,是它适合进调度器与流水线脚本的根本原因。

自检:一次 headless 运行结束后进程退出,也没有人守在屏幕前。你凭什么还能审计这次自动化,说清模型当时做了什么、为什么失败?

凭会话轨迹。session 以仅追加的 SessionEvent 日志保存系统提示词、推理、工具调用与结果等完整执行记录,挂在 ctx.sessions 上;这条轨迹可检索、可重放、可分叉,Trajectory 视图按来源展示。headless 负责一次性执行,会话负责留痕与复现——两者合起来才构成可审计的自动化。