自建Agent真实任务评测框架:从榜单到实战的鸿沟

1. 为什么我要自己搭一套 Agent 评测框架

模型厂商的宣传页现在越来越像手机发布会了。跑分图一张比一张漂亮,SWE-bench 刷到 70% 多,GAIA 榜单排到前三,工具调用成功率标着 99.2%。我一开始也信,直到我把同一个真实任务分别丢给三个号称"榜单前十"的 Agent,结果一个把数据库删了半张表,一个在第三步就开始循环调用同一个接口,还有一个干脆把用户需求理解反了,输出了一份完全无关的报告。

那一刻我意识到一个问题:榜单分数和真实任务表现之间,存在一条巨大的鸿沟。榜单题目往往是精心设计的、边界清晰的、单轮就能搞定的任务,而真实场景里的任务是模糊的、多轮的、带副作用的、需要跨工具协作的。你在榜单上看到的 90 分,落到真实业务里可能连 40 分都不到。

所以我花了大概三周时间,搭了一套自己的 Agent 真实任务评测框架。核心目标只有一个:不看宣传,只看它在我的任务集上到底行不行。这套框架跑完之后,我对市面上几个主流 Agent 框架和模型组合的真实能力有了完全不一样的认知,也踩了不少坑。这篇文章就把整套框架的设计思路、任务设计方法、执行追踪机制、评分归因逻辑,以及我实测下来的一些结论,完整地分享出来。

这套东西适合谁?如果你正在做 Agent 应用开发、正在选型 Agent 框架(LangChain、Dify、CrewAI、AutoGen 这类)、或者正在评估某个模型能不能扛起你的业务,那这套框架你可以直接抄作业。如果你只是好奇 Agent 到底能干什么,看完你也会对"宣传"和"现实"的差距有个清醒认识。

先说结论性的判断:Agent 的能力上限由模型决定,但能力下限由框架的编排、工具设计和错误处理决定。很多 Agent 在真实任务里翻车,不是模型不行,是框架没给它兜住。这一点,只有你自己搭评测框架跑一遍才能体会到。

2. 评测框架的整体设计与思路拆解

2.1 为什么现成的评测工具不够用

市面上不是没有 Agent 评测工具,比如一些开源的 benchmark harness、LangSmith 的评估模块、还有各种榜单的评测脚本。但我用下来发现几个硬伤。

第一,任务集不贴合我的业务。榜单题目大多是通用推理、代码修复、网页操作,而我的真实任务是"从一堆混乱的会议记录里提取待办、判断优先级、写入项目管理工具、并给相关人发提醒"这种复合型任务。榜单不测这个。

第二,评分维度太单一。大多数评测只看"最终答案对不对",但 Agent 的真实价值在于过程——它调用了哪些工具、调用顺序对不对、有没有多余操作、出错后能不能自愈。只看结果,等于把 Agent 当成了一个黑盒问答模型。

第三,缺乏执行追踪。Agent 跑任务时会产生大量的中间步骤:思考链、工具调用、观察结果、再思考。这些中间过程才是诊断问题的关键,但很多评测工具只记录最终输出,中间过程全丢了。

所以我决定自己搭。核心设计原则有三条:任务要真实、过程要可追踪、评分要可归因。

2.2 框架的四层架构

整套框架我拆成了四层,从下到上分别是:

  • 执行层:负责实际跑 Agent,对接不同的模型和框架,统一成一套调用接口。
  • 追踪层:拦截并记录 Agent 的每一步动作,包括思考、工具调用、参数、返回、耗时、token 消耗。
  • 任务层:管理任务集,每个任务包含输入、期望结果、评分规则、以及可选的中间检查点。
  • 评分层:根据追踪记录和最终结果,做多维度打分,并输出归因分析。

这四层解耦的好处是,换模型、换框架、换任务集,互不影响。我实测下来,这种分层设计让迭代效率高了很多——想加一个新模型,只需要在执行层加一个适配器;想加一类新任务,只需要在任务层写配置。

2.3 为什么选择"过程优先"的评测理念

传统评测是"结果导向":只看最终输出对不对。我把它改成了"过程优先":先看过程合不合理,再看结果对不对。

原因很直接。一个 Agent 可能碰巧蒙对了结果,但过程一塌糊涂——比如它调用了 15 次工具,其中 10 次是无效重试,最后靠一次运气调用拿到了正确答案。这种 Agent 你敢放到生产环境吗?不敢。因为下一次它可能就蒙错了。

