第 21 章 Agent Harness:运行时、状态与权限¶
开场问题:同一个模型装进两个 Coding Agent,为什么一个能连续工作几个小时,另一个十分钟后就忘记目标、重复调用工具?差异往往不在模型,而在 Harness。
21.1 Harness 到底是什么¶
Agent 是第 20 章的“观察—决策—行动”闭环;Agent Harness 是承载并约束这个闭环的运行外壳。它至少负责:
- 把模型输出解析成动作并执行。
- 管理工具注册、选择、参数验证与权限。
- 保存任务状态、文件和中间产物。
- 在上下文变长时压缩、淘汰和重新注入信息。
- 处理错误、重试、暂停、恢复和人工审批。
- 调度子 Agent,并合并结果。
- 记录完整轨迹、成本和可观测信息。
模型只负责预测“下一步做什么”。长任务能否可靠完成,很大程度取决于 Harness 是否把正确的信息留在上下文、把危险动作挡在外面、把工具错误变成模型可理解的观察。
它不是一个新模型架构,也不只是几行 ReAct prompt。可以把它理解为 Agent 的操作系统:向上给模型稳定接口,向下管理工具、状态、资源与权限。
21.2 不要混淆三种 Harness¶
三个相似名字解决不同问题:
- Agent Harness:线上运行 Agent 的控制循环,本章主题。
- Evaluation Harness:第 14 章的实验协议,用同一套任务、解析和指标公平评测模型。
- Environment Harness:包裹训练/评测环境,动态调整任务、观察和反馈,但保留底层验证器。它属于第 22 章的前沿方向。
Evaluation Harness 可以启动 Agent Harness 去完成任务;Environment Harness 可以为 Agent 提供变化后的训练环境。三者可以组合,但不是同义词。
21.3 控制循环不能全交给模型¶
一个典型循环是:
- 从持久状态恢复任务。
- 根据当前阶段选择需要的工具定义。
- 组装受预算约束的上下文。
- 调用模型得到文本、工具调用或结束信号。
- 校验动作与权限。
- 执行并把结果转成结构化观察。
- 更新状态、预算和轨迹。
- 判断成功、失败、等待审批或继续。
步骤 2、3、5、7、8 必须有确定性代码参与。若连“是否已经成功”和“还有多少预算”都让模型用自然语言自行判断,系统会重复工作、越权或永不结束。
21.4 上下文管理是 Harness 的核心能力¶
长任务会产生大量工具输出、代码 diff、网页和子 Agent 报告,全部塞回上下文最终一定溢出。Harness 要区分:
- 始终保留:用户目标、硬约束、权限和验收条件。
- 结构化状态:待办、已完成项、文件路径、提交 ID、测试状态。
- 按需取回:长文档、旧日志、网页内容。
- 可以丢弃:重复输出、已被后续结果取代的中间尝试。
Compaction 不是把历史随便总结成一段话。摘要必须保存未完成承诺、失败原因和外部资源引用;事实性产物仍放在文件或数据库中,需要时重新读取。上下文是工作内存,不是唯一存储。
21.5 工具发现与按需加载¶
工具增到几百个后,把所有 schema 放进 prompt 会浪费 token,也降低选择准确率。现代 Harness 先让模型搜索工具目录,只把相关工具定义加载到当前上下文。
还可以让 Harness 直接用代码编排简单的数据流:并行调用多个搜索工具、过滤结果、做确定性聚合,只把精炼结果交回模型。这样既省上下文,也减少模型在机械搬运数据上出错。
原则是:能由确定性程序完成的编排,不要消耗一次模型推理。
21.6 持久任务、暂停与恢复¶
真正的 Agent 任务可能持续数小时或数天,会等待 CI、人工审批或外部事件。Harness 不能依赖一个常驻 Python 进程和内存中的消息数组。
每一步完成后保存:
- 当前状态和待办。
- 模型与 prompt 版本。
- 已用预算。
- 工具调用及幂等键。
- 环境快照或外部资源版本。
- 下一次恢复所需的最小上下文。
恢复时要避免重复执行已有副作用。持久化、幂等性和事件驱动状态机,比“再给模型一个更强 prompt”更能决定长任务可靠性。
21.7 子 Agent:上下文隔离比角色扮演更重要¶
子 Agent 的主要价值不是让多个模型扮演“研究员、评论家”,而是:
- 并行处理真正独立的子任务。
- 给每个子任务独立、干净的上下文。
- 防止主线程被大量搜索细节污染。
- 让不同子任务使用不同模型、工具和预算。
父 Agent 必须给出明确的输入、交付物和终止条件。子 Agent 返回的应是结论和证据引用,而不是完整思考历史。共享同一份可写状态时还要处理冲突,不能假设多个 Agent 自然会协调。
21.8 权限、审批与策略引擎¶
Harness 才是真正的执行边界。每个工具应声明:
- 读还是写。
- 可访问的资源范围。
- 是否有外部副作用。
- 是否可自动重试。
- 是否需要人工批准。
模型不能通过在参数里写“用户已经批准”绕过策略。审批结果应来自独立通道,并绑定具体动作摘要;动作发生变化后要重新审批。
21.9 可观测性:记录语义事件¶
除了模型延迟和 token,Harness 还应记录:
- 每轮选择了什么动作,为什么进入下一状态。
- 哪些上下文被加入、压缩或丢弃。
- 工具发现和选择结果。
- 重试原因和错误分类。
- 子 Agent 的派发、取消和合并。
- 权限拒绝和人工审批。
只有这些语义事件,才能判断失败源于模型、上下文、工具还是控制逻辑。
21.10 Agent 评测的单位是完整轨迹¶
保存每一步:
- 当时可见的消息和状态。
- 模型原始输出。
- 解析出的动作。
- 工具输入、输出、耗时和错误。
- 环境状态变化。
- token 和费用。
- 最终验证结果。
只有最终答案无法分析失败,也无法把成功轨迹回流训练。
21.11 环境必须可重置¶
编程 Agent 要固定仓库 commit、依赖和测试;浏览器 Agent 要固定网站快照或模拟后端;操作系统任务要在容器/虚拟机中恢复初始状态。
每次运行前重置,每次结束清理副作用。否则前一个 Agent 的修改会让后一个任务变简单或变难。
21.12 成功判定必须独立¶
编程任务以隐藏测试通过和改动范围为准,不以“Agent 说已修复”为准;客服任务看真实订单状态和政策遵循;研究任务检查引用和结论是否被来源支持。
公开测试可给 Agent 反馈,隐藏测试只用于最终验收,防止它针对测试投机。
21.13 消融:找出提升来自哪里¶
至少做:
- 同模型,换新旧 prompt。
- 同模型,换新旧工具。
- 同系统,换模型。
- 固定步数/预算后比较。
- 去掉记忆、规划、反思模块分别测试。
如果不固定预算,允许新系统多跑三倍步骤得到的成功率提升不能归因于“更聪明”。
21.14 轨迹怎样回流训练¶
失败轨迹先分类。工具 schema 错修工具;环境返回不清修接口;模型决策错才进入训练。
成功轨迹也不能原样全收:可能包含多余搜索、偶然试错、泄露测试信息。应压缩成高质量示范,保留关键决策和必要观察;也可以构造 chosen/rejected 轨迹对,训练模型偏好更短、更可靠的路径。
对可验证任务,可把最终环境结果作为 RLVR reward;步骤级工具成功可作为过程信号。但要防止 Agent 通过修改测试、绕过权限等方式 hacking 环境。
21.15 模型与 Harness 的共同进化¶
模型升级后,旧 prompt 的过度解释可能成为负担;模型工具调用更强后,手写纠错逻辑可能反而干扰。因此每次换基座都要重新做系统消融,不能永久继承所有 scaffold。
判断一项脚手架是否还需要:去掉它后质量是否下降、成本是否改善、失败类型是否变化。能由新模型稳定完成的逻辑应逐渐删掉,保持系统简单。
21.16 从离线任务到线上闭环¶
上线顺序:
- 离线固定环境大量回放。
- shadow 模式,只观察会怎么做,不真正执行副作用。
- 低权限 canary,小流量执行。
- 扩大权限与流量,同时保留人工接管。
线上收集的不只是成功/失败,还包括用户中断、人工修正、重试路径和成本。这些形成下一轮失败样本库。
核心问答¶
Q:Agent 与 Agent Harness 一句话怎么区分? Agent 是利用观察选择动作的策略;Harness 是让这个策略能够长期、安全、可恢复地运行的控制系统。
Q:Harness 最重要的能力是什么? 不是某个花哨规划算法,而是上下文管理、工具与权限、持久状态、错误恢复和完整轨迹。
Q:Agent benchmark 为什么比普通问答难复现? 结果依赖环境状态、工具版本、Harness、预算和多步随机性;任何一项变化都可能改变分数。
Q:成功轨迹能直接拿去 SFT 吗? 不应直接拿。成功可能靠大量无效试错或利用漏洞,需要清洗和压缩成可取的策略。
Q:怎样证明提升来自模型而非更多预算? 固定工具、prompt、步数、token 和时间预算做消融,只替换模型。
Q:模型变强后旧 scaffold 怎么办? 重新消融。没有持续贡献的规划、纠错和提示应删除,避免历史补丁永久堆积。