Agentic Harness Engineering
- 原文:本地 PDF
- 在线版本:arXiv v4
- HTML:arXiv HTML
- 官方代码:china-qijizhifeng/agentic-harness-engineering
- 版本:v4,2026-05-18
- 阅读状态:阅读中
一句话理解
AHE 固定基础模型,通过反复执行任务、记录轨迹、分析失败根因、修改模型外部的 Harness 文件,并在下一轮验证修改是否兑现预测,使 Coding Agent 的执行框架可以被自动、可审计地改进。
它优化的是 Harness,不是模型权重。
1. 问:Harness 到底是什么?
Harness 是围绕模型、负责把模型变成可执行 Agent 的外部工程系统。它决定:
- 给模型什么指令和上下文;
- 模型能够调用哪些工具;
- 工具参数和返回值如何表达;
- 调用前后执行哪些拦截、恢复或压缩逻辑;
- 如何维护短期状态和长期记忆;
- 如何组织 skill 和 sub-agent;
- 如何把模型动作落实到真实执行环境。
AHE 把 Harness 暴露成七类可编辑文件:
- System prompt。
- Tool description。
- Tool implementation。
- Middleware。
- Skill。
- Sub-agent configuration。
- Long-term memory。
所以“mem、skill、规约”确实可以属于 Harness,但 Harness 不只是这些,它还包括工具实现、上下文组织、执行保护和恢复机制等。
2. 问:单个 Agent 也有 Harness 吗?
有。
Harness 不是只有多 Agent 系统才存在。即使只有一个模型实例,只要它通过 system prompt、工具协议、memory、middleware 和运行循环与外界交互,就已经处于某个 Harness 中。
例如一个最小命令行 Agent:
用户任务
↓
System Prompt
↓
模型
↓
Shell Tool
↓
终端环境
其中模型之外负责“如何调用 Shell、怎样返回输出、何时停止”的部分就是最小 Harness。
3. 问:Agent、Model、Harness、Environment 应该怎样定义?
一个比较清楚的边界是:
$$
\text{Agent System}
=
\text{Model}
+
\text{Harness}
+
\text{Runtime State}
$$
Agent System 再与 Environment 交互:
$$
\text{Agent System}
\longleftrightarrow
\text{Environment}
$$
四者分别是:
Model
模型参数和推理能力,例如 GPT、Qwen、Gemini。输入消息,输出 token 或工具调用意图。
Harness
模型外部、为模型组织观察和动作的可编辑控制层,例如 prompt、tool、middleware、skill、memory 和 sub-agent orchestration。
Runtime State
某一次 Agent 运行过程中形成的临时状态,例如当前消息历史、待办列表、已经访问的文件、当前工作阶段和 token 使用情况。
Environment
Agent 操作的外部世界,例如容器、操作系统、代码仓库、文件系统、网络、Benchmark 任务和 verifier。
因此不应简单定义为:
$$
\text{Harness}=\text{除 Model 外的一切}
$$
原因是 Environment 和 verifier 通常不属于 Harness。在 AHE 中,它们故意被冻结且不可编辑;只有明确暴露在 Harness workspace 中的控制组件才是优化对象。
更合适的口径是:
Agent 是一个具有目标、状态和行动接口的运行系统;Model 是其中的决策核心,Harness 是包围 Model 的可编辑控制与执行层,Environment 是 Agent 所操作但不归 Agent 所有的外部世界。
4. 问:AHE 优化的是一种什么 Harness?
论文实例化的是 Coding Agent / Terminal Agent 的 Harness。
它运行在 Terminal-Bench 2 上,任务通常需要 Agent 在隔离容器中:
- 阅读任务说明;
- 检查文件系统;
- 执行 Shell 命令;
- 编写或修改代码;
- 安装依赖;
- 运行测试;
- 生成 verifier 要求的交付文件。
初始 Harness 只有一个 Shell 执行工具;演化过程中可以逐渐增加 prompt 规则、工具保护、middleware、skill、memory 等。
因此可以说它是“为命令行 Coding Agent 服务的 Harness”,但不能把 Harness 本身缩减为“命令执行器”。命令行只是它连接 Environment 的主要动作接口。
5. 问:Trajectory 一般怎样记录?
Trajectory 是一次 Agent 从接收任务到结束的完整时序记录。可以形式化为:
$$
\tau
=
(o_0,a_0,o_1,a_1,\ldots,o_T,a_T,z)
$$
其中:
- $o_t$:第 $t$ 步 Agent 看到的 observation;
- $a_t$:第 $t$ 步 Agent 产生的 action;
- $z$:最终 verifier 结果。
对于 LLM Agent,实际文件通常记录:
- system、user、assistant 消息;
- tool call 的名称和参数;
- tool output;
- middleware 事件;
- 错误、超时和重试;
- token、延迟等元数据;
- verifier 输出和 reward;
- 对应 task、trial、iteration、Harness 版本和模型配置。
AHE 公开仓库中每个 trial 的主要文件包括:
agent/nexau_in_memory_tracer.cleaned.json
agent/nexau.txt
verifier/reward.txt
cleaned.json:结构化消息、工具调用和 middleware 事件;nexau.txt:Agent 运行日志;reward.txt:verifier 的通过或失败结果。
轨迹必须关联精确的 Harness commit、模型配置和 sandbox,才能回答“哪个版本产生了这个行为”。
6. 问:这里有没有“训练数据”?
没有传统意义上的模型参数训练数据,因为 AHE 不做梯度更新。
但从 Harness 搜索的角度,Benchmark $D$ 相当于优化数据:
$$
D={d_1,d_2,\ldots,d_m}
$$
参考实验使用 Terminal-Bench 2 的 89 个任务,每个任务每轮运行 $k=2$ 次。一次迭代会产生:
$$
89\times2=178
$$
次独立 trial。
这些 task、trajectory、verifier result 和 debugger report 构成 Harness 演化的经验数据。它们用于:
- 找重复出现的失败机制;
- 比较同一任务的成功与失败轨迹;
- 判断上一轮修改是否造成修复或回归;
- 为下一轮 Harness 修改提供证据。
这里的风险是:同一组 Benchmark 反复参与修改选择,可能产生 benchmark overfitting。因此论文又通过跨 Benchmark 和跨模型迁移检查 Harness 是否学到了较一般的工程经验。
官方仓库没有发布论文十轮实验的全部原始 trajectory、manifest 和分析产物;仓库提供的是复现实验框架、部分 Agent Debugger 能力和示例配置。
7. 问:跑实验可以只使用大模型 API Key 吗?
大模型 API Key 可以承担模型推理部分,但不能单独完成整个实验。
完整 AHE 实验还需要:
- 可调用的模型 endpoint 和 API Key;
- NexAU Agent runtime;
- Harbor Benchmark runner;
- Terminal-Bench 2 数据和 verifier;
- E2B 或等价隔离 sandbox;
- 保存 trajectory、workspace snapshot 和 Git history 的运行目录;
- 足够的调用额度、并发额度和执行时间。
API Key 只替代下式中的 $M$:
$$
\operatorname{Rollout}(M,H,D,k)
$$
它不能替代 $H$、$D$、sandbox 和 verifier。
完整参考配置的调用量很大:十轮、89 个任务、每题两次 Code Agent rollout,此外每轮还有 Agent Debugger 和 Evolve Agent 的模型调用。因此适合先用少量任务和较小的 $k$ 做 smoke test。
8. 问:三种 Observability 应该怎样理解?
AHE 认为自动 Harness 演化的瓶颈不是“模型不会改代码”,而是模型看不清三个问题:
- 可以改什么?
- 为什么失败?
- 上次修改究竟有没有效果?
三种 observability 分别解决这三个问题。
8.1 Component Observability
目标是让修改空间可见。
七种 Harness 组件都表现为明确文件,Evolve Agent 可以知道:
- 当前有哪些可编辑组件;
- 某个失败应该在哪一层处理;
- 某次修改具体改变了哪些文件;
- 如果发生回归,应该恢复哪些文件。
这使 Harness 修改从“重写一个巨大 Prompt”变成文件级、组件级的操作。
8.2 Experience Observability
目标是让大量轨迹中的证据可见。
处理层次为:
原始 trajectories
↓
清洗后的 canonical traces
↓
逐任务 root-cause reports
↓
benchmark overview
Evolve Agent 默认先读 overview,需要时才下钻到 task detail 和 raw trace。这就是 progressive disclosure:既节省上下文,又保留证据来源。
8.3 Decision Observability
目标是让每一次修改的判断可验证。
每项修改都必须记录:
- 观察到的 failure pattern;
- 推断出的 root cause;
- 修改了哪个组件和文件;
- 预计修复哪些任务;
- 哪些任务可能回归。
下一轮拿实际 task delta 检验预测。于是每项修改不再只是“听起来合理的解释”,而成为可以被下一轮否证的 contract。
三者合起来形成:
可见的修改空间
+
可下钻的运行证据
+
可验证的修改预测
=
可审计的 Harness 演化闭环
9. Algorithm 1:所有符号
论文算法输入为:
$$
H_0,;M,;D,;k,;N
$$
| 符号 | 定义 |
|---|---|
| $H_0$ | 初始 Harness |
| $M$ | 固定基础模型 |
| $D$ | Benchmark 任务集合 |
| $k$ | 每个任务的 rollout 次数 |
| $N$ | 最大迭代轮数 |
| $H_t$ | 第 $t$ 轮结束时新生成的 Harness;第 $t+1$ 轮才评测 |
| $T_t$ | 第 $t$ 轮使用 $H_{t-1}$ 得到的原始轨迹和 reward |
| $\widetilde T_t$ | 规范化后的轨迹 |
| $C_t$ | 描述 $H_{t-1}\rightarrow H_t$ 的 change manifest |
| $V_t$ | 对上一轮 manifest $C_{t-1}$ 的验证报告 |
| $R_t$ | Agent Debugger 从本轮轨迹提炼的分层证据 |
| $H_{\mathrm{best}}$ | 历史上已经实际评测过的最佳 Harness 快照 |
最重要的“一代错开”关系是:
第 t 轮:评测 H_(t-1) → 产生 H_t
第 t+1 轮:评测 H_t → 验证 C_t
10. Algorithm 1:逐步解释
10.1 初始化
$$
H_{\mathrm{best}}\leftarrow H_0
$$
初始时先把 seed Harness 作为唯一候选。更严谨的实现还需要维护历史最好分数:
$$
s_{\mathrm{best}}\leftarrow-\infty
$$
10.2 Rollout
$$
T_t
=
\operatorname{Rollout}(M,H_{t-1},D,k)
$$
它不是训练函数,而是评测执行器。
对于任务 $d_i$ 的第 $j$ 次运行:
$$
(\tau_{i,j}^{(t)},r_{i,j}^{(t)})
=
\operatorname{Execute}(M,H_{t-1},d_i)
$$
其中:
- $\tau_{i,j}^{(t)}$:完整 Agent 轨迹;
- $r_{i,j}^{(t)}\in{0,1}$:verifier 结果;
- $i=1,\ldots,|D|$;
- $j=1,\ldots,k$。
所以:
$$
T_t
=
\left{
(\tau_{i,j}^{(t)},r_{i,j}^{(t)})
\right}_{i=1,j=1}^{|D|,k}
$$
每次 trial 使用独立 sandbox。模型和 Harness 接收同一任务,但模型采样、工具调用和环境分支可能不同。
$k\ge2$ 的主要价值是发现 partial-pass:如果同一 Harness 在同一任务上一次成功、一次失败,说明模型可能具有完成能力,但 Harness 没有让成功策略成为稳定默认行为。
10.3 Pass@1
$$
\operatorname{Pass@1}(T_t)
=
\frac{1}{k|D|}
\sum_{i=1}^{|D|}
\sum_{j=1}^{k}
r_{i,j}^{(t)}
$$
例如三个任务分别为:
$$
0/2,\qquad1/2,\qquad2/2
$$
则:
$$
\operatorname{Pass@1}
=
\frac{0+1+2}{2\times3}
=
50%
$$
它不是“运行 $k$ 次至少成功一次”的 pass@$k$。论文统计中 timeout 或基础设施中止也按失败计入 pass@1。
10.4 Clean
$$
\widetilde T_t
=
\operatorname{Clean}(T_t)
$$
这一步负责:
- 把 tracer 中的嵌套事件整理为时间顺序;
- 统一 system、user、assistant、tool 消息;
- 统一 tool input 和 tool output;
- 去掉重复或与分析无关的内容;
- 保留关键错误、reward 和来源信息。
它只整理数据,不判断根因,不重新评分,也不修改 Harness。原始 $T_t$ 仍然保留供下钻核查。
10.5 Attribute
准确函数名是 Attribute,意思是“验证并归因上一轮修改”:
$$
V_t
=
\operatorname{Attribute}
(C_{t-1},T_{t-1},T_t)
$$
三个输入分别回答:
- $C_{t-1}$:上一轮声称做了什么;
- $T_{t-1}$:修改前任务表现如何;
- $T_t$:修改后任务表现如何。
先计算相邻两轮的任务变化:
$$
F_t
=
{d_i\mid d_i:\mathrm{fail}\rightarrow\mathrm{pass}}
$$
$$
G_t
=
{d_i\mid d_i:\mathrm{pass}\rightarrow\mathrm{fail}}
$$
对于 manifest 中的一项修改 $c$,定义:
$$
P_c=\operatorname{predictedFixes}(c)
$$
$$
Q_c=\operatorname{riskTasks}(c)
$$
于是实际兑现的修复为:
$$
\operatorname{actuallyFixed}(c)
=
P_c\cap F_t
$$
没有兑现的预测为:
$$
\operatorname{stillFailed}(c)
=
P_c-F_t
$$
实际发生的预测风险为:
$$
\operatorname{riskRealized}(c)
=
Q_c\cap G_t
$$
公开实现生成五种 verdict:
| Verdict | 判定含义 |
|---|---|
EFFECTIVE |
所有非空 predicted fixes 都实际发生,且没有预测风险发生 |
PARTIALLY_EFFECTIVE |
只修复了一部分 predicted fixes |
MIXED |
有预测修复发生,也有预测风险发生 |
INEFFECTIVE |
没有 predicted fix 兑现 |
HARMFUL |
没有 predicted fix 兑现,且发生了预测风险 |
例如:
predicted_fixes = [A, B]
risk_tasks = [C]
A:fail → pass
B:fail → fail
C:pass → fail
得到:
actually_fixed = [A]
still_failed = [B]
risk_realized = [C]
verdict = MIXED
这不是严格因果推断。多个修改同时进入同一 Harness、rollout 存在随机性、不同组件之间存在交互时,相邻两轮的 task flip 不能唯一证明是哪项修改造成结果。
第一轮没有上一轮 manifest,因此:
$$
V_1=\varnothing
$$
10.6 Rollback
论文伪代码写成:
$$
\bar H_{t-1}
=
\operatorname{Rollback}(H_{t-1},V_t)
$$
概念上的处理是:
EFFECTIVE:保留;PARTIALLY_EFFECTIVE:保留并继续观察或调整;MIXED:保留有效部分,撤销有害部分;INEFFECTIVE:回滚或重新设计;HARMFUL:优先回滚。
执行 Rollback 阶段不意味着一定撤销文件;没有被否定的修改可以全部保留。
论文伪代码与公开实现需要区分:公开主循环中的 Attribution 只生成 change_evaluation.json,真正的 keep、improve、rollback 或 pivot 由 Evolve Agent 根据报告执行。代码中的注释明确称其为 report only, rollback decided by evolve agent。
公开代码另有 perform_auto_rollback(),但它用于把整个 workspace 恢复到历史快照,不是正常迭代中的逐 change 回滚函数。
10.7 AgentDebugger
$$
R_t
=
\operatorname{AgentDebugger}(\widetilde T_t)
$$
Debugger 才负责理解轨迹:
- 将同一任务的 $k$ 条 trace 放在一起;
- 标记 pass、fail、timeout;
- 结合 verifier 输出定位失败点;
- 比较同一任务成功与失败轨迹的分叉位置;
- 提取 root cause、成功机制和可泛化 failure pattern;
- 生成逐任务 detail report;
- 聚合 benchmark overview。
输出 $R_t$ 不是标量 reward,而是一套分层证据:
analysis/
├── overview.md
└── detail/
├── task-A.md
├── task-B.md
└── ...
10.8 Evolve
$$
(H_t,C_t)
=
\operatorname{Evolve}
(\bar H_{t-1},R_t,V_t)
$$
输入包括:
- 当前保留或回滚后的 Harness;
- 本轮轨迹证据 $R_t$;
- 上一轮修改的验证报告 $V_t$。
Evolve Agent 会:
- 查看历史得分、task delta 和上一轮 verdict;
- 从 overview 下钻到 detail 或 raw trace;
- 将多个任务归纳成可处理的 failure pattern;
- 判断应修改哪一类 Harness 组件;
- 在 Harness workspace 中修改文件;
- 为每个逻辑修改记录新的预测。
输出:
- $H_t$:新的完整 Harness 文件树;
- $C_t$:描述 $H_{t-1}\rightarrow H_t$ 的 change manifest。
manifest 大致为:
{
"iteration": 5,
"changes": [
{
"id": "chg-1",
"type": "improvement",
"description": "保护已经生成并验证过的交付文件",
"files": ["tools/run_shell_command.py"],
"failure_pattern": "验证后错误删除交付物",
"predicted_fixes": ["path-tracing"],
"risk_tasks": ["需要主动重建输出的任务"],
"constraint_level": "tool_impl",
"why_this_component": "Prompt 提醒不足,需要在执行层拦截"
}
]
}
$H_t$ 和 $C_t$ 在当前轮不会被验证,要等到下一轮:
$$
T_{t+1}
=
\operatorname{Rollout}(M,H_t,D,k)
$$
$$
V_{t+1}
=
\operatorname{Attribute}(C_t,T_t,T_{t+1})
$$
10.9 Commit
$$
\operatorname{Commit}(H_t,C_t,t)
$$
Commit 不计算新分数,而是:
- 保存 Harness 文件;
- 保存 manifest;
- 保存 Git diff 和 commit;
- 给 iteration 打 tag;
- 保存 workspace snapshot。
它只证明“这个候选版本和它的预测已被完整记录”,并不证明修改有效。有效性由下一轮 rollout 决定。
10.10 更新最佳 Harness
论文伪代码这一行存在下标歧义:
$$
T_t
=
\operatorname{Rollout}(M,H_{t-1},D,k)
$$
所以 $T_t$ 评价的是 $H_{t-1}$,不是本轮刚生成、尚未运行的 $H_t$。
如果本轮得分创新高,严格来说应该保存:
$$
H_{\mathrm{best}}
\leftarrow
H_{t-1}^{\mathrm{evaluated}}
$$
更严谨的写法是:
H_eval = Snapshot(H_(t-1))
T_t = Rollout(M, H_eval, D, k)
score_t = Pass@1(T_t)
if score_t > best_score:
H_best = H_eval
best_score = score_t
随后再生成 H_t
公开仓库的目录也体现了这个一代错开关系:
iteration_NNN/input/workspace
上一轮产生、本轮刚刚被评测的 Harness
iteration_NNN/evolve
本轮新产生、下一轮才评测的修改
因此 Algorithm 1 中将本轮分数直接关联到 $H_t$,应理解为记号简化或 off-by-one,而不是允许未评测版本直接成为 best。
11. 用两个迭代把流程串起来
设:
$$
D={A,B,C},\qquad k=2
$$
第 1 轮:评测 $H_0$
| 任务 | 结果 |
|---|---|
| A | 0/2 |
| B | 1/2 |
| C | 2/2 |
$$
\operatorname{Pass@1}(T_1)
=
\frac{0+1+2}{6}
=
50%
$$
第一轮没有历史修改,所以:
$$
V_1=\varnothing
$$
Debugger 发现:
- A:正确交付文件生成后又被清理命令删除;
- B:行为不稳定;
- C:稳定通过。
Evolve Agent 修改 Shell tool,生成 $H_1$,同时在 $C_1$ 声明:
predicted_fixes = [A]
risk_tasks = [C]
$H_1$ 此时还没有被评测。
第 2 轮:评测 $H_1$
| 任务 | 修改前 $T_1$ | 修改后 $T_2$ |
|---|---|---|
| A | 0/2 | 2/2 |
| B | 1/2 | 1/2 |
| C | 2/2 | 1/2 |
$$
\operatorname{Pass@1}(T_2)
=
\frac{2+1+1}{6}
=
66.7%
$$
Attribute 得到:
- A:fail → pass,预测修复命中;
- B:仍然 partial-pass;
- C:pass → fail,预测风险发生。
因此:
actually_fixed = [A]
risk_realized = [C]
verdict = MIXED
接下来 Evolve Agent 可以保留修复 A 的机制,同时缩小造成 C 回归的拦截范围,然后根据 Debugger 对 B、C 的分析产生 $H_2,C_2$。$H_2$ 到第 3 轮才会被评测。
论文中的 path-tracing 是类似的真实案例:Agent 已生成正确的 /app/reconstructed.ppm,却在清理阶段把文件删除,导致 verifier 失败;加入 publish-state 和 Shell guard 后,删除操作被拦截,任务从 $0/2$ 变成 $2/2$。
12. Code Agent、Agent Debugger、Evolve Agent 是同一个 Agent 吗?
不是同一个运行角色或同一次 Agent 实例。
三者分工是:
| 角色 | 输入 | 职责 | 输出 |
|---|---|---|---|
| Code Agent | 任务、当前 Harness、sandbox | 真正执行 Benchmark 任务 | trajectory、任务产物 |
| Agent Debugger | cleaned trajectories、verifier 结果 | 分析成功和失败机制 | $R_t$ |
| Evolve Agent | 当前 Harness、$R_t$、$V_t$、历史记录 | 修改 Harness 并填写预测 | $H_t,C_t$ |
论文主实验让三个角色共享同一种基础模型和 reasoning 设置,以控制“是不是换了更强模型”这个变量;但它们使用不同 prompt、上下文、工具权限和运行目标,所以是三个不同角色。
其中 Attribute 主要是程序化集合比较,不是第四个 LLM Judge。
13. 论文结果怎样理解?
在 Terminal-Bench 2 的 89 个任务上,论文进行十轮演化:
| 方法 | Pass@1 |
|---|---|
| NexAU 初始 Harness | 69.7% |
| OpenCode | 47.2% |
| Terminus-2 | 62.9% |
| Codex | 71.9% |
| ACE | 68.9% |
| Training-Free GRPO | 72.3% |
| AHE | 77.0% |
AHE 总体高于这些对照,但在 Hard tier 上仍略低于 Codex。组件消融显示,收益主要存在于 tool、middleware 和 long-term memory;只迁移 system prompt 反而可能回归。
冻结最终 Harness、不再演化后,把它迁移到 SWE-bench-verified 和其他基础模型仍然获得收益。这支持的解释是:Harness 学到了一部分可迁移的工程协调机制,而不只是背下 Terminal-Bench 任务答案。
但论文不能证明:
- 每个 change manifest 的自归因都具有严格因果性;
- AHE 不会对反复使用的 Benchmark 过拟合;
- Harness 能开放式、无限自我改进;
- 所有组件的有效修改叠加后仍然线性增益;
- 在 Coding Agent 以外的领域会自然得到相同效果。
14. 这项工作最有价值的地方
它的核心贡献不是“又让一个 Agent 改自己的 Prompt”,而是把自我改进变成一个有证据、有版本、有预测、有下一轮验证的工程过程:
$$
\text{执行}
\rightarrow
\text{记录}
\rightarrow
\text{分析}
\rightarrow
\text{修改}
\rightarrow
\text{预测}
\rightarrow
\text{验证}
\rightarrow
\text{回滚或保留}
$$
更准确地说,AHE 展示的是:
当模型、推理预算、Benchmark 和 verifier 保持固定,并且 Harness 的可编辑面、运行证据和修改历史都足够可观察时,Agent 可以参与搜索比人工 seed 更有效的外部执行结构。
它离开放式 RSI 仍有距离,因为:
- 修改对象限定为 Harness;
- 模型权重不变;
- 评价目标和 verifier 由人定义;
- 数据域有限;
- 编辑权限受控;
- 自归因不是严格因果归因。
15. 后续值得继续追问
- 如何把 task-level flip 升级为更可靠的 change-level 因果归因?
- 多个 Harness 组件非线性交互时,怎样判断应该保留哪一部分?
- 是否应该设置完全不参与演化的最终 test set?
- 当 $k=2$ 时,怎样降低随机波动对
Attributeverdict 的影响? - 是否可以用更便宜的模型承担 Debugger,而不降低根因质量?
- 如何在工具层真正强制
runs/、verifier 和模型配置只读,而不只依赖 prompt 约束? - AHE 的方法能否迁移到语音 Agent、浏览器 Agent 或实时 RTC Agent 的 Harness?
- 如何让 Evolve Agent同时优化 Harness 的使用方式与 Harness 内容,而不是只编辑静态文件?