langchain 成本追踪

成本与用量

给用 LangChain 搭应用的开发者一套可落地的成本追踪写法:把每次调用的花费记到日志里,能查、能对账、能定位是哪个环节在烧钱。

用 LangChain 跑起来不难,难的是月底想知道钱花哪了。链条里一层套一层,模型调用、工具调用、重试混在一起,回调里拿到的用量信息各家用得还不一样。这篇给一套能直接改的追踪思路:在哪挂回调、字段怎么存、多出来的开销怎么归因。

用 LangChain 把应用跑起来不难,难的是月底回头看账单,发现钱花出去了但不知道花在哪。链条里一层套一层,模型调用、工具调用、重试、缓存混在一起,回调里拿到的用量字段各家给得还不一样,有人甚至连输出 token 都没拿到就写进了统计。

这篇给一套能直接改的追踪思路:回调挂在哪、每次调用该存什么、怎么把用量换成钱、最后怎么和平台账单对上。目标是让每一次调用都变成日志里的一行可查记录,而不是等出账了才开始猜。

追踪点挂在哪儿:回调是唯一稳的位置

优先用框架自带的回调机制,在模型开始和结束的钩子里取用量,不要跑到业务代码里到处手动加计数器。手动埋点漏一处,整条链的账就偏了,而且后面加新功能时很容易忘记补上。回调的好处是无论你从哪个入口调模型,只要走了框架,账就会自动落下来。

链式嵌套时要特别注意父子关系。一次上层链跑完,底下可能触发了好几次模型调用,统计单位要按「单次调用」记,上层链再按自己的 ID 聚合。如果直接在上层记总数,中间某次调用失败重试,次数和金额都会对不上,排查时你会不知道是哪一层多出来的。

  • 流式输出场景下,用量往往在结束事件里才给全,别在流式中间急着写账,容易写到一半的数字;
  • 想统一入口也可以自己包一层函数,但要确保所有调用都走这个入口,绕过它的地方就是账目黑洞;
  • 重试要单独记一行,不要覆盖原记录,否则重试造成的翻倍永远查不出来;
  • 回调里拿不到用量的情况要先兜底记一条「用量缺失」,而不是直接跳过,缺失本身也是信号。

每次调用该记哪些字段:一张最小清单

字段设计决定了后面能不能查。基础项至少要有时间、调用方、模型标识、输入用量、输出用量、是否命中缓存、耗时。调用方要细化到业务模块、用户或任务 ID 这一级,不然聚合视图只能看总数,看不出哪个功能在烧钱。

字段类别具体字段为什么必须记
基础项时间、调用方、模型标识、输入/输出用量、耗时没有这些连一行记录都不成立
缓存项是否命中缓存、缓存读/写用量缓存命中和未命中的计价口径不同
归因项所属链/任务、第几次尝试重试要单独成行,不能覆盖原记录
成本项单价、币种、汇率、计算后金额单价和用量分开存,价格变了才能回算历史

工具调用和嵌入调用也要各记一行,它们经常被漏掉但占比不低。工具调用是否计费、按什么单位计费,各家口径不一样,到站内的 /stacks/frameworks 对照框架清单时,顺便确认一下你用的那套工具链有没有额外计费项。单价这类会变的数字,到 /official-api 核对当时的数字后再写进配置。

把用量换算成钱:别在代码里写死价格

单价放配置或数据库,代码只负责「用量 × 单价」。写死在代码里的价格是最常见的账目失真来源,价格一调,历史账目全部错位。输入和输出单价通常不同,缓存命中的读、写又各有一套口径,这四类要分开累加,别用一个平均价糊过去,否则对账时差额从哪来都说不清。

  1. 先确定计价口径到 /tools/model-cost-calculator 和 /tools/token-price-calculator 对着算一遍,确认输入、输出、缓存读、缓存写各是什么单位,再决定自己代码里怎么拆字段。
  2. 把单价抽成配置单价、币种、汇率都放进配置或数据库,代码只做乘法;每次价格调整只改一处,历史记录里的单价字段保持原样不动。
  3. 多币种单独记汇率和币种也要落到每条记录上,过段时间回看时,才知道那笔账当时按什么价算的。
  4. 写一条校验规则对每条记录断言「用量不为空」和「单价不为空」,缺任何一个就打日志告警,别等月底才发现某类调用一直没记上。

聚合、对账与排查:从日志回到账单

日志攒起来之后,至少做三张聚合视图:按任务、按模型、按天。哪张视图对不上,就说明那个维度的归因字段缺失。比如按天对得上、按任务对不上,多半是某些调用没记任务 ID;按模型对不上,多半是某类模型调用没走统一入口。

和平台账单对账时,差额通常来自三类:重试没单独记、缓存命中口径不一致、工具调用是否计费没算清。按这个顺序排查最快,因为重试造成的重复计费最容易在自家日志里先发现,缓存和工具计费则往往要回头查平台说明。想先跑一轮数据链路是否通畅,也可以用站内的 /tools/speed-test 检查连通性,避免把网络问题误判成账目问题。

  • 发现某条链成本异常,先看调用次数再看待办量:是次数涨了还是单次变贵了,两个方向的修法完全不同;
  • 日志字段要留可扩展位,将来接新模型或新的计价单位时不用改表结构;
  • 对账差额超过预期时,先把重试记录单独拉出来看,它是最常见的解释;
  • 聚合视图里保留未归因调用这一栏,它的比例应该越来越小,如果一直很大说明埋点有漏。
成本追踪不是月末算总账,而是每一次调用都留下能对账的一行。
下一步想先确认自己用的模型和计价口径,再去 /official-api 看当时的资料库,顺手用 /tools/model-cost-calculator 跑一遍换算,回来改自己的字段设计会更有底。

常见问题

LangChain 回调里拿到的 token 数和平台账单对不上,正常吗?

经常对不上。差异集中在三处:重试产生的重复调用框架可能只记一次;缓存命中的读写在两边的口径不同;流式输出提前中断时用量可能没回传。先把日志按「每次调用一行」拆开,再去 /usage 或平台账单核对总量,一般能定位到是哪一类。

一定要用回调吗,自己手动加计数行不行?

能用但容易漏。手动计数的问题在于只要有一个人加了新调用没带上计数,账就少一块,而且很难事后发现。回调的好处是挂一次就全覆盖,代价是要理解嵌套和流式事件的触发顺序,一次搞懂长期省事。

价格变了,历史记录怎么算?

别只存最终金额。把「用量」和「当时单价」分开存两列,价格调整后只影响新记录,历史记录用当时单价回算即可。如果只存了金额,价格一变历史数据就失去可比性,只能整体作废重来。

本文依据各平台公开文档与官方公告整理,只做中文归纳;具体做法、参数与限制以官方文档为准。