最近有一篇论文,标题直白得有点不留情面:《Stop Comparing LLM Agents Without Disclosing the Harness》。它的核心立场是:当你拿两个 LLM Agent 做对比评测,并得出“A 比 B 强”的结论时,如果论文或报告没有完整交代背后的 harness(评测脚手架/智能体运行框架),这个结论很可能不是模型能力的真实排序,而是两套工程系统之间的差异。更直接的说法是:在长程智能体评测里,harness 对结果的影响,可能超过模型本身。
这个观点是不是危言耸听,要看你怎么定义 Agent 评测。传统 LLM 评测是输入 prompt、输出答案、比对分数,变量相对可控。Agent 评测不是这样,模型需要在任务环境里持续决策、调用工具、观察返回结果、修正策略,最终完成一个长流程目标。在这个过程里,每一步都可能出错:工具调用格式解析失败、参数类型不对、上下文超长被截断、任务中途状态丢失、找不到可用工具。而这些环节大多不是模型决定的,是 harness 决定的。换句话说,你以为在测模型,实际上有很大一部分是在测 harness。
这篇文章我会先拆解什么叫 Agent Harness,再解释为什么它会影响 LLM 对比结果,接着分析长程智能体评测中误差为什么会被放大,最后给出一个可落地的负责任评测清单:评测时到底要披露什么、怎么设计对比实验、怎么避免把 harness 的差异误归因成模型的差异。
1. 核心观点速览
| 维度 | 内容 |
|---|---|
| 论文立场 | 对比 LLM Agent 时,如果不披露 harness 配置,结果可能失真 |
| 问题的本质 | Agent 评测结果 = 模型能力 × Harness 工程质量 × 任务环境 |
| harness 范围 | 提示词模板、工具定义、解析器、执行循环、重试机制、状态管理、上下文压缩、预算控制 |
| 最容易被误读的点 | 不同 Agent 框架的对比结果,常常被当成模型能力对比 |
| 长程任务影响 | 步骤越多,单步差异叠加越明显,harness 影响被放大 |
| 对开发者的要求 | 记录并公开完整 harness 配置,不止报告模型名称和分数 |
| 实操建议 | 用模型 × harness 交叉矩阵做实验,给结果附带方差和完整日志 |
2. 为什么 Agent 评测比传统 LLM 评测更依赖工程实现
先回到一个基本问题:评测一个 LLM Agent 到底在评测什么?
如果你只用单轮问答测模型,那问题很简单:输入一句 prompt,模型输出答案,看匹配度或人工打分。此时影响结果的变量主要是模型自身能力、提示词、解码参数。评测者不需要额外写太多工程代码,一个 requests 请求加一个评分函数就够了。
但 Agent 评测完全不同。以常见的工具调用任务为例,模型先根据用户目标生成一个工具调用意图,这个意图经过 harness 的解析之后变成具体的 API 请求,环境执行请求后把结果返回给模型,模型再根据结果决定下一步动作,直到任务结束。
这里可以拆出几个典型的失败点:
- 模型输出的工具调用格式是否正确被解析。不同模型习惯的输出格式不一样,有的偏好 JSON,有的偏好 XML,有的直接输出纯文本。如果 harness 只针对某一种格式做了严格解析,另一个模型的正确输出反而可能被丢弃。
- 模型是否能收到完整、准确的环境反馈。工具执行结果可能很长,比如终端输出几百行,harness 需要决定截断长度、保留哪些字段、按什么格式回填给模型。
- 模型失败后有没有重试机会。调用工具时参数类型写错,harness 是把错误信息返回给模型让它自己修正,还是直接判定任务失败。
- 模型能不能在长流程中维护任务状态。很多 Agent 任务不是一步完成的,模型需要知道“我已经做到了哪一步”,如果 harness 不维护状态或没有提供足够的履历信息,模型很快就会丢失上下文。
传统评测里,这些环节要么不存在,要么被简单封装在评测框架中,影响很小。但 Agent 评测里,这些环节会直接决定任务成功还是失败。
更关键的是,不同团队在研究同一个 Agent 任务时,往往会用不同的 harness。有些团队会自己实现一套工具调用循环,有些团队直接用现成框架,有些团队会在框架上做大量二次开发。大家最后报告的都是“XX 模型在 XX benchmark 上得分 XX”,但背后的实现差异可能非常大。这就是论文标题里那句 “Without Disclosing the Harness” 想点破的问题:对比结果,却没有披露跑出这个结果的完整工程上下文。
3. 拆解 Agent Harness:它到底包含什么
Harness 不是一个独立的新概念。把它理解为“把 LLM 接到任务环境上的所有工程代码”,会更容易看清楚边界。一个完整的 Agent Harness 至少包含下面这些组件。
3.1 任务提示词与状态管理
模型第一次进入任务时,会收到一段系统提示词和任务描述。这段文本是否清晰、是否提供了足够的任务背景、是否包含工具使用说明,直接影响模型后续行为。同一个模型,系统提示词写得好一点和差一点,任务成功率可能拉开很大差距。
状态管理则更进一步。长程任务里,模型通常无法一次性看到全部历史,harness 需要用消息列表、摘要、结构化记忆等方式,把任务当前进度反馈给模型。有的 harness 只是简单拼接历史消息,有的则会定期让模型总结,再控制上下文列表长度。这两种策略会导致模型在任务后半段的“记忆力”完全不一样。
3.2 工具调用循环与解析器
工具调用是 Agent 任务的核心动作。模型输出一段文本后,harness 要判断这段文本是不是一个工具调用,如果是,就解析出工具名、参数、目标,然后把请求发到执行环境。
这个环节看起来不复杂,但细节极多。比如:
- 模型输出内容中混有解释性文本和工具调用标记,harness 能否正确提取工具调用部分。
- 参数值是字符串、数字、数组还是嵌套对象,类型不匹配时是否需要强制转换。
- 工具不存在时,是直接报错,还是提示模型从可用工具列表里重新选择。
- 模型一次输出多个工具调用时,是顺序执行、并行执行还是只执行第一个。
如果 harness 的解析器只针对一个模型的常见输出格式做过优化,那评测另一个模型时,极有可能出现误判。这个误判会被记录为“模型调用工具失败”,但真实原因其实是 harness 解析不够鲁棒。
3.3 重试与错误恢复策略
Agent 在执行任务过程中一定会出错。模型给出的参数不对、工具超时、API 返回异常、环境状态与预期不符,这些错误有的是模型导致的,有的是环境导致的,有的是 harness 导致的。
不同 harness 的错误处理逻辑差异很大:
- 出现一次错误后直接终止任务,算失败。
- 出现错误后把错误信息返回给模型,让它重新尝试。
- 设置最大重试次数,超过次数才终止。
- 对不同类型的错误做分级处理,例如参数错误自动重写,环境超时直接跳过。
如果一个评测任务错误率较高,那么重试策略的不同,会直接影响最终成功率。评测结果差异可能不是模型能力差异,而是“允许模型犯多少次错”的差异。
3.4 上下文与预算控制
Agent 任务里,模型需要调用工具才能完成目标,但模型可利用的上下文窗口是有限的。随着任务推进,历史消息会越来越长,直到触达上下文上限。
Harness 的上下文管理策略包括:
- 直接裁剪最早的消息,保留最近消息。
- 把历史消息交给模型生成摘要。
- 用向量检索方式提取与当前目标最相关的历史片段。
- 允许模型主动结束任务并输出最终答案。
同一个模型,在上下文管理策略不同的 harness 里,可能表现截然不同。长程任务尤其明显,因为任务越长,模型越需要依赖 harness 对重要信息的保留能力。
3.5 Harness 核心组件小结
| 组件 | 主要作用 | 对评测结果的影响 |
|---|---|---|
| 系统提示词与任务描述 | 定义任务目标和行为约束 | 影响任务理解、工具使用倾向 |
| 工具定义与 Schema | 告诉模型有哪些工具可用、参数要求是什么 | 影响工具选择准确率、参数生成正确率 |
| 工具调用解析器 | 从模型输出中提取并解析工具调用 | 不同模型适应度可能完全不同 |
| 执行循环与错误处理 | 决定失败后是否重试、如何反馈错误 | 直接影响最终成功率 |
| 状态维护与上下文管理 | 决定模型能否感知任务进度和历史 | 长程任务影响尤其明显 |
| 上下文预算与成本控制 | 限制总调用次数、总 token 消耗 | 影响任务可执行范围和评测成本 |
4. Harness 如何左右对比结果
理解 Harness 对对比结果的影响,可以先看一个简单的误差累积模型。Agent 任务是多步决策过程,每一步都可能失败。假设一个任务需要执行 100 步,每一步的成功率是 p,那么最终整体成功率大约等于 p 的 100 次方。
# 用简单概率模型演示误差累积 # 注意:这只是理解误差累积的直觉模型,不是论文实验数据 for p in [0.99, 0.98, 0.95, 0.90]: total_success_rate = p ** 100 print(f"单步可靠率 {p:.2f},100 步后整体成功率约 {total_success_rate:.4f}")输出结果:
单步可靠率 0.99,100 步后整体成功率约 0.3660 单步可靠率 0.98,100 步后整体成功率约 0.1326 单步可靠率 0.95,100 步后整体成功率约 0.0059 单步可靠率 0.90,100 步后整体成功率约 0.0000如果两个 harness 的单步可靠率分别是 0.99 和 0.95,那么跑同一个 100 步任务,整体成功率会从 36.6% 掉到 0.59%,差距接近两个数量级。而这个单步可靠率的差异,很可能来自工具调用解析器对不同模型输出格式的适配程度,来自错误重试策略,来自返回结果截断逻辑。它们都算 harness 的一部分。
这就解释了论文标题强调的问题:当你在两个不同 harness 上跑同一个 Agent benchmark,得到的分数差异可能非常大。如果直接把 A 模型在 harness X 上的分数,拿去和 B 模型在 harness Y 上的分数比,你无法判断结果是模型能力差异还是 harness 工程差异。
实际开源社区里也常有类似的讨论:同一批模型,在不同 Agent 框架里跑同一个任务集,排名经常不一致。这不是模型本身不稳定,而是每个框架都带了一套自己的 harness,它们对提示词组织、工具调用格式、上下文管理、错误恢复都有各自实现。框架不统一,评测结果很难直接横向对比。
5. 长程智能体评测:放大效应更明显
论文标题特意强调“长程智能体评测”。为什么长程任务会让 harness 的影响更突出?因为短任务里,harness 的差异可能被任务随机性掩盖;长任务里,harness 的差异会通过逐步累积变得不可忽视。
长程任务的典型特征有四个。
第一,步骤数量大。任务目标往往需要通过几十次甚至上百次工具调用才能完成。按上面的误差累积模型,单步可靠率差 1% 都可能造成最终成功率的巨大差别。
第二,上下文长期处于高压状态。模型不可能把所有历史都装进上下文,harness 必须决定如何压缩、摘要、检索信息。一个任务进行到后半段,模型还记不记得任务最初的目标、已经完成哪些步骤、还有哪些约束条件,直接决定了最终能否完成。
第三,错误恢复更复杂。短任务出错后,重试一次往往就能解决。长任务里错误会叠加:某一步拿到错误数据,后续步骤基于错误数据继续推理并做出决策,harness 如果不提供回滚机制,错误会被不断放大。
第四,评测成本更高,导致很多实验只跑一次或少数几次。长程任务的 token 消耗大、运行时间长,研究团队往往不会做大量重复实验,一次成功率的随机波动也可能被误读为模型能力差异。此时如果 harness 本身不稳定,结果方差会非常大。
因此在长程智能体评测中,一个鲁棒的 harness 应该做到:帮助模型精确定位当前任务阶段、保留关键历史信息、在错误发生后提供可恢复路径、控制上下文长度不失控、对每次运行保留完整轨迹以便复盘。这些都不是模型能力,但会深刻影响模型能力能否被真实地体现出来。
6. 负责任评测:一组必须披露的 Harness 信息
论文的核心诉求不是“不要对比”,而是“对比之前先披露 harness”。把这句话转成工程行动,就是任何 Agent 评测结果都应该附带一份 harness 配置说明。这样其他团队才能判断分数是否可比、是否需要在相同条件下复现。
一份负责任的 Agent 评测报告,至少需要披露下面这些信息。
6.1 模型侧配置
- 模型名称和具体版本,例如某个模型的具体 checkpoint 或 API 版本。
- 解码参数:temperature、top_p、max_tokens、seed 是否固定。
- 是否有特殊系统提示词或角色设定。
- 是否启用了结构化输出、JSON 输出约束等功能。
6.2 Harness 核心实现
- 工具 Schema 由谁编写:人工编写还是使用模型生成。
- 工具调用解析方式:基于正则、基于 JSON 解析、基于特殊 token 解析。
- 模型输出中夹杂普通文本时如何提取工具调用。
- 一次输出多个工具调用时的处理策略。
- 工具调用出错时的反馈格式和重试次数上限。
- 是否允许任务失败后整体重跑,重跑多少次。
6.3 上下文与状态管理
- 系统提示词的完整内容。
- 是否使用摘要机制,摘要由谁生成、多久生成一次。
- 历史记录如何处理:裁剪、摘要、向量检索还是三者结合。
- 最大上下文长度设置。
- 任务状态是否被持久化保存,中途崩溃能否恢复。
6.4 统计口径
- 同一任务集跑了多少遍。
- 报告的是平均值、中位数还是最佳值。
- 是否报告了方差或置信区间。
- 任务成功判定的标准由谁定义,是否有自动评分脚本。
- 是否有失败任务的人工复核。
一个简单的方式是,在每次评测前先写好一份类似下面的 JSON 配置记录。它既是你实验的复现依据,也是你和其他团队沟通的基础。
{ "experiment": "demo-agent-benchmark", "model_config": { "model_name": "your-model-name", "temperature": 0.0, "max_tokens": 4096 }, "harness_config": { "system_prompt": "请复现实验记录中的完整系统提示词或附文件路径", "tool_schema_source": "manual-or-generated", "tool_call_parser": "json-mode-or-regex-or-special-token", "max_retry_per_step": 3, "enable_summary": true, "summary_interval_steps": 10, "history_strategy": "truncate-or-summary-or-vector-retrieval", "max_context_tokens": 128000 }, "eval_config": { "task_count": 100, "repeats_per_task": 5, "timeout_seconds": 60, "success_metric": "task-completion-rate" } }注意:上面这个 JSON 只是记录模板,模型名称、任务名和具体数值需要按实际实验填写。真正的问题不在于你是否用了这个格式,而在于你是否能够完整回答模板里每一个字段。
7. 从论文到实践:设计你自己的 Agent 对比实验
如果你正在做 Agent 评测,接下来这段是作业级别的参考。无论是写技术报告,还是要给自己的项目选型,建议按下面的方法设计实验。
7.1 先建立模型 × Harness 的交叉矩阵
不要只在同一个模型上换 harness,也不要在同一个 harness 上换模型。要一次把两个维度同时变起来,至少是“2 个模型 × 2 个 harness”的 4 组实验。这样才能看到模型差异和 harness 差异分别在多大程度上影响结果。
# 伪代码:设计一个模型 × Harness 的交叉评测 # 真实项目中需要替换成你自己的任务加载和评分逻辑 models = ["model-a", "model-b"] harnesses = ["harness-x", "harness-y"] tasks = load_task_set("benchmark_tasks.jsonl") results = {} for model in models: for harness in harnesses: key = f"{model}+{harness}" all_scores = [] for repeat in range(5): # 每个组合跑 5 次 runner = create_runner(model=model, harness=harness) scores = runner.run(tasks) all_scores.append(scores) results[key] = summarize(all_scores) # 计算均值、方差 print(f"{key}: {results[key]}")跑完后的结果分析,应该重点关注两个问题。
第一,固定 harness、换模型时,模型排名是否稳定。如果稳定,说明这个 harness 有能力区分模型能力差异;如果不稳定,说明你需要检查任务集是否太短或方差太大。
第二,固定模型、换 harness 时,分数是否出现明显跳变。如果同一个模型在两个 harness 下分数差距过大,说明这个模型对 harness 的敏感度很高。此时单点跑出来的分数没有代表性。
7.2 保留完整的轨迹日志
只看一个最终成功率是不够的。Agent 评测失败后的排查高度依赖过程日志。建议每次运行都保存一份 JSONL 格式轨迹文件,记录每一轮的系统提示词、模型输出、解析结果、工具调用、工具返回值、错误信息和最终判定。
推荐使用下面这种目录结构组织评测项目:
agent-eval/ ├── configs/ # 每个实验的 JSON 配置 │ ├── exp-model-a-harness-x.json │ └── exp-model-b-harness-x.json ├── harnesses/ # 各版本 harness 实现 ├── tasks/ # 任务集文件 ├── logs/ # 每次运行留下的 JSONL 完整轨迹 │ └── 2025-01-01_model-a_harness-x_run-1.jsonl ├── results/ # 汇总分数表 └── scripts/ # 评测启动与评分脚本目录结构本身不限制用哪种语言或框架,核心目标是:任何一次评测结果都可以追溯到完整的运行配置和执行轨迹。这样如果某一天结果被质疑,你可以快速定位是模型输出问题,还是 harness 解析问题,还是任务集本身有歧义。
7.3 先跑小任务集验证稳定性
在正式烧钱跑长程评测之前,先抽 10 到 20 个任务组成小集合,把程序跑通,看重复实验结果是否稳定。如果小集合上同一个配置跑 3 次,分数方差都大得离谱,那么换大集合只会更不稳定,先调 harness 比先换模型更迫切。
8. 评测结果归因:分清楚是模型差还是 Harness 差
跨框架对比 Agent 时,最常见的错误是把结果差异直接归因到模型。如果严格按交叉矩阵实验做归因,可以按下面几条规则梳理。
第一,如果模型 A 在 harness X 和 harness Y 上都稳定优于模型 B,才能说模型 A 的综合能力强。此时结论对 harness 不敏感,可信度较高。
第二,如果模型 A 在 harness X 上优于模型 B,但在 harness Y 上相反,说明结论对 harness 高度敏感。这种情况很难说谁更强,只能描述为“在特定 harness 配置下,A 的表现如何”。
第三,如果同一模型在同一 harness 上重复跑多次,分数波动本身就很大,那先别急着比较模型。任何差异都可能落在噪声区间内。解决方法是增加重复次数,或者修改评测指标让任务判定标准更稳定。
第四,如果某个模型的失败日志集中在工具调用解析错误上,不要急着给模型下结论。去检查 harness 的解析器是否对该模型的输出格式适配良好,把正常的工具调用误判成失败也是常见原因。
第五,如果某个模型的任务中途失败集中在同一类型步骤,那就要区分是模型规划问题还是 harness 缺失某项提示。可以把该步骤的工具返回结果、错误信息和模型下一步输出单独抽出来复盘。真实原因往往藏在日志里,而不是藏在分数表里。
为了帮组复盘,可以考虑把一次完整的失败轨迹记录下来,格式可以简化为下面的 CSV 结构:
step,type,content,success 1,user,任务目标...,true 2,assistant,我需要先查询库存,true 3,tool_call,search_inventory({"sku": "A123"}),true 4,tool_result,"{""status"": ""ok"", ""count"": 0}",true 5,assistant,库存不足,需要换一个供应商,true 6,tool_call,list_suppliers({"region": "east"}),true 7,tool_result,"{""status"": ""error"", ""message"": ""region field required""}",false ...这样每一帧都能单独查看,哪些步骤是模型规划导致,哪些是 harness 解析导致,一目了然。
9. 需要避免的评测误区
| 常见做法 | 潜在风险 | 正确姿势 |
|---|---|---|
| 仅报告模型名和最终分数 | 无法复现,无法判断差异来源 | 附完整 harness 配置和任务集版本 |
| 用不同框架跑不同模型,直接对比结果 | 把 harness 差异误当成模型差异 | 用交叉矩阵,多个 harness 上相互验证 |
| 只跑一次,分数出现随机波动 | 偶然性被误读为能力差异 | 多次运行,报告均值和方差 |
| 失败后反复重试直到成功,只记录最终结果 | 掩盖了模型真实单次成功率 | 把重试策略写进配置,不要默认“无限重试” |
| 只报告成功率,不保存轨迹 | 无法定位失败原因 | 保存完整 JSONL 轨迹,支持事后复盘 |
| 任务集不固定,中途随意修改 | 不同版本任务集之间不可比 | 给任务集加版本号,改动就换版本 |
| 忽略模型输出格式差异 | 某些模型因为格式不匹配被误判 | 检查解析器对不同模型的适配程度 |
10. 总结与建议
这篇论文最值得记住的一点,不是“harness 越复杂越好”,而是:Agent 评测的结果必须放在工程上下文中解释。Agent 框架、提示词、解析器、上下文管理、重试策略、错误恢复这些 harness 组件,和模型本身一样,都会对最终分数产生决定性影响。尤其是在长程智能体评测中,harness 的差异会随着任务步骤增加而逐步放大。
对普通开发者来说,这意味着几件事:
第一,不要只看一张 leaderboard 就去评判模型。先看这份榜单背后用的是什么任务集、什么 agent 框架、什么工具定义。评测框架不同,结果含义可能完全不同。
第二,用 Agent 框架时需要意识到,所有现成框架本身就自带一套 harness。你在框架上改一行系统提示词,或者在设置里调整最大重试次数,本质上都在改变评测条件。
第三,如果要发布模型对比结果,请把论文标题当成一份格式规范:披露 harness 配置、报告重复运行统计、附上完整运行轨迹。否则别人很难判断你的结论到底是模型能力的体现,还是 harness 实现细节的体现。
建议收藏这份披露清单,在你下一次跑 Agent benchmark 或写技术报告时,逐项对比检查。只要能在评测结论前多写一段“本次实测的 harness 配置如下”,这篇论文提出的大部分问题,就已经避开了。