做了快两年Agent评测最近被同事一句话问住了超时算失败吗我刚想回答又觉得哪里不对。因为同一份任务日志在不同人手里能算出从70%到88%完全不同的成功率。分歧点不在失败这两个字怎么定义而在于大家压根没说清分母是什么。这个现象在评测报告里太常见了写着整体成功率90%但你要是不去翻原始日志根本不知道这90%的分母到底是任务总数、剔除超时后的任务数还是重试之后的任务数。Agent超时算失败吗其实是个伪问题真正要回答的是——你把哪些任务放进了统计范围。这篇文章从一次统计翻车案例讲起拆一下分母的种类、超时的成因分类以及我目前觉得比较靠谱的落地方案。如果你正在搭Agent评测流水线或者准备对外发评测报告这篇值得看完。1. 超时到底算不算失败一次差点被95%成功率带偏的评测复盘1.1 同一个日志四个成功率先看一组模拟数据。某次评测一共发了100个任务给Agent执行限时300秒。我把最终结果分成五类70个任务在限时内跑完并验证通过8个任务首轮超时但按策略自动重试后通过12个任务首轮超时重试后仍超时或失败最终放弃4个任务因为外部API短暂故障直接异常重试后通过6个任务因为外部API故障重试后仍失败。这个结果已经很清晰了不管怎么算真正能稳定跑通的核心是70个任务。但接下来开会时大家报出来的成功率完全不一样。我用四种口径算了一遍统计口径计算公式成功率特点口径A所有任务算数只看最终结果(7084)/10082%把重试后的成功算进去口径B所有任务算数只看首轮70/10070%严格单次执行口径口径C剔除外部API故障的10个任务(708)/9086.67%把环境故障排除在外口径D连超时任务也一起剔除70/8087.5%只统计正常完成或直接失败的任务同一份原始日志四个数字差出17个百分点。最夸张的是口径D把超时任务全部扔出分母之后成功率直接变成87.5%配上一个效果不错的结论毫无违和感。可实际上一百个任务里有二十个都没有按时交付。当时会议上我直接说了一句如果报表只写87.5%不写分母怎么来的这报告不能信。先把分母说清楚。争论的核心根本不是超时算不算失败而是那80个任务作为分母把12个最困难、最容易拖垮Agent的任务悄悄藏起来了。1.2 为什么大家总是忍不住剔除超时人下意识想剔除超时任务理由听上去很合理超时不是模型能力不足而是任务太长、外部接口太慢、算力排队把这些算进去对Agent不公平。这个出发点没错但危险在于剔除是一个极其主观的操作。今天超时剔除明天外部故障剔除后天觉得某些任务设计得不好也剔除。剔除到最后分母里只剩跑得顺的任务成功率自然一路走高。更麻烦的是一旦下游同学拿这个数字做Agent选型对比他会以为87.5%是你的真实水平。我的态度是超时任务不能默认算失败也不能默认剔除而是要单独标记、单独统计、单独汇报。失败、超时、异常这三类状态对研发同学的指导意义完全不同混在一起等于信息全丢。2. 被忽略的分母四种统计口径各自的适用场景与偏差2.1 任务总数、有效任务数、可完成任务数要把分母说清楚先得知道分母至少有三种完全不同的取法。第一种是任务总数指评测环境里实际发起的全部任务。这个分母最硬做横向对比时最公平但它会包含环境因素导致的失败比如机器被OOM干掉、外部API宕机、网络抖动。第二种是有效任务数就是剔除掉脏数据之后的数量。比如任务描述本身就是错的、夹具代码写坏了、参考答案给错了这类任务的失败和Agent能力无关剔除是合理的但必须在报告里写明剔了多少、为什么剔除。第三种是可完成任务数它连超时任务也排除了只留下在合理条件下原本能做完的任务。这个口径只能用于内部问题定位比如分析当Agent拿到足够时间时完成质量到底如何绝不能作为对外宣称的成功率基准。三种分母没有绝对的对错但结论会有天壤之别。一个95%可完成的任务集受外部故障影响严重时可能只能在任务总数口径下给出75%的成功率。我不反对用有效任务数做内部复盘但任何报告都必须有meta信息分母哪一种、剔除规则是什么、时间窗口多久、并发多大。否则别人把你的70%和另一个团队的92%放在一起比纯粹是拿苹果比橘子。2.2 成功率从来不应该孤零零出现除了分母种类我还建议任何Agent评测报表至少同时输出一组核心指标而不是只有一个成功率首轮成功率一次执行、不重试、不人工干预限时内通过的比例重试后成功率允许自动重试N次后的最终通过比例超时率最终状态为超时的任务占总任务数的比例平均完成时长和P95时长看Agent跑得快不快平均token消耗与平均工具调用次数看Agent跑得贵不贵。这几个指标一起看比单独一个成功率健康得多。假设一个Agent首轮成功率只有60%但重试后成功率达85%说明它有能力但不够稳适合配重试策略用另一个Agent首轮成功率78%重试后还是78%说明它遇到不会的任务就是不会重试只是浪费钱。这两种结论单看平均数根本看不出来更别指望删几个超时任务就能看出真相。3. 超时背后藏着四类成因归类之前先看最后几行日志3.1 四类超时成因卡死、拖沓、外部慢、排队不是所有超时都长一个样。如果把超时任务全部堆在一起当一种失败处理你会连问题在哪都找不到。第一类是死循环型。Agent在思考—调用工具—观察结果—再思考的循环里出不来日志的最后一条永远是分析中……或者连续十次调用同一个工具且参数几乎不变。这类超时是典型的策略缺陷应该算失败。第二类是拖沓型。整个轨迹是正常的Agent每步都在推进但上下文越来越长、推理速度越来越慢累积耗时超过阈值。典型特征日志的相邻两条消息间隔从3秒逐步涨到30秒。这类超时不能简单算失败因为给更多时间它大概率能完成。解决办法通常是截断上下文、简化工具输出或者换更快的基础模型。第三类是外部超时型。Agent本身没有停在死循环而是调用的第三方工具、数据库、上游大模型的API迟迟不返回整个轨迹卡在等待响应状态。这时超时是外部依赖问题不是Agent能力问题。要单独打标不能混进Agent的失败率里。第四类是排队型。评测脚本并发太高任务在加载、推理、写日志的各个阶段排队单个任务实际执行时间被拉长最终超时被杀。这不是Agent慢是评测环境设计不合理。区分这四类的方法不复杂调出每条任务最后五条日志和每一步的时间戳。死循环型通常有大量相同工具调用拖沓型的时间间隔递增外部超时型的最后一条日志显示正在等待某个API排队型则可以从评测系统的任务队列日志里看到明显的等待区间。分类之后再看统计口径至少不会把外部API故障归结成Agent笨。3.2 重试之后的状态不能覆盖首轮状态以前我吃过一次亏评测框架把首轮超时、重试成功直接记录成success最后统计成功率时完全没暴露首轮超时问题。结果一份看起来85%成功率的报告细分之后发现其中15个点是靠重试补出来的Agent本身的稳定性只有70%。所以状态记录要分两层首轮状态和最终状态。首轮状态记录第一次执行的结局是success、failure还是timeout最终状态记录整个评测流程结束后经过重试和人工干预后的结局。一份任务可以同时具有首轮timeout、最终success这种组合它比单纯的success信息量大得多。这样再算成功率时你可以明明白白写出首轮成功率70%最终成功率85%差异一目了然也不需要私下争论超时算不算失败。4. 把终态枚举和日志字段对上一套可复算的落地流程4.1 五类终态枚举从success到aborted现在团队内部统一把任务终态定义成五类不再用模糊的成功/失败二元组。success在规定时间内产出结果且结果通过校验failure在允许时间内正常结束但结果错误或没有满足任务要求timeout超过时限被评测框架强制终止aborted评测系统检测到异常或安全风险主动紧急中断invalid任务本身存在数据问题不能归因于Agent能力。timeout下面再加一级子状态对应上面说的死循环、拖沓、外部慢、排队。failure也一样可以加结果格式错误内容缺少关键项安全拒绝等子状态。这套枚举本质上就是统计学里的事件定义事件定义不确定后面所有指标都是空中楼阁。4.2 日志字段与自动归类规则每条评测记录至少要有这些字段task_id、started_at、ended_at、final_status、first_status、status_detail、attempts、timeout_limit_s、last_event、last_event_ts、llm_tokens、tool_call_count、error_message。其中last_event及其时间戳特别重要它决定了超时的最终归类。可以写一个简单的判定逻辑比如这样def classify_timeout(task): if task.last_event waiting_api_response: return timeout_external if task.repeated_tool_calls 5: return timeout_loop if task.avg_interval_increasing: return timeout_sluggish return timeout_unknown这段代码不复杂但它把超时这件事从一团模糊感觉变成了可审计的分类。跑完一批任务后自动把任务的分类标签写回记录报表里按类别出一行统计效果非常直观。某次评测外部接口挂了两次系统把对应的超时任务自动标记成timeout_external最后报表单独列出这10条没有污染Agent的真实成功率。后来只需要扫一眼分类结果就知道这轮评价的问题是出在模型策略、上游依赖还是并发调度不用再去翻裸日志。4.3 报告里的meta块让别人能复算你的分母每次发评测结果不管是对内同步还是对外发布我都在报告开头加一个meta块评测时间窗口: 2025-04-01 ~ 2025-04-02 任务总数: 1000 超时阈值: 180s 重试策略: 失败或超时最多重试1次 分母定义: 任务总数 成功定义: 最终状态为success 排除样本数量: 12invalid均为参考答案缺失 原始日志: 见附件/task_logs.tar.gz有了这个块任何人都能按照同样的规则复算出成功率。没有这个块的报告我一律只当参考消息不会拿来和别的模型对比。把meta块当成评测流程的强制字段能让很多无意义的争论直接消失讨论的重点会从你凭什么叫它失败变成你这个分母和我的分母哪个更符合这个需求。5. 评估设计里五个容易带偏口径的坑5.1 任务级分母和调用级分母混着用一个Agent任务运行过程中可能要调用十几次LLM接口其中某一次调用因为上下文太长慢了几秒被调用框架判定为超时。但任务本身还有重试机制最终正常完成。这种情况在任务级统计里是success在调用级统计里是timeout。如果报告里不经意把调用级超时率15%和任务级成功率90%放在一起又没有明确说明两者分母不同读者很容易产生误解。我见过有同学拿着一份调用级失败数据判断某个模型不能用于生产结果发现其实任务整体大部分都完成了。正确做法是报告里明确区分任务级和调用级两类指标分开列不要混着讲。5.2 固定超时阈值不跟token预估联动给Agent任务设固定超时时间看起来省事实际会误杀一批长输出任务。比如要求模型写一份结构化长报告输出token总量上限是8000在不限速的本地推理环境下需要80到120秒但你统一设了90秒超时那这类任务里有一半大概率超时最终在成功率上表现很差而真实原因只是阈值不合理。动态超时设计可以缓解这个问题timeout f预估输入长度 推理上限token 工具调用期望时间 缓冲时间。至少也应该是分场景配置——短问答任务用一个短阈值长文档生成任务用另一个阈值工具密集型任务单独给更高的阈值。评测要回答的问题是Agent能不能完成这类任务而不是它能不能在你随手写的一个超时数字前完成。5.3 评测并行度抬高超时率被误读成Agent不稳定评测框架并发开得越大单位时间内塞给推理服务的请求越多。模型服务本身如果排队一长每个任务的等待时间就增加超时率同步上升。此时你看到的现象是同一版Agent某天成功率从85%掉到70%但真实原因是并发数从10调到了40任务处理队列积压并不是Agent能力变化。排查方法也很简单记录每个任务从提交到真正开始执行的时间也就是调度等待时间。如果超时任务的调度等待时间普遍都很长说明问题出在评测资源规划上和Agent本身无关。报告meta块里一定要写清楚并发数否则换一个环境别人复现不了你的数字就会开始怀疑你的Agent是不是真有那么强。5.4 隐藏的排除异常操作有一些测评习惯是任务报错了我先看看日志如果是环境问题就把它删掉。这个做法单独看问题不大但如果不记录、不统计、不对外说明它就成了一个黑盒子。长期下来报告里的分母会变得越来越干净成功率也会像滚雪球一样离真实用户环境越来越远。更透明一些的做法是报表里同时展示两行一行是包含所有任务的严格口径一行是剔除无效样本后的口径。让看报告的人自己判断到底哪一种更符合他想了解的问题。比如我会在报告末尾加一句严格口径成功率67.2%剔除无效样本与环境故障后为81.5%差异主要来自22个API故障任务。这样既保留了测度又给了上下文。5.5 只看终于成功不看尝试了多少次最后这个坑比较隐蔽最终状态为success的任务背后尝试次数可能差别很大。同样是成功一个任务第一次执行就通过另一个任务重试了7次才勉强通过两者的可持续性完全不同。如果最终成功率只统计最终状态高重试次数的Agent会显得和稳准狠的Agent一样好但实际上它的成本可能高出一大截。我的习惯是统计每个任务的平均尝试次数以及零重试成功占比。零重试成功占比高说明Agent面对大部分任务一步到位重试拉高成功率则说明Agent有兜底能力但在首轮决策上有短板。这两种Agent在线上需要配不同的降级策略不能混为一谈。回到标题这个问题Agent超时算失败吗我现在不再单独回答而是要求所有评测报表先回答三个问题分母是什么、超时阈值是多少、重试策略是什么。三个问题都写清楚之后超时算不算失败自然就有了上下文——对某些任务它应该算失败对某些任务它只是需要更长的时间对某些任务它压根是环境问题不该算在Agent头上。最后分享一个小习惯每次评测开工前的第一件事先把meta块模板写好确定本轮的分母定义和超时阈值再灌数据。这样跑完的每一份报告都自带可复算属性团队之间讨论时也终于处在同一个坐标系里。这比争论定义有营养得多。