云计算百科
云计算领域专业知识百科平台

Token预算与调用成本建模

文章编号:article_411

本文是《可靠LLM应用工程》系列的实操开篇。上一篇“系列导读”交代了整体框架与目标,本文落到第一个具体切口:在调用大模型之前,先把 token 数量与调用成本估算清楚,并用预算护栏在付费动作发生前完成拦截。下一篇将进入“提示模板的版本化设计”。

问题背景

大语言模型按 token 计费,输入与输出通常执行两套单价,输出单价往往比输入高数倍,因为生成阶段是逐 token 的自回归解码,算力更贵。很多项目直到月底对账才发现成本失控,而事后复盘几乎总能归到三个原因:一是并发请求放大估算误差,单条调用看着便宜,乘以每天几十万次调用就变成大额;二是 max_tokens 设置过大,让单次成本虚高;三是预算只做事后统计、不做调用前拦截,等报警时钱已经花出去了。可靠工程的做法,是把“估算、报价、预留、拒绝”固化成可复用的建模函数,让成本在每一笔调用发生之前就可见、可控。

核心原理

成本建模的核心只有两个公式。单次成本等于输入 token 数乘以输入单价,再加上输出 token 数乘以输出单价。输入 token 数可以用经验折算近似:一个中文字符约占 1 个 token,英文与其他符号约每 4 个字符占 1 个 token,结果向上取整。举一个手算的例子:假设某条提示含 120 个中文字符和 40 个英文字符及符号,那么输入 token 约为 120 加 10,即 130;若输出上限设为 200,输入单价 0.015 元/千 token、输出单价 0.060 元/千 token,则单次成本约为 130×0.015/1000 + 200×0.060/1000 = 0.00195 + 0.012 = 0.01395 元。输出侧因为无法预知模型实际生成多少内容,工程上习惯用 max_tokens 上限做悲观估算,宁可高估、不做乐观假设。需要强调,这套折算是预算规划用的近似值,不是精确分词结果;若要精确计数,必须接入具体模型配套的 tokenizer,那是另一个独立话题,本文不展开。

第一次代码实验及输出

先用标准库实现一个最小可用的估算器,观察中英文样本的差异。

from math import ceil
from statistics import mean, median

def count_tokens(text: str) > int:
chinese = sum(1 for ch in text if "\\u4e00" <= ch <= "\\u9fff")
other = len(text) chinese
return chinese + ceil(other / 4)

samples = [
"请用三句话总结这篇文章的要点。",
"Summarize the key points in three sentences.",
"把以下日志翻译成中文并解释错误原因:Connection timeout after 30s",
"列出五个可落地的性能优化建议,并说明优先级。",
]
tokens = [count_tokens(s) for s in samples]
print("每条样本token数:", tokens)
print("平均:", mean(tokens))
print("中位数:", median(tokens))
print("最大:", max(tokens))
print("预留20%余量的单条预算:", ceil(median(tokens) * 1.2))

运行输出:

每条样本token数: [15, 11, 25, 21]
平均: 18.0
中位数: 18.0
最大: 25
预留20%余量的单条预算: 22

从输出可以看到:中文样本的 token 数接近字符数;纯英文样本按 4 字符折算后只有 11;混合样本同时计入中文字符与英文折算。做单条预算时,中位数比平均值更抗极端样本的影响,再预留 20% 余量,就把基准从 18 抬到 22,给边界请求留出空间。这个“中位数加余量”的取值方式,会在第二次实验中直接复用。

工程化改进

上面的实验只有估算,还没有把钱和预算串起来。工程化需要补三点。第一,金额统一用 Decimal 而不是 float。float 是二进制浮点数,0.1 加 0.2 在二进制下并不是精确的 0.3,长链路累加会出现几分钱的漂移;金额账本必须用 Decimal 这类十进制表示。第二,预算做成有状态对象,每笔请求先报价、通过校验才预留额度,而不是事后累加,这样护栏在付费动作发生前生效。第三,输出 token 数用 max_tokens 做悲观上限,把生成长度的不确定性折算进成本,而不是忽略。三点合起来,才能把成本从“事后统计”升级为“事前控制”。

第二次代码实验及输出

