算一个功能的账而不是一次调用的账:把花费归到触发它的那个按钮
本文讲解如何按产品功能给 LLM 请求打标签并汇总成本,让 Agent、摘要器、聊天框各自承担可见开销,而不是混在同一个 key 里。
为什么按 key 记账会失真
很多团队一开始只用一个 API key,月底看一眼总消耗就结束了。问题在于:这个数字无法回答"钱花在哪个功能上"。
同一个 key 后面往往挂着好几种负载,它们的触发方式、调用次数和单次长度完全不同:
- 用户在聊天框里手动发一条消息,一次调用,短输入短输出。
- 后台摘要器定时批量处理历史内容,调用频繁但每次很小。
- 多步 Agent 由一次点击触发,却在后台连续调用几十次,中间还夹着工具返回的长文本。
把这三者混在一起,你会得到一个稳定增长的数字,却不知道是哪条链路在放大它。功能 A 慢了一倍、功能 B 从三步变成十步,总账单上看起来只是"这周涨了一点"。
把"一次调用"换成"一次功能触发"
要解决这个问题,思路是把记账单位从"一次 API 调用"换成"一次功能触发"(feature invocation)。
一个功能触发可以包含一次调用,也可以包含很多次。关键是在这个功能开始执行时生成一个标识,然后让它跟着后续每一次调用走。
这个标识通常需要两个部分:
- 功能名:稳定、可枚举,比如
chat、summarizer、agent-research。不要用自由文本,否则汇总时会裂成上百个变体。 - 触发 ID:一次具体触发的唯一值,比如一个 UUID 或业务侧已有的请求 ID。它用来把同一触发的多次调用重新聚合起来。
有了这两者,你就能同时回答两个层面的问题:某个功能整体花了多少,以及某一次具体触发内部是怎么分布的。
标签怎么传下去
聚合站通常兼容各家模型的接口,请求体结构以官方为准。所以打标签时,优先选择不依赖某个厂商专有字段的做法。
常见方式有两种:
- 附加元数据字段:如果所用接口支持透传自定义字段或请求头,把功能名和触发 ID 放进去。这是最干净的方案,因为它不污染提示词内容。
- 可解析的约定前缀:如果只能控制提示词,就把标识写成结构化前缀。但这会占用上下文,也可能影响输出,需要谨慎。
无论哪种方式,都要注意一点:标识必须随调用上下文传播。在多步 Agent 里,这意味着工具调用、重试、并行分支都要继承同一个触发 ID,而不是每次调用重新生成。
常见的漏打点位置包括:
- 失败后的重试调用。
- Agent 内部的"思考—行动"循环。
- 流式响应中因超时被截断后重新发起的请求。
- 摘要器批处理里逐条拆分的子调用。
汇总时看什么
拿到带标签的调用记录后,可以先做最基础的聚合,再逐层细化。
按功能名汇总,得到每个功能的总消耗和调用次数。这一步就能暴露很多问题:某个功能的调用次数远高于预期,或者平均单次消耗突然抬升。
按触发 ID 汇总,得到每次触发的调用次数和总消耗。这一步对 Agent 类功能尤其有用——你能看到"一次点击"实际等于多少次模型调用。
把两者结合,就能算出更有意义的指标,比如:
- 每个功能的单次触发平均成本。
- 每个功能的成本在总成本中的占比。
- 同一功能在不同时间段的触发成本变化。
这些指标是相对量,不依赖具体单价,所以即使费用规则调整,历史对比依然成立。
归因到按钮之后能做什么
当成本能稳定归到功能上,很多之前说不清的事情就变得可讨论。
容量规划:知道聊天框和摘要器各自占多少,才能判断增长来自用户量还是后台任务量。
迭代决策:Agent 从三步变成六步,成本翻倍是设计代价还是实现问题,只有拆开才看得见。
对外展示:如果产品要给用户显示用量或额度,按功能拆分的口径比一个黑盒总数更容易解释。
异常发现:某个触发 ID 的调用次数异常高,往往意味着循环没有正确终止,而不是模型变贵了。
落地时的几个提醒
标签体系要尽早统一,但不必一次做完。可以从最贵的那个功能开始,先让它的成本可见,再逐步覆盖其他功能。
功能名保持粗粒度。chat 和 chat-v2-mobile 这种命名会在几个月后变成负担,除非确实需要区分。
注意标签本身的隐私边界。触发 ID 最好是内部生成的随机值,而不是用户 ID、会话内容或其他敏感标识。
最后,把标签当作可观测性的一部分,而不是财务流程的一部分。它的价值在于让工程决策有据可依,而不只是月末对账。