反过来,一个 Agent 可能结果差一点点,但过程非常干净:3 步搞定,工具调用精准,出错后立刻调整策略。这种 Agent 虽然这次没拿满分,但它的稳定性是可预期的,值得继续优化。

所以我的评分体系里,过程分和结果分的权重是 6:4。过程分看的是工具调用效率、步骤合理性、错误恢复能力;结果分看的是最终输出与期望的匹配度。这个权重不是拍脑袋定的,是我跑了上百个任务后,发现过程分高的 Agent 在重复任务上的方差明显更小,才定下来的。

3. 任务设计:评测框架的灵魂

3.1 任务分级的三个维度

任务设计是整套框架里最费脑子的部分。我一开始想按"简单/中等/困难"来分,后来发现这个分法太粗糙,因为"困难"可能来自不同原因。最后我按三个维度来分级:

维度说明示例
步骤复杂度完成任务需要的工具调用步数单步查询 vs 十步跨系统操作
信息模糊度输入信息的完整程度明确指令 vs 隐含需求
副作用风险操作是否会产生不可逆影响只读查询 vs 写库删数据

这三个维度组合起来,就能覆盖绝大多数真实场景。比如"步骤多+信息模糊+有副作用"的任务,就是最考验 Agent 的硬骨头,也是我重点测试的对象。

3.2 任务集的构成比例

我最终的任务集有 60 个任务,构成比例如下:

  • 基础能力任务(20 个):单工具调用、明确指令、无副作用。用来测 Agent 的基本功,比如"查询某天的订单数量"。
  • 多步编排任务(20 个):需要 3-8 步工具调用,跨多个工具。比如"找出上周销售额下降的原因并生成报告"。
  • 模糊需求任务(10 个):输入信息不完整,需要 Agent 主动澄清或做合理假设。比如"帮我整理一下最近的客户反馈"。
  • 高风险任务(10 个):涉及写操作、删除操作、对外发送操作。比如"把过期的优惠券批量下架"。

这个比例是我反复调整后的结果。基础任务太多,测不出真实能力;高风险任务太多,跑一次成本太高(因为要准备回滚)。20:20:10:10 这个比例,实测下来性价比最高。

3.3 每个任务的配置结构

每个任务我用一个 YAML 文件来描述,结构大概是这样:

task_id: multi_step_007 name: 销售下降原因分析 category: multi_step difficulty: steps: 6 ambiguity: medium side_effect: none input: | 上周(2024-06-10 至 2024-06-16)的销售额比前一周下降了 23%, 请分析可能的原因,并给出至少三条可执行的建议。 expected: must_contain: - 数据对比 - 原因假设 - 可执行建议 must_call_tools: - query_sales_data - query_traffic_data max_steps: 12 scoring: process_weight: 0.6 result_weight: 0.4 checkpoints: - step: 1 expect: 调用销售数据查询 - step: 3 expect: 调用流量数据查询做对比

这个结构的关键在于checkpoints。它定义了任务执行过程中必须经过的关键节点。Agent 可以自由发挥,但必须踩到这些点。这样既给了 Agent 灵活性,又保证了评测的可控性。

3.4 任务设计的三个坑

设计任务时我踩了三个坑,这里直接说。

第一个坑:任务描述太详细。我一开始把任务写得像操作手册,结果 Agent 只是机械执行,测不出它的理解能力。后来我改成"给目标不给路径",比如不说"先查 A 再查 B",只说"分析原因",让它自己决定查什么。

第二个坑:期望结果太死板。我一开始用精确字符串匹配,结果 Agent 输出"销售额下降了约 23%"和期望的"下降 23%"匹配不上,白白扣分。后来改成语义匹配+关键要素检查,只要包含核心信息就给分。

第三个坑:忽略了任务的"可复现性"。有些任务依赖实时数据,今天跑和明天跑结果不一样,没法对比。后来我把所有任务的数据源都做了快照,保证每次跑的环境一致。

4. 执行追踪:把 Agent 的每一步都看清楚

4.1 追踪层要记录什么

