AI API 怎么省钱:从 Token 估算到模型分流的实用方法
AI API 的费用通常不是由“调用次数”单独决定的,而是由输入 Token、输出 Token、缓存命中、工具调用和重试等因素共同决定。只更换一个低价模型,往往不能解决长上下文、重复请求或失败重试带来的浪费。
更可靠的做法是建立一条可观测的成本链路:
- 估算请求规模;
- 记录实际用量;
- 按任务复杂度分流模型;
- 控制上下文、输出和重试;
- 用质量指标验证节省是否值得。
本文使用通用 API 作为例子,不依赖某个厂商或某个客户端。模型能力、可用性、上下文限制和计费方式都会变化,实施前应核对对应服务的最新文档和控制台。
一、先定义“省钱”边界
在修改代码前,先明确三个问题。
1. 成本按什么单位衡量
可以选择以下任意一种单位:
- 每次请求成本;
- 每个完成任务的成本;
- 每位用户每天的成本;
- 每月总预算;
- 每 1,000 个有效结果的成本。
“每次请求更便宜”不一定等于“每个有效结果更便宜”。如果便宜模型的格式错误更多,后续人工修正和重试可能抵消节省。
2. 哪些质量不能降低
为任务定义可检查的质量条件,例如:
- JSON 是否能通过 Schema 校验;
- 代码是否能通过测试;
- 摘要是否包含指定事实;
- 检索结果是否带有来源;
- 分类结果是否需要人工复核;
- 敏感场景是否必须经过审批。
质量条件越明确,模型分流越容易验证。
3. 费用数据是否可审计
建议为每次请求保留以下元数据:
| 字段 | 用途 |
|---|---|
request_id | 关联重试、工具调用和业务任务 |
model_id | 统计不同模型的真实成本 |
prompt_version | 判断提示词变更的影响 |
input_tokens | 计算输入费用 |
output_tokens | 计算输出费用 |
cached_input_tokens | 区分缓存命中部分 |
retry_count | 发现失败重试造成的额外费用 |
result_status | 统计成功率和格式错误 |
latency_ms | 观察响应时间变化 |
不要默认记录完整的敏感提示词。可以记录提示词版本哈希、长度、字段名和脱敏后的错误信息。只处理自己有权限处理的数据,也不要把 API 密钥粘贴到公开文章、工单或评论区。
二、Token 估算:先算结构,再看账单
1. 明确计费公式
当价格按“每百万 Token”给出时,基础公式可以写成:
单次成本 =
(有效输入 Token × 输入单价
+ 输出 Token × 输出单价
+ 其他单列项目)
÷ 1,000,000
“其他单列项目”可能包括缓存输入、推理过程、图片或音频处理、工具调用等。是否单独计费、字段如何命名,都要以具体服务的文档为准。
输入 Token 不只包含用户最后输入的一句话,还可能包括:
- 系统指令;
- 多轮历史消息;
- 检索到的文档片段;
- 工具定义和参数 Schema;
- 对话中的示例;
- 图片、音频等多模态内容对应的计费单位。
输出 Token 也不一定等于最终显示的字符数。不同语言、标点、代码和格式的切分方式可能不同。
2. 字符数只能做粗略预警
可以用字符数估计“是否可能超长”,但不能把字符数直接当作计费 Token。中文、英文、代码、表格和混合文本的切分规律不同,具体 Token 数应由对应分词器或 API 返回的用量字段确认。
实用的估算流程是:
- 统计固定提示词的 Token 数;
- 统计用户输入和历史消息的 Token 数;
- 统计检索片段、工具定义的 Token 数;
- 为输出设置一个合理上限;
- 调用后读取实际
usage或等价字段; - 用实际数据修正估算模型。
下面的脚本只负责按已知 Token 数和价格计算金额,不假定任何厂商的价格。它适合做预算表、单元测试和离线分析。
from decimal import Decimal, ROUND_HALF_UP
from typing import Optional
def estimate_cost(
input_tokens: int,
output_tokens: int,
input_price_per_million: str,
output_price_per_million: str,
cached_input_tokens: int = 0,
cached_input_price_per_million: Optional[str] = None,
) -> Decimal:
"""Return the estimated cost in the same currency as the supplied prices."""
if input_tokens < 0 or output_tokens < 0:
raise ValueError("Token counts cannot be negative")
if cached_input_tokens < 0 or cached_input_tokens > input_tokens:
raise ValueError("Cached input tokens must be within input tokens")
input_price = Decimal(input_price_per_million)
output_price = Decimal(output_price_per_million)
cached_price = (
Decimal(cached_input_price_per_million)
if cached_input_price_per_million is not None
else input_price
)
uncached_input_tokens = input_tokens - cached_input_tokens
total = (
Decimal(uncached_input_tokens) * input_price
+ Decimal(cached_input_tokens) * cached_price
+ Decimal(output_tokens) * output_price
) / Decimal(1_000_000)
return total.quantize(Decimal("0.000001"), rounding=ROUND_HALF_UP)
if __name__ == "__main__":
cost = estimate_cost(
input_tokens=12_000,
output_tokens=1_500,
input_price_per_million="1.20",
output_price_per_million="4.00",
cached_input_tokens=4_000,
cached_input_price_per_million="0.30",
)
print(f"estimated cost: {cost}")
如果服务没有缓存输入这一项,把 cached_input_tokens 保持为 0 即可。生产系统应把各家返回的用量字段转换为内部统一格式,再进行汇总,避免不同命名导致统计错误。
三、选择接入入口时,先核对计费口径
如果需要额外核对一个独立入口的当前支持工具和计费信息,可把 moli 作为可选查询入口。它是独立的第三方服务,与任何模型提供商或 CSDN 没有隶属关系;链接只用于查看当时公开的支持范围和计费说明。实际能力、可用性、上下文限制和账单仍应以对应服务的最新文档与控制台为准。
无论使用哪种入口,都建议在上线前确认:
- 价格单位是每千 Token 还是每百万 Token;
- 输入和输出是否采用不同单价;
- 缓存、批处理、工具调用是否单列;
- 失败请求和重试请求如何计费;
- 账单币种、税费和结算周期如何计算;
- 用量字段是否会延迟更新。
四、模型分流:按任务风险,而不是按模型名称
模型分流的核心不是给模型贴上“强”或“弱”的永久标签,而是让每类任务使用足够完成目标的能力。
| 任务特征 | 初始路由 | 建议校验 |
|---|---|---|
| 短文本分类、去重、字段提取 | 低成本路线 | 标签集合、Schema、必填字段 |
| 常规改写、摘要、翻译 | 标准路线 | 长度、术语表、事实抽样 |
| 多步骤代码分析、复杂推理 | 高能力路线 | 测试、反例、人工抽查 |
| 证据冲突、重要决策、敏感内容 | 高能力路线或人工流程 | 来源核对、审批和审计 |
| 超长文档 | 先检索或分块 | 覆盖率、重复率、上下文预算 |
模型名称、版本和价格可能变化,因此应用代码最好使用内部别名,例如 route_small、route_standard 和 route_advanced,把真实模型配置放在可热更新的配置文件中。
一个简单的分流函数如下。它只是策略示例,阈值应通过自己的评测集确定。
def choose_route(
task_type: str,
input_tokens: int,
needs_strict_schema: bool,
risk_level: str,
validator_confidence: float,
) -> str:
"""Choose an internal route alias, not a specific provider model."""
if risk_level in {"high", "critical"}:
return "route_advanced"
if needs_strict_schema and validator_confidence < 0.90:
return "route_standard"
if task_type in {"classification", "extraction"} and input_tokens <= 4000:
return "route_small"
if task_type in {"code_review", "multi_step_reasoning"}:
return "route_advanced"
if input_tokens > 12000:
return "route_standard"
return "route_standard"
if __name__ == "__main__":
route = choose_route(
task_type="extraction",
input_tokens=1800,
needs_strict_schema=True,
risk_level="normal",
validator_confidence=0.96,
)
print(route)
推荐采用“两阶段路由”
- 先用成本较低的路线生成候选结果;
- 通过解析器、规则或测试检查结果;
- 只有在校验失败、证据不足或任务风险较高时才升级;
- 记录升级原因,定期分析哪些规则最有价值。
不要直接相信模型自己返回的“置信度”。这类分数未必经过校准,应结合历史准确率、格式通过率和人工复核结果。
五、降低 Token 的几个有效位置
1. 缩短重复上下文
把固定规则放在稳定的提示词前缀中,避免每一轮重复拼接无关说明。对历史对话可以采用:
- 只保留与当前任务有关的轮次;
- 定期生成经过校验的摘要;
- 使用检索结果替代整篇文档;
- 对相同内容去重;
- 为不同任务维护不同的提示词版本。
删除上下文前,先验证关键信息是否仍然存在。单纯按字符数截断,可能把定义、约束或代码边界截掉。
2. 控制输出长度,但要处理截断
为摘要、分类和结构化提取设置合理的输出上限。上限过大,会增加最坏情况下的费用;上限过小,则可能导致结果不完整。
程序必须检查完成原因或等价状态:
- 正常结束:进入下一步;
- 达到长度上限:标记为不完整并决定是否续写;
- 格式错误:进入修复或升级流程;
- 服务错误:按错误类型处理。
3. 谨慎使用缓存
精确缓存适合重复的只读请求,例如相同文档、相同版本提示词和相同参数。缓存键至少应包含:
- 租户或用户范围;
- 输入内容哈希;
- 提示词版本;
- 模型别名;
- 输出 Schema 版本;
- 关键生成参数;
- 语言和权限范围。
语义缓存需要设置相似度阈值和过期时间,并防止不同用户之间互相命中。涉及权限、个人信息或实时数据的请求,默认不应共享缓存结果。
4. 控制重试和工具调用
一次失败并不代表没有产生费用。重试前应区分:
- 网络超时;
- 服务端暂时错误;
- 参数错误;
- 内容被拒绝;
- 本地解析失败。
只有对可能恢复的错误进行有限次数重试,并使用指数退避。遵守服务公开的调用限制,不通过重试规避限制。对会产生副作用的工具调用,使用幂等键或业务去重,避免一次操作被执行多次。
六、一个可落地的成本优化流程
第一步:建立请求清单
按业务任务分类,而不是按接口路径分类。例如:
- 知识库问答;
- 发票或表格字段提取;
- 代码解释;
- 研究资料摘要;
- 邮件或报告改写。
为每类任务记录输入长度、输出长度、成功标准、敏感等级和可接受延迟。
第二步:采集代表性样本
选择经过授权的数据,覆盖正常样本、长文本、空字段、格式异常和边界案例。不要只选最容易的请求,也不要在真实用户数据上直接试验未经审核的提示词。
第三步:记录基线
先不分流,记录一段具有代表性的流量:
- 总费用和每任务费用;
- 输入、输出及缓存 Token;
- 成功率和格式错误率;
- 重试次数;
- 人工修改比例;
- 延迟分布;
- 超预算请求数量。
这一步的目的不是追求某个固定数字,而是得到可比较的起点。
第四步:逐项改变
每次只改变一个主要因素,例如:
- 缩短历史上下文;
- 增加精确缓存;
- 调整输出上限;
- 引入两阶段路由;
- 修改重试策略。
为每个版本保留提示词、路由规则和配置快照,便于回滚和解释结果。
第五步:同时验证成本和质量
至少比较以下指标:
每个成功任务的成本 = 总计费金额 ÷ 成功完成的任务数
同时检查:
- 关键字段准确率;
- Schema 通过率;
- 测试通过率;
- 人工修正比例;
- 升级比例;
- 截断比例;
- 超时和错误比例。
如果成本下降但人工修正明显增加,不能把它当作成功。对长尾和高风险样本应单独统计,不能只看平均值。
七、常见失败模式
| 现象 | 可能原因 | 修复方向 |
|---|---|---|
| 预算估算总是偏低 | 忽略历史、工具定义、缓存或重试 | 以 API 返回的实际用量校准 |
| 便宜路线格式错误多 | 没有 Schema 校验和升级条件 | 增加解析、重试和升级 |
| 缓存命中后内容过时 | 缓存没有版本和过期策略 | 加入版本号、权限范围和 TTL |
| 重试后账单突然增加 | 把参数错误也当作临时错误 | 按错误类型设置重试白名单 |
| 长文档请求费用很高 | 把全文放进每次请求 | 先分块、检索、去重和摘要 |
| 质量下降但平均指标正常 | 长尾案例被平均值掩盖 | 单独评估边界、高风险样本 |
| 日志泄露隐私 | 记录了原始提示词和响应 | 脱敏、最小化记录并限制访问 |
| 月度账单对不上 | 单价、币种或计费周期变化 | 保存价格来源和核对日期 |
八、上线前检查清单
- 价格、计费单位和缓存规则已核对最新文档。
- 生产请求记录了实际输入和输出 Token。
- 失败、重试、工具调用都能关联到同一个任务。
- 每类任务都有明确的成功标准。
- 路由规则使用内部别名,模型配置可独立更新。
- 低成本路线有格式校验和升级条件。
- 输出上限、超时和重试次数有明确边界。
- 缓存键包含版本、权限和租户范围。
- 日志经过脱敏,密钥不进入日志和公开内容。
- 已用代表性样本进行离线或影子验证。
- 已检查高风险、长文本和异常输入。
- 预算告警、熔断和人工兜底流程可以实际触发。
- 变更记录包含提示词、路由、价格和评测结果。
总结
AI API 省钱的关键不是寻找一个固定的“最便宜模型”,而是把成本拆解到 Token、上下文、缓存、重试和任务质量上。先用实际用量建立基线,再按风险和复杂度分流,配合结构化校验、有限重试和权限隔离,才能在降低费用的同时保持结果可用。
价格、能力、可用区域、上下文上限和计费政策都可能调整。上线前和定期复核时,都应以当前官方文档、控制台账单和自己的评测数据为准。
转载自 CSDN-专业IT技术社区
原文链接:https://blog.csdn.net/qq_50812489/article/details/163886014




