Token 单价更低,Agent 任务为什么反而更贵:4 层成本口径 + 最小事件账本 + 3 个决策问题
Token 单价更低,Agent 任务为什么反而更贵
TL;DR
- 场景:IBM Research(Yara Rizk / Eyal Shnarch / Jason Tsay / Merve Unuvar)在 HF 社区 2026-07-15 发表的 “Model Routing Is Simple. Until It Isn’t.”,报告在同一 CodeAct Agent 下跑 417 个 AppWorld Test Challenge 任务:Claude Sonnet 4.6 总成本 79 美元(约 0.19 美元/任务),GPT-4.1 总成本 155 美元(约 0.37 美元/任务);GPT-4.1 当时 Token 单价更低,但总账反而更贵。
- 结论:多步 Agent 的成本单位不是一次 ModelCall,而是包含缓存、工具、重试、升级、失败和人工处理的完整任务轨迹。把 Price Sheet 当作最终成本答案会选错计量单位;只有建立 Price Sheet → ModelCall → Trajectory → Cost per Successful Task 四层口径,才能判断哪个模型"更贵"。
- 产出:四层成本口径、4 个公式(ModelCall Cost / Trajectory Cost / Effective Task Cost / Cost per Successful Task)、最小事件账本(Task → Step → ModelCall / CacheEvent / ToolCall → Retry / Escalation → Outcome → Human Review),以及三个治理决策问题。
版本矩阵
| 功能 / 组件 | 状态 | 说明 |
|---|---|---|
| HF 社区文章 “Model Routing Is Simple. Until It Isn’t.” | ✅ 已验证 | 2026-07-15T17:27:01.654Z 发布,2026-07-15T17:43:43.281Z 修改;IBM Research;54 upvote |
| 作者 4 人 | ✅ 已验证 | Yara Rizk(yarizk)/ Eyal Shnarch(eishna)/ Jason Tsay(jsntsay)/ Merve Unuvar(mrvnvr)— HF schema.org creator/author 4 个条目 |
| 417 AppWorld Test Challenge 任务 | ✅ 已验证 | 原文 |
| Claude Sonnet 4.6 总成本 79 美元 | ✅ 已验证 | 原文 |
| Sonnet 4.6 单任务成本 ≈ 0.19 美元 | ✅ 已验证 | 原文(79 / 417) |
| GPT-4.1 总成本 155 美元 | ✅ 已验证 | 原文 |
| GPT-4.1 单任务成本 ≈ 0.37 美元 | ✅ 已验证 | 原文(155 / 417) |
| IBM 价格快照:GPT-4.1 输入与输出 Token 单价更低 | ✅ 已验证 | 原文 |
| IBM 价格快照:Sonnet 4.6 推理步数约多 3 倍 | ✅ 已验证 | 原文 |
| 同一 CodeAct Agent | ✅ 已验证 | 原文 |
| AppWorld 任务集 | ✅ 已验证 | Harsh Trivedi et al., arXiv:2407.18901(ACL 2024) |
| AppWorld 用基于状态的单元测试判断任务完成 | ✅ 已验证 | 原文 [2] |
| AppWorld 同时检查非预期状态变化 | ✅ 已验证 | 原文 [2] |
| IBM 主要解释:Agent 轨迹对大段上下文的重复使用 | ✅ 已验证 | 原文 |
| IBM 同时强调缓存读取价格带来的影响 | ✅ 已验证 | 原文 |
| IBM 提醒实际成本取决于模型 × 工作负载 × 服务基础设施交互 | ✅ 已验证 | 原文 |
| IBM 提醒任务难度在执行前往往不可见,端点繁忙 / 缓存预热 / 基础设施状态可能主导端到端延迟 | ✅ 已验证 | 原文 |
| 四层成本口径:Price Sheet / ModelCall / Trajectory / Cost per Successful Task | ⚠️ 推算 | 这是本文给出的核算方法,原文用了"ModelCall/Trajectory/Effort Cost"等术语,本文做了四层化命名 |
| Effective Task Cost 公式(含失败 / 升级 / 人工入分子) | ⚠️ 推算 | 本文给出的核算公式,原文未用此名称 |
| Cost per Successful Task 公式 | ⚠️ 推算 | 本文给出的核算公式,原文未给统一名称 |
| 最小事件账本(Task → Step → ModelCall / CacheEvent / ToolCall → Retry / Escalation → Outcome → Human Review) | ⚠️ 推算 | 本文给出的工程方案,原文未给完整数据模型 |
| 1 美元/任务 等"目标单价"具体数字 | ⚠️ 未公开 | 原文未给团队目标单价 |
| 各供应商当前 Token 实时单价 | ⚠️ 随时间变化 | 原文写"IBM 当时采用的价格快照",本表不复述当时单价;如要复现请以执行当时为准 |
| IBM 当前是否仍在跟踪 AppWorld / Sonnet / GPT-4.1 对比 | ⚠️ 推算 | 原文未承诺持续跟踪;价格快照已固定 |
**摘要:**IBM 的一次 AppWorld 实验中,纸面 Token 单价更低的模型反而产生
更高任务总成本。多步 Agent 的正确计量单位不是一次调用,而是包含缓存、
工具、重试、升级、失败和人工处理的完整任务轨迹。
**关键词:**Agent 成本、Token、缓存、Trajectory Cost、FinOps、任务成功率
目录
- 为什么 Price Sheet 会误导 Agent 选型
- 缓存怎样改变整条轨迹
- 四层 Agent 成本口径
- 如何定义成功结果
- 最小事件账本
- 对账、升级与停止规则
很多团队为 Agent 选模型时,第一步仍是打开价格表:比较每百万输入 Token、输出 Token 和缓存 Token 的单价,再把"单价更低"直接等同于"任务成本更低"。对一次性、短上下文、无工具的调用,这种比较尚可作为粗略估算;对会规划、调用工具、读取结果、纠错并继续执行的多步 Agent,它经常选错计量单位。
IBM Research 在 2026 年 7 月 15 日发布的文章给出了一个反直觉案例:在相同 CodeAct Agent 下运行 417 个 AppWorld Test Challenge 任务,Claude Sonnet 4.6 的总成本为 79 美元,约 0.19 美元/任务;GPT-4.1 的总成本为 155 美元,约 0.37 美元/任务。按 IBM 当时采用的价格快照,GPT-4.1 的输入和输出 Token 单价更低,Sonnet 的推理步骤还大约多三倍,但最终总账却相反。IBM 将主要解释指向 Agent 轨迹对大段上下文的重复使用,以及不同缓存读取价格带来的影响。[1]
这组数字不能改写成"GPT-4.1 普遍比 Sonnet 更贵",也不能脱离 IBM 的任务集、Agent、价格快照、缓存行为和运行配置外推。它真正揭示的是:多步 Agent 的成本单位不是一次 ModelCall,而是一条走到业务结果的完整任务轨迹。价格表没有错,错的是把价格表当成了最终成本答案。
一、Price Sheet 只描述单位价格,不描述任务会怎样运行
价格表回答的是静态问题:某一价格版本下,新输入、输出、缓存写入和缓存读取分别按什么单位计费。它不回答动态问题:一个任务需要多少步、每一步带入多少历史、哪些前缀能够复用、会调用多少次工具、失败后是否重试、何时升级到其他模型、最终是否得到可接受结果。
多步 Agent 通常形成一个状态循环:模型读取系统指令、任务上下文、工具说明和历史轨迹,生成动作;工具返回结果或错误;模型再读取更新后的上下文,决定下一步。随着步骤增加,账单不再由"初始提示词长度"决定,而由整条轨迹中不同类型 Token、工具事件和异常分支的累积决定。
这意味着,单次调用便宜的模型可能因为步骤更多、输出更长、错误恢复更频繁而让任务总成本上升;单次调用较贵的模型也可能因为更快收敛、更少重试或更高的上下文复用率而降低总账。反过来也成立。没有轨迹数据,就无法从单价推导任务成本排序。
二、缓存改变的不是一个折扣项,而是整条轨迹的成本结构
Agent 轨迹有一个区别于普通单轮问答的特征:相邻步骤会反复携带大量相同内容。系统提示、工具定义、任务约束、较早的观察结果和执行历史,可能在连续调用中重复出现。若服务端能够识别并复用稳定前缀,重复部分可能按缓存读取计价;若前缀变化、缓存失效或端点状态不满足条件,同样的内容又会回到普通输入或缓存写入路径。
因此,单次调用成本至少要拆成以下组成,而不能只记录一个总 Token 数:
ModelCall Cost = uncached input + cache write + cache read + output这里的关键不是简单追求更高缓存命中率,而是确认命中的内容、计价方式和业务价值。一个命中率很高但步骤过多的轨迹仍可能昂贵;一个命中率一般但三步完成的轨迹也可能更便宜。缓存还会受到提示前缀稳定性、缓存生命周期、请求路由、并发方式和端点预热状态影响。工程上必须记录真实的缓存读写事件,而不是在估算表中假设"后续步骤都会命中"。
IBM 的案例说明缓存读取价格足以改变其测试中的模型成本排序,但 IBM 同时强调,实际成本取决于模型、工作负载与服务基础设施的交互。[1] 因此,“缓存是重要解释"不等于"缓存是唯一原因”。步骤数、输出长度、工具调用、重试、端点繁忙程度和基础设施状态仍会共同决定结果。
三、Agent 成本必须建立四层口径
1. Price Sheet:公开单价层
这一层保存供应商、模型、端点、区域、价格生效时间、输入/输出/缓存计价项和币种。它适合做预算边界、价格版本管理和情景重算,但不能直接用于模型优劣排序。
价格表必须版本化。历史任务应保留执行当时的价格版本和实际账单;需要做跨期比较时,可以另算统一价格口径下的归一化成本,但不能用今天的价格覆盖昨天的真实支出。否则,模型行为变化和价格变化会被混在一起。
2. ModelCall Cost:单次调用层
这一层对应一次实际模型请求。最少应记录模型与端点、开始与结束时间、未缓存输入 Token、缓存写入 Token、缓存读取 Token、输出 Token、供应商返回的计费金额、首 Token 延迟、总延迟、错误码和请求状态。
ModelCall Cost 能回答"这一次调用为什么贵",也能发现输出异常膨胀、缓存未命中或端点错误。但它仍不能回答"这个任务是否值得",因为一次调用可能只是失败轨迹中的一个步骤。
3. Trajectory Cost:完整轨迹层
Trajectory Cost 把同一任务中的全部步骤合并起来,范围包括模型调用、缓存读写、搜索与检索、浏览器或代码沙箱、数据库和向量服务、网络与存储、重试、回退、模型升级及相关基础设施成本。可采用如下方法口径:
Trajectory Cost(task) = Σ(model input/output + cache write/read) + Σ(tool and infrastructure) + Σ(retry and escalation increments)同一个任务可能有多条轨迹:首次执行失败后重新开始,低成本模型无法完成后升级,或自动执行结束后进入人工补救。这些分支不能被拆成互不相关的调用,否则失败成本会从任务账本中消失。
Harness 会改变上下文组织、工具接口和重试方式,从而改变轨迹;本文只把它作为成本账本的运行条件,不展开 Benchmark 阅读方法。
4. Cost per Successful Task:成功任务层
这是生产系统真正需要的成本单位。分母不是请求数、调用数或启动的任务数,而是满足业务成功条件的任务结果;失败尝试、升级路径和人工处理的成本仍留在分子中。
Effective Task Cost = model calls + cache write/read + tools and infrastructure + retries and escalation + expected failure cost + human review在统计窗口内,可以进一步写成:
Cost per Successful Task = (Σ trajectory cost + Σ expected failure cost + Σ human review cost) / successful outcomes该公式是一种核算方法,不代表所有失败都必须用同一种金额估值。已发生的重试、工具和人工成本应按实际值入账;尚未直接记账的业务损失可以作为单独的期望失败成本,并明确概率、影响范围和估值依据,不能与供应商账单混成一个不可追溯的数字。
四、成功必须由任务状态定义,而不是由模型是否返回文本定义
AppWorld 面向需要迭代生成代码并操作多种应用 API 的交互式 Agent,使用基于状态的单元测试判断任务是否完成,同时检查非预期状态变化。[2] 这一设计对生产成本治理有直接启示:Agent 输出了"已完成"不等于任务成功,模型调用返回 200 也不等于业务结果正确。
生产系统需要为每类任务建立可执行的 Outcome Policy。它至少包含目标状态、质量阈值、时限、允许的副作用和不可违反的约束。例如,创建工单不只是生成一段工单文本,还可能要求工单真实写入指定系统、字段完整、权限正确且没有重复提交。只有满足这些条件,任务才能进入成功分母。
部分完成、结果不可验证、静默失败和产生附带损害的任务应分别标记。把它们统一记为成功,会人为压低 Effective Task Cost;把它们全部记为失败,也会掩盖可以低成本补救的中间状态。成本账本必须保留结果等级与验证证据,而不是只留一个布尔值。
五、最小事件账本要能够重建每条成本路径
最小数据链应保持任务级关联:
Task → Step → ModelCall / CacheEvent / ToolCall → Retry / Escalation → Outcome → Human ReviewTask记录任务标识、租户或业务域、任务类型、预算、服务等级和成功策略版本。Step记录顺序、父步骤、开始与结束时间以及当时可见的状态摘要。所有后续事件都必须携带同一条 trace 标识,才能把分散在模型网关、工具平台和人工系统中的成本重新聚合。
ModelCall记录模型、供应商、端点、价格版本、Token 分类、实际费用、延迟和错误。CacheEvent记录写入、读取、命中、未命中、失效和对应的前缀或缓存键摘要;它可以作为 ModelCall 的明细事件,但计费汇总时必须避免重复计算。ToolCall记录工具名称、调用次数、超时、返回状态、直接费用和是否产生外部副作用。
Retry不能只写"重试一次",而要保存原因:限流、超时、格式错误、工具失败、验证失败还是策略性再尝试。Escalation记录从哪个模型或流程升级到哪个模型或人工队列、触发条件、复用了哪些已有状态,以及新增成本。Outcome保存成功、失败、部分完成或不可判定状态,并关联自动验证证据。Human Review记录排队时间、处理时长、人工成本、复核结论和返工结果。
账本还应区分三种金额:供应商或工具实际返回的 billed cost、内部按资源分摊的 allocated cost,以及用于风险决策的 expected cost。三者可以同时存在,但必须分别命名。把它们混成一个cost字段,会让财务对账、工程诊断和风险评估互相污染。
六、三个决策问题决定成本账本是否可用
第一个问题是:成功如何定义。没有稳定的成功策略版本,就无法比较模型、Agent 版本或时间窗口。成功标准发生变化时,旧任务不能直接与新任务混算,应保留策略版本并按同一口径重放或分组分析。
第二个问题是:失败如何计价。失败任务已经消耗的模型、缓存、工具和基础设施费用必须完整入账;可恢复失败还要计入后续重试或人工补救。对业务损失的估计应单列,区分可观测事实与风险假设。这样既不会把失败当作"零产出但无成本",也不会用未经验证的损失数字夸大模型费用。
第三个问题是:何时升级。升级不是免费的保险,而是轨迹中的一个成本事件。系统需要记录触发条件,例如连续工具错误、验证不通过、预算耗尽比例、剩余时限或置信度不足。随后比较"继续当前路径"“切换更强模型”"转人工"三种路径的历史成功率、增量成本和延迟。这里的重点不是写一套通用动态路由算法,而是让升级决策能够被测量、复盘和治理。
七、模型比较应基于可比轨迹,而不是孤立平均价
一个可用的成本看板至少要同时展示成功率、Cost per Successful Task、轨迹步骤数、缓存读取占比、工具成本占比、重试率、升级率、人工复核率和端到端延迟。平均值之外还要看分位数和任务类型,因为少量超长失败轨迹可能主导总成本,却被"平均每次调用"掩盖。
模型 A 与模型 B 只有在任务集合、Agent 与执行框架版本、工具权限、缓存策略、端点区域、并发条件、价格版本和成功定义一致时,成本差异才有解释力。若其中任一条件变化,报告应把变化列为实验变量,而不是把差异全部归因于模型。
IBM 还指出,任务难度在执行前往往不可见,端点是否繁忙、缓存是否预热以及基础设施状态可能主导端到端延迟。[1] 这意味着成本和延迟都需要在轨迹层观察。一个价格更低或理论速度更快的模型,可能因服务状态、额外步骤和恢复路径产生更差的业务结果;反之亦然。
八、先验证账本完整性,再讨论优化
成本口径成立的前提是事件能够对账。模型网关汇总金额应与供应商账单在同一价格版本和时间窗口内核对;工具平台、基础设施和人工系统也要提供可关联的费用或工时。孤立的 ModelCall、重复上报的 ToolCall、没有 Outcome 的已结束任务,以及未关联原始轨迹的人工返工,都会系统性低估或重复计算 Effective Task Cost。
运行中的任务还应与失败任务分开。超时窗口尚未结束、人工队列尚未处理或外部系统尚未确认结果时,状态应保持 pending,而不是提前写成成功或失败。成本看板需要同时报告事件覆盖率、费用对账差异和结果判定覆盖率;数据不完整时,应先标明口径缺口,不能用一个精确到小数点后的数字制造确定性。
结论
Agent 成本治理的核心不是找到价格表上最便宜的模型,而是建立从 Task 到 Outcome 的可追溯账本。Price Sheet 是输入,ModelCall Cost 是局部事实,Trajectory Cost 是执行总账,Cost per Successful Task 才是业务决策单位。
当缓存读写、步骤数、工具、重试、升级、端点状态、失败和人工复核都进入同一条任务轨迹,团队才能回答真正有用的问题:每得到一个合格结果要花多少钱,成本由哪一段路径驱动,失败是否值得继续修复,以及何时应升级或停止。没有这套口径,Token 单价比较只是在优化账单中最容易看到、却未必最重要的一行。
参考来源
[1] IBM Research, “Model Routing Is Simple. Until It Isn’t.”, 2026-07-15.
https://huggingface.co/blog/ibm-research/model-routing-is-simple-until-it-isnt
[2] Harsh Trivedi et al., “AppWorld: A Controllable World of Apps and People for Benchmarking Interactive Coding Agents”, ACL 2024 / arXiv:2407.18901.
https://arxiv.org/abs/2407.18901
FAQ
为什么不能只比较每百万 Token 单价?
因为多步 Agent 的步骤数、缓存命中、工具调用、重试和失败路径都会改变最终总账。
失败任务应该计入成本吗?
应该。失败、升级和人工返工留在分子,才能得到真实的成功任务成本。
这篇文章是否说明 GPT-4.1 普遍比 Sonnet 更贵?
不是。IBM 数字只适用于其任务集、Agent、价格快照、缓存行为和运行配置。
错误速查卡
| 症状 | 根因 | 定位 | 修复 |
|---|---|---|---|
| "GPT-4.1 比 Sonnet 便宜"被简单外推 | 单价 vs 任务成本被等同;忽略步骤数、缓存、重试、工具 | 查是否有完整 Trajectory Cost 拆分 | 用 ModelCall + Trajectory + Cost per Successful Task 三层数据 |
| 缓存命中率很高但任务成本未下降 | 命中率不等于节省:步骤过多、缓存前缀不稳定、缓存读取价格仍高 | 查 ModelCall Cost 的 cache read / cache write 占比 | 不仅看命中率,还要看 cache read 价 × 实际命中量 |
| 价低模型总账更贵被归因于"模型问题" | 步骤数、工具调用、重试、失败路径改变整条轨迹 | 拆四层:模型 / 编排 / 基础设施 / 失败恢复 | 先比 Trajectory Cost 各项占比,再看具体子项 |
| 模型 A 比 B 便宜但 A 总成本更高 — 结论互相矛盾 | "单价"与"任务"混用,未控制变量 | 查价格版本、Agent 版本、缓存策略、并发条件是否一致 | 同条件比较:任务集、Agent、Harness、价格快照、Outcome 全部锁定 |
| 失败任务从成本分子里消失 | 失败被记为"未计费"或"零产出" | 查 Outcome 字段是否包含 failed/partial/pending 等级 | 失败入分子;expected cost 单列;不与 billed cost 混 |
| 工具调用成本被低估 | 工具直接费用未进账本,或与模型费用混在一起 | 查 ToolCall 事件是否含直接费用与副作用 | ToolCall 单独计费;副作用标记;外部服务费用对账 |
| 升级路径不计入成本 | 升级被当作"免费保险"或一次选择 | 查 Escalation 事件是否记录切换路径与新增成本 | 升级是轨迹事件;记录从哪个路径升级到哪个、触发条件、新增成本 |
| 人工返工成本被漏算 | 人工系统未与 trace 关联 | 查 Human Review 是否携带 task_id / run_id | Human Review 必带 trace 标识;工时按内部 allocated cost 入账 |
| 价格表覆盖昨天的支出 | 用今天价格覆盖历史账单 | 查每条 ModelCall 的 price_version | 价格版本必须版本化;跨期比较走归一化口径 |
| CacheEvent 重复计费 | 既把 CacheEvent 当 ModelCall 明细,又单独汇总 | 查计费汇总是否去重 | CacheEvent 视作 ModelCall 明细;汇总时只能保留 ModelCall 主键 |
| 总价写在 PPT,但事件覆盖率不足 | 看板报"精确实价"但事件缺失或事件未对齐 | 查事件覆盖率 / 费用对账差异 / 结果判定覆盖率 | 数据不完整时写"pending"并标明缺口,不伪造精确数 |
| LLM Judge 算成本时没算自己 | 评测自身消耗的 Token 漏记 | 查 Eval 任务是否带 trace 标识 | Eval 自身也走 Trajectory Cost;评审计费对账 |
| 缓存读取价格与命中率混淆 | 假设"后续步骤都命中" | 查每次 ModelCall 的 cache_read_tokens 与 cache_hit | 必须记录真实 CacheEvent;不假设命中 |
| Harness 改动未隔离 | 跨 Harness 比较模型成本,差异归因错 | 查 Harness 版本是否锁定 | 同 Harness 比模型;同模型比 Harness;分两组报告 |
| 端点繁忙被当成"模型慢" | 没区分服务端状态和模型自身速度 | 查端点区域、并发、缓存预热状态 | 同区域同并发下重复;区分服务端延迟与模型延迟 |
| 任务难度在执行前不可见被忽略 | 用同样的预算做高难度任务 | 查任务类型与预算是否分层 | 任务按难度分层设预算;不要单一预算 |
| 不同业务用同一 Cost per Successful Task | 任务类型不同,成本结构不同 | 查分维度看 | 按业务域 / 任务类型 / 客户分层汇总 |
| Cost per Successful Task 被压低 | 失败任务被排除分母 | 查 Outcome 字段是否包含 failed | 失败入分子;成功分母只能收 Outcome=success |
| AppWorld 之外的 App 不一定有"状态测试" | 把"模型返回了文本"当成功 | 查验证机制是状态断言还是字符串比对 | 自动验收需状态断言;模糊匹配不替代 |
| 远程任务因 pending 状态被误算 | pending 任务被提前算成功或失败 | 查 pending 状态是否被覆盖 | pending 单独;超时窗口结束后再判定 |
作者:武子康的个人博客
原文链接:https://huggingface.co/blog/ibm-research/model-routing-is-simple-until-it-isnt
AppWorld 论文:https://arxiv.org/abs/2407.18901