追踪层是整套框架里技术含量最高的部分。它需要在 Agent 执行过程中,拦截并记录以下信息:

  • 思考过程:Agent 的推理链,包括它为什么选择这个工具、为什么调整策略。
  • 工具调用:调用了哪个工具、传了什么参数、返回了什么结果、耗时多少。
  • token 消耗:每一步的输入输出 token 数,用来算成本。
  • 错误与重试:哪里出错了、错误类型、重试了几次、怎么恢复的。
  • 状态变化:Agent 的内部状态(如果有记忆机制)如何演变。

这些信息合起来,就是一份完整的"执行轨迹"。有了它,Agent 的任何一个决策都能被复盘。

4.2 拦截机制的实现思路

拦截的核心是在 Agent 和工具之间插一层代理。不管 Agent 用什么框架,工具调用最终都要经过一个统一的入口,我就在这个入口做拦截。

具体做法是给每个工具包一层 wrapper,wrapper 负责记录调用前后的状态,然后透传给真实工具。伪代码大概是这样:

def traced_tool(tool_func, tracer): def wrapper(*args, **kwargs): step_id = tracer.start_step(tool_func.__name__, args, kwargs) try: result = tool_func(*args, **kwargs) tracer.end_step(step_id, result, status="success") return result except Exception as e: tracer.end_step(step_id, str(e), status="error") raise return wrapper

这个 wrapper 的好处是对 Agent 完全透明。Agent 不知道自己被追踪了,它的行为不会因为追踪而改变。这一点很重要,否则测出来的就不是真实表现了。

4.3 追踪数据的存储与查询

追踪数据我用 SQLite 存,因为单机跑评测,SQLite 足够用,而且查询方便。表结构大概是这样:

表名用途关键字段
runs一次完整评测run_id, agent_name, model, start_time
steps每一步动作step_id, run_id, step_type, content, tokens
tool_calls工具调用详情call_id, step_id, tool_name, args, result, duration
errors错误记录error_id, step_id, error_type, message, recovered

有了这些表,我就能做各种维度的分析。比如"这个 Agent 平均每个任务调用多少次工具"、"错误恢复率是多少"、"token 消耗分布如何"。这些数据是评分归因的基础。

4.4 追踪带来的一个意外收获

追踪层搭好之后,我发现了一个意外的好处:它能帮我定位框架本身的问题。

有一次我测一个 Agent,发现它在某个任务上总是第 4 步失败。看追踪记录才发现,是框架的工具描述写得有歧义,Agent 理解错了参数格式。这个问题在只看结果的时候根本发现不了,因为结果只是"失败",但追踪记录直接指向了根因。

所以追踪层不只是评测工具,它还是调试工具。我现在开发 Agent 应用时,也一直开着追踪,随时看 Agent 到底在干什么。

5. 评分归因:从"多少分"到"为什么"

5.1 多维度评分体系

评分不是简单打个总分,而是拆成多个维度,每个维度单独打分,最后加权。我的评分维度如下:

维度权重评分依据
结果正确性40%最终输出与期望的语义匹配度
过程合理性25%工具调用顺序、步骤数是否在合理范围
工具使用效率15%有效调用占比、无效重试次数
错误恢复能力10%出错后能否自愈、恢复速度
成本控制10%token 消耗、耗时

这个权重是我根据业务优先级定的。对我来说,结果正确最重要,但过程合理性紧随其后,因为过程合理的 Agent 更稳定。成本控制权重最低,因为现阶段 Agent 的成本还不是主要矛盾,但随着规模化,这个权重以后会调高。

5.2 归因分析怎么做

光有分数没用,关键是知道为什么扣分。归因分析就是把扣分项映射到具体原因。

我的做法是给每个扣分点打标签。比如:

  • 结果不正确 → 标签可能是"理解偏差"、"信息缺失"、"工具返回错误未处理"
  • 过程不合理 → 标签可能是"多余步骤"、"顺序错误"、"循环调用"
  • 工具使用低效 → 标签可能是"参数错误重试"、"选错工具"

这些标签不是自动生成的,而是我在看追踪记录时手动标注的。标注了几十个任务后,我总结出了一套常见的失败模式,现在可以半自动地识别。

5.3 一个真实的归因案例

举个具体例子。我测某个 Agent 做"客户反馈整理"任务,结果分只有 55 分。看追踪记录发现:

  • 第 1-3 步正常,读取了反馈数据。
  • 第 4 步开始,Agent 试图对反馈做情感分类,但调用的工具返回格式和它预期的不一样。
  • 第 5-8 步,Agent 反复重试同一个工具,参数几乎没变。
  • 第 9 步,Agent 放弃分类,直接输出了原始反馈的罗列。

