月度对账的时候被问了一句:「AI 这一千多块,是哪个功能花的?」
我打开服务商后台,只有一张按 key 汇总的账单,一行总数、一行 token 明细,没有别的维度。而那把 key 底下接着三路调用:客服离线兜底、内部批量生成视频摘要、还有我自己跑回归集和各种临时验证。
前两路是常驻的,第三路看心情,有时候一天几百次。真要摊,摊不清。
先打点
最直接的补法是给每次调用打个来源,这个半天就做完了:
def generate_reply(user_content, history=None, source='chat'):
...
usage = resp.usage
AIAudit.objects.create(
source=source,
usage_prompt=usage.prompt_tokens,
usage_completion=usage.completion_tokens,
model=settings.AI_CHAT_MODEL,
cache_hit='prompt_tokens_details' in (resp.usage or {}),
...
)
三路各自传 source='chat' / 'summary' / 'eval'。加上这个之后,至少能回答"各路调用了多少次、各占多少 token"。
以前 77 篇里提过一句给入口加 source 字段,当时是随手写的,没真去用。这次才把它接进成本表。
| 误差来源 | 影响方向 | 能不能精确分摊 |
|---|---|---|
| 提示词缓存折扣 | 估算偏高 | 不能,服务商只按整把 key 给 |
| 失败重试漏记 | 估算偏低 | 能,补 attempt 字段后归到发起方 |
| 内部测试重复跑 | 全落在 eval | 能归,但多少算必要说不清 |
有了打点,粗估公式就很简单:某路的成本 ≈ 该路 prompt token × 输入单价 + completion token × 输出单价,按天的汇率和单价算下来,跟账单总量对一下误差。
第一回跑出来差了 18%。
差在哪
查了两天,主要是两处,都挺有意思。
第一处是提示词缓存。客服那路的 SYSTEM_PROMPT 是固定的,前缀命中了服务商的缓存,实际单价打折。可我们的估算按全价算,自然偏高。麻烦的是缓存命中跟调用顺序有关:三路共用一个 key、前缀又各不相同,一路的请求插进来,另一路的缓存就可能失效。也就是说,分得越细,缓存越难命中,总账反而越贵。我们把 source 打点加上之后,一度想把三路拆成三个 key 方便对账,试算了一下缓存损失,比省下来的对账精力值钱多了,就没拆。
第二处是重试。81 篇那套结构化输出会重试,失败的那次也计费,但审计表只记了最终成功的那条。批量摘要那路重试最多,漏记的 token 全堆在它头上。补了个 attempt 字段、把失败调用也记进去,误差才降到 5% 上下。
剩下那 5% 我一直没查清,可能是跨时区结算的时间差,也可能是某次临时脚本忘了传 source,落到默认值 chat 上了。这个默认值现在看是个坏设计:漏传不报错,悄悄把账记到别人头上。
估算表长什么样
最后是一段很朴素的查询,按天按来源聚合:
SELECT date(created_at) AS d, source,
sum(usage_prompt) AS pt, sum(usage_completion) AS ct,
count(*) AS calls,
sum(CASE WHEN attempt > 1 THEN 1 ELSE 0 END) AS retries
FROM downloader_aiaudit
WHERE created_at >= now() - interval '30 days'
GROUP BY 1, 2 ORDER BY 1, 2;
导进表格,乘单价,再拿账单总数校一次。这个数只能叫"估算",因为缓存折扣没法精确归到某一路——服务商只告诉你整把 key 省了多少,不告诉你哪次请求命中的。
我们试过按 source 的固定前缀比例去摊,越算越像在做会计分录,最后放弃了,表里就留一行"未分摊折扣"。
没解决的问题
重试那次算谁的,到现在没定论。同一笔业务请求因为模型返回格式不对重试了一次,钱是这业务花的,可失败原因是模型不稳定,算到功能头上有点冤。现在一律记给发起方,理由只是实现简单。
内部测试那路更尴尬。回归集是我手动跑的,有时候为了对比连跑三遍,三遍都算进 eval。财务问"测试为什么要花钱",我只能说这是必要成本,但多少算必要,说不出数。
还有个一直想做没做的:给每路设独立预算和告警。现在只有一个总的月度上限,真哪路写了个死循环,得等整把 key 都快烧完才发现。分 key 能解决,可又绕回缓存那笔账。
VidDown 技术博客,免费那部分功能的账也得有人算。
那张估算表我每个月手工跑一次,跑完发群里,从来没人回过。上个月我忘了发,也没人问。