Agentic Harness Engineering: Observability-Driven Automatic Evolution of Coding-Agent Harnesses

Agentic Harness Engineering

一句话理解

AHE 固定基础模型,通过反复执行任务、记录轨迹、分析失败根因、修改模型外部的 Harness 文件,并在下一轮验证修改是否兑现预测,使 Coding Agent 的执行框架可以被自动、可审计地改进。

它优化的是 Harness,不是模型权重。

1. 问:Harness 到底是什么?

Harness 是围绕模型、负责把模型变成可执行 Agent 的外部工程系统。它决定:

  • 给模型什么指令和上下文;
  • 模型能够调用哪些工具;
  • 工具参数和返回值如何表达;
  • 调用前后执行哪些拦截、恢复或压缩逻辑;
  • 如何维护短期状态和长期记忆;
  • 如何组织 skill 和 sub-agent;
  • 如何把模型动作落实到真实执行环境。

AHE 把 Harness 暴露成七类可编辑文件:

  1. System prompt。
  2. Tool description。
  3. Tool implementation。
  4. Middleware。
  5. Skill。
  6. Sub-agent configuration。
  7. 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 演化的瓶颈不是“模型不会改代码”,而是模型看不清三个问题:

  1. 可以改什么?
  2. 为什么失败?
  3. 上次修改究竟有没有效果?

三种 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 才负责理解轨迹:

  1. 将同一任务的 $k$ 条 trace 放在一起;
  2. 标记 pass、fail、timeout;
  3. 结合 verifier 输出定位失败点;
  4. 比较同一任务成功与失败轨迹的分叉位置;
  5. 提取 root cause、成功机制和可泛化 failure pattern;
  6. 生成逐任务 detail report;
  7. 聚合 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 会:

  1. 查看历史得分、task delta 和上一轮 verdict;
  2. 从 overview 下钻到 detail 或 raw trace;
  3. 将多个任务归纳成可处理的 failure pattern;
  4. 判断应修改哪一类 Harness 组件;
  5. 在 Harness workspace 中修改文件;
  6. 为每个逻辑修改记录新的预测。

输出:

  • $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. 后续值得继续追问

  1. 如何把 task-level flip 升级为更可靠的 change-level 因果归因?
  2. 多个 Harness 组件非线性交互时,怎样判断应该保留哪一部分?
  3. 是否应该设置完全不参与演化的最终 test set?
  4. 当 $k=2$ 时,怎样降低随机波动对 Attribute verdict 的影响?
  5. 是否可以用更便宜的模型承担 Debugger,而不降低根因质量?
  6. 如何在工具层真正强制 runs/、verifier 和模型配置只读,而不只依赖 prompt 约束?
  7. AHE 的方法能否迁移到语音 Agent、浏览器 Agent 或实时 RTC Agent 的 Harness?
  8. 如何让 Evolve Agent同时优化 Harness 的使用方式与 Harness 内容,而不是只编辑静态文件?

16. 相关资料