AI 应用如何控制 Token 成本?缓存、路由、预算和降级策略
LLM 成本控制不是简单压缩 Prompt,而是先按功能、用户和请求记录可归因用量,再依次治理无效请求、上下文、输出长度、缓存、模型路由与重试。价格应来自实际供应商账单或已核验价目表;本文不写固定单价,只给可替换费率的计算与预算方法。
先说结论
控制成本应遵循“先可见、再约束、后优化”:
- 建立请求级用量台账,区分输入、输出、缓存命中和重试;
- 在调用前做预算检查,限制上下文、输出和循环次数;
- 优先消除重复调用和无效长上下文;
- 按任务能力路由模型,而不是一律选择最高能力或最低单价;
- 超预算时明确降级,不静默牺牲权限、事实性和关键质量。
适合谁,不适合谁
本文适合已产生稳定调用量,需要做成本归因、限额和优化的 API、RAG 或 Agent 应用。
早期原型也应设置硬上限,但无需立刻建设复杂计费平台。若供应商按非 Token 维度收费,仍可沿用“请求级归因、预算、路由、降级”的方法,只需替换计量单位。
先建立可归因成本模型
单次请求的估算可以写成:
estimatedCost =
inputUnits × verifiedInputRate
+ outputUnits × verifiedOutputRate
+ toolOrHostingCost费率必须来自当前合同、账单或核验过的官方价目表,并保存币种、计费单位、生效时间和来源。不能把文章中的示例数值直接用于生产预算。
请求台账至少包含:
- 业务功能、租户或成本中心;
- 内部模型别名与实际 Provider;
- 输入、输出和可用的缓存计量;
- 重试次数与失败原因;
- 工具调用或自托管资源归因;
- Prompt、路由策略和应用版本。
用量缺失应记为 unknown,不要记为零。定期用供应商账单对账,才能发现统计口径差异。
调用前预算守卫
下面的 TypeScript 示例把价格配置与业务逻辑分离,费率由已核验配置注入:
interface VerifiedRates {
inputPerUnit: number
outputPerUnit: number
currency: string
source: string
effectiveAt: string
}
interface UsageEstimate {
inputUnits: number
maxOutputUnits: number
}
function estimateMaximumCost(
usage: UsageEstimate,
rates: VerifiedRates,
): number {
return (
usage.inputUnits * rates.inputPerUnit +
usage.maxOutputUnits * rates.outputPerUnit
)
}
function assertWithinBudget(
usage: UsageEstimate,
rates: VerifiedRates,
remainingBudget: number,
): void {
const maximum = estimateMaximumCost(usage, rates)
if (maximum > remainingBudget) {
throw new Error('request exceeds remaining budget')
}
}生产实现还应处理计费单位换算和小数精度。预算守卫使用“最大可能输出”做保守估算;请求结束后再以实际用量记账。
六类优化手段怎么选
| 手段 | 适用条件 | 主要收益 | 风险与边界 |
|---|---|---|---|
| 输入裁剪 | 历史或检索片段冗余 | 减少输入用量与延迟 | 不能裁掉权限、目标和关键证据 |
| 输出上限 | 任务可定义长度 | 防止失控长输出 | 上限过低会截断 JSON 或答案 |
| 语义/结果缓存 | 输入规范化且结果可复用 | 避免重复生成 | 权限、时效、模型版本必须进缓存键 |
| 模型路由 | 任务难度可分层 | 简单任务不用高能力模型 | 需要评估集验证最低能力 |
| 批处理 | 非实时且接口支持 | 降低调度开销 | 增加等待,失败隔离更难 |
| 降级 | 预算或依赖异常 | 维持核心服务 | 必须告诉调用方结果能力下降 |
缓存不是只用 Prompt 字符串做键。至少考虑租户/权限范围、任务版本、模型别名、Schema 版本、数据版本和时效窗口。含个人数据或权限结果时,宁可不跨用户复用。
路由策略的可执行边界
路由应先满足能力,再比较成本和延迟:
interface RouteCandidate {
name: string
supportsSchema: boolean
supportsTools: boolean
estimatedMaximumCost: number
healthy: boolean
}
function selectRoute(
candidates: RouteCandidate[],
needsSchema: boolean,
needsTools: boolean,
budget: number,
): RouteCandidate {
const eligible = candidates
.filter(item => item.healthy)
.filter(item => !needsSchema || item.supportsSchema)
.filter(item => !needsTools || item.supportsTools)
.filter(item => item.estimatedMaximumCost <= budget)
.sort((a, b) => a.estimatedMaximumCost - b.estimatedMaximumCost)
if (eligible.length === 0) {
throw new Error('no healthy route satisfies capability and budget')
}
return eligible[0]
}仅按估算成本排序仍不够。候选模型必须先通过该任务的最小评估集;质量不达标的低成本路线不能进入候选集合。
超时、重试、降级和观测边界
- 超时:超时也是成本边界。调用方结束后要取消上游生成,避免“用户已经离开、模型仍在计量”。
- 重试:把重试用量归到原请求;限制总尝试次数和总预算。格式错误不应无限重试。
- 降级:按顺序考虑减少非关键上下文、改用已验证的低成本路由、返回缓存、切换确定性搜索或进入异步队列。不得移除安全检查。
- 观测:看每个成功业务结果的总用量,而不只看单次调用;Agent 多轮和失败重试可能让单次便宜变成整体昂贵。
建议告警维度包括:每功能单位成本异常、未知用量比例、缓存命中变化、重试放大、输出接近上限、单请求预算拒绝和租户配额耗尽。
常见误区与失败边界
- 只比较单价:忽略质量、输出长度、重试率和工具循环。
- 压缩所有上下文:关键证据丢失后,错误与重试反而增加。
- 缓存不含权限信息:造成跨用户数据泄漏。
- 预算只在月末统计:没有调用前守卫,异常循环无法及时停止。
- 降级静默发生:调用方以为仍获得完整能力。
- 用估算代替账单:估算用于守卫,财务核对应基于真实计费记录。
本文没有提供任何供应商价格。实施时必须对费率来源和生效日期做配置审查;模型、地区、合同或计费方式变化后重新核验。
可执行检查清单
- [ ] 每个请求可归因到功能、租户和路由
- [ ] 输入、输出、重试和工具成本分开记录
- [ ] 未返回用量时记为未知,并能与账单对账
- [ ] 调用前检查上下文、输出、循环和金额预算
- [ ] 缓存键包含权限、版本和时效边界
- [ ] 低成本路由已通过相同评估集
- [ ] 客户端取消会终止上游生成
- [ ] 重试共享总超时和总预算
- [ ] 降级不绕过安全与业务校验,且对调用方可见
- [ ] 费率配置包含来源、币种和生效时间
下一步
回到 AI 工程化专题 可以查看成本与可靠性、部署的关系。预算策略确定后,使用 AI 项目上线前检查清单 统一核对安全、权限、成本、监控和回滚。
