Agent评测失真?Harness才是影响模型对比的关键变量 最近研究 LLM Agent 评测时反复注意到一个现象不少模型对比结论看似来自公开 benchmark但真正决定结果的并不是模型本身而是论文或项目里那套评测 harness。这个问题在长程智能体任务中尤为突出。论文《Stop Comparing LLM Agents Without Disclosing the Harness》的核心观点就是评测 harness 对 LLM Agent 表现的影响可能超过模型自身比较模型之前如果不披露 harness结论的可靠性会大打折扣。这篇论文的讨论点并不仅是学术圈的问题它直接影响我们做 Agent 落地选型、版本回归、竞品分析和内部质检。很多团队在发布 Agent 评测报告时只公布了“用了什么模型、得了多少分”却忽略了运行时细节工具怎么编排、终态判定用什么规则、超时多久、上下文被裁剪还是完整保留、评分是规则抽取还是大模型代评。这些都属于 harness评测护栏与执行编排的范畴。本文会把这篇论文所针对的问题转译成工程实践语言先解释什么是 Agent 评测 harness再分析长程智能体评测中哪些 harness 差异会显著影响排名然后给出一套可配置、可披露、可复现的评测工程样例帮助你在实际项目中尽量减小 harness 带来的“非模型噪音”。1. 论文在批评什么LLM Agent 对比为什么失真1.1 模型即系统的现实先看一个很常见的对比逻辑团队想评估“模型 A 和模型 B 哪个更适合做智能客服”于是把两个模型接到同一套工具调用函数里跑同一批用户问题最后统计成功率。如果 A 得分高结论通常写成“模型 A 优于模型 B”。但从论文角度看这种结论默认了一个前提模型是唯一变量。然而在 Agent 场景里模型并不是孤立的推理单元。它要依赖系统提示词、工具描述、工具返回格式、解析器、重试逻辑、环境变量、评测判分器、会话恢复逻辑。整套外部机制合起来才会产生一个可观察的行为。这套外部机制就是 harness。论文把 harness 定义为“模型在评测过程中被调入的解决方案的非模型部分”包括测试环境的代码、抽象、prompt、解码设置以及控制任务执行的 scaffold。只要 harness 不同同样的模型可以表现出完全不同的能力水平反过来相同 harness 下模型的优劣才更有可比性。换句话说LLM Agent 的能力 模型权重 × harness 编排 × 任务环境很多评测报告的失真是因为只报道了最左边的权重差异忽略了右边两项。1.2 “模型分数差异”可能小于“harness 差异”论文的一个强主张是在许多长程 agentic 任务中harness 的影响超过模型权重的影响。这听起来反直觉却和工程经验一致。长程智能体任务的特点是步骤多、反馈稀疏、决策链条长例如让 Agent 在一个克隆代码库里完成 bug 修复、跑测试、提交代码或者让 Agent 调研多个网页资料后生成对标报告。这类任务中模型的单步能力很强真正决定成败的是上下文记忆能不能可靠读写、失败后能不能自动恢复、工具返回的巨大内容会不会污染上下文、终点判定会不会太宽松或太严。这些执行细节由 harness 控制。如果一个 benchmark 的 harness 实现了极其高效的“观测-行动-压缩”循环另一个 benchmark 的 harness 只是把每步全量内容堆进上下文两个系统即便使用同一模型最终分数也可能拉开很大差距。论文主张评测对比时必须公开 harness因为不公开 harness 就相当于拿同一道菜但用了不同锅、不同火候、不同调料顺序来比较“哪种食材更鲜美”。2. 什么是评测 harness它由哪些部分组成2.1 从 LLM 评测到 Agent 评测的迁移传统 LLM 评测比较接近“出题-答卷-批改”输入 prompt让模型输出再用规则或评测模型打分。评测差异大多来自 few-shot 示例数量、prompt 措辞、温度、token 限制和打分方式。到了 Agent 评测模型会进入一个循环读取任务、推理、调用工具、观察结果、再推理。这个循环的每一环都有设计自由度。评测系统不只是评测集还包括了任务的运行环境、工具执行层、智能体运行时交互协议、状态处理、结果判分等。这里把整个运行时稳定支撑称为 Agent 评测 harness。2.2 Harness 的关键模块下面是一个尽可能完整的 harness 组成清单模块主要设计问题可能带来的偏差任务定义与初始化任务是单轮还是多轮初始状态是否固定是否提供背景信息初始状态不同的任务难度可能完全不同工具执行层工具由脚本真实执行还是模拟是否限制并发结果大小是否截断不同工具返回长度影响模型决策提示词模板工具描述采用什么格式任务描述是否完整是否注入额外引导prompt 工程差异远超模型差距智能体运行协议直接输出动作还是先让模型自由生成文本再抽取不同协议影响模型的推理机会观察与记忆上下文保留哪些历史信息中间步骤会压缩还是裁剪长程任务高度依赖观察管理恢复与干预能力Agent 中途失败能否自动重试、报错后能否回滚操作可恢复性直接影响长程任务成功率终止条件与终态判定以什么条件判定任务完成是模型自评、规则检查还是外部测试脚本验证判定宽严会改变分数排名判分器用准确率、奖励函数还是 LLM-as-judgejudge prompt 是否固定LLM 判分存在位置偏差和措辞偏差沙箱与环境版本代码在什么操作系统、包版本和网络访问下执行结果可能被隐藏的依赖版本影响重放与随机控制随机种子是否固定外部 API 是否允许随机变化随机性会掩盖真实的性能差距每个模块都可以是一个参数旋钮。论文所指出的问题是大多数评测报告不会把这些旋钮全部公布只公布“模型 得分”这样一来其他团队复现不出来的原因不只是数据不可得而是 harness 描述不完整。2.3 Agent 评测和传统推理题评测的区别Agent 评测更像是“模拟考试”而传统 LLM 基准更像“选择题测验”。选择题测验的题目没有执行过程Prompt 给了模型模型直接输出选项判分简单。模拟考试则包括读题、查资料、写草稿、改错、交卷。Agent 评测的复杂性在于每一步都可能发生“非模型因素”系统提示词可能有先入为主的偏见。工具描述不清晰导致模型找不到正确工具。任务中需要修改文件但 harness 没给文件写入权限。一个长步骤的执行结果被截断了后续模型信息不足。判分器只判断最终答案没有判断过程质量。论文的核心意思是这些因素不该被忽略应该被视为和模型同等重要的系统组件来披露。3. 长程智能体评测为什么 harness 影响被放大3.1 长程任务的特征长程智能体任务long-horizon agentic tasks在时间尺度和决策次数上明显高于普通问答。例如模型需要连续调用 20 次以上工具。中间有大量中间状态需要维护。任务只能从最终结果证明成功。某些步骤可能失败需要 Agent 自己检测并纠正。任务越长评测系统引入的偶然性就越多。论文将此视为“harness 影响被放大”的重要原因任何一个微小实现差异都可能沿着长链路累积。这和软件系统的“蝴蝶效应”类似一个日志查询接口少返回了一个字段最后模型可能完全走偏。3.2 误差放大器上下文管理、恢复能力和状态持久化长程任务对上下文管理的敏感度远高于短任务。假设有两个 harnessHarness A 在每轮工具调用后把返回结果压缩成 10 行以内的要点摘要再放回上下文Harness B 直接把 200KB 工具输出追加到消息历史。同一个模型在 Harness A 中可能保持稳定的任务记忆在 Harness B 中则可能因上下文关键信息被淹没而反复偏离任务。评测模型 A 和模型 B 时如果分别使用这两套方案分数差异主要测量的就不再是模型本身而是上下文整理策略。恢复能力同样关键。长程任务里Agent 经常遇到执行失败文件不存在、端口被占用、网页超时、命令返回非零退出码。优质 harness 会把错误信息整理好允许模型尝试其他路径低质量 harness 可能把一次偶然的网络失败判定为 Agent 的整体失败。评测分数因此大幅波动。状态持久化也常被忽略。某些 Agent 评测要求模型在浏览器里完成多次跳转中途如果评测系统把网页 Session 丢失Agent 随后无法完成操作这不是模型能力问题而是 harness 环境状态管理的问题。3.3 信用分配困难在一个 30 步的任务中最后成功或失败的信号很稀疏。模型最终得分低到底是模型规划能力差还是工具返回内容晦涩还是评测系统过早断开如果缺少分步评测所有问题都会被归因到模型头上。论文强调不披露 harness 时读者无法判断某个 benchmark 排名是因为任务本身能区分模型还是因为 harness 设计对某些模型更友好。例如提示词模板按模型的偏好格式写还是按通用格式写都会产生不对等效果。4. Harness 的“隐藏变量”分析设计差异如何改变排名4.1 同模型不同 harness结论可能反转考虑一个简化场景。模型 X 和模型 Y 在“浏览网页并回答商品价格对比”任务上对比。Harness 1 使用典型的 ReAct 循环把网页正文全文附加到对话记录中工具描述写得很简单。模型 X 可能因为长文本处理能力弱在任务后期失去关键信息模型 Y 因为对网页噪声不敏感胜出。Harness 2 使用 summary 循环每轮自动提取页面中的价格信息输出为字段级结构化数据。模型 X 对结构化信息推理更擅长反超模型 Y。如果评测报告只写 Harness 2 下模型 X 优于模型 Y读者很可能认为模型 X 能力更强。实际上差异由 harness 对信息的结构化预处理贡献了一大半。论文并不否认模型能力存在真实差异只是想说在不控制、不披露 harness 的情况下很难做出公平的比较。4.2 对 Agent 评测打分“宽松”与“严格”的影响终止条件和终态判定方法是对比实验中很容易翻车的地方。宽松判定只要模型在最后一轮输出中说“我已完成”就判成功。严格判定必须检查产物是否存在、格式是否正确、关键步骤是否真的执行。不同论文如果用不同严格程度的判定器同一模型在同一任务集上的成绩可能相差 20% 甚至更多。论文将这种差距归入 harness 影响。同样评分器若采用 LLM-as-judgejudge 本身也受温度、system prompt、评价维度和示例顺序影响。这在长程 Agent 任务中更隐蔽因为分数不是简单的 0/1而是多维度打分。评分器看到了多少上下文、看到的是完整 trace 还是摘要也会改变结果。4.3 对测试集的污染和数据泄露还有一类 harness 问题是评测数据与预训练语料的重复。任务描述越接近网上公开 benchmark模型越容易“背答案”。这不完全属于 harness但论文提醒评测时记录模型是否见过该任务数据也是评测基础设施的一部分。因此工程上建议将“数据版本管理”纳入 harness 范畴记录任务数据的来源、去重规则、发布时间线以及是否被用于任何提示词示例。5. 可配置的最小评测 Harness 示例为了把上述概念落到工程层面这里给出一套轻量可运行的 Python 示例。它不追求像 SWE-bench 那样覆盖完整代码环境而是演示如何将 harness 模块化使评测过程可复现、可披露。5.1 定义 TaskSpec先建立任务描述的数据结构。# harness_demo/specs.py from dataclasses import dataclass, field from typing import Optional dataclass class ToolSpec: name: str description: str parameters_schema: dict field(default_factorydict) dataclass class TaskSpec: task_id: str prompt: str initial_state: dict field(default_factorydict) tool_specs: list[ToolSpec] field(default_factorylist) max_steps: int 20 timeout: float 60.0 # 是否允许评测系统在模型失败后自动恢复 enable_resumption: bool False # 是否使用上下文压缩策略 use_context_compression: bool False judge_method: str rule # rule / llmTaskSpec 的目的不仅是记录数据集里的 prompt还记录了该 task 对应的 harness 参数。每个任务都必须知道自己的超时时间、恢复策略、上下文策略和判分方法。这样后续跑分时才不会出现不同任务用不同方案但没留下记录的问题。5.2 Agent 运行时抽象为了让同一个模型接入不同交互协议定义一个 base runtime。# harness_demo/runtime.py from dataclasses import dataclass, field from typing import Optional dataclass class AgentMessage: role: str content: str step: int 0 dataclass class AgentRuntime: model_name: str system_prompt: str messages: list[AgentMessage] field(default_factorylist) max_tokens: int 1024 temperature: float 0.0 def reset(self): self.messages [AgentMessage(rolesystem, contentself.system_prompt)] def append(self, role: str, content: str, step: int 0): self.messages.append(AgentMessage(rolerole, contentcontent, stepstep)) def step(self, observation: str) - str: 这只是协议占位方法具体模型调用由子类实现。 raise NotImplementedError这里的 runtime 应被视为一个抽象边界。实际使用 OpenAI SDK、Anthropic SDK 或本地模型推理时可以分别实现同一接口。对外评测时至少要披露 runtime 的协议是什么原生 tool call、纯文本 JSON 输出还是 ReAct 文本格式。5.3 Orchestrator控制观察、行动和判定Orchestrator 是评测 harness 的核心编排逻辑。# harness_demo/evaluator.py import json import time from dataclasses import dataclass, field from .runtime import AgentRuntime from .specs import TaskSpec from .tool_registry import ToolRegistry from .judge import RuleJudge dataclass class EvalRecord: task_id: str model_name: str success: bool False steps_used: int 0 duration: float 0.0 trace: list[dict] field(default_factorylist) metadata: dict field(default_factorydict) class HarnessEvaluator: 一个可配置的最小评测器。 它负责执行循环并把所有可能影响结果的参数写入 trace。 def __init__( self, runtime: AgentRuntime, tool_registry: ToolRegistry, spec: TaskSpec, judge: Optional[RuleJudge] None, ): self.runtime runtime self.tools tool_registry self.spec spec self.judge judge or RuleJudge() self.record EvalRecord(task_idspec.task_id, model_nameruntime.model_name) def run(self): self.runtime.reset() self.runtime.append(user, self.spec.prompt) self.record.metadata[harness_version] HARNESS_VERSION self.record.metadata[use_context_compression] self.spec.use_context_compression self.record.metadata[enable_resumption] self.spec.enable_resumption start time.time() for step in range(1, self.spec.max_steps 1): # 实际项目中这里调用 LLM 生成动作 action self._invoke_model(step) if action.get(type) finish: final_answer action.get(answer, ) success self.judge.judge(self.spec, final_answer, self.runtime.messages) self.record.success success break if action.get(type) tool_call: obs self._execute_tool(action, step) if self.spec.use_context_compression: obs self._compress(obs) self.runtime.append(tool, obs, stepstep) self.record.steps_used step self.record.duration time.time() - start return self.record在简单示例中_invoke_model和_execute_tool可根据需要实现。为了不引入具体模型依赖这里不做完整模型调用。5.4 加入 ToolRegistry工具注册表决定模型能调用什么# harness_demo/tool_registry.py from typing import Callable, Dict class ToolRegistry: def __init__(self): self._tools: Dict[str, Callable] {} def register(self, name: str, fn: Callable): self._tools[name] fn def execute(self, name: str, payload: dict): if name not in self._tools: return {error: ftool {name} not found} try: return self._tools[name](**payload) except Exception as exc: return {error: str(exc)}需要强调的是Harness 差异常常藏在 ToolRegistry 的提示和错误消息设计里。例如工具返回{error: permission denied}模型是否知道如何修复如果工具注册表把错误规范化模型就能更好恢复。5.5 记录 trace实现“可披露”评测记录中应包含完整的 trace。trace 不只是成功与否还包括每一步的模型输出、工具调用、观测摘要、耗时和触发恢复的次数。def _capture_trace(self, step, action, obs): self.record.trace.append( { step: step, action: action, observation: obs, context_size: sum(len(m.content) for m in self.runtime.messages), } )披露 trace 一方面方便论文复现另一方面也是团队内部 Review 的依据。很多 Agent 系统上线后无法调试就是因为没有保留评测时的完整 trace。6. Harness 参数如何影响模型分数的实验建议6.1 建议做“Harness 消融”论文的合理推论是如果我们要对比模型不应只比较模型不同、harness 固定的单点结果而应该做两组交叉实验。如果你评估两个模型 M1、M2 和两套 harness H1、H2至少要做 2×2 矩阵模型Harness得分M1H1score AM1H2score BM2H1score CM2H2score D只有当 A-B 与 C-D 的差异模式和模型能力差异一致时才能判断模型优劣。如果 M1 在 H1 下落后 M2但在 H2 下反超那么结论必须写明“依赖 harness”。工程上可以把这次 2×2 结果输出为 JSON{ experiment_version: 2025-06-01, task_set: web_qa_50, model_matrix: [ {model: M1, harness: H1, success_rate: 0.72}, {model: M1, harness: H2, success_rate: 0.66}, {model: M2, harness: H1, success_rate: 0.68}, {model: M2, harness: H2, success_rate: 0.71} ] }如果对比时只做了对角线上的两组实验论文所述的风险就真实存在错误地把 harness 差异归因为模型差异。6.2 披露清单一份可直接用于论文或报告的模板报告里可以加入下面表格覆盖“模型对比时必须披露的信息”类别必须披露内容模型模型名称、版本、量化精度、解码参数temperature/top_p提示词System prompt、In-context examples、tool description 是否共享工具每个工具的名称、描述、参数 schema、真实执行策略上下文是否截断、压缩、摘要策略、最大 token 数恢复失败重试策略、错误处理方式、是否允许 Agent 纠正判分判定规则来源、LLM judge 的提示词、人工校验流程环境操作系统、Python 版本、依赖版本、外部 API 版本终止max_steps、timeout、停止条件数据任务集版本、去重记录、污染检测结果有了这个清单其他团队复现你的 Agent 结果时就不至于折腾两天还复现不出来。这也是论文标题里 “Disclosing the Harness” 的工程含义把评测结果的两个组成部分都放到阳光下。7. 工程落地把评测 Harness 当作可版本化基础设施7.1 单独建立 eval-harness 仓库许多团队把评测代码写在项目仓库的一个eval/目录里版本跟随业务代码一起走。这样做不是不行但长程 Agent 评测场景下建议单独建立eval-harness仓库或独立模块原因如下评测工具要服务多模型、多任务不应绑定某一套业务实现。harness 修改可能来自评测经验而不是产品功能更新。复盘结果时需要固定代码版本。仓库内至少包含eval-harness/ ├── harness/ # 执行编排逻辑 ├── tasks/ # 任务集定义 ├── run_eval.py # 运行入口 ├── configs/ # 模型、工具、判分器配置 ├── outputs/ # 结果记录与 trace └── README.md7.2 将配置与代码分离评测过程中最容易出现的不可复现原因是代码相同但配置不同。比如测试 A 用了temperature0测试 B 用了temperature0.8却没有在结果文件里记录。因此建议所有实验参数都写进 YAML。# configs/eval_demo.yaml model: name: your-model-name temperature: 0.0 max_tokens: 2048 task_set: name: dev_quiz_v1 path: ./tasks/dev_quiz_v1.jsonl harness: max_steps: 30 timeout: 120.0 use_context_compression: true tool_error_behavior: return_error_to_model judge: method: llm judge_model: your-llm-judge prompt_version: 2025-06-01 dimensions: [correctness, process_quality]运行实验时先读配置再执行评测。7.3 防止 harness 渐变定期执行校准集模型升级后Agent 的应用效果通常会变化。但如果同时改了 harness很难判断效果来自哪一侧。一个实用方法是维护一份“黄金校准集”用固定模型、固定任务、固定 judge校验 harness 改动是否影响了旧模型的得分。校准集不必太大20 到 50 条具有代表性的长程任务即可。每次修改 harness 后先对校准集跑一遍如果旧模型分数出现明显漂移说明 harness 的改动引入了额外偏差需要评估是否继续。7.4 引入人工抽样复审即使评测系统使用了规则或 LLM judge也不能完全避免评分错误。论文的工程启示是披露 harness 不意味着数字完全客观还需要在关键轮次做人工抽样复审。例如从成功组和失败组各抽 5% 的 trace检查分数是否与任务终态一致。如果人工复审发现大量“假成功”或“假失败”则不是模型问题而是评测判定规则需要修正。7.5 对评分器做对抗性测试LLM judge 在 Agent 评测里尤其危险因为它可能只看到最终回答没看到完整 trace也可能被模型在最后加一句“我严格完成了所有步骤”误导。可以构造几个精心设计的错误 trace测试 judge 是否能识别工具确实调用失败但模型声称成功。最终答案正确但过程完全未调用必要工具。调用了正确工具但忽略了工具中的错误提示。只有当 judge 能识别这些对抗性样本时评测系统才适合用来对比 Agent 系统。8. 常见误区和 FAQ8.1 误区只要固定 model任何 harness 都可以测出真实能力真实能力无法脱离运行环境单独讨论。固定 model 而不固定 harness得出的不是模型能力而是某个系统的端到端能力。论文建议把两者同时披露才能让别人理解这个系统的能力来自哪里。8.2 误区Harness 不该干预 Agent所以评测环境越“裸”越好还有另一类做法是刻意不给 Agent 太多支持不提供错误恢复、不管理上下文、不做工具结果压缩想让模型“裸奔”展示原始能力。这种做法的风险在于真实的 Agent 产品不会让模型裸奔。产品方必然会在上线下做上下文记忆、工具 schema 优化、重试、提示词工程。用裸 harness 得出的排名并不代表上线后的最终体验。8.3 FAQ 整理问题回答什么是最需要优先披露的 harness 内容至少包括 tool 定义、终止条件、上下文管理方式、判分器构成我们只做内部评测不对外发论文也需要披露吗需要。内部记录可以让后续版本对比不被实现差异误导如果多个模型方案来自不同厂商Harness 应该统一吗尽量统一成同一套评测代码如果无法统一则记录差异长程 Agent 评测一定要用 LLM judge 吗能用规则判终态的就用规则过程质量才需要用 LLM judge如何知道任务集被污染了检查任务文本与模型训练语料的重叠以及模型是否在零样本下异常高分8.4 最小复现清单当你们团队发布一个 Agent 评测报告时请贴出下面的最小配置片段保证任何读者可以快速判断评测的公平性harness_meta { model: demo-model-v2, prompt_version: p_20250601, tools_version: tools_v1.3, environment_version: env_20250601, retry: tool_call_error_retry_1, context_compression: summary_10lines, judge: rule_acceptance_test_v2, max_steps: 40, timeout_seconds: 180, }这个片段看似简单却能把评测报告的“可复现性”从不可知提升到工程可检查的级别。9. 论文观点给开发者的启发读这篇论文时我不只看到对 benchmark 的批判也看到了 Agent 工程化中的一个正向机会既然 harness 对表现的影响如此巨大那么 harness 本身就是一项值得专门设计、优化和发布的高价值组件。如果把 harness 当作可交付的技术资产团队就可以做以下事情沉淀一整套工具定义和提示词模板形成组织内部的 Agent 交互规范。对上下文压缩、错误恢复、任务判定进行模块化改造而不是每次把代码散落在业务里。建立评测回归流水线每次模型升级时既能看模型带来的增益也能定位环境变化带来的扰动。开展 harness 级消融实验用同一模型在不同 harness 上跑找出真正适合目标任务的交互协议。论文也提示我们LLM Agent 的竞争远不只是模型权重的竞争更像是系统工程能力的竞争。谁能更高质量地把模型接入任务环境、更完整地保留关键信息、更准确地判定任务完成谁就能在相同的模型基座上做出更可靠的产品。在写评测结论时将“模型名称”“harness 版本”并列写出不只是一种学术严谨更是一种工程自觉。下次你的报告中如果出现“模型 A 明显优于模型 B”这类断言不妨先问一句你控制好 harness 了吗如果还没有可能你的结论要改成“在 XX harness 下模型 A 优于模型 B”这样才准确。如果你正在搭建 Agent 评测系统一定要在设计指标的同时设计好记录层。把 trace 输出来、把版本管理起来、把判分规则固定下来这些“繁琐细节”才是长程智能体评测公正性的真正基础设施。