
上个月我帮一个项目组复核跑分结果。同一个模型、同一个 benchmark 名称、指定的评估版本也一致但在两套环境里跑出来的分数差了快 7 个百分点。当时大家第一反应是“模型权重下载错了”第二反应是“prompt 文件版本不一样”最后排查下来问题出在 harness 的拼接逻辑里一组 few-shot 示例前面的分隔符少了一个空行导致模型在长上下文里把示例当成了待回答问题的一部分。这个经历让我意识到一件事在讨论 benchmark 时我们太容易把 harness 当成透明背景板一旦分数偏离预期就默认是模型的问题。实际上评测结果的真实性很大程度上由 harness 决定。如果评估框架本身没有被验证过那分数越大越没有意义。这也是为什么我对“harness-only benchmark”这个概念非常感兴趣。1. 先想清楚我们到底在测模型还是在测模型周围的机器1.1 benchmark 从来不是“一份题目”那么简单大多数人看到 benchmark 这个词脑子里浮现的是一份包含问题、标准答案和分数线的试卷。但对于今天的大模型评估benchmark 更像是一条流水线。一条典型的评估流水线包括数据集读取、prompt 模板渲染、few-shot 示例拼接、API 调用或本地推理、上下文传递与截断、输出文本清洗、规则或模型打分、结果聚合和报告生成。这一整套东西在英文里经常会被称为 harness而在中文社区里有人叫它“评测底座”“评测框架”或“跑分工具链”。问题是流水线的每个环节都可能改变结果。哪怕模型的权重完全一样只要 prompt 模板里系统提示词的位置不同、示例顺序不同、解码参数设置不同、打分逻辑对某些合法输出过于严格跑出来的数字就可能出现几个点的波动。很多团队在对比模型能力时实际上是在对比两条流水线之间的差异而不完全是模型本身的差异。1.2 同一个分数可能在描述完全不同的行为举个最简单的例子。有的 harness 会把模型输出里所有空白符压缩后再匹配答案有的会保留换行有的用“是否包含关键词”来判断正误有的要求模型输出严格 JSON 再解析有的在调用模型时默认 temperature0有的默认 1.0。这些差异在单条测试里看起来微不足道累积到几百条甚至几千条任务时就会变成几个点的分数差。所以如果我们只看一个 benchmark 的最终分数其实是在用一个笼统的数字去掩盖若干变量的叠加。当模型能力快速迭代的时候大家可能还会觉得“分数提高是模型进步”但实际上有一部分提升可能来自 harness 改得更友好了提示词写得更清楚、打分器对某种回答格式更宽容、上下文截取策略对长文本更合理。1.3 为什么过去很少单独评估 harness这里有一个很现实的原因在过去很长一段时间里评测工作流相对简单。模型输出短、答案格式固定、任务类型以分类和完形填空为主harness 更难影响结果。你可以把那时的 harness 想象成一条传送带上负责把零件送到检测仪的机械臂只要零件没掉就很难想到要去校准机械臂。到了现在大模型评测出现了两个明显变化。第一任务从短答案变成了长文本、自由格式、步骤化推理甚至多轮交互输出解析的复杂度大大提高第二评测对象从“模型能不能答对”变成了“模型能不能在真实任务里完成一系列操作”比如调用工具、读取文件、在受限代码环境里执行脚本。这种情况下harness 不再是一个透明的传送臂它变成了任务执行环境的一部分。于是一个问题浮现出来如果一个 harness 本身不可靠那它测出来的模型能力还有多少可信度这个问题的自然延伸就是标题里的那个提议构建一个只评估 harness 的 benchmark。换句话说不测模型的智商而是测评测框架本身的稳定性、忠实性和运行效率。2. 为什么现在需要一个只保留 harness 的基准2.1 agent 类评估让问题从“模型会不会”转移到“框架传没传到位”在 agent 类任务里模型通常需要根据一个目标在多次交互中决定调用哪个工具、读取哪段文件、如何更正中间错误。评测的关键不仅在于模型最终是否给出正确答案更在于 harness 是否正确地执行了模型的工具调用、是否把工具返回结果拼进了上下文、是否对非法调用给出了合理兜底。这些能力听起来像是外围工程细节但它们直接影响结果。比如一个 agent 模型可能在一次工具调用中返回了{action: search, query: ...}如果 harness 没有正确解析并执行这个调用模型后续的推理就失去了关键信息。在这种情况下最终分数低并不能说明模型能力差只能说明这家 harness 对工具调用的支持不够好。2.2 分数波动、胜率失真、harness 成为被质疑的对象在最近的社区讨论里越来越多人在评估中关心这类问题同一套模型跑出来的成绩为什么波动不同评测框架对同一模型的结论为什么忽高忽低任务成功率、胜率这些指标到底能不能复现。虽然这些讨论背后各说各话但它们共同指向一个软肋评测框架本身缺乏一个“标准尺”来度量。“harness-only benchmark” 想做的就是造一把这样的尺子。它不去评判模型有多聪明而是构造一组已知难度、已知答案、已知失败模式的任务用来测试评测框架在多大程度上能忠实地完成“输入 → 推理 → 输出 → 判分”这条链路。如果一个 harness 在这组任务上表现稳定那么用它跑出来的模型分数才更值得信任如果 harness 在这些基础检查点上都出错那报告再高的分数也值得怀疑。2.3 这类项目的定位不是“替代现有评测”而是“评测的评测”我觉得这类项目的目标并不是取代现有的各类评测框架而是作为一个质量层存在。就好比单元测试不能替代代码审查但它是代码质量的第一道闸门。harness-only benchmark 的任务是回答几个低层但关键的问题同一个任务重复执行时结果是否可复现任务输入在被框架处理后模型真正看到的内容是否符合预期模型输出在经过解析和评分后是否忠实反映原始输出多任务、并行、资源受限时框架是否能避免串扰和漏测一旦这些问题有了可靠的答案上层的模型排行榜才具备了可解释性。3. 设计 harness-only benchmark四个核心维度如果你想参与或者自己搭建一个类似的项目可以从下面四个维度入手。这也是我在评估框架时通常会检查的四个面。3.1 维度一稳定性与可复现性稳定性是指同一任务在相同配置下多次执行结果是否一致。注意这里的结果一致不一定要模型输出逐字相同尤其是当你用温度大于 0 的采样时。更合理的做法是看最终判分结果是否一致以及日志是否能记录足够的参数来让其他人重放实验。具体检查指标在相同 seed、相同 temperature、相同 prompt 下任务重复运行 5 次判分结果的标准差是否是 0。如果存在随机采样harness 是否会把采样参数一并记录到结果文件中。是否能在不重新推理的情况下用保存的模型输出重放“解析 打分”环节。结果文件里是否包含任务的唯一标识、模型名称、harness 版本、启动时间。这些都是很基础的工程要求但在真实的评测框架里经常被满足得不够好。3.2 维度二输入构造的忠实性这个维度考察 harness 是否把开发者想发的输入完整、无失真地送给了模型。看似简单却是在真实项目中最容易翻车的一环。我建议至少检查这几种情况系统提示词是否被正确拼接是否和用户消息顺序正确。few-shot 示例之间的分隔符、换行、缩进是否稳定是否会受模板渲染影响。长文本任务是否会因为上下文窗口限制而被静默截断以及截断后是否在日志里有警告。多模态输入时图片链接或文件路径是否在传给模型前仍然有效。是否有意外的字符串转义或编码转换。比如包含中文、LaTeX、JSON 嵌套引号的任务是否会在模板渲染后变得不可解析。一个比较实用的做法是抽取日志里真正发送给模型的最终 prompt人工比对一次。很多问题在原始定义里看不出来但在最终 payload 里一目了然。3.3 维度三输出解析与判分的可靠性这是另一个容易出问题的地方。模型输出一段文字harness 需要把它变成可判分的结果。如果模型按要求输出 JSON但 JSON 里多了一个说明字段解析器是忽略它还是报错如果模型把答案和思考过程写在同一个段落里解析器能不能把关键结果提取出来如果模型输出了“答案是 A不过 B 也有道理”算对还是算错建议检查对格式合法但不标准的输出解析器的容错程度如何。对格式非法输出是否有重试机制重试是否会消耗额外 token 且改变结果。评分规则是否透明具体是“包含关键词判对”“正则匹配判对”还是“调用模型判分”。判分器的结果是否可审计即能否从最终分数反向定位到每一条判分依据。这里有一个很常见的坑用规则打分时判对逻辑过于严格或过于宽松都会让分数失真。以“包含关键词”为例如果模型输出“我不认为答案是 A它更可能是 B”不少简易解析器会因为包含字母 A 而误判为选择 A。这种错误会让模型排名完全失去意义。3.4 维度四任务隔离性、资源消耗与批量伸缩性最后一个维度偏向工程能力。一个 harness 如果只能单条跑那它只能用于演示。真正到了 benchmark 阶段必然要成百上千条任务一起跑这时就要看任务之间是否互相独立。需要检查任务 A 的模型输出会不会被写进任务 B 的上下文。某些框架复用同一个进程或上下文池时容易出现串场。工具调用或临时文件是否被清理会不会影响后续任务。并行数提升后内存和显存占用是否线性增长。某个任务超时或失败时会不会导致整个批次中止并丢失已完成的结果。输出目录结构是否清晰方便失败后断点续跑。综合来看这四个维度可以用来做一个简单的 harness 评分表每个维度下设 3 到 5 个检查项。任何框架只要在某个维度上连续踩中多个坑那它产出的分数就应该打上“谨慎参考”的标签。4. 实操路线从一个最小样例开始验证 harness如果你接受了“harness 需要被评测”这个想法下一步就是动手。不过我不建议一上来就做一个庞大的平台更推荐从一条小链路开始。4.1 第一步准备一张“已知答案但容易出错”的检查任务集在模型评估里通常需要选择困难样本在 harness 评测里你需要选择容易被处理错误的样本。比如包含较长段落的文本测试截断策略。要求输出 JSON 或多行格式的任务测试解析器。包含歧义答案和否定语气的任务测试判分逻辑。需要工具调用或多步推理的任务测试执行流程。相同问题但改变输入顺序的任务测试 prompt 稳定性和模型稳定性。数量不需要很多二三十条足够暴露大部分问题。关键是每条任务都要有明确的目标这条任务到底想验证 harness 的哪个环节。4.2 第二步写一个简单的“输出 → 解析 → 打分 → 重复运行”脚本这里不需要先实现一个完整框架用 Python 脚本就够了。大致结构是这样# 伪代码仅用于说明结构 def run_task(task): messages build_messages(task) # 构造输入 raw model_api.generate(messages) # 获取模型原始输出 parsed parse_output(raw) # 解析结果 score judge(task, parsed) # 判分 return {raw: raw, parsed: parsed, score: score} results [] for _ in range(5): # 重复运行多次 for task in tasks: results.append(run_task(task)) print(compute_stability(results)) # 观察标准差 print(detect_leakage(results)) # 检查 task 之间是否有串场如果不确定模型 API 的细节可以先用一个本地小模型或固定返回内容的 mock 来验证 harness 行为。mock 模型的好处是可控你让它在某个任务里返回非法 JSON就能立刻看到解析器怎么处理。这种思路比直接在真实模型上跑更容易定位问题。4.3 第三步设定 5 个检查点而不是一上来追求高分数运行完一次小规模验证后最该做的不是改模型 prompt 来提高分数而是先回答以下五个问题多次运行后判定结果是否一致日志里有没有记录最终发送给模型的完整 prompt模型原始输出和解析后结果之间有没有明显的信息丢失判分器给出的分数是不是符合人工判断并行跑任务时有没有出现资源竞争或上下文污染只有当这五个问题都有明确答案后才建议扩大任务集进入正式评测。这里的逻辑和工程测试一样先保证测试本身正确再去看被测对象的分数。4.4 第四步把验证结果沉淀成一份基准报告如果这个工作只做一次它只能帮助个人避开当次的坑。真正有价值的是把结论沉淀下来形成类似下面这样的记录检查项预期行为实际行为影响评估同一任务重复 5 次判分一致5/5 一致4/5 一致需要记录采样参数JSON 输出包含额外中文字段解析成功且忽略冗余字段解析失败会导致结果偏低长文本超过上下文窗口应截断并告警静默截断需要增加日志检查这份基准报告不仅对当前项目有意义在团队里复核他人结果时也是一张快速体检表。5. 最容易踩的坑以及一套排查链路在真实环境中构建 harness-only benchmark 会遇到的问题其实和评估模型时遇到的问题很相似。我把常见的坑整理成了一套排查链路供你遇到异常结果时参考。5.1 高发问题输入到模型时已经发生了变化很多评测框架会用模板字符串来渲染 prompt一旦模板里存在不可见字符、条件分支或是脚本注入最终发送给模型的输入就可能与设计稿不一致。常见表现模型输出中出现了你从未在 prompt 里写过的句子。示例中的中文出现乱码或反斜杠转义。换行符被统一成了空格导致 markdown 表格或代码块结构错乱。处理建议在手写模板时不要在字符串里做复杂的拼接和替换操作。如果必须动态插入内容优先使用专门的语言模板库并在送入 API 前打印最终 payload 做一次人工审查。5.2 高发问题解析器“一眼对但严格错”有时候模型给出的答案和标准答案语义一致但因为解析器按严格字符串匹配导致判错。这会让模型分数偏低。反过来如果解析器过于宽松会把错误答案判对又会让分数虚高。常见表现标准答案是 “3.5%”模型输出 “3.5 percent”判错。模型输出包含推理过程解析器只能提取最后一个冒号后的内容导致把“所以答案不是 B”误判为答案 B。JSON 字段顺序不同解析器按位置取值取错字段。处理建议判分环节一定要做到“可审计”。每一条判分结果最好都能回溯到模型输出和判分规则。如果你用正则解析至少要把正则表达式和匹配内容一起写进日志。如果人工复核的样本里发现大量不该错或不该对的案例就说明解析器需要重建。5.3 高发问题并行任务之间的“隐形串场”agent 类评估中如果 harness 复用了同一个会话对象或同一个临时目录任务 A 产生的上下文就可能污染任务 B。尤其当多个任务共用同一个进程时变量泄漏和缓存残留都是常见的隐性 bug。常见表现任务 B 的 prompt 里出现了任务 A 的内容。工具调用返回了任务 A 创建的文件。后执行的任务结果异常偏高因为继承了前面会话里的上下文提示。处理建议每个任务尽量使用全新的会话、独立的临时目录和独立的工具执行沙箱。如果不确定是否存在串场可以故意在任务 A 的 prompt 末尾加入一个独特标记然后检查任务 B 的日志里是否出现该标记。这是一个非常简单但有效的“染色实验”。5.4 排查链路按现象一层一层往下查如果跑完 harness 后发现结果异常不要急着修改模型或 prompt我一般按下面这个顺序排查看现象。是分数整体偏低、忽高忽低、某类任务全部失败还是日志报错先明确异常的类型。看输入。把最终发送给模型的 prompt 打印出来确认它不是在设计阶段就出了问题。这是最重要的第一步。看环境。Python 依赖版本是否一致、GPU/CPU 差异、网络超时、文件路径是否存在权限问题。看参数。temperature、top_p、max_tokens、seed 是否被正确传递批处理时并行数是否过大导致部分任务超时。看解析。模型原始输出是否符合预期解析器和判分规则是否忠实地处理了这些输出。看工具边界。是不是框架本身不支持某类任务比如不支持多模态、不支持流式输出、不支持工具调用。这个链路看起来朴素但在真实复盘里非常管用因为绝大多数问题都发生在“prompt 拼接”和“输出解析”两个环节而不是模型本身。6. 对普通开发者和团队的长期价值6.1 可以先从“给自己做一次 harness 体检”开始即便你不打算发起一个开源项目你也可以用这套思路审视自己手头的评测脚本。这个过程不需要很复杂只需要在一张表格里记录下面几个问题跑分脚本有没有版本管理日志里能否还原每一次实验的完整输入和原始输出判分标准有没有写在文档里同一个脚本换一台机器结果是否可复现如果有人质疑你的分数你能不能在 10 分钟内给出完整证据链如果这些问题目前回答不上来那么与其急着换更强的模型或更大的 benchmark不如先把评测链路加固一层。因为在大模型应用落地阶段可信的分数比好看的分数更有价值。6.2 哪些场景暂时不需要 harness-only benchmark对于纯探索型项目比如只是想快速看两个新模型哪个更强或者只是在早期原型里验证想法跑一个已有的公共评测工具就够了。这时去单独建设 harness 评测体系属于过度投入。还有一类场景也可以跳过上游已经维护得很成熟的评测框架并且你已经连续使用过多个项目、没有发现过异常。这种情况下做一次小规模体检即可不必常态化为每条任务都做 harness 验证。真正值得长期投入的场景是你正在做 agent 评测、工具调用评测或者需要把评测结果作为对外宣称的产品指标这时 harness 可信度就不是锦上添花而是底线要求。6.3 最终我们会把“评估工具”本身当成一个需要打磨的产品回到文章开头那个 7 个百分点的排查。那次问题的最终修复方式出奇简单在模板里补上缺失的分隔符。但真正让人后怕的是在那之前这个 harness 已经跑过几百次实验没有人发现这个肉眼可见但不容易注意到的错误。它存在了很多天产出了一大批参考结论而这些结论的基础竟然是一个输入构造缺陷。这件事让我对评测的态度有了一个转变你可以把 benchmark 理解为一把尺子但尺子本身也可能变形。过去我们默认尺子是直的现在的大模型评测越来越复杂尺子的校准不应该再是沉默的前提而应该变成评测流程里一个被显式检查的环节。“harness-only benchmark” 听起来像一个偏小众的工程提议但它想解决的是一个基础问题当模型能力数字越来越重要时我们用什么来保证这些数字诚实。如果你也想参与这类项目建议先不要急着写很多代码而是从自己最近一次跑分经历开始问一句上次那个数字真的是模型自己考出来的吗这个问题的答案就是这个方向最好的出发点。