TL;DR

Harness 对照评测要固定任务、环境、验收和预算,让模型与配置交叉组合。先比较同一任务的结果,再用消融定位组件作用,检查模型与 Harness 的交互。等预算实验回答效率,成本曲线回答取舍;更多重试带来的成功,不能直接归功于架构改进。

分数涨了,先查实验改了什么

如果同时换了模型、加了浏览器工具,又把重试次数从一次增加到五次,成功率提高无法归因。要判断 Harness 的价值,必须让其他条件可比。

Agent Harness 包括工具接口、上下文策略、调度、重试和验收。所谓“同一个模型”,也要核对模型版本、采样参数和系统指令;两个商业产品声明使用相同模型,不代表它们的隐藏配置相同。

Anthropic 的 2026 年应用开发实验展示了单 Agent 与完整 Harness 的结果差异。其中一组案例约为 20 分钟、9 美元,对比 6 小时、200 美元。作品对比有参考价值,但预算差别很大,不能据此宣称等成本提升。这些数值也是作者的特定运行记录,并非标准价格或独立复现。

实验开始前写清一个问题。例如:“在每任务相同费用上限内,加入结构化评估反馈是否提高验收通过率?”这比“我们的新架构是否更强”更容易设计,也更容易被推翻。

用四组实验分开模型与 Harness

两个模型分别运行两套 Harness,可以观察模型收益、Harness 收益,以及二者是否互相影响。

配置 旧 Harness H0 新 Harness H1
模型 M0 Y00 Y01
模型 M1 Y10 Y11

Y 表示相同任务集上的平均验收结果。任务、起始快照、评分器和预算规则保持一致。以通过率为例:

text
M0 上的 Harness 收益 = Y01 - Y00
M1 上的 Harness 收益 = Y11 - Y10
交互效应 = (Y11 - Y10) - (Y01 - Y00)

交互效应大,说明 Harness 收益依赖模型。某模型需要频繁重置上下文,另一模型通过压缩就能继续;工具描述对一种模型有效,也不一定能原样迁移。不要把其中一格的结果写成普遍最佳实践。

同一任务在四组都运行,形成配对比较。随机打乱执行顺序,避免某组集中遇到接口故障或模型服务更新。无法冻结的外部服务要记录时间、版本和异常,并在报告中说明影响。

固定预算,再看成本曲线

等预算比较和质量—成本曲线解决不同问题,应该同时报告。

比较方式 固定条件 可以得出的结论
等费用上限 每任务总费用,包括失败和评估 该费用约束下哪组更好
等时间上限 每任务墙钟时间与并发规则 该延迟约束下哪组更好
多预算点 逐步增加费用或时间上限 改善是否值得额外成本
原生默认配置 各产品按默认策略执行 默认体验的差异,难以归因

费用包含生成、评估、工具和失败重试;时间包含排队与等待。达到上限仍未通过的任务计为失败或未完成,不能从统计中删除。报告所有任务的成本分布,同时单列成功任务的成本,避免幸存者偏差。

“每次成功的成本”可以用总费用除以成功数,但成功数为零时应报告未定义。不能把失败任务的费用忽略,也不能让两个系统用不同的成功定义。

消融要保留可解释的对照

消融是逐项关闭组件,观察结果变化。它测量组件在当前组合中的作用,不能直接证明组件单独使用也有效。

flowchart LR A[冻结任务与验收] --> B[模型和 Harness 四组对照] B --> C[按任务配对比较] C --> D[关闭一个组件] D --> E[保持预算与其余配置] E --> F[比较通过率 成本 失败类型] F --> G[补测关键组件交互]

例如评估反馈依赖浏览器运行。关闭浏览器后反馈失效,不能简单推断“评估器无用”。应补测浏览器开关与反馈开关的四组组合,区分单独作用和依赖关系。

关闭反馈时,可以给对照组同等预算的独立重采样,测量“多花推理”与“获得缺陷信息”的区别。该对照回答的是预算内替代策略的价值;如果只是不执行评估器,则回答的是移除组件后系统整体如何变化。两种问题都合理,但不要混写。

