如果由代理决定要发起多少次 API 调用,那么你的成本上限就是代理当天的心情。大多数时候这没问题。可当某个工具出错,链条开始循环重试时,情况就不同了,而在这一切发生时,你的日志里不会出现任何异常。
我维护着 budget-guard,一个用于限制 LLM API 账单的小型开源断路器。它提供了 LangChain.js 适配器,整个集成只需一个回调处理器:
import { ChatOpenAI } from '@langchain/openai';
import { BudgetGuardHandler } from 'budget-guard/langchain';
const handler = new BudgetGuardHandler({
project: 'my-app',
dailyCapUSD: 25,
model: 'gpt-4o',
feature: 'support-bot',
});
const model = new ChatOpenAI({ model: 'gpt-4o' });
await model.invoke(messages, { callbacks: [handler] });
Enter fullscreen mode Exit fullscreen mode
以上就是全部的设置。在每次模型调用之前,处理器会检查该项目当天的花费。如果在限额内,调用就会执行,其真实的 token 用量会被换算成美元并加入累计总额。若超出限额,它会在请求离开你的进程前抛出一个 BudgetExceededError,因此失控的循环会在第一次被阻断的调用上终止,而不是第一百次。
以下是一些实际应用中需要注意的细节。
上限的单位是美元,而不是 token。 自从缓存输入和推理 token 有了各自的价格后,token 就不再是一个有用的计量单位。单个响应中的一个“token”可能按四种不同的费率计费。该处理器会读取 usage_metadata(在旧代码路径上回退到 llmOutput.tokenUsage),区分缓存输入和非缓存输入,统计由提供商单独计费的推理 token,并对每一部分进行计价。
feature 标签是我不会跳过的部分。 单一的每日总额只能告诉你存在问题。而按功能细分的明细则能告诉你问题出在哪里。在我的案例中,泄露来自一个几周都没人再想到的数据丰富任务,它悄无声息地占到了总花费的 60%。
阻断之所以有效,是因为处理器在内部设置了 raiseError。 LangChain 默认会吞掉回调中的错误,这会让上限变成一个礼貌性的建议。这里无需任何配置,但值得了解为什么抛出错误确实能停止链条。
默认情况下它是进程本地的。 默认的存储在进程重启时会重置。有一个 Redis 存储可供整个 worker 集群共享同一个上限(带有原子的“先预留后结算”路径,因此一百个并发调用不会一起突破限制),还有一个文件存储,适用于仅存活几秒的 cron 任务。
而它故意不做的事:它不是一个网关,没有仪表盘,也看不到不经过它的调用。它是你保险盒里的断路器,而不是电力公司。如果你已经在运行一个完整的 LLM 可观测性平台,你可能不需要这个。如果你只有一个 Node 应用,并且对你的用量页面感到紧张,那么只需 npm i budget-guard 并加上上面的五行代码即可。
促成构建它的经验教训记录在之前的文章中:7 things I learned trying to stop LLM API bills from silently exploding。
0 Comments
Log in to join the conversation.No comments yet. Be the first to share your thoughts.