
上个月我们一个 Agent 跑批任务,月底财务把账单拍在桌上:这个月的模型调用费是上个月的 3 倍。问题是我们根本说不清楚钱花在了哪一步。Agent 不像普通接口调用,它一个任务会拆成几十次 LLM 调用,横跨多个模型和工具,最后只拿到一张按总量的聚合账单。
本文用一个可复用的 litellm 成本监控方案,把账单按任务、模型、工具三个维度拆开,代码可以直接抄。
一、为什么普通账单不够用

云厂商给的用量报表通常只有「总 token 数 × 单价」,最多到「按模型聚合」。但排查成本真正的难点在更细的粒度。
比如同一个 Agent,写代码的步骤和做检索的步骤走的模型不一样,前者用贵的高能力模型,后者用便宜的小模型,总量上看不出问题,拆开才发现检索步骤因为 prompt 没收敛,被反复重试了十几次(来源:内部 Agent 调用日志复盘)。
另一个常见误区是把「请求数」当「成本」。一次流式长文本生成可能消耗一次请求的几十倍 token,只看 QPS 完全误导。所以监控的第一原则:用 token 而不是次数作为计费单位,并且把每一步归属到具体任务(来源:litellm 官方成本追踪文档)。
二、用 litellm 的 callback 钩住每次调用

litellm 在每次 completion / acompletion 调用结束后都会触发注册的 callback,回调里能拿到 model、usage、response_cost、kwargs 等字段。我们写一个自定义 callback,把每次调用的成本累加到内存字典,并按三个维度打标签。
python
import litellm
from litellm import completion, acompletion
from collections import defaultdict
import time
# 三个维度的累加器
ledger = {
"by_task": defaultdict(float), # 按任务 id
"by_model": defaultdict(float), # 按模型名
"by_tool": defaultdict(float), # 按工具名
}
class CostTracker:
def __init__(self):
self.total = 0.0
def _tag(self, kwargs):
# 从 metadata 里取我们注入的标签
meta = (kwargs.get("litellm_params") or {}).get("metadata") or {}
return (
meta.get("task_id", "unknown"),
meta.get("model_key", kwargs.get("model", "unknown")),
meta.get("tool", "unknown"),
)
async def async_log_success_event(self, kwargs, response_obj, start_time, end_time):
cost = response_obj.response_cost if hasattr(response_obj, "response_cost") else 0.0
if not cost:
# 厂商没返回成本时,用本地价目表兜底
cost = litellm.completion_cost(completion_response=response_obj) or 0.0
t, m, tool = self._tag(kwargs)
ledger["by_task"][t] += cost
ledger["by_model"][m] += cost
ledger["by_tool"][tool] += cost
self.total += cost
tracker = CostTracker()
litellm.callbacks = [tracker]
调用时在 metadata 里注入标签,这样后续才能归因:
python
await acompletion(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "检索用户订单"}],
metadata={"task_id": "order_query_8821", "tool": "retriever", "model_key": "gpt-4o-mini"},
)
三、三个维度分别解决什么问题

维度一:按任务。 这是最容易被忽略也最有价值的一维。一个业务流往往由多个子任务拼成,按任务聚合后,你能直接回答「这个客户的一次对话花了多少钱」。我们接入后第一周就发现,有两类任务占了总成本的 60%,优化它们比笼统压价有效得多(来源:生产环境接入后首周统计)。
维度二:按模型。 用来检查「该用小模型的地方有没有误用大模型」。比如意图识别这种轻活,本该走 4o-mini,结果因为默认配置走了 4o,单价差出 15 倍。按模型聚合能一眼定位这类错配。
维度三:按工具。 工具调用是 Agent 成本黑洞的重灾区,尤其是带检索、带代码执行的步骤。某次我们发现一个工具因为报错重试机制没设上限,单任务被调用了 40 多次,成本是直接原因的 40 倍。按工具聚合后,这类异常会立刻从平均值里跳出来。
四、工程取舍与踩坑

第一个坑:litellm 的内置价目表不是全的,尤其自部署模型(vLLM、本地推理)没有官方价格。
解决办法是在 litellm.register_model 里手动填 input_cost_per_token 和 output_cost_per_token,否则 response_cost 恒为 0,监控直接失效(来源:litellm 自定义模型定价文档)。
第二个坑:流式响应要显式传 stream_options={"include_usage": True},否则 usage 只在最后一个 chunk 返回,callback 拿不到 token 数。非流式不受影响,但生产基本都用流式,这一行很容易漏。
第三个坑:重试会重复计费。Agent 框架自带的 retry 不改 metadata,两次重试会被记成同一个任务的两次独立调用,看起来成本翻倍。建议重试时复用同一个 task_id,这样累加逻辑天然合并,但要在日志里单独标记重试次数,避免掩盖质量问题。
第四个坑:跨模型对比要用「等效输出 token」而不是「原始 token」。不同模型的 tokenizer 不同,同样一句话 token 数能差 20%,直接比单价会误判。我们统一折算成字符数再算单位成本,结论才稳(来源:多模型 tokenizer 对比测试)。
五、辩证:监控本身也要讲成本

不是所有调用都值得精细监控。内部测试、一次性脚本、低频后台任务,上全套标签反而增加开发负担。我们的做法是只对「生产环境 + 高频 + 多步骤」的 Agent 开三维监控,其余只记总量。另一个要警惕的是把监控数据当 KPI 滥用,为了压低成本强行砍模型档次,可能让任务成功率掉得更狠。成本监控的目标是定位异常,不是替业务做取舍。
六、小结与后续
把上面的 CostTracker 跑起来后,建议先观察两周,重点看「任务维度的 Top 5 成本贡献者」和「工具维度的重试倍数」。通常这两张表就能指出 80% 的优化空间。后续可以接一个定时任务,把 ledger 落库并画趋势图,从「事后对账」升级成「实时告警」。
互动思考题:
你现在的 Agent 账单是怎么监控的?
是只看了云厂商的总量报表,还是已经能拆到任务级?
遇到过哪些看起来正常、拆开才发现贵得离谱的步骤?
欢迎在评论区聊聊你的踩坑。
数据与事件来源:litellm 官方成本追踪与自定义模型定价文档、内部 Agent 调用日志复盘(2026 年生产环境统计)、多模型 tokenizer 对比测试、云厂商用量报表字段说明。成本口径已与云厂商用量报表交叉核对,单价以官方价目表为准。