失败切片比总分更容易指导修改:工具选择错误下降,但超时上升,说明组件可能改善决策却增加延迟。相关定义见Agent 轨迹。

Go 实践:计算配对差异与交互

下面的 Go 1.23 程序使用四个合成任务,每个任务包含四组通过结果。它演示计算口径,不代表实际模型表现。

go
package main

import "fmt"

type Task struct {
	ID   string
	Pass [4]int
}

func analyze(tasks []Task) ([4]float64, error) {
	var means [4]float64
	if len(tasks) == 0 {
		return means, fmt.Errorf("empty task set")
	}
	seen := make(map[string]bool)
	for _, task := range tasks {
		if task.ID == "" || seen[task.ID] {
			return means, fmt.Errorf("missing or duplicate task identity")
		}
		seen[task.ID] = true
		for cell, value := range task.Pass {
			if value != 0 && value != 1 {
				return means, fmt.Errorf("invalid outcome for %s", task.ID)
			}
			means[cell] += float64(value)
		}
	}
	for cell := range means {
		means[cell] /= float64(len(tasks))
	}
	return means, nil
}

func main() {
	tasks := []Task{
		{ID: "t1", Pass: [4]int{0, 1, 1, 1}},
		{ID: "t2", Pass: [4]int{0, 0, 0, 1}},
		{ID: "t3", Pass: [4]int{1, 1, 1, 1}},
		{ID: "t4", Pass: [4]int{0, 0, 0, 1}},
	}
	y, err := analyze(tasks)
	if err != nil {
		panic(err)
	}
	gainM0 := y[1] - y[0]
	gainM1 := y[3] - y[2]
	fmt.Printf("cells=%.2f %.2f %.2f %.2f\n", y[0], y[1], y[2], y[3])
	fmt.Printf("harness_gain M0=%.2f M1=%.2f interaction=%.2f\n",
		gainM0, gainM1, gainM1-gainM0)
}

运行 go run main.go:

text
cells=0.25 0.50 0.50 1.00
harness_gain M0=0.25 M1=0.50 interaction=0.25

数组顺序是 M0H0、M0H1、M1H0、M1H1。新 Harness 在 M0 上提升 25 个百分点,在 M1 上提升 50 个百分点,交互为 25 个百分点。这里只有四个任务,不能据此判断统计显著性。

正式实验应为每格重复执行,先计算逐任务平均,再配对聚合。用任务级 bootstrap 估计差异区间,并报告样本量和抽样方法。具体重复指标见Agent 可靠性评测;把所有 Trial 当独立样本会低估任务聚集带来的不确定性。

结果报告保留哪些信息

一份可复查的报告至少包含任务与环境版本、四组配置、预算、验收器、重复次数、配对差异及失败切片。只给平均分,别人无法判断改动是否值得采用。

分析前确定主要指标与切片,避免结果出来后只挑有利任务。开发集用于调策略,最终比较使用未参与调优的任务。若反复用同一测试集选方案,它也会成为开发集。

报告可以写:“在这批代码任务、该模型版本和每任务费用上限下,结构化反馈提高了通过率,同时增加了中位延迟。”不要省略条件写成“反馈 Harness 全面领先”。机制详见Evaluator 反馈与终止策略。

常见问题

没有权限查看商业产品的完整配置怎么办?

把比较定义为产品默认体验评测,记录可见设置。可以比较结果和成本,但不要声称隔离了模型权重或隐藏指令的影响。

所有消融结果都应该相加吗?

不应该。组件可能互补或互相替代。只有实验支持近似可加时,才适合叠加估计;否则直接测量关键组合。

某组频繁超时,是否应该剔除?

如果超时属于系统运行结果,计入主指标。基础设施事故可另做敏感性分析,但剔除规则应预先确定,并同时保留原始结果。

一个预算点领先,是否就应该迁移?

还要检查任务覆盖、延迟、严重失败和维护成本。用多个预算点验证收益,再在真实工作流中做小规模验证。

参考资料