Agent 成本控制:模型路由、工具调用预算、缓存和失败重试怎么设计

"OpenAI API Pricing"
凌晨 3 点,后台 report Agent 返回空响应。HTTP 状态码是 200,body 却是空的。重试逻辑只检查状态码,于是继续尝试。每次请求发送 500 token 输入,重试 1500 次,烧掉 75 万 token。第二天早上看到账单,血压拉满。
根本原因不是“模型选贵了”,而是三个缺失:熔断器、预算检查、错误分类。Agent 成本失控有三种典型模式:
重试无上限。 失败模式分类缺失,把空响应当成可重试错误。熔断器缺失,连续失败 1500 次也不停。每次重试都重新发送完整上下文,成本放大 2-5 倍。
上下文膨胀。 长任务跑 6 小时后对话历史膨胀到 80K tokens。没有 checkpoint,失败后从头重跑,每一步都重新付费。
模型滥用。 所有任务都用 frontier 模型,没有路由策略。简单分类任务也用最贵模型,浪费 70% 的 token。
Agent 成本控制不是单点优化,而是多层设计:预算对象、路由策略、缓存命中、重试熔断、成本日志和告警阈值。把这六个工程对象拆成判断表或可执行清单。
预算对象设计:记什么、记在哪、怎么熔断
成本控制先要有预算对象,不能只记总数。没有分层,账单异常时你无法定位是哪个用户、哪个任务、哪个工具烧钱。
七层预算对象
预算对象从粗到细分成七层:
| 预算层级 | 预算对象 | 预算上限建议 | 告警触发条件 |
|---|---|---|---|
| Layer 1 | user | 每用户每日/每月上限 | 剩余 < 20% 触发告警 |
| Layer 2 | tenant | 每租户独立预算池 | 剩余 < 30% 触发告警 |
| Layer 3 | workflow | 每类 workflow 独立预算 | 剩余 < 40% 触发告警 |
| Layer 4 | task | 每任务类型独立预算 | 剩余 < 50% 触发告警 |
| Layer 5 | tool | 每工具调用独立预算 | 超上限跳过工具 |
| Layer 6 | retry | 重试次数上限 + 熔断 | 连续失败 N 次停用工具 |
| Layer 7 | cache | 缓存命中率监控 | 命中率 < 预期值告警 |
每层预算上限依赖业务模型,属于易变配置;分层结构本身更稳定。预算剩余字段要写入成本日志,用于告警和熔断判断。
每层要记的字段
每层预算都要记录以下字段:
| 字段名 | 用途 | 类型 | 必须记录原因 |
|---|---|---|---|
model | 定位模型 | string | 判断模型路由是否合理 |
inputTokens | 输入 token 数 | integer | 计算输入成本 |
outputTokens | 输出 token 数 | integer | 输出成本常常高于输入,需独立记录 |
cachedTokens | 缓存命中 token 数 | integer | 计算缓存节省 |
costEstimate | 本次成本估算 | float | 实时成本累加 |
budgetRemaining | 预算剩余 | float | 熔断判断依据 |
预算对象设计核心是“按维度记账”,不是“只记 total_cost”。多租户场景要按 tenantId 分摊成本,多工具场景要按 toolName 定位黑盒。
熔断逻辑
预算剩余 < 阈值时触发熔断:
def check_budget_before_retry(budget_remaining, retry_cost_estimate):
if budget_remaining < retry_cost_estimate:
return "skip_retry" # 超预算跳过重试
if budget_remaining < threshold: # threshold 如 20%
return "wait_approval" # 剩余不足,等待审批
return "continue"
熔断逻辑要检查预算剩余,不是重试后才发现没预算。每次重试前先估算成本,检查是否超预算,避免“空响应重试 1500 次”烧掉整日预算。
关于上下文工程中哪些内容进入稳定 prefix、哪些只放运行时变量,会在同系列的上下文工程文章中展开。
模型路由策略:不是所有任务都需要最贵模型
一个 ticket triage Agent 的实际分布:70% 任务是简单分类,20% 需要起草回复,只有 10% 要发客户邮件前才升级 frontier 模型。路由策略可节省 40-85% 成本。
模型层路由
模型层路由按任务复杂度分级:
| 任务层级 | 典型任务 | 推荐模型层级 | 占比 | 成本特征 |
|---|---|---|---|---|
| 70% - S 级 | 分类、提取、过滤、简单问答 | nano/flash(最便宜) | 70% | 输出短、轮次少、工具调用少 |
| 20% - M 级 | 起草、总结、代码生成、中等推理 | mid-tier(中等价格) | 20% | 输出中等长度、可能调用工具 |
| 10% - L 级 | 审核、架构设计、复杂推理、多工具协调 | frontier(最贵) | 10% | 输出长、轮次多、工具调用频繁 |
路由策略三步骤:
第一步:任务分级。 给每个 workflow 定义 S/M/L 三档复杂度标准,包括输出长度、工具调用次数、推理深度和风险级别。
第二步:默认路由到 S 级。 只有命中复杂特征时才升级到 M 或 L 级。
第三步:级联路由。 S 级模型失败时升级到 M 级,M 级失败升级到 L 级,L 级失败进入人工介入。升级前检查预算剩余,超预算时跳过升级。
服务层路由
同一模型也可按 latency priority 分流。折扣比例和完成窗口属于易变事实,发布前复核官方定价。
| 服务层级 | 成本折扣 | 完成时间 | 适用场景 |
|---|---|---|---|
| 实时 API | 无折扣 | 即时响应 | 交互式 Agent 对话、高优先级任务 |
| Batch API | 50% cost discount(发布前复核) | 24-hour turnaround(发布前复核) | 批量 eval、分类、embedding、内容仓库处理 |
| Flex Processing | 低成本(发布前复核) | 较慢响应,偶发不可用 | 低优先级异步任务、model evaluations、data enrichment |
离线任务路由决策清单:
- 是否需要即时响应?是 -> 实时 API,走模型路由。
- 是否可以接受 24 小时延迟?是 -> Batch API。
- 是否低优先级且可容忍偶发失败?是 -> Flex Processing。
- 是否批量处理 eval、分类、embedding?是 -> Batch API。
风险分级
任务按风险级别分级,对应不同路由路径:
| 风险级别 | 典型操作 | 路由策略 | 预算分支 |
|---|---|---|---|
| 低风险 | 分类、提取、内部总结 | S 级模型 + 自动路径 | 无审批,预算上限宽松 |
| 中风险 | 起草客户回复、代码变更建议 | M 级模型 + 可选审批 | 超预算触发审批,可继续 |
| 高风险 | 发客户邮件、扣款、架构变更 | L 级模型 + 必审批 | 审批等待、拒绝、超时进入预算分支 |
关于审批等待、拒绝、超时如何影响预算分支,会在同系列的 Human-in-the-Loop 文章中展开。
关键约束
路由策略的关键约束:
- 不硬编码价格数字:价格是易变事实,记录 model + pricingVersion,不写死成本公式。
- 预算剩余检查:升级前检查 budgetRemaining,超预算时跳过升级或进入审批。
- 错误分类:路由失败要区分可恢复的模型能力不足,以及不可恢复的参数错误、权限拒绝。
工具调用预算:per-tool budget、timeout 和重试上限
工具调用本身有 schema + API 双重成本。每次调用要发送 schema、参数、响应解析,加上外部 API 可能限流或超时,这些开销独立于模型调用。
工具预算控制
每个工具设置独立预算控制:
| 控制项 | 建议配置 | 监控字段 | 触发动作 |
|---|---|---|---|
| Per-tool budget | 单次调用上限 | tool_cost_estimate | 超上限跳过工具或降级处理 |
| Tool timeout | 外部 API 被动超时 | tool_duration | 超时标记为可重试错误 |
| Retry limit per tool | 单工具重试上限 | tool_retry_count | 超上限放弃工具,不进入重试循环 |
外部 API 用工具,包括搜索、数据库和第三方服务,都要单独记账,避免工具调用成为成本黑盒。
工具失败分类
工具失败分两类:可恢复和不可恢复。
| 失败类型 | 典型错误 | 处理策略 | 成本影响 |
|---|---|---|---|
| 可恢复失败 | 网络超时、503 Service Unavailable、429 Rate Limit | 自动重试,配合指数退避和 retry-after | 每次重试消耗完整上下文 |
| 不可恢复失败 | 403 Permission Denied、400 Bad Request、工具不存在 | 不重试,注入错误让模型判断 | 不重试,避免无效消耗 |
失败分类的核心是“外部原因导致的临时失败才重试,内部配置错误不重试”。
熔断器
连续失败 N 次后停用工具,避免“空响应重试 1500 次”:
def circuit_breaker_tool(tool_name, consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "disable_tool" # 停用工具
return "continue"
熔断器状态要写入日志,用于排查“为什么工具停用”。熔断后等待人工介入或自动恢复检查,不要反复调用不稳定工具。
关于工具调用基础,已在工具调用文章中展开,本文扩展 per-tool budget、timeout 和重试上限。
Prompt Caching 设计:稳定 prefix、变量位置、1024 token 门槛
Prompt Caching 是稳定 prompt prefix 的输入 token 优化,不是业务结果缓存。它用相同 prompt prefix 路由到近期处理过同前缀的服务器,能降低延迟和输入 token 成本。
Prompt Caching ≠ 结果缓存
Prompt Caching 缓存的是稳定 prompt prefix,不是业务结果。关键区别:
- Prompt Caching:缓存稳定 prompt prefix,如 system prompt、tool schema;命中时节省输入 token,但仍会重新执行推理。
- 业务结果缓存:缓存完整输出,如工具调用结果、数据库查询;命中时直接返回,不调用模型。
两者目的不同:Prompt Caching 优化输入 token 成本,业务结果缓存优化完整调用成本。可以同时使用:稳定前缀走 Prompt Caching,高频工具调用结果走业务缓存。
结构要求
Prompt Caching 的关键是区分稳定前缀和运行时变量:
| 内容类型 | 位置 | 命中缓存可能性 | 典型内容 |
|---|---|---|---|
| 稳定前缀(进 cache) | Prompt 前部 | 高 | System prompt、Tool schema、Policy 文档、Few-shot 示例 |
| 运行时变量(不进 cache) | Prompt 后部 | 低 | User input、File fragments、Runtime state(当前对话轮次、临时变量) |
设计步骤:
- 把 System prompt、Tool schema、Policy 放在前部:这些内容在多次调用中稳定不变,容易命中缓存。
- 把 User input、File fragments、Runtime state 放在后部:这些内容每次调用都变化,不应该进稳定前缀。
- 监控 Cache hit rate:记录 cachedTokens 和总输入 token,计算命中率。Cache hit rate > 40% 为健康,< 20% 需调整前缀结构。
门槛与效果
Prompt Caching 自动启用门槛和效果都属于易变事实,发布前复核官方文档:
- 门槛:1024 tokens 及以上自动启用(发布前复核)。
- 效果:命中可降低成本和延迟(发布前复核具体比例)。
- 检查命中:
usage.prompt_tokens_details.cached_tokens字段。
Prompt Caching 的支持模型、门槛、折扣比例都是易变事实,发布前按官方 pricing 页复核。关键设计原则是“静态内容在前,变量在后”,这个结构本身是稳定的。
失败重试熔断:幂等性、状态保存、预算剩余检查
重试是成本失控的最大来源。一个后台 report Agent 卡在不稳定工具上,每次失败都重新发送完整上下文,重试 1500 次后成本会迅速偏离正常路径。
重试前检查预算
重试前检查预算剩余,不是重试后才发现没预算:
def should_retry(error_type, budget_remaining, retry_cost_estimate):
# 错误分类
if error_type in ["403", "400", "tool_not_exist"]:
return False # 不可恢复错误,不重试
# 预算检查
if budget_remaining < retry_cost_estimate:
return False # 超预算,不重试
return True # 可重试
预算检查要写在重试逻辑之前,避免“空响应重试 1500 次”烧掉整日预算。
幂等性
重试不会重复执行副作用,比如发邮件、扣款:
- 工具调用用幂等 ID,例如 requestId;外部 API 收到相同 ID 时返回缓存结果,不重复处理。
- 幂等 ID 要写入成本日志,用于判断是否重复调用。
幂等设计的核心是“同一操作不重复消耗”,避免重试放大成本。
状态保存(Checkpoint)
长任务失败恢复不重跑完整流程:
- Agent 执行到关键节点时保存 checkpoint,包括已完成步骤、当前状态和上下文摘要。
- 失败恢复时从 checkpoint 继续,不从头重跑。
- Checkpoint 要写入持久化存储,不只留在内存。
关于 checkpoint 和 thread state 的设计,已在LangGraph Agent 架构文章中展开。
重试策略
不同错误类型要不同重试策略:
| 错误类型 | 典型错误 | 重试策略 | 成本影响 |
|---|---|---|---|
| 网络超时 | 10 秒未响应 | 指数退避 + retry-after,最多 3 次 | 每次重试消耗完整上下文 |
| 503/429 | Service Unavailable、Rate Limit | 等待限流窗口 + retry-after,最多 3 次 | 等待时间不消耗 token,但重试会消耗 |
| 403/400 | Permission Denied、Bad Request | 不重试,注入错误让模型判断 | 不重试,避免无效消耗 |
重试策略核心是“可恢复错误才重试,不可恢复错误不重试”,避免把无效请求反复发给模型。
熔断器
连续失败 N 次后停止重试,等待人工介入:
def circuit_breaker(consecutive_failures, threshold=5):
if consecutive_failures >= threshold:
return "stop_retry" # 停止重试
return "continue"
熔断器要写入日志,用于排查“为什么停止重试”。熔断后等待人工介入或预算恢复,不要反复调用不稳定工具或模型。
成本日志与告警:记录哪些字段、设置什么阈值
成本可观测性是控制的前提。日志字段不完整就无法定位问题。
OpenTelemetry trace span attributes
成本日志按三层 span 设计:
Agent run span(顶层):
| 字段名 | 用途 | 类型 | 必须记录原因 |
|---|---|---|---|
runId | 定位具体执行 | string | 区分同一 workflow 的多次执行 |
tenantId | 定位租户 | string | 多租户场景分摊成本 |
userId | 定位用户 | string | 按用户统计成本趋势 |
workflowName | 定位 workflow | string | 按 workflow 类型统计成本 |
totalCost | 总成本估算 | float | 实时成本累加 |
budgetRemaining | 预算剩余 | float | 熔断判断依据 |
totalRetries | 总重试次数 | integer | 重试放大成本 |
Model call span(子 span):
| 字段名 | 用途 | 类型 | 必须记录原因 |
|---|---|---|---|
model | 定位模型 | string | 判断模型路由是否合理 |
pricingVersion | 定价版本 | string | 成本公式不写死,记录版本 |
inputTokens | 输入 token 数 | integer | 计算输入成本 |
outputTokens | 输出 token 数 | integer | 输出成本需独立记录 |
cachedTokens | 缓存命中 token 数 | integer | 计算缓存节省 |
costEstimate | 本次成本估算 | float | 实时成本累加 |
latencyMs | 调用延迟 | integer | 判断是否走 Batch/Flex |
Tool call span(子 span):
| 字段名 | 用途 | 类型 | 必须记录原因 |
|---|---|---|---|
toolName | 定位工具 | string | 定位工具调用 overhead |
toolBudget | 工具预算上限 | float | 熔断判断依据 |
toolTimeout | 工具超时时间 | integer | 判断是否超时失败 |
retryCount | 重试次数 | integer | 判断重试放大 |
errorType | 错误类型 | string | 区分可恢复和不可恢复 |
不要只记 total_cost,要按维度拆分。没有这些字段,账单异常时你只能看到“超标”,却不知道是哪个用户、哪个工具、哪次重试烧钱。
关于日志、告警、失败恢复的完整设计,已在Agent 监控恢复文章中展开,本文补充成本字段和预算对象。
告警阈值
告警阈值按维度设置:
| 告警维度 | 告警阈值 | 告警方式 | 触发动作 |
|---|---|---|---|
| 全局预算消耗 | 70%、90%、100% | Slack/邮件告警 | 70% 提示,90% 降级,100% 熔断 |
| 单用户/tenant 消耗 | 超过均值 3 倍 | Slack/邮件告警 | 检查是否有异常调用 |
| 单模型调用失败率 | > 5% | Dashboard 告警 | 检查模型路由或服务状态 |
| 单工具重试次数 | > 阈值 | Dashboard 告警 | 检查工具稳定性 |
| 缓存命中率 | < 预期值 | Dashboard 告警 | 检查 prompt 结构是否错误 |
告警阈值要写在成本日志里,用于自动触发告警和熔断。
降级策略
告警触发后的降级路径:
| 降级路径 | 降级方式 | 适用场景 | 成本影响 |
|---|---|---|---|
| 模型降级 | 大模型 -> 小模型 | 单模型失败率过高 | 降成本,可能降质量 |
| 路径降级 | 实时 API -> Batch API -> Flex Processing | 全局预算消耗过快 | 延迟增加,成本下降 |
| 功能降级 | 关闭非核心工具调用 | 单工具重试次数过高 | 减少工具调用 overhead |
| 用户降级 | 限流、排队、提示“稍后重试” | 单用户消耗异常 | 避免单用户烧预算 |
降级策略要写在预算逻辑里,告警触发时自动降级,而不是人工介入后才降级。
下一步推荐
Agent 成本控制不是孤立的,需要监控、工具调用、上下文工程配合:
- 已发布:Agent 监控恢复:日志字段、告警设置、失败恢复,本文补充成本字段和预算对象。
- 已发布:LangGraph Agent 架构:checkpoint、thread state、失败恢复,用于说明长任务状态保存。
- 已发布:工具调用:工具调用基础,本文扩展 per-tool budget、timeout、重试上限。
- 同系列:上下文工程:稳定 prefix 与缓存命中,哪些上下文进入稳定 prefix,哪些只放运行时变量。
- 同系列:Human-in-the-Loop:审批等待、拒绝、超时的预算分支,审批如何影响成本和重试路径。
从 Session 级防线开始:设置 per-session cost limit,单次会话超预算自动终止。这是最快见效的第一步,避免一个跑飞的任务烧掉整日预算。后续逐步扩展到预算对象分层、模型路由、工具调用预算、Prompt Caching、重试熔断和成本日志。
设计 Agent 成本预算和熔断机制
用预算对象、模型路由、服务层路由、缓存、工具预算和重试熔断,把 Agent 成本控制前移到每次 run 执行前。
⏱️ 预计耗时: 45 分钟
- 1
步骤 1: 列出所有成本路径
列出 Agent 的模型调用、工具调用、文件读取、外部 API、批处理、缓存和重试路径。 - 2
步骤 2: 定义预算对象
按 tenant、user、run、workflow、model、tool、retry、cache 和 time window 记录预算对象。 - 3
步骤 3: 设置路由策略
为不同任务类型设置模型层路由和服务层路由,区分 online、batch、flex 和 queue。 - 4
步骤 4: 设计缓存命中
把稳定上下文放入 prompt prefix,变量内容放后面,同时区分 prompt caching、业务结果缓存和工具响应缓存。 - 5
步骤 5: 限制工具和重试
为每个工具设置 timeout、max retries、idempotency key、per-tool budget 和 fallback。 - 6
步骤 6: 记录成本 span
在 run/span 上记录 token、cached token、tool、retry、latency、estimated cost、budget remaining 和 traceId。 - 7
步骤 7: 配置降级和熔断
设置降级、暂停、熔断和告警阈值,并用真实失败案例回归测试。
常见问题
Agent 成本应该按用户、会话、任务还是工具统计?
模型路由是不是只要把简单任务换成小模型?
Prompt Caching 和普通业务缓存有什么区别?
工具调用失败时重试几次才算合理?
Agent 长任务跑到一半没预算了,应该降级、暂停还是直接失败?
成本日志应该记录哪些字段?
16 分钟阅读 · 发布于: 2026年9月17日
AI Agent 工程化专题:架构、工具调用、评估与恢复
如果你是从搜索进入这篇文章,建议顺手补上上一篇或继续下一篇,这样更容易把同一主题读完整。
上一篇
Human-in-the-loop Agent 设计:哪些步骤必须人工审批
用风险分级设计 AI Agent 的人工审批点:哪些动作可以自动执行,哪些必须暂停等待确认,以及 approve/reject/resume、超时、补偿和审计记录怎么做。
第 19 / 22 篇
下一篇
Agent 权限模型设计:用户身份、工具权限、审计日志和密钥隔离
把 AI Agent 接入真实工具前,先设计权限模型:用户身份映射、service account、per-tool permission、scope、secret vault、key rotation、approval policy、data boundary 和 audit log。
第 21 / 22 篇



评论
使用 GitHub 账号登录后即可评论