尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

智能体评测分数为何不可直接比?揭秘评测harness的影响

智能体评测分数为何不可直接比?揭秘评测harness的影响 智能体排行榜上的分数看起来像是一套公平规则测出来的能力实际更像是被测模型和评测 harness 共同作用的结果。很多人在对比智能体模型时只盯着榜单上的数字却没有意识到决定这个数字的不只是模型本身的推理水平还有任务怎么定义、工具怎么执行、失败怎么重试、结果怎么判分。评测 harness 对最终分数的影响往往比模型本身还要大。普通大模型评测里输入一条 prompt模型输出一段文本再用答案匹配或裁判模型打分评测流程相对独立。到了智能体评测任务变成了多轮交互模型要理解目标、选择工具、处理工具返回、修正计划最后还要被判定是否完成任务。整个过程涉及数据加载、上下文组装、工具协议、沙箱环境、超时重试、结果记录和评分脚本。这些环节合在一起就是评测 harness。换句话说模型是驾驶员harness 是赛道、赛车仪表盘、裁判和后勤团队的合集。换一条赛道同一个驾驶员的名次就会变。这篇文章要解决的不是“哪个智能体更强”而是“排行榜上的分数为什么不可直接比较”。我会从评测 harness 的组成部分讲起用一个最小实验演示两个 harness 对同一个模型能产生多大的分数差距再列出影响分数的隐藏变量、失真场景和检查清单最后给出在真实项目中自建评测 harness 的建议。读完你会明白在完全理解一个分数是怎么算出来之前它不具备模型对比价值。1. 智能体评测为什么不能简单用“对错”来打分1.1 普通模型评测和智能体评测差别在哪普通模型评测的任务形态是“单轮问答”。模型输入的是一段文本输出的是另一段文本。评测方只要把输出和参考答案做比对或者请一个裁判模型打分就能得到分数。评测 harness 的影响主要体现在少量答案抽取规则、few-shot 示例和评分提示词上。智能体评测的任务形态完全不同。模型需要在多个回合内做出决策调用外部工具观察返回结果然后继续推进。以“查询商品价格并确认是否小于预算”为例模型要经历以下过程解析用户意图。生成工具调用请求。接收工具返回结果。判断结果是否满足条件。输出最终答复。这个过程中任何一个环节被 harness 干扰最终分数都会变。比如工具调用格式不匹配、工具返回内容被截断、评测环境没有重置、失败后不允许重试都会让同一个模型出现完全不同的结果。维度普通模型评测智能体评测任务形态单轮问答多轮决策 工具调用模型输出固定文本文本、工具调用、状态更新混合判分方式答案匹配或裁判打分最终答案、过程证据、任务完成情况外部依赖基本无沙箱、API、浏览器、代码仓库结果稳定性较高较低受 harness 影响大分数含义接近模型知识能力模型 harness 环境的合成结果这也是“智能体排行榜分数难以信任”的根源榜单把多轮交互压缩成一个成功率数字却没有说明数字背后环境依赖有多重。1.2 评测 harness 在智能体评测里的位置可以把评测 harness 理解为“运行智能体任务的最小装配线”。它至少包含以下模块任务数据加载器读取任务描述、参考答案、环境配置。Agent 运行器负责把模型 API 和自己的 agent 循环接起来。工具执行处理器执行模型请求的工具调用。沙箱环境提供隔离状态比如临时代码目录、浏览器页面、数据库。结果记录器保存每一步输入输出、工具结果、异常信息。评分器根据任务类型判断成功或者失败。用一段伪流程表示任务数据 - harness 运行器 - 大模型 / 智能体框架 - 工具沙箱 | v 结果记录 - 评分器 - 分数模型只负责中间那段“思考-输出工具调用-接收反馈”的循环而 harness 负责定义这段循环的规则。1.3 为什么 harness 的影响可能超过模型本身模型能力决定的是“在信息充分时能否完成任务”harness 决定的是“信息是否充分、失败如何反馈、正确结果是否被认可”。举一个最典型的情况同一个模型对同一个任务输出了同一条工具调用两个 harness 的解析器不同。一个严格解析器要求函数名必须在function.name字段里另一个兼容解析器允许从name字段里读取。模型序列化格式稍有偏差严格 harness 直接判定工具调用失败宽松 harness 却可以继续执行。模型的推理能力没有任何变化分数却可能从接近 0 变成接近满分。所以看到一个智能体排行榜时第一反应不应该是“这个模型为什么这么强”而应该是“这套 harness 为什么不那么强”。先检查 harness再谈模型能力。2. 用同一个模型跑两套 harness验证分数漂移2.1 实验设计固定模型只改工具调用解析策略很多评测团队会把分数差异归因于模型版本实际上只要换一种 harness 行为同一个模型就能在同一个任务集上产生明显分数漂移。这里做一个最小实验设计固定一个模型标识比如model-a。固定一个任务集比如 50 条“商品价格查询与判断”任务。固定工具列表只有一个get_price工具。两个 harness 的唯一差异是“工具调用解析策略”。严格 harness 的策略是模型输出必须是合法 JSON且必须包含function.name和function.arguments字段否则该任务按失败记录。宽松 harness 的策略是尝试解析 JSON兼容name字段如果解析失败会给模型一条修复提示并允许重试。实验要验证的问题是模型没变任务没变工具没变分数是否会因为 harness 的容错程度而大幅波动。2.2 最小评测代码抽象模型接口保留真实解析过程下面这段代码用于说明思路。实际项目中call_llm会被替换成你自己的模型网关或 API 调用。模型输出格式按 OpenAI 兼容接口描述。import json def call_llm(messages: list[dict], tools: list[dict], model: str) - dict: # 实际项目里替换成你自己的模型调用 # 返回值示例 # {finish_reason: tool_calls, tool_calls: [{ # id: 0, # function: {name: get_price, arguments: {\product_id\: \p1001\}} # }]} raise NotImplementedError def strict_parse_tool_call(raw: str) - dict: 严格 harness必须是合法 JSON且必须包含 function 字段。 data json.loads(raw) if function not in data: raise ValueError(missing function field) return { name: data[function][name], arguments: json.loads(data[function][arguments]), } def tolerant_parse_tool_call(raw: str) - dict | None: 宽松 harness尝试清理代码块兼容 name 字段解析失败返回 None。 if not raw: return None raw raw.strip() if raw.startswith(): raw raw.removeprefix(json).removesuffix().strip() try: data json.loads(raw) except json.JSONDecodeError: return None if isinstance(data, dict) and name in data and arguments in data: try: return {name: data[name], arguments: json.loads(data[arguments])} except Exception: return {name: data[name], arguments: {}} if isinstance(data, dict) and function in data: return { name: data[function][name], arguments: json.loads(data[function][arguments]), } return None def execute_tool(name: str, arguments: dict) - str: if name get_price: return price: 99.00 return unknown tool def judge(task: dict, answer: str) - bool: # 这里只做最简单的答案包含判断 return task[expected] in answer def run_episode(harness_type: str, task: dict, model: str) - bool: messages [{role: user, content: task[prompt]}] max_steps task.get(max_steps, 8) for _ in range(max_steps): output call_llm(messages, TOOLS, model) if output[finish_reason] stop: return judge(task, output[content]) raw_arguments output[tool_calls][0][function][arguments] if harness_type strict: try: call_info strict_parse_tool_call(raw_arguments) except Exception: return False else: call_info tolerant_parse_tool_call(raw_arguments) if call_info is None: messages.append({ role: user, content: 工具调用解析失败请重新输出合法 JSON 格式的工具调用。, }) continue result execute_tool(call_info[name], call_info[arguments]) messages.append({ role: tool, tool_call_id: 0, content: result, }) return False TOOLS [ { type: function, function: { name: get_price, parameters: { type: object, properties: {product_id: {type: string}}, }, }, } ] def run_comparison(model: str, tasks: list[dict]): for harness_type in [strict, tolerant]: correct 0 for task in tasks: if run_episode(harness_type, task, model): correct 1 print(f{harness_type}: {correct / len(tasks):.2f}) if __name__ __main__: sample_tasks [ {prompt: 请查询商品 p1001 的价格并判断是否低于 100 元。, expected: 低于}, ] run_comparison(model-a, sample_tasks)运行方式python comparison.py在没有接入真实模型时代码会抛出NotImplementedError。实际使用时要先实现call_llm和execute_tool。这个最小脚本的重点不是跑通而是展示两个 harness 在同一个循环里的差异点。2.3 可能看到的实验结果如果model-a经常在工具调用字段上输出name而不是function.name结果会像下面这样strict: 0.18 tolerant: 0.64这组数字是示意不是固定结论。真实测试中具体分数取决于模型、任务集和提示词。但趋势普遍存在模型输出格式与 harne ss 解析规则的匹配度会直接决定成功率。所以在智能体评测里一个分数至少包含三层含义模型能不能完成推理。模型输出能不能被 harness 正确解析。解析后的动作能不能被环境和评分器认可。只要第二层或第三层出了问题再强的模型也会被压到低分。3. 影响分数的七个隐藏变量不在模型里在 harness 里3.1 工具调用协议和 schema 宽容度模型对工具调用协议的掌握程度不同。有的模型擅长 OpenAI 风格的 function calling有的模型更习惯 ReAct 式的自然语言输出。评测 harness 如果只支持一种协议就会天然偏向支持这种协议的模型。常见差异包括arguments是 JSON 字符串还是 JSON 对象。函数名在name字段还是function.name字段。模型是否会在 JSON 外套上 Markdown 代码块。工具调用 ID 是否需要与 assistant 消息中的 ID 一一对应。工具结果返回后上下文里是否保留完整历史。高容错的 harness 会把这些差异全部吸收低容错的 harness 会直接判失败。表面看是分数差距实际是解析器实现差距。3.2 状态重置与失败重试策略智能体任务往往需要真实环境。比如浏览器任务、代码仓任务、订单流程任务。环境状态必须在每个任务开始前重置否则后一个任务会看到前一个任务留下的数据。一个常见的评测问题是harness 在重置环境时只清空了某个缓存目录但数据库里还残留着上一轮的测试账号。模型在第二轮任务里直接找到了“之前创建的商品”实际能力并不是在任务要求的状态下完成的。失败重试策略同样重要。某些 harness 允许模型在一次工具调用失败后重新尝试某些 harness 规定一次失败整个任务判负。还有网络超时、API 限流这类与模型能力无关的失败也会消耗重试次数。评测时如果没有把“环境失败”和“模型决策失败”分开最终分数就不可信。3.3 终止条件、超时和评分粒度任务什么时候算结束由 harness 控制。常见终止条件包括模型输出finish_reasonstop。达到最大工具调用步数。达到总时长上限。模型主动输出“任务完成”。不同设置对评测结果影响很大。一个任务需要 12 步才能完成但 harness 只允许 10 步模型再强也难以成功。另一个任务用 2 步能完成但 harness 允许模型反复尝试直到上下文被截断反而拉低成功率。评分粒度也要统一。只看最终答案是否包含关键词和检查完整步骤是否合理是两种不同的评分逻辑。前者可能把“蒙对结果”判成成功后者可能把“结果正确但过程有冗余”判成失败。3.4 数据集构建方式难度、污染和任务泄露任务集本身是 harness 的一部分。同一个模型跑同一个 benchmark会因为任务说明的详细程度、用例数量、难度分布和参考答案质量产生明显差异。任务难度差异最常见。有的 harness 给每个任务提供详细步骤比如“先搜索再筛选前三条再调用工具确认”。这等于把 agent 的规划能力弱化成了工具执行能力。有的 harness 只给用户原话要求模型自己规划难度完全不同。评测集污染更隐蔽。如果评测任务在模型训练阶段已经出现过模型可能记住答案而不是真正完成任务。即使模型没有完整记忆公开 benchmark 任务也容易被数据蒸馏、微调过程“反推”出来。harness 是否提供版权声明、去重逻辑和污染过滤时间点直接决定分数含义。3.5 沙箱环境和依赖版本智能体任务里工具执行结果随环境变化而变化。同样一段代码Python 3.8 下能运行Python 3.11 下可能因为标准库行为变化而失败。同样一个浏览器自动化工具体系不同版本对按钮的定位结果可能不同。典型的评测环境变量包括操作系统和基础镜像版本。包管理器 lock 文件。浏览器版本和渲染引擎。网络策略和外部 API 白名单。容器 CPU、内存和并发数。如果 harness 没有对环境和依赖做版本控制同一个模型在两次评测中得到不同分数是正常现象。对比排行榜时必须先确认两个分数是否来自同一个环境快照。3.6 评分脚本和裁判模型的标准化程度评分是 harness 里的收尾环节也是最大的黑盒之一。对于代码类任务评分可能是运行单元测试。此时测试用例选择、测试顺序、环境依赖都会影响结果。对于问答类任务评分可能是字符串匹配或关键词匹配。对于开放任务评分可能是 LLM judge也就是用另一个大模型来给结果打分。LLM judge 本身就是另一个评测系统。裁判模型的提示词、模型版本、温度参数和对“成功”的定义都会影响分数。裁判提示词A只要输出中包含预算判断就判通过。 裁判提示词B必须明确输出“低于/高于”且给出商品名称和价格依据。同一个 agent 输出在两种裁判规则下可能有完全不同的结果。所以如果评测说明里只写“由评测模型打分”没有公开裁判提示词那这个分数基本不具备复现条件。3.7 模型服务版本和采样参数模型标识相同不代表模型行为完全相同。模型服务方可能升级推理后端、调整量化精度、修改默认参数量甚至更新安全策略。外部调用者只能感知到“模型名没变”但实际跑的是另一个行为版本。采样参数也要固定。temperature、top_p、max_tokens 对多轮任务影响很大。即使设置为 temperature0不同推理框架、不同批处理方式也可能产生微小波动。评测 harness 如果每次请求随机分配后端实例分数波动会叠加到模型差异上。因此在做横向对比时harness 应当固定模型服务版本或 snapshot 标识。推理框架版本。解码参数。并发策略。否则分数变化无法被归因。4. 三个典型的智能体分数失真场景4.1 场景一同一模型、同一任务兼容解析让分数从 0.2 变成 0.7项目里发生过类情况模型在调用工具时喜欢输出带 Markdown 代码块的 JSON导致严格解析器直接报错。评测团队一开始以为模型能力不足后来把解析器改成兼容模式再去掉外层代码块结果分数大幅上升。项目严格 harness宽容 harness工具输出格式必须 function.name兼容 name / 代码块失败后处理直接失败提示重试单任务得分0.20.7这个场景说明当模型输出与 harness 解析规则不匹配时分数低的不是模型而是评测方案。4.2 场景二同一个代码仓库换个时间跑分数漂移评测代码仓库会持续更新。任务描述可能被改得更容易评分脚本可能增加对部分答案的容错任务集可能增加新样本。如果只记录了“用同一个模型跑了同一个 benchmark 的两个版本”很难判断分数差异来自模型还是来自评测集。同样的问题出现在依赖升级上。harness 仓库锁定的依赖从版本 A 升级到版本 B由于环境差异一个原本能通过的智能体任务失败分数随之下降。模型本身没有任何变化。所以任何分数都应当绑定仓库 commit、依赖版本和任务集版本。丢掉了这些信息分数就只是历史快照不能用于当前模型对比。4.3 场景三最终答案正确过程评分却判失败有些智能体评测不只看结果还看过程。比如规定“每一步工具调用都必须成功”或者“不允许无意义的工具调用”。这种评分规则在防止刷榜的同时也会误伤真实能力。设想一个任务用户要求“查询并预订会议室”。模型最终订到了正确的会议室但在查询过程中第一次工具调用因为参数格式错误而失败第二次成功。如果 harness 规定“任何工具调用失败整个任务判负”模型即使完成目标也会得 0 分。这种评分策略是否合理取决于评测目标。如果是测试模型从错误中恢复的能力那把失败重试当成扣分项就不合适如果是测试模型一次性执行正确率那过程评分可以接受。怕的是评测方没有说明过程规则读者却把分数理解成“任务完成率”。5. 判断一个智能体排行榜能不能信检查清单5.1 发布前必须回答的问题不要把公开排行榜当成白盒测试报告。阅读排行信息时至少确认以下信息是否完整。检查项需要看到的内容缺失时的风险harness 源码和版本仓库地址、commit hash无法复现任务集版本评测集版本号、构建日期可能使用旧数据模型服务版本API snapshot 或推理后端版本模型行为不确定环境说明操作系统、镜像、依赖锁定环境漂移无法排查解析规则工具调用协议、失败重试策略分数偏向特定格式评分脚本判分逻辑、测试用例、裁判提示词评分不可审计基准分数参考模型在同一 harness 的分数缺少横向量纲稳定性指标多次运行的标准差单次结果偶然性大5.2 复现一个分数的最短路径复现分数时不要直接跑全量先验证环境和配置。git clone harness_repo ./harness cd ./harness git checkout commit_id python -m venv .venv source .venv/bin/activate pip install -r requirements.txt # 先跑 mini 子集验证模型连通、工具执行、评分脚本都正常 python run_benchmark.py \ --model model_name \ --dataset mini \ --seed 42 \ --output ./results/mini.jsonl # 确认 mini 通过后再跑全量 python run_benchmark.py \ --model model_name \ --dataset full \ --seed 42 \ --output ./results/full.jsonl跑完一次还不够。建议用不同 seed 至少跑两遍并把两次结果的差异记录到实验结果里。如果两次结果波动超过 5%先不要相信这个 benchmark 的排序能力。5.3 哪些信息缺失时可以视为高风险只公布一个平均数不公布任务级明细。不公开模型输出的日志或 trace。不说明失败后是否允许重试。不说明工具执行环境。不说明评分裁判模型和提示词。不固定 seed 和模型服务版本。满足其中任何一条排行榜分数都应该降级为“参考信息”而不是决策依据。6. 在真实项目中自建评测 harness 的实践建议6.1 学习环境、内部回归和生产验收不是一回事公开排行榜关心“模型相对强弱”公司内部评测更关心“业务任务能不能稳定完成”。两者对 harness 的要求不一样。场景主要目标harness 要求学习环境快速理解评测流程单任务脚本、日志输出完整内部回归防止模型升级引入退化任务集固定、版本记录、回归阈值生产验收判断是否可上线成功率、成本、延迟、可观测性、回滚机制学习时可以用临时脚本生产必须把评测 pipeline 当成正式工程来维护。否则一次模型版本切换后“分数没变化”并不能说明业务不受影响也可能是因为评测 harness 根本没有覆盖到真实业务。6.2 最小 harness 应该有哪些模块一个可维护的评测 harness 至少包含四部分任务加载、环境沙箱、运行器、报告器。下面是一个最小配置示例。eval: name: my_agent_regression dataset: tasks/sample.jsonl runner: model: model_name max_steps: 10 timeout_sec: 60 temperature: 0 sandbox: type: docker image: eval-sandbox:1.0.0 reset: before_each_task executor: tool_schema_file: tools/schema.json retry_on_tool_error: true max_retry: 2 judge: type: hybrid exact_match: true unit_test: true llm_model: judge_model_name llm_prompt_file: prompts/judge.txt reporter: type: jsonl path: results/run_${timestamp}.jsonl version: code_commit: ${git_commit} dataset_commit: dataset_sha sandbox_image: eval-sandbox:1.0.0每个字段都要有明确含义。retry_on_tool_error是影响分数的关键参数允许重试和不允许重试评测的是两种不同能力。配置写成 YAML 后每次评测都会产生一份可见的记录而不是埋在代码里。6.3 从公开榜单迁移到业务评测的三步走第一步从公开 benchmark 里选 20 到 50 条与自身业务相似的任务组成“业务种子集”。不要一上来就跑几千条先保证单条任务可解释。第二步把工具调用、中间结果、最终答案全部记录成 JSONL。每次评测后至少回答三个问题完成率是多少、失败原因是工具还是模型、失败发生在第几步。第三步建立回归基准。固定模型 A 为 baseline每次模型更新后重新跑任务集分数下降超过阈值就阻止发布。阈值要根据多次运行的波动范围确定通常需要先跑 3 到 5 次才能得到合理区间。在 Dify、Coze 这类可编排智能体平台上接入模型时同样要遵循这套思路。平台本身具备一定的评测能力但平台里配置的工具描述、重试开关、上下文轮数和用户提示词都会影响最终效果。从平台里导出的分数到了另一个 harness 里可能完全不同。7. 常见坑与排查顺序7.1 常见坑问题现象可能原因检查方式处理建议分数复现不出来harness 配置或模型服务版本不一致对比配置文件、commit、模型标识记录所有版本信息后再跑工具调用成功但被判错解析器只兼容一种 schema查看 agent trace 中的原始输出增加 schema 兼容或修正模型提示任务经常超时沙箱资源不足或外部 API 过慢检查容器监控和网络日志调大 timeout区分超时与失败重跑分数波动大采样参数、并发和后端实例不稳定多次运行观察标准差固定 seed降低并发增加重复次数裁判模型打分不稳定judge 模型版本或提示词不一致记录 judge prompt 和模型名固定裁判版本公布提示词7.2 排查一个智能体评测分数的顺序遇到“模型分数异常”时按以下顺序排查不要先怀疑模型能力。确认任务输入是否一致。对比 prompt、few-shot、任务 ID 的哈希值。确认模型调用参数是否一致。temperature、top_p、max_tokens、模型名。确认工具执行环境是否一致。镜像版本、依赖 lock、工具返回格式。确认解析和重试策略是否一致。严格解析还是会修错重试。确认评分脚本是否一致。评分配置 hash、裁判提示词。查看完整 trace。定位第一个失败发生在哪一步。用 mini 子集重复运行两次看波动是否来自随机性。如果以上步骤都正常但分数还是和榜单对不上就要怀疑榜单是否完整公开了评测条件。这种情况下问题可能不在你的本地复现而在榜单发布方的信息不透明。8. 让分数可解释而不是让分数好看8.1 给分数附上环境说明智能体评测分数不是天生不可信而是被压缩得太厉害。一个成功率数字背后藏着工具 schema、任务集难度、环境版本、失败重试规则、评分脚本等多个变量。榜单发布者如果只给数字不给环境说明读者就会把它当成模型能力的直接度量。更合理的做法是每个分数都附加一份环境说明至少包含模型版本、harness commit、任务集版本、沙箱镜像和 judge 配置。读者拿到这些信息后才能判断分数是否可以复现、是否适合自己的业务。对发布方来说这也能减少“榜单无法复现”的信任危机。8.2 下一次做智能体评测时最该注意什么第一次搭建评测 harness 时不要追求任务数量先追求结果可解释。用 20 条任务把完整链路跑通保证每一条失败都能被定位到“模型决策、工具执行、评分逻辑”中的某一层。然后再扩大到全量任务集。同时要记住评测 harness 本身也是被测对象。当模型分数异常时先问一个问题这个分数是模型能力的结果还是 harness 设计的结果多数情况下答案都会指向后者。分数只有在你完全理解它是怎么算出来之后才具备比较意义。
返回列表