下面把 Pricing 与 Budget 建模成 dataclass,用 Decimal 完成报价与预留。Budget 维护总额与已预留两个字段,available 方法实时返回剩余额度。

from dataclasses import dataclass, field
from decimal import Decimal
from math import ceil

@dataclass(frozen=True)
class Pricing:
input_per_1k: Decimal
output_per_1k: Decimal

@dataclass
class Budget:
total: Decimal
reserved: Decimal = field(default=Decimal("0"))
def available(self) > Decimal:
return self.total self.reserved

def count_tokens(text: str) > int:
chinese = sum(1 for ch in text if "\\u4e00" <= ch <= "\\u9fff")
other = len(text) chinese
return chinese + ceil(other / 4)

pricing = Pricing(Decimal("0.015"), Decimal("0.060"))
budget = Budget(Decimal("0.100"))
requests = [
("请用三句话总结这篇文章的要点。", 200),
("把以下日志翻译成中文并解释错误原因:Connection timeout after 30s", 500),
("写一篇关于缓存策略的短文,并给出代码示例。", 1000),
]
for prompt, max_output in requests:
in_tokens = count_tokens(prompt)
cost = pricing.input_per_1k * in_tokens / 1000 + pricing.output_per_1k * max_output / 1000
if cost <= budget.available():
budget.reserved += cost
print(f"通过 输入={in_tokens} 输出={max_output} 成本={cost} 已预留={budget.reserved}")
else:
print(f"拒绝 成本={cost} 超过可用预算={budget.available()}")
print("剩余预算:", budget.available())

运行输出:

通过 输入=15 输出=200 成本=0.012225 已预留=0.012225
通过 输入=25 输出=500 成本=0.030375 已预留=0.042600
拒绝 成本=0.060300 超过可用预算=0.057400
剩余预算: 0.057400

前两条请求通过并累计预留,已预留从 0.012225 涨到 0.042600;第三条成本 0.060300 超过剩余可用预算 0.057400,在调用前被拒绝,剩余预算保持 0.057400 不变。这就是预算护栏的价值:拦截发生在付费动作之前,而不是事后再对账。真实系统里,被拒绝的请求可以降级为更短的输出上限,或者转人工队列,而不是硬着头皮调用。

常见陷阱

第一,只估算输入、忽略输出,或反过来,都会留下预算缺口。第二,用 float 累加金额,长链路下会出现几分钱的漂移,金额越大、调用越多越明显。第三,把经验折算当成精确值,忽略不同 tokenizer 的差异,可能导致系统性低估,实际账单比预算高出一截。第四,max_tokens 设置过大,成本预算随之虚高,反过来又逼高预留额度,挤压真正的可用预算。第五,预算不留安全余量,边界请求会直接穿透护栏,让“护栏”名存实亡。

落地清单

建立 Pricing 与 Budget 两个数据模型;金额统一使用 Decimal 并写成常量表;每条请求严格遵循报价、校验、预留的顺序;单条预算取中位数加安全余量,而不是平均值;为预算护栏编写单元测试,至少覆盖通过、拒绝与边界三种情况。

参考来源

  • Python 官方文档 decimal 模块:https://docs.python.org/zh-cn/3/library/decimal.html
  • Python 官方文档 statistics 模块:https://docs.python.org/zh-cn/3/library/statistics.html
  • Python 官方文档 dataclasses 模块:https://docs.python.org/zh-cn/3/library/dataclasses.html

👍 觉得有用就点个 赞 + 收藏,方便回头查阅;有疑问直接在评论区留言,我看到都会回。

🚀 本文属于 《可靠LLM应用工程》 系列,持续更新,关注不迷路。

📌 文章里的代码都能直接跑。想要可直接 clone 的完整工程 + 配套部署脚本 / 踩坑清单?评论一声或发邮件到 cj2664@qq.com,我免费发你。
如果你正好在做类似系统、或有工程化难题想找人做,也欢迎邮件聊一句——我按实际情况评估,能落地的就接单或出方案。评论和邮件都能直接找到我,不用跳别的平台。

赞(0)
未经允许不得转载:网硕互联帮助中心 » Token预算与调用成本建模
分享到: 更多 (0)

评论 抢沙发

评论前必须登录!