
1. 为什么工程化 Agent 的评测不能只看跑分1.1 从“能跑通”到“敢上线”之间的鸿沟做 Agent 的人都有一个共同的体感Demo 阶段惊艳上线之后崩溃。你在本地用几个精心设计的 query 跑通了 ReAct 循环工具调用准确、推理链清晰截图发到群里一片叫好。但一旦接入真实流量用户输入千奇百怪工具返回格式不稳定多轮对话上下文爆炸整个系统就开始胡言乱语。这个落差不是模型能力的问题而是评测体系缺失的问题。传统 NLP 任务的评测有明确的输入输出对分类看 F1生成看 BLEU 或 ROUGE检索看 RecallK。但 Agent 不一样它是一个有状态的、多步的、与外部环境交互的系统。它的输出不是一段文本而是一串动作序列加上最终结果。你没法用单一指标衡量它“好不好”。羲和 XiheAgent 这个项目在做的就是把这件模糊的事情工程化。它选择 GAIA 作为评测基准不是为了刷榜而是因为 GAIA 的设计哲学和工程化 Agent 的真实挑战高度吻合多步推理、工具使用、信息整合、对噪声的鲁棒性。我在这篇文章里会拆解整个评测流程的设计思路、实操细节、踩过的坑以及那些文档里不会写的经验。1.2 GAIA 基准到底在考什么GAIA 全称 General AI Assistants benchmark它的题目设计有几个鲜明特点。第一题目看起来简单但需要多步操作才能完成。比如“找出某篇论文中第三作者所在机构在2023年发表的另一篇论文的标题”这需要你检索、解析、再检索。第二答案格式高度确定通常是一个数字、一个名字或一个短字符串这让自动评测成为可能。第三题目分为三个难度等级Level 1 基本是单步工具调用Level 2 需要多步组合Level 3 则需要长链条推理和跨模态信息处理。对工程化 Agent 来说GAIA 的价值在于它逼着你把每一个环节都做扎实。你不能靠 prompt 里塞一堆 few-shot 例子蒙混过关因为题目类型太分散了。你也不能靠单次大模型调用直接出答案因为很多信息根本不在模型参数里。你必须真正把工具调用、结果解析、错误恢复、上下文管理这些工程环节串起来。注意GAIA 的答案格式要求非常严格大小写、空格、标点都可能影响判定。很多团队第一次跑 GAIA 时模型其实答对了但因为输出格式不对被判错。这不是模型的问题是评测管道的问题。2. 评测体系的整体架构设计2.1 三层分离Agent 运行时、评测器、分析层羲和 XiheAgent 的评测架构我把它拆成三层。最底层是Agent 运行时负责实际执行任务包括 LLM 调用、工具调度、记忆管理、循环控制。中间层是评测器负责把 Agent 的输出和 GAIA 的标准答案做比对同时记录执行轨迹。最上层是分析层负责统计指标、归因失败原因、生成可视化报告。为什么要做这种分离因为如果你把评测逻辑和 Agent 逻辑混在一起改一个评测规则就要动 Agent 代码迭代效率极低。而且不同难度的题目可能需要不同的评测策略分离之后可以灵活替换。具体来说Agent 运行时对外暴露一个统一接口输入是任务描述输出是一个结构化的执行结果包含最终答案、执行步数、每步的工具调用记录、耗时、token 消耗。评测器只认这个结构不关心 Agent 内部怎么实现的。分析层则从评测器拿到的结构化数据里做聚合和归因。2.2 工具选型为什么用这套组合在工具层面我选的是 Python 作为主语言原因很简单GAIA 的题目涉及大量网页解析、文件处理、数据计算Python 生态最成熟。LLM 调用层用 OpenAI 兼容接口做抽象这样切换模型只需要改配置。工具执行用沙箱环境避免 Agent 执行危险操作影响宿主机。评测器部分我没有用现成的评测框架而是自己写了一套轻量的比对逻辑。原因是 GAIA 的答案判定需要一些自定义规则比如数值答案允许一定误差字符串答案需要归一化处理。现成框架要么太重要么不够灵活。分析层用 Pandas 做数据处理用 Matplotlib 做可视化。没有上更重的 BI 工具因为评测数据量不大几千条题目而已Pandas 完全够用。2.3 数据流与状态管理整个评测流程的数据流是这样的GAIA 数据集加载后每条题目被包装成一个任务对象包含题目 ID、难度等级、问题描述、标准答案、答案类型。任务对象被送入 Agent 运行时运行时执行完毕后返回结果对象。结果对象和任务对象一起送入评测器评测器输出判定结果。所有判定结果汇总到分析层。状态管理的关键在于执行轨迹的完整记录。每一步的工具调用、输入参数、返回结果、LLM 的思考过程都要落盘。这不仅是为了调试更是为了后续做失败归因。我见过太多团队评测做完只知道“准确率 60%”但完全不知道那 40% 错在哪里。有了完整轨迹你可以回放任何一个失败案例精确定位是检索错了、解析错了、还是推理错了。3. 核心环节的实操细节3.1 任务加载与预处理GAIA 数据集从 HuggingFace 加载后需要做几件事。第一过滤掉需要人工附件的题目除非你的 Agent 支持文件上传。第二把题目按难度分组方便分层次统计。第三对标准答案做归一化预处理比如去掉首尾空格、统一大小写、把数字转成统一格式。这里有个细节GAIA 有些题目的答案是列表形式比如“列出三个名字”。这种题目的判定逻辑和单值答案不同需要单独处理。我的做法是在预处理阶段就给每条题目打上答案类型标签评测器根据类型选择对应的比对策略。def normalize_answer(answer, answer_type): if answer_type string: return answer.strip().lower() elif answer_type number: return float(answer.replace(,, )) elif answer_type list: return sorted([item.strip().lower() for item in answer]) return answer3.2 Agent 执行循环的控制策略Agent 的执行循环是评测的核心。羲和 XiheAgent 用的是改进版 ReAct 模式但加了几个工程化的控制点。第一个控制点是最大步数限制。GAIA 的题目理论上可以在 10 步内解决但 Agent 有时候会陷入循环。我设置的最大步数是 15超过就强制终止并标记为超时。这个值不是拍脑袋定的是我跑了 200 条题目后统计出来的95% 的成功案例在 8 步内完成设 15 步留了足够余量。第二个控制点是工具调用失败的重试策略。网页请求可能超时文件解析可能失败这些都需要重试。但不是无脑重试而是根据错误类型决定。网络超时重试 2 次解析错误重试 1 次并换解析方式权限错误直接跳过。第三个控制点是上下文窗口管理。多步执行后历史记录会越来越长。我的策略是保留最近 5 步的完整记录更早的步骤只保留摘要。摘要是用一个小模型生成的成本很低。3.3 评测器的判定逻辑评测器的判定逻辑比想象中复杂。表面上看就是字符串比对但实际上要考虑很多边界情况。对于数值答案我允许 1% 的相对误差。原因是有些题目涉及汇率换算或单位转换不同数据源的结果会有微小差异。对于字符串答案我做归一化后精确匹配但会额外检查是否包含关系。比如标准答案是“Paris”Agent 输出“The answer is Paris”这种情况应该判对。对于列表答案我要求元素集合完全一致但顺序无关。如果标准答案是三个名字Agent 输出了四个即使前三个都对也判错。这是 GAIA 的规则严格但合理。def judge(agent_answer, gold_answer, answer_type): agent_norm normalize_answer(agent_answer, answer_type) gold_norm normalize_answer(gold_answer, answer_type) if answer_type number: if gold_norm 0: return abs(agent_norm) 1e-6 return abs(agent_norm - gold_norm) / abs(gold_norm) 0.01 elif answer_type list: return set(agent_norm) set(gold_norm) else: if agent_norm gold_norm: return True return gold_norm in agent_norm3.4 执行轨迹的落盘与分析每次执行完毕后我会把完整轨迹写成一个 JSON 文件包含任务 ID、执行步数、每步的详细信息、最终答案、判定结果、耗时、token 消耗。这些文件按批次存放在统一目录下分析层直接读取。轨迹分析最有价值的部分是失败归因。我把失败原因分成几类检索失败没找到正确信息、解析失败找到了但没解析对、推理失败信息对了但推理错了、格式失败答案对了但格式不对、超时失败步数用完。统计各类失败的占比就能知道系统瓶颈在哪里。我跑过一轮 300 条题目的评测失败分布大概是检索失败 35%推理失败 30%解析失败 15%格式失败 12%超时 8%。这个分布直接指导了后续的优化方向优先提升检索质量然后优化推理链。4. 常见问题与排查技巧实录4.1 工具调用返回格式不稳定怎么办这是最常见的问题。同一个网页今天返回的 HTML 结构可能和明天不一样。同一个 API字段名可能突然变了。Agent 如果硬编码解析逻辑很容易崩。我的做法是加一层适配器。工具返回原始结果后适配器负责提取关键信息。适配器内部用多种策略尝试先试结构化解析JSON、XML失败则试正则提取再失败则把原始内容丢给 LLM 做信息抽取。这样即使格式变了也有兜底方案。实操心得适配器的 LLM 抽取环节要用小模型成本低且够用。我试过用大模型做抽取效果提升不明显但成本翻了十倍。4.2 Agent 陷入循环怎么破循环的典型表现是 Agent 反复调用同一个工具参数几乎一样但就是不推进。原因通常是上一步的结果没有正确反馈给模型或者模型误判了当前状态。我的解决方案是加一个循环检测器。每次工具调用前检查最近 3 步是否调用了相同工具且参数相似度超过 90%。如果是强制注入一条提示“你已经尝试过类似操作请换一种方式或给出当前最优答案。”这条提示会打断循环让模型重新规划。实测下来这个简单的机制能解决 80% 的循环问题。剩下的 20% 通常是题目本身需要多步相似操作比如批量查询多个实体这种情况检测器会误判所以我把阈值设得比较保守。4.3 评测结果波动大怎么定位同一个 Agent跑两次 GAIA准确率可能差 5 个百分点。这种波动让人很头疼因为你不确定是模型随机性导致的还是系统有 bug。我的排查步骤是这样的。第一步固定随机种子把 LLM 的 temperature 设为 0看波动是否消失。如果消失说明是采样随机性属于正常范围。第二步如果还有波动检查工具调用是否有超时或失败这些外部因素会引入不确定性。第三步对比两次执行的轨迹找出判定结果不同的题目逐条分析原因。我遇到过一种情况某道题目的答案依赖实时网页内容而网页在两次评测之间更新了。这种波动无法消除只能标记为“环境依赖”并在统计时单独处理。4.4 常见问题速查表问题现象可能原因排查方法解决方案准确率突然下降工具接口变更检查最近工具调用失败率更新适配器解析逻辑执行步数普遍偏高提示词引导不足查看平均步数和轨迹优化系统提示词明确步数预期特定类型题目全错缺少对应工具按题目类型分组统计补充工具或调整工具描述格式失败占比高输出约束不够检查失败案例的输出格式在提示词中强化格式要求评测耗时过长并发度不够检查执行日志的时间分布提高并发数优化工具响应速度4.5 几个容易忽略的细节第一个细节是工具描述的质量。Agent 选择工具的依据是工具的名称和描述。如果描述写得含糊模型就会选错工具。我花了不少时间打磨每个工具的描述确保它清晰说明“这个工具做什么、什么时候用、输入输出是什么”。第二个细节是错误信息的处理。工具执行失败时返回的错误信息如果直接丢给模型模型可能会被误导。我的做法是把错误信息标准化比如“网络请求超时请稍后重试”或“文件不存在请检查路径”这样模型能更好地决定下一步。第三个细节是评测环境的隔离。每次评测前我会清理临时文件、重置数据库状态、清空缓存。否则上一次评测的残留数据可能影响下一次导致结果不可复现。5. 从评测结果反推优化方向5.1 检索环节的优化检索失败占比最高优化空间也最大。我做了几件事。第一把单一检索工具拆成多个专用工具网页搜索、学术搜索、代码搜索每个工具针对不同场景优化。第二在检索结果返回后加一层重排序用一个小模型判断哪些结果最相关。第三对于需要多跳检索的题目在提示词里明确引导模型先检索再检索。这些优化做完检索失败占比从 35% 降到了 22%。提升明显但还没到位。后续我打算引入查询改写让模型在检索前先把问题拆解成更精确的查询语句。5.2 推理链的加固推理失败通常发生在多步推理的中间环节。模型可能在某一步做了错误假设然后一路错下去。我的做法是在关键步骤后加自检提示让模型确认当前结论是否合理。比如在得出一个中间结果后提示模型“请验证这个结果是否与已知信息一致”。这个机制会增加 token 消耗但能显著降低推理错误率。我实测下来推理失败占比从 30% 降到了 18%。代价是平均步数增加了 1.2 步总体可接受。5.3 格式合规的强制约束格式失败是最冤的失败答案对了但格式不对。我在系统提示词里加了明确的格式要求并且在 Agent 输出最终答案前加了一个格式校验步骤。如果格式不符合要求自动触发一次修正。这个改动几乎消除了格式失败占比从 12% 降到了 2% 以下。剩下的 2% 是模型在修正时仍然出错属于极端情况。6. 一些个人体会跑完这一整套评测流程我最大的感受是Agent 评测的难点不在评测本身而在于让 Agent 的行为可观测、可复现、可归因。如果做不到这三点评测就只是跑个数字没有指导意义。另外GAIA 虽然是个好基准但它不是万能的。它的题目偏重信息检索和推理对多轮对话、情感理解、创意生成这些能力覆盖不足。如果你的 Agent 面向的是客服场景或内容创作场景GAIA 的分数参考价值有限。你需要根据实际业务场景设计补充评测集。最后分享一个实用技巧把评测当成持续集成的一部分。每次 Agent 代码有改动自动跑一遍小规模评测集比如 50 条看核心指标有没有退化。这样能及时发现回归问题避免上线后才发现性能下降。我现在的做法是小评测集跑得快5 分钟出结果大评测集每周跑一次做全面分析。