Prompt 之外,生产级 Agent Harness 到底在控制什么
Prompt 之外,生产级 Agent Harness 到底在控制什么
发布边界:本文的六层控制面是作者工程综合,不是 OpenAI 官方产品架构。预算阈值、权限强度和状态转换必须按具体系统验证。
摘要
“最多搜索五次”“不要浪费 Token”“完成后立刻停止”写在 Prompt 中,并不代表系统真的拥有这些约束。生产级 Agent 需要一个位于模型之外的 Harness,负责作用域、持久状态、模型与工具调度、多维预算、验证恢复和回归评测。本文给出六层职责地图、层间交接物和最小实现顺序。
关键词
Agent Harness、任务契约、预算监督、状态机、工具调度
一、模型负责判断,Harness 负责让约束真正成立
模型擅长理解目标、生成计划、解释不确定性和选择下一步。Harness 更适合执行确定性规则:
- 允许访问哪些文件和系统;
- 当前还剩多少轮次、时间和工具预算;
- 哪些动作可以并行,哪些写入必须串行;
- 哪些验收项已经满足;
- 失败应重试、回滚、降级还是请求用户;
- 新线程恢复时需要加载哪些状态。
核心关系不是 Harness 替代模型,而是把不可依赖模型自觉完成的职责移到模型之外:
模型:建议继续搜索 ↓ Harness:检查证据缺口、搜索预算和最近收益 ↓ 允许 / 缩小范围 / 停止 / 请求新授权一个实用判断是:
如果某条规则必须在模型失误时仍然成立,它就不应该只写在 Prompt 里。
二、生产级 Harness 的六层控制面
1. 任务契约
任务契约把自然语言请求转换为可执行边界:
goal:要解决什么问题scope:允许读取和修改什么constraints:哪些行为禁止done_when:什么证据代表完成permissions:工具、网络和写入权限budget:时间、轮次、工具、搜索和子代理上限它的消费者不只是模型。权限层读取 scope,预算层读取 budget,验证层读取 done_when,恢复层保存当前契约版本。
没有任务契约,后续每层都只能猜测“做到什么算完成”。
OpenAI Codex Best Practices 明确建议在任务入口写清Goal、Context、Constraints和Done when。这直接支持“任务输入需要结构化”的判断;但把权限检查、预算阻断和完成验收真正执行起来,是本文进一步给出的 Harness 工程边界。
2. 状态与上下文
上下文管理不是把全部聊天记录持续塞回模型。Harness 应区分:
- 稳定规则:仓库规范和长期约束;
- 当前任务:目标、范围和验收;
- 已确认事实:入口、接口和错误证据;
- 工作状态:阶段、已完成项和剩余风险;
- 原始档案:完整日志与旧对话,需要时再取。
这一层向模型提供最小必要上下文,同时向恢复机制输出状态快照。它解决的是“下轮需要知道什么”,而不是追求上下文窗口占满。
3. 模型与工具调度
模型路由决定当前阶段需要什么能力;工具调度决定调用依赖、并发、限速、超时、结果压缩和幂等。
“并行越多越好”不是调度策略。应先建立依赖图:
- 独立只读任务可以批处理;
- 相互依赖的调查保留反馈回路;
- 写入和外部副作用默认串行;
- 输出过大的批次拆分或先聚合。
模型选择也不应只发生在入口。复杂发现阶段可能使用高能力模型,机械实现阶段可以降级,验证发现新风险时再升级。
OpenAI 的 Programmatic Tool Calling 文档说明,工具编排可以包含并行、循环、条件和中间结果处理,同时由应用决定能力是否开放、哪些工具可被调用。这说明“工具调度”是显式控制流;本文进一步主张把失败行为、权限、幂等和审批写进 Harness 合同。
4. 预算与停止
Token 上限无法覆盖全部资源。Agent 可能 Token 尚未超限,却已经等待工具一小时、重复搜索二十次或创建多个子代理。
预算至少应覆盖:
Credits / 模型轮次 / 工具调用 / 搜索 子代理 / 墙钟时间 / 重试 / 写入范围预算监督器还需要同时观察消耗与进展:
| 观察 | 动作 |
|---|---|
| 消耗正常,验收持续推进 | 继续 |
| 消耗快速增长,进展停滞 | 暂停并重规划 |
| 同一错误重复出现 | 禁止盲目重试 |
| 连续搜索没有新增证据 | 停止搜索 |
| 全部验收通过 | 立即停止 |
| 写入越界 | 拒绝并回滚 |
预算监督器真正需要的权力,不是提醒模型“节省一点”,而是拒绝继续执行。
5. 验证、回滚与恢复
模型说“完成了”不是状态转换条件。验证器必须逐项检查任务契约里的 done_when:
root_cause_explained:truerequired_tests_added:truetests_passed:truescope_respected:truerisk_documented:true若失败,Harness 应区分:
- 实现错误:回到 Implement;
- 发现新约束:回到 Plan;
- 环境错误:保留变更并交付阻塞证据;
- 越界写入:拒绝并回滚;
- 预算不足:保存检查点并等待授权。
这样,任务才是可恢复状态机,而不是只能从头重跑的长对话。
6. 事件账本与回归评测
最终 Token 总数无法解释为什么任务变贵、为什么失败,也无法解释模型升级后哪里发生了回归。
事件至少应记录:
- 当前阶段与执行者;
- 模型和 reasoning effort;
- 工具、批次、延迟和结果大小;
- 输入、缓存输入和输出;
- 重试、错误分类和副作用;
- 对验收进展的增量;
- 状态快照与恢复点。
这些事件支持更有用的指标:验收通过率、每个通过任务的资源、人工返工、P95/P99 单任务消耗、重复读取、重复搜索、回滚率与越界动作。
三、层与层之间必须交接结构化产物
只有六个模块名称,仍然只是一张概念图。真正的 Harness 必须规定层间交接:
| 上游 | 交接产物 | 下游用途 |
|---|---|---|
| 任务契约 | Goal、Scope、Done when、权限、预算 | 共同基线 |
| 状态与上下文 | 已确认事实、阶段、状态快照 | 模型执行与恢复 |
| 调度 | 模型选择、工具依赖图、幂等信息 | 安全执行 |
| 预算监督 | 剩余量、消耗速度、继续或暂停决定 | 控制任务扩张 |
| 验证与恢复 | 验收结果、失败分类、回滚点 | 决定状态迁移 |
| 事件账本 | 可重建执行轨迹 | 回归、审计与优化 |
任何一层缺少结构化输出,下一层都会退回自然语言猜测:
- 没有 Done when,验证器只能相信模型自述;
- 没有工具依赖图,并行会制造写冲突;
- 没有失败分类,重试会重复同一个错误;
- 没有状态快照,新线程只能复制完整历史;
- 没有进展增量,预算监督只能等额度耗尽。
四、最小可用 Harness 的实现顺序
不需要一开始就构建复杂多代理图编排。可以按以下顺序建立闭环:
- 任务契约与作用域:先让 Goal、Scope、Constraints 和 Done when 可被机器读取。
- 工具事件与幂等:记录参数摘要、耗时、错误与副作用,为写操作建立幂等或回滚单元。
- 阶段状态与检查点:至少区分 Discover、Plan、Implement、Verify、Deliver。
- 验证器:让状态迁移由验收证据触发。
- 多维预算与停止条件:控制轮次、工具、搜索、重试和时间。
- 有限路由和子代理:单代理闭环稳定后,再增加模型升级、降级和有限子代理。
- 回归与长尾观测:固定简单修改、跨模块实现、工具超时、权限拒绝和长线程恢复任务。
这个顺序的原则是:先获得控制权,再增加自主性。
五、什么时候不需要完整 Harness
一次只读解释、无外部副作用、几分钟可以人工复核的任务,简单 Prompt、受限工具和最终检查可能已经足够。完整 Harness 的状态持久化、事件采集和恢复机制本身也有开发与运行成本。
需要升级到完整控制面的信号通常包括:
- 任务持续时间长,可能跨线程或跨人员恢复;
- 工具会写入代码、数据库或外部系统;
- 失败代价高,必须审计和回滚;
- 子代理或并行调用让执行树明显扩大;
- 成本、延迟或限额需要稳定预算;
- 模型升级后必须比较行为回归;
- 人工无法逐步盯住每次执行。
Harness 不是成熟度勋章,而是风险和复杂度达到阈值后的工程选择。
OpenAI Multi-agent 文档把并行执行、聚焦上下文和根 Agent 协调列为收益,同时提醒顺序依赖、共享可变状态竞争和单一慢外部操作会削弱收益。这支持本文的边界:多 Agent 不是默认升级项,只有任务可拆分且控制面已经稳定时才值得引入。
结论
生产级 Agent 的关键差异,不是系统提示写得更长,而是模型之外是否存在一个真正能执行约束的控制面。
这个控制面至少承担六种职责:任务契约、状态与上下文、模型与工具调度、预算与停止、验证与恢复、事件账本与回归。
它们共同回答:
- Agent 现在允许做什么;
- 为什么还要继续;
- 什么证据代表完成;
- 失败后怎样恢复而不是从头再来。
已有的安全治理、模型路由、Context Ledger、工具沙箱和异步网关可以继续深入每个模块。Harness 总览的价值,是说明它们位于同一条可控执行链上的不同职责。
参考来源
- OpenAI Codex Best Practices
- OpenAI Codex Models
- OpenAI Programmatic Tool Calling
- OpenAI Multi-agent Guide
- OpenAI Codex Subagents