据披露的内部报告,亚马逊多个项目因 AI 使用出现成本超支,其中一个项目超支约 180 万美元,标题所述幅度达到 860%。涉事任务并不神秘:工程师借助 Claude Sonnet 编写程序,用于匹配电商平台中的作者信息和商品列表。
这件事值得工程团队警惕,不是因为“AI 不该写代码”,而是因为一个看似普通的数据处理任务,也可能把模型调用、批处理规模和失败重试叠加成一张失控的账单。AI 项目不能只评估代码是否能跑,还要把单位经济性、预算上限和降级路径写进系统。
常规数据任务为什么可能变贵
来源摘要没有披露 180 万美元成本的完整构成,因此不能直接断言费用全部来自模型推理,也不能确定是生成代码、运行代码还是配套基础设施造成了超支。不过,从工程角度看,数据匹配任务通常存在几个典型放大器。
按记录调用模型。 如果程序为每个作者和每件商品分别发起请求,请求数量会随数据规模线性增长。若代码还执行笛卡尔积比较,成本可能接近 作者数 × 商品数。
把完整上下文重复发送。 每次请求都附带长提示词、商品描述、历史结果或大段候选列表,输入 token 会被反复计费。模型输出很短,并不代表请求便宜。
失败后无上限重试。 超时、限流和格式校验失败都会触发重试。如果没有幂等缓存和重试上限,同一批数据可能被处理多次。
用大模型完成确定性工作。 ISBN、作者 ID、规范化姓名和出版社编号等字段可以先用 SQL、哈希或模糊匹配处理。若所有记录都直接进入大模型,昂贵能力会消耗在本可确定解决的问题上。
只限制计算资源,没有限制外部消费。 Kubernetes 的 CPU 和内存限制无法约束模型 API 账单。任务可能只占一个小 Pod,却持续产生高额外部调用费用。
先算单价,再决定架构
团队在运行批处理之前,至少应回答四个问题:总记录数是多少、每条记录平均消耗多少 token、失败与重试比例是多少、允许花多少钱。
可以这样实践:使用下面的标准库 Python 脚本做运行前估算。把脚本中的输入、输出 token 单价替换为实际合同或供应商价格;这里的默认数字只是演示假设,不代表 Claude Sonnet 的真实价格。
#!/usr/bin/env python3
import argparse
def estimate(rows, batch_size, input_tokens, output_tokens,
input_price, output_price, retry_rate):
requests = (rows + batch_size - 1) // batch_size
billed_requests = requests * (1 + retry_rate)
input_cost = billed_requests * input_tokens / 1_000_000 * input_price
output_cost = billed_requests * output_tokens / 1_000_000 * output_price
return requests, billed_requests, input_cost + output_cost
parser = argparse.ArgumentParser(description="Estimate an LLM batch job cost")
parser.add_argument("--rows", type=int, required=True)
parser.add_argument("--batch-size", type=int, default=20)
parser.add_argument("--input-tokens", type=int, default=3000)
parser.add_argument("--output-tokens", type=int, default=500)
parser.add_argument("--input-price", type=float, default=3.0,
help="USD per 1M input tokens; replace with your price")
parser.add_argument("--output-price", type=float, default=15.0,
help="USD per 1M output tokens; replace with your price")
parser.add_argument("--retry-rate", type=float, default=0.05)
parser.add_argument("--budget", type=float, required=True)
args = parser.parse_args()
requests, billed_requests, cost = estimate(
args.rows, args.batch_size, args.input_tokens, args.output_tokens,
args.input_price, args.output_price, args.retry_rate
)
print(f"logical requests: {requests:,}")
print(f"requests including retries: {billed_requests:,.0f}")
print(f"estimated cost: ${cost:,.2f}")
if cost > args.budget:
raise SystemExit(
f"BLOCKED: estimate exceeds ${args.budget:,.2f} budget"
)
保存为 estimate_llm_job.py 后,可以直接运行:
python3 estimate_llm_job.py \
--rows 10000000 \
--batch-size 20 \
--input-tokens 3000 \
--output-tokens 500 \
--retry-rate 0.08 \
--budget 10000
这个估算器故意在超预算时返回非零退出码,因此可以放进 CI 或工作流的前置步骤。关键不是把预测做到分毫不差,而是在扫描数百万条数据之前暴露数量级错误。
给批处理增加三道硬约束
成本面板只能告诉团队已经花了多少钱,不能阻止程序继续消费。真正的控制点应进入执行链路。
1. 先过滤,再调用模型
匹配流程可以拆成三层:
- 使用标准化 ID、ISBN 等字段完成精确匹配。
- 使用规范化姓名、编辑距离或搜索索引生成少量候选。
- 只把低置信度、确实需要语义判断的记录交给模型。
同时按候选集合的稳定哈希缓存结果。相同输入再次出现时读取缓存,不重新付费。
2. 按美元设置断路器
每次调用后记录供应商返回的实际 token 用量,并按照当前价格换算成本。累计消费接近预算时停止领取新任务,而不是等账单告警。
可以采用类似下面的配置。字段是通用示例,需要接入团队自己的任务执行器:
job:
name: author-product-matching
max_records: 10000000
batch_size: 20
llm_budget:
hard_limit_usd: 10000
warning_at_percent: 70
stop_new_requests_at_percent: 90
max_input_tokens_per_request: 4000
max_output_tokens_per_request: 600
max_attempts: 2
fallback:
on_budget_exhausted: write_to_manual_review_queue
cache_results: true
require_idempotency_key: true
预算检查必须由调用模型的网关或执行器强制执行。仅把数字写在配置文件里,却允许业务代码绕过网关,不构成真正的限制。
3. 先做小样本闸门
不要让首次运行直接扫描全量数据。先处理 1,000 条记录,测量实际 token、准确率、缓存命中率和重试率,再外推全量成本。扩大到 1%、10% 和 100% 时,每个阶段都应重新审批。
如果样本估算为 5,000 美元,而全量运行实际可能达到 50,000 美元,系统应自动暂停并要求负责人确认。审批记录还应固定模型版本、价格表、提示词版本和数据快照,避免批准后悄悄改变成本条件。
AI 生成的代码也要接受 FinOps 审查
代码审查通常关注正确性、性能和安全,但涉及付费 API 的程序还需要检查“每处理一条业务记录要花多少钱”。一个实现即使输出正确,只要调用复杂度错误,就不具备上线条件。
上线前可以使用这份检查清单:
- 是否计算了全量运行的请求数、token 数和美元成本?
- 是否存在按记录循环调用模型或候选集笛卡尔积?
- 是否先用 SQL、规则和搜索索引缩小了候选范围?
- 是否限制输入、输出、重试次数、并发数和总预算?
- 是否使用幂等键及缓存避免重复计费?
- 达到预算后,系统能否自动停止并保留检查点?
- 是否用小样本验证准确率和实际单位成本?
- 模型、提示词、价格或数据规模变化后,是否重新审批?
亚马逊案例暴露的核心问题,不是模型能不能完成数据匹配,而是组织是否把付费推理当成一种需要计量和限额的生产资源。让 AI 写出程序只需要几分钟;证明它在一千万条数据上仍然经济可控,才是工程工作真正困难的部分。