归因结论:Agent 缺乏工具返回格式的容错能力,且没有重试策略的调整机制。这不是模型能力问题,是框架的工具描述和错误处理没做好。后来我改进了工具描述,明确返回格式,并给 Agent 加了"重试时调整参数"的提示,同样的任务分数提到了 82 分。

这个案例说明,归因分析的价值在于把"分数低"转化成"具体改哪里"。没有归因,你只知道 Agent 不行;有了归因,你知道怎么让它行。

6. 实操过程:从零搭起这套框架

6.1 环境准备与依赖选择

整套框架我用 Python 写的,因为 Agent 生态里 Python 的库最全。核心依赖就几个:

  • 执行层:对接不同框架,我用了各框架自己的 SDK,没有强行统一。
  • 追踪层:自己写的 wrapper,没依赖第三方。
  • 存储:SQLite,标准库自带。
  • 评分:语义匹配用了 sentence-transformers,轻量够用。

环境准备没什么特别的,一个虚拟环境,装好依赖就行。我建议用 Python 3.10 以上,因为有些 Agent 框架对版本有要求。

6.2 执行层的适配器写法

执行层的关键是统一接口。不管底层是 LangChain 还是 CrewAI,对外都暴露同一个run(task)方法。适配器的写法大概是这样:

class BaseAgentAdapter: def run(self, task_input: str) -> dict: raise NotImplementedError class LangChainAdapter(BaseAgentAdapter): def __init__(self, agent_executor): self.executor = agent_executor def run(self, task_input: str) -> dict: result = self.executor.invoke({"input": task_input}) return {"output": result["output"]}

这样评分层就不用关心底层是什么框架,只管拿统一的输出。我实测下来,加一个新框架的适配器,半小时就能搞定。

6.3 追踪层的接入方式

追踪层的接入有两种方式,取决于框架是否支持回调。

如果框架支持回调(比如 LangChain 的 callbacks),直接挂一个 callback handler 就行,最干净。如果不支持,就得用 wrapper 包工具。我两种都用过,wrapper 更通用,但需要改工具定义;callback 更省事,但依赖框架支持。

我的建议是优先用 callback,不行再上 wrapper。因为 wrapper 会侵入工具定义,改起来麻烦。

6.4 任务集的加载与执行

任务集用 YAML 存,加载逻辑很简单,遍历目录读文件就行。执行时,每个任务跑 N 次(我默认跑 3 次),取平均分,减少随机性带来的波动。

跑的时候要注意任务之间的隔离。有些任务会写数据,如果不隔离,前一个任务的数据会影响后一个。我的做法是每个任务跑之前重置数据快照,跑完再重置一次。

6.5 评分与报告生成

评分逻辑是纯计算,输入是追踪记录和任务期望,输出是各维度分数和归因标签。报告我用 Markdown 生成,包含:

  • 总分和分维度得分
  • 每个任务的详细追踪摘要
  • 扣分点和归因标签
  • 跨任务的趋势分析

报告生成后,我一般会人工过一遍,确认归因标签打得准不准。这一步不能省,因为自动归因总有误判。

7. 实测结论:谁真行,谁在吹

7.1 几个主流组合的真实表现

我测了四组组合,分别是不同模型配不同框架。为了避免引战,我用代号表示。实测结果如下(满分 100):

组合结果分过程分总分主要问题
A 模型 + 框架 X786572工具调用冗余,重试策略差
B 模型 + 框架 X827177结果不错,但步骤偏多
A 模型 + 框架 Y758077过程干净,但结果偶尔偏差
B 模型 + 框架 Y858384综合最稳

这个结果和榜单排名完全不一样。榜单上排第一的组合,在我的任务集上只排第三。原因就是榜单任务和我的任务差异太大。

7.2 三个反直觉的发现

发现一:模型强不代表 Agent 强。A 模型在纯推理榜单上比 B 模型高不少,但配上同样的框架后,Agent 表现反而略低。原因是 A 模型在工具调用格式上更"自由",经常输出框架解析不了的格式,导致额外重试。

