不可求~头像
关注
AI API 怎么省钱:从 Token 估算到模型分流的实用方法封面图

AI API 怎么省钱:从 Token 估算到模型分流的实用方法

AI API 怎么省钱:从 Token 估算到模型分流的实用方法

AI API 的费用通常不是由“调用次数”单独决定的,而是由输入 Token、输出 Token、缓存命中、工具调用和重试等因素共同决定。只更换一个低价模型,往往不能解决长上下文、重复请求或失败重试带来的浪费。

更可靠的做法是建立一条可观测的成本链路:

  1. 估算请求规模;
  2. 记录实际用量;
  3. 按任务复杂度分流模型;
  4. 控制上下文、输出和重试;
  5. 用质量指标验证节省是否值得。

本文使用通用 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 返回的用量字段确认。

实用的估算流程是:

  1. 统计固定提示词的 Token 数;
  2. 统计用户输入和历史消息的 Token 数;
  3. 统计检索片段、工具定义的 Token 数;
  4. 为输出设置一个合理上限;
  5. 调用后读取实际 usage 或等价字段;
  6. 用实际数据修正估算模型。

下面的脚本只负责按已知 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_smallroute_standardroute_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)

推荐采用“两阶段路由”

  1. 先用成本较低的路线生成候选结果;
  2. 通过解析器、规则或测试检查结果;
  3. 只有在校验失败、证据不足或任务风险较高时才升级;
  4. 记录升级原因,定期分析哪些规则最有价值。

不要直接相信模型自己返回的“置信度”。这类分数未必经过校准,应结合历史准确率、格式通过率和人工复核结果。

五、降低 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

文章来源转载

评论

赞0

评论列表

微信小程序
QQ小程序

关于作者

点赞数:0
关注数:0
粉丝:0
文章:0
关注标签:0
加入于:--