发现二:框架的编排能力被严重低估。同一个模型,换框架后总分能差 7 分。框架 Y 的错误处理和重试策略明显更好,同样的错误它能自愈,框架 X 就直接卡死。

发现三:高风险任务上,所有组合都翻车。涉及写操作的任务,没有一个组合能稳定通过。主要问题是 Agent 在执行写操作前,缺乏"确认"意识,经常直接执行,导致不可逆后果。这一点是所有框架的通病。

7.3 这些结论对我选型的影响

跑完这套评测后,我的选型逻辑变了。以前我看榜单,现在我只看两件事:过程分和错误恢复率。因为这两个指标决定了 Agent 在生产环境里稳不稳。

具体来说,我现在选型会先跑一遍我的任务集,重点看:

  • 过程分是否稳定在 75 以上
  • 错误恢复率是否超过 60%
  • 高风险任务是否有"确认机制"

这三个指标达标,才进入下一轮评估。榜单分数?参考一下就行,不作为决策依据。

8. 常见问题与排查技巧实录

8.1 评测跑出来的分数波动大怎么办

这是最常见的问题。同一个 Agent,跑三次分数能差 15 分。原因通常是模型的随机性(temperature 没设成 0)或者任务本身有随机因素。

解决办法:固定随机种子 + 多次取平均。我把 temperature 设成 0,每个任务跑 3 次取平均,波动就小多了。如果还波动,说明任务设计有问题,需要检查是否有未固定的变量。

8.2 Agent 在评测中"作弊"怎么办

有些 Agent 会绕过工具,直接凭记忆回答。比如任务要求查数据库,它不查,直接编一个答案。这种在只看结果的评测里可能蒙混过关,但在过程评测里会暴露——因为 checkpoints 没踩到。

我的做法是强制工具调用检查。如果任务要求必须调用某个工具,而追踪记录里没有,直接判 0 分,不管结果对不对。

8.3 追踪数据太多,怎么快速定位问题

一个复杂任务可能产生上百条追踪记录,人工看很累。我的技巧是先看错误记录,再看步骤摘要。错误记录直接指向失败点,步骤摘要能看出整体流程是否合理。只有这两步都定位不到问题时,才去看完整的思考链。

8.4 常见问题速查表

问题现象可能原因排查方向
分数波动大随机性未固定检查 temperature、随机种子
结果对但过程乱Agent 蒙对看工具调用是否冗余
总是某步失败工具描述有歧义检查工具 schema 和描述
高风险任务全挂缺乏确认机制加执行前确认步骤
token 消耗异常高循环调用看是否有重复调用同一工具

8.5 几个独家避坑技巧

技巧一:任务集要定期更新。Agent 能力在涨,老任务很快就不够用了。我每两个月会加一批新任务,淘汰一批太简单的。

技巧二:保留"失败案例库"。每次 Agent 翻车的追踪记录,我都存下来,作为后续优化的参考。这个库比任何文档都有价值。

技巧三:评分标准要写死,不能边跑边改。我一开始边跑边调评分规则,结果前后数据没法对比。后来我把评分规则版本化,每次改规则都记版本号,保证可比性。

技巧四:别只看总分,要看分布。一个 Agent 总分 80,可能是所有任务都 80,也可能是半数 100 半数 60。后者风险大得多。所以报告里一定要有分数分布图。

9. 框架的扩展方向

这套框架现在还在用,也在持续迭代。我最近在做的扩展有几个方向。

一是加入多轮对话任务。现在的任务大多是单轮的,但真实场景里 Agent 经常需要和用户来回澄清。多轮任务的评测复杂度高很多,但更贴近现实。

二是加入成本追踪的细粒度分析。现在只统计总 token,我想细化到每个工具调用的成本,这样能看出钱花在哪了。

三是自动化归因。现在归因还是半自动的,我想训练一个小模型,根据追踪记录自动打标签,减少人工。

四是对抗性任务。专门设计一些"陷阱任务",测试 Agent 会不会被误导、会不会执行危险操作。这类任务对评估 Agent 安全性很有价值。

我个人在实际操作中的体会是,评测框架的价值不在于给出一个分数,而在于把 Agent 的行为变成可观测、可归因、可改进的。你搭完这套东西,再回头看那些宣传页,心里就有底了。最后分享一个小技巧:如果你时间有限,先别搭全套,就从"追踪层+10 个真实任务"开始,跑一遍你就会有完全不一样的认知。