LLM API 越来越贵,别让 token 像自来水一样哗哗流
Posted on 五 08 5月 2026 in Journal
| Abstract | LLM API 越来越贵,别让 token 像自来水一样哗哗流 |
|---|---|
| Authors | Walter Fan |
| Category | Journal |
| Status | v1.0 |
| Updated | 2026-05-10 |
| License | CC-BY-NC-ND 4.0 |
短大纲
展开看看
- **核心观点**:省 token 不是少用 LLM,而是把 LLM 当成昂贵的计算资源来调度。 - **第一个动作**:先把 token 用量打点,别凭感觉优化。 - **模型分级**:小模型做分类、抽取、改写,大模型做推理、设计、复杂判断。 - **Prompt 瘦身**:固定规则放前面,变量放后面,删掉礼貌废话和重复上下文。 - **上下文控制**:RAG 不要把整本书塞给模型,只给当前问题真正需要的材料。 - **成本工具箱**:prompt caching、response cache、Batch API、输出长度限制、预算告警。 - **模式与反模式**:模型路由、预算盒、上下文漏斗是好模式;一把梭、自来水、资料倾倒是反模式。 - **落地清单**:一张能直接抄走的 token 成本自查表。正文
有一种账单,平时安安静静,月底突然跳出来给你一巴掌。
LLM API 就是这种账单。
刚开始大家都挺开心:"这个需求让 AI 写吧","这批文档让 AI 总结吧","这个工单让 AI 分类吧"。跑 Demo 的时候一切美好,效果不错,老板点头,同事鼓掌,连你自己都觉得生产力革命已经到门口了。
然后月底账单来了。你盯着那串数字,心里只剩一句话:这哪是 AI 助手,这是会说话的碎钞机。
问题不在于 LLM 不能用。恰恰相反,我觉得 LLM 该用,而且要用得更深。但它不是免费的魔法,也不是随便开的自来水。token 是一种工程资源,和 CPU、内存、带宽一样,需要度量、预算和治理。
一句话:不要为了省钱少用 AI,要为了做成事聪明地用 AI。
先弄清楚:token 到底花在哪里
很多团队一说降本,第一反应是换便宜模型。有用,但经常不是第一步。第一步该看账。
LLM API 的成本一般来自这几个地方:
| 成本来源 | 常见浪费 |
|---|---|
| Input tokens | system prompt 太长,历史消息无限追加,RAG 塞太多上下文 |
| Output tokens | 没限制回答长度,模型写成小作文 |
| Reasoning tokens | 简单任务用了强推理模型,杀鸡用牛刀 |
| Embedding tokens | 文档重复索引,chunk 切太碎,增量更新没做好 |
| Retry tokens | 超时重试、解析失败重试、Agent 循环调用 |
| Tool call tokens | 工具列表太多,每次都传完整 schema |
没有这些拆分,优化就是玄学。玄学降本的常见姿势是:今天换模型,明天改 prompt,后天禁止大家用。最后成本好像下来了,效果也一起下去了。
这就像看病不验血,直接让病人少吃饭。体重是降了,人也快没了。
第一步:给 token 上仪表盘
省 token 之前,先把 token 量出来。每次调用至少记录这些字段:
request_id
user_id / tenant_id
feature_name
model
prompt_tokens
completion_tokens
cached_tokens
reasoning_tokens
latency_ms
success / failure
retry_count
estimated_cost
created_at
然后按几个维度看:
按功能看:哪个 feature 最烧钱?
按用户看:是否有少数用户占了大头?
按模型看:大模型是否被滥用?
按失败看:失败重试吃掉了多少 token?
按时间看:批处理任务是否在高峰期挤占预算?
如果你用 OpenAI 这类 API,返回的 usage 字段里通常有 prompt tokens、completion tokens,有些模型还会返回 cached tokens 或 reasoning tokens。不要只把它当日志看,要把它当账本。
一个简单的成本日志长这样:
{
"feature": "ticket_summary",
"model": "gpt-4.1-mini",
"prompt_tokens": 1820,
"completion_tokens": 360,
"cached_tokens": 1024,
"latency_ms": 1280,
"retry_count": 0,
"estimated_cost_usd": 0.0031
}
别嫌麻烦。没有账本的系统,迟早靠拍脑袋治理。拍脑袋在工程里通常有个别名,叫事故预备役。
第二步:别拿大模型干所有活
LLM 不是越强越好,任务要和模型匹配。
我比较喜欢把任务分成四层:
| 任务类型 | 例子 | 模型选择 |
|---|---|---|
| 规则型 | 格式校验、字段映射、简单分类 | 尽量不用 LLM,用代码 |
| 轻语义 | 文本分类、关键词提取、短摘要、query 改写 | 小模型 |
| 中等理解 | 文档摘要、客服回复草稿、工单归因 | 中等模型 |
| 复杂推理 | 架构设计、故障分析、复杂代码 review、法律/财务风险判断 | 强模型 |
很多 token 浪费不是 prompt 太长,而是任务分配错了。
比如判断一句话是不是投诉,没必要上最强模型;抽取工单里的产品名,正则、词典、轻量分类器可能就够了。反过来,复杂事故复盘、跨文档推理、代码审查这类任务,硬上小模型省钱,最后会得到一堆"看起来差不多"的答案——最贵的不是大模型,最贵的是便宜模型给了错误答案,然后人再花半天返工。
如果你的模型网关把模型封装成类似下面这种命名,就可以直接拿来做路由规则:
xxx-{low|medium|high|xhigh}-[fast]
xxx-thinking-{low|medium|high|xhigh}
这里的 xxx 可以是 gpt,也可以是 claude-4.7-opus、claude-4.7-sonnect 这类模型族名称。先不纠结名字是否漂亮,关键是把它们当成不同的"计算档位",而不是一堆随手可点的下拉选项。
这里的 low / medium / high / xhigh 不是厂商标准,而是模型网关里的能力和成本分层。也有团队把 xhigh 叫 max,意思差不多:都是最高档。
| 档位 | 大致含义 | 典型用途 | 成本特征 |
|---|---|---|---|
low |
便宜、快、能力够用,但上下文理解和复杂推理有限 | 分类、抽取、格式转换、短文本改写 | 适合高频低风险任务 |
medium |
质量和成本比较均衡,适合做默认工作档 | query 改写、普通摘要、客服草稿、FAQ 生成 | 大多数日常任务先从这里试 |
high |
理解能力、稳定性和长上下文处理更好 | 长文档总结、复杂工单归因、代码解释 | 适合中高价值任务,要控制调用量 |
xhigh |
最高能力档,也可能叫 max |
架构评审、事故复盘、复杂代码 review、高风险决策 | 贵,应该有明确使用理由 |
| 模型形态 | 适合做什么 | 不适合做什么 | 成本提醒 |
|---|---|---|---|
xxx-low-fast |
格式检查、简单分类、短文本改写、标题生成 | 复杂推理、长文档总结、代码 review | 便宜、快,但别指望它懂太多上下文 |
xxx-medium-fast |
query 改写、FAQ 初稿、普通摘要、轻量客服回复 | 高风险判断、跨文档推理 | 很适合作为默认工作马,先从这里起步 |
xxx-high |
长文档总结、复杂工单归因、较复杂的代码解释 | 大批量低价值任务 | 质量更稳,但要配合 token budget |
xxx-xhigh |
架构设计、事故复盘、复杂代码 review、法律/财务等高风险分析 | 日常分类、字段抽取、模板化生成 | 贵,应该像生产变更一样有使用理由 |
xxx-thinking-low/medium |
需要一点推理的规划、分步分析、复杂 prompt 自检 | 简单问答、固定格式转换 | reasoning tokens 会额外烧钱,别默认开启 |
xxx-thinking-high/xhigh |
多约束决策、疑难故障分析、跨系统方案评审 | 高频在线请求、低风险批处理 | 适合"少量高价值问题",不适合当自来水 |
模型族也要分工。一个粗略但实用的判断是:
| 模型族 | 适合场景 | 使用建议 |
|---|---|---|
gpt-* |
通用问答、结构化输出、工具调用、批量自动化任务 | 适合作为默认通用模型族,配合 low/medium/high 做成本分层 |
claude-4.7-sonnect-* |
日常写作、总结、需求分析、代码解释、较长上下文处理 | 适合作为主力工作模型,质量和成本之间比较容易平衡 |
claude-4.7-opus-* |
复杂推理、架构评审、深度代码 review、重要文档润色 | 适合关键任务兜底,不建议所有请求都直接打到 Opus 档 |
一句话:先选任务档位,再选模型家族,最后才决定要不要 thinking。 顺序反了,就容易变成"这个模型最强,所以全都用它"。这在 Demo 阶段很爽,在账单阶段很疼。
我的建议是做一张模型路由表:
if task == "format_check":
use code
elif task in ["classify", "extract", "rewrite_query"]:
use small_model
elif task in ["summarize", "draft_reply"]:
use medium_model
else:
use strong_model
不是为了优雅,是为了可控。否则每个调用点都自由发挥,成本曲线会像青春期的孩子,长得快,还不听话。
第三步:Prompt 要减肥
很多 prompt 的问题不是写得不好,而是写得太胖。
常见肥胖来源:
- system prompt 里堆了十几条重复规则
- 每轮对话都带完整历史
- RAG 上下文里塞了大量无关段落
- 给模型讲太多背景故事
- 工具 schema 又长又多,每次全量发送
- 输出格式要求写了三遍,生怕模型看不见
Prompt 要像函数参数——能少传就少传,能结构化就结构化,能复用就复用。
一个瘦身例子
胖的写法:
你是一个专业、优秀、有经验的客服专家。
请你仔细阅读下面的大量背景资料,并结合用户的问题,
给出一个详尽、完整、专业、有帮助、语气友好的回答。
如果资料中没有答案,也请尽量根据你的经验回答。
......
瘦的写法:
角色:客服助手
规则:
1. 只基于参考资料回答
2. 资料不足时回答:"根据现有资料无法确认"
3. 输出不超过 200 字
4. 必须给出来源编号
参考资料:
{context}
用户问题:
{question}
少一点文学,多一点约束。模型不需要你夸它"专业优秀",它需要你告诉它边界在哪里。
固定内容放前面,变量放后面
如果服务商支持 prompt caching,prompt 的结构会直接影响成本和延迟。以 OpenAI 为例,prompt caching 更容易命中完全一致的前缀,所以静态内容应该放前面,用户问题、临时上下文这类变量放后面:
固定部分:
- 角色
- 输出格式
- 安全边界
- 示例
- 工具定义
变量部分:
- 用户问题
- 当前检索结果
- 当前会话状态
这件事看起来像小优化,流量一大就不小了。就像写代码时把循环里的常量挪出去,单次看不出什么,跑一百万次就知道差别了。
第四步:RAG 上下文别乱塞
RAG 是 token 消耗大户。
很多系统的思路是:怕模型答不出来,那就多塞点文档。结果模型像一个被塞了十本参考书的学生——书是都有了,人也懵了。
RAG 的核心不是"给模型更多内容",而是"只给模型当前问题需要的内容"。可以按这个顺序优化:
- 先粗召回:从向量索引、BM25、元数据过滤里找候选。
- 再重排序:用 reranker 把最相关的 3-5 个 chunk 排前面。
- 做去重和压缩:重复段落不要塞两遍,长段落先摘要。
- 保留引用信息:来源、章节、更新时间必须跟着 chunk 走。
- 按预算截断:超过 token budget 就丢弃低分内容,别平均主义。
一个上下文预算可以这样定:
total_context_budget = 6000 tokens
system_prompt: 1000
user_question: 200
retrieved_context: 3500
output_budget: 1000
reserve: 300
reserve 很重要。没有余量的系统,就像出门只带刚刚好的钱,路上多买一瓶水都尴尬。
第五步:限制输出,别让模型写散文
很多人盯着 input tokens,却忘了 output tokens 也要钱。
模型很听话。你让它"详细说明",它就详细;你让它"全面分析",它就全面;你让它"给出完整方案",它能给你写出一篇小论文。
所以输出也要有预算:
普通问答:100-300 字
工单摘要:5 条 bullet
代码解释:先给结论,再给不超过 3 个关键点
风险分析:高/中/低 + 证据 + 建议动作
长文生成:先生成大纲,确认后再展开
尤其是长文生成,不要一上来就让模型写全文。更稳的方式:
Step 1: 生成大纲
Step 2: 人或程序检查大纲
Step 3: 分章节生成
Step 4: 最后统一润色
这样不仅省 token,也更容易控制质量。一口气让模型写全文,像让一个实习生关进会议室写 20 页方案,中间不检查——出来以后你会发现他很努力,也很离题。
第六步:缓存,缓存,还是缓存
缓存是工程师的老朋友,到了 LLM 时代依然管用。
可以分三层:
| 缓存类型 | 缓存什么 | 适合场景 |
|---|---|---|
| Prompt cache | 固定 prompt 前缀、工具定义、示例 | 大量请求共享同一系统提示 |
| Response cache | 完整问题对应的答案 | FAQ、制度查询、高频问题 |
| Retrieval cache | query 对应的检索结果 | RAG 检索成本高,知识库变化不频繁 |
Response cache 要小心。缓存答案必须考虑:
- 用户权限是否相同?
- 知识库是否更新?
- 问题是否真的等价?
- 答案里是否包含个人信息或敏感信息?
千万不要把 A 用户的权限答案缓存后返回给 B 用户——那不是省钱,是给安全事故预热。
一个可用的缓存 key 通常要包含:
tenant_id
user_role / permission_scope
normalized_query
knowledge_base_version
prompt_version
model
缓存不是简单的 hash(question)。在企业系统里,权限和数据版本永远要放进设计里。
第七步:离线任务用 Batch,不要全走同步
有些任务不需要立即返回:
- 批量文档摘要
- 历史工单分类
- 离线评估集打分
- 大规模 embedding
- 每日知识库质量检查
这些如果全部走同步 API,不但成本高,还会挤占在线请求的配额。更合理的做法是用 Batch API 或类似的异步队列。
OpenAI 的 Batch API 文档明确写了,适合不需要立即响应的任务,成本更低,速率限制也独立。具体数字会随平台政策变化,真正用之前看最新文档,但思路不变:在线请求要快,离线任务要便宜。
这和后台任务不要挤占前台流量是一个道理。用户正在等回答,你却让离线摘要任务把额度吃满——这不叫智能系统,这叫内部抢饭。
第八步:管住 Agent 的手
Agent 很迷人,也很烧钱。
一个普通问答也许只调用一次模型;一个 Agent 可能这样干:
思考一次
调用搜索
再思考一次
调用工具
解析工具结果
发现不够
继续搜索
再调用模型总结
最后输出答案
每一步都在花 token。更麻烦的是,如果没有边界,它可能绕圈。
Agent 必须有护栏:
max_steps: 5
max_tool_calls: 3
max_total_tokens: 8000
max_wall_time: 10s
stop_when_confidence_high: true
fallback_to_human: true
还要记录每一步的 token 和工具调用。否则你只看到最终答案,不知道它在后台跑了一场马拉松。
我不反对 Agent——复杂任务里 Agent 很有价值。但 Agent 应该像实习生:有任务、有预算、有截止时间、有复盘。不能给它一张公司信用卡然后说"你看着办"。
第九步:把"不用 LLM"也当成一种能力
不是所有问题都需要 LLM。很多场景,传统方法更稳、更快、更便宜:
| 场景 | 更合适的方案 |
|---|---|
| 固定格式校验 | JSON Schema / 正则 / 代码 |
| 精确字段抽取 | Parser / 规则引擎 |
| 高并发 FAQ | 搜索 + 模板答案 |
| 权限判断 | 后端授权服务 |
| 金额计算 | 业务代码 |
| 审计记录 | 结构化日志 |
LLM 擅长语言理解、生成和模糊推理,不擅长当数据库、计算器和权限系统。把所有问题都扔给 LLM,就像家里买了个电钻,从此拧螺丝、切菜、刷牙都想用它。
工具好不好,看你怎么用。
尤其是那些重复、确定、不需要推理的常规任务,脚本通常比 LLM 更经济、更稳定,也更容易审计。LLM 每次回答都像请了个聪明外包,脚本则像一台自动售货机:投币、出货、少废话。
| 场景 | 更合适的做法 | 为什么别优先用 LLM |
|---|---|---|
| 去除个人敏感信息 | 用正则、NER、字段白名单做脱敏,比如邮箱、手机号、身份证、IP、access token | 规则清晰,必须稳定;LLM 漏掉一次就是事故 |
| 政治正确 / 合规敏感词 | 用词库、Trie、Aho-Corasick、规则引擎做匹配,再配人工复核 | 需要可解释、可回溯、可配置;LLM 判断会漂 |
| 模板生成文件 | 用 Jinja2、Handlebars、Mustache 这类模板引擎生成配置、报告、代码骨架 |
输入输出结构固定,用 LLM 反而可能改坏格式 |
| 批量字段转换 | 用脚本、SQL、ETL、JSON Schema 校验 | 成本低,结果可重复,失败原因清楚 |
| 固定业务判断 | 用业务规则或决策表,比如金额区间、权限开关、状态流转 | 这是系统逻辑,不该交给概率模型临场发挥 |
我的经验法则是:如果规则能写清楚、输入输出能定义、失败后要追责,就先写脚本。 LLM 更适合处理模糊语言、复杂上下文和开放式推理,不要让它去抢正则表达式和模板引擎的饭碗。
最佳实践、常见错误与反模式速查
前面讲的东西不少,这里收束成三张表。做设计评审的时候拿出来对一遍比看完全文管用。
最佳实践
| 做法 | 说明 |
|---|---|
| 先度量,再优化 | 记录 prompt_tokens、completion_tokens、cached_tokens、latency_ms、estimated_cost,按 feature 归因 |
| 任务分级用模型 | 规则能解决的不用 LLM,小模型做抽取和分类,大模型做复杂推理 |
| Prompt 模板化与版本化 | 每个 prompt 有版本号,方便 A/B 测试成本和效果 |
| 固定前缀 + 动态后缀 | 固定规则放前面,变量放后面,利于 prompt caching |
| 限制输出长度 | 明确字数、结构、字段,不让模型自由发挥 |
| RAG 上下文预算化 | 只传 top-k 高质量 chunk,做去重、rerank 和截断 |
| 离线任务走 Batch | 摘要、分类、评估、embedding 等不急的任务,不要挤在线 API |
| 缓存带权限边界 | cache key 必须包含 tenant、role、knowledge version,避免串数据 |
常见错误
| 错误 | 后果 |
|---|---|
| 没有 token 账本 | 月底才知道钱花哪了,排查像考古 |
| 所有任务都上大模型 | 分类、抽取、格式化也用强模型,典型"杀鸡用牛刀" |
| 历史消息无限追加 | 对话越聊越贵,最后模型也迷路 |
| RAG 什么都塞 | 以为上下文越多越好,噪声把答案淹了 |
| 不限制输出 | 一句"详细分析"换来一篇收费小论文 |
| Agent 没有步数限制 | 工具调用绕圈,token 在后台悄悄烧 |
| 缓存只按 question hash | 忽略权限、租户、知识库版本,省钱省出安全事故 |
| 只看单次调用成本 | 忽略重试、失败、批量任务和工具调用的链式成本 |
模式 vs 反模式
| 好模式 | 坏模式 |
|---|---|
| 模型路由:按任务难度选模型 | 一把梭:所有请求打最强模型 |
| 预算盒:每个功能有 token budget | 自来水:调用点想用多少用多少 |
| 上下文漏斗:召回、重排、压缩后再喂模型 | 资料倾倒:整本知识库塞进 prompt |
| 缓存分层:prompt、retrieval、response 分别缓存 | 裸奔:同样问题每次重新算 |
| 人机分工:规则、搜索、LLM 各做擅长的事 | LLM 万能:数据库、计算器、权限判断都交给模型 |
| 离线批处理:不急的任务异步跑 | 在线堵车:批量任务和用户请求抢额度 |
| Agent 护栏:限制步数、工具调用和总 token | Agent 放羊:让它自己"看着办" |
| 版本实验:prompt/model 变更可比较 | 拍脑袋:改了不知道效果变好还是变坏 |
这张表的价值不在于背下来,而在于做设计评审时能拿出来问一句:咱们现在是在用模式,还是在制造反模式?
一张可以抄走的 token 成本检查表
上线前拿这张表过一遍:
| 检查项 | 问题 |
|---|---|
| 用量打点 | 是否记录 prompt、completion、cached、reasoning tokens? |
| 成本归因 | 能否按 feature、tenant、user、model 看成本? |
| 模型路由 | 简单任务是否用了小模型或规则代码? |
| Prompt 版本 | prompt 有没有版本号,方便比较成本和效果? |
| Prompt 结构 | 静态内容放前面了吗,变量放后面了吗? |
| 历史消息 | 是否限制对话历史长度,做了摘要压缩? |
| RAG 上下文 | 有没有 top-k、rerank、去重、截断和来源信息? |
| 输出预算 | 是否限制 max output tokens 和回答格式? |
| 缓存策略 | 是否区分 prompt cache、response cache、retrieval cache? |
| 权限隔离 | 缓存 key 包含 tenant、role、数据版本了吗? |
| Batch 任务 | 离线任务走异步或批处理了吗? |
| Agent 护栏 | 限制了 max steps、tool calls、total tokens 吗? |
| 重试策略 | 解析失败是否无限重试?有没有退避和上限? |
| 预算告警 | 成本异常时能及时发现吗? |
| 敏感数据 | 是否避免把 secrets、token、隐私数据发给模型? |
明天就能做的五件小事
如果你还没时间搭一套完整治理体系,先做五件小事:
- 给所有 LLM 调用加上 usage 日志,至少能按 feature 聚合。
- 把最贵的 10 个 prompt 打印出来,人工砍掉废话和重复上下文。
- 给每个调用点标注任务类型:规则、小模型、中模型、强模型。
- 给 RAG 设一个上下文 budget,不允许无限塞 chunk。
- 给 Agent 加上
max_steps、max_tool_calls和max_total_tokens。
这些事情不花哨,但能马上见效。很多成本不是被大模型吃掉的,是被"没人管"吃掉的。
总结
LLM API 贵不贵?贵。值不值得用?当然值得。
关键是别把它当许愿池——许愿池里丢硬币,响一下就没了;LLM API 里丢 token,响得更小,账单更大。
靠谱的做法是把 LLM 当成一套工程系统来管:度量、预算、路由、缓存、降级、安全边界、持续评估,一个都不能少。
好钢用在刀刃上。token 也是。
思维导图
@startmindmap
* LLM API 成本控制
** 先度量
*** usage 日志
*** feature / tenant / model 归因
*** 成本告警
** 选对模型
*** 规则优先
*** 小模型处理轻任务
*** 强模型处理复杂推理
** Prompt 瘦身
*** 删除废话
*** 固定前缀
*** 变量后置
*** 限制输出
** 控制上下文
*** RAG top-k
*** Rerank
*** 去重压缩
*** token budget
** 复用结果
*** Prompt cache
*** Response cache
*** Retrieval cache
** 异步处理
*** Batch API
*** 离线评估
*** 批量摘要
** 模式与反模式
*** 模型路由 vs 一把梭
*** 预算盒 vs 自来水
*** 上下文漏斗 vs 资料倾倒
*** Agent 护栏 vs Agent 放羊
** 管住 Agent
*** max steps
*** max tool calls
*** max total tokens
*** fallback to human
** 安全边界
*** 不上传 secrets
*** 权限进入 cache key
*** 敏感数据脱敏
@endmindmap

扩展阅读
本作品采用知识共享署名-非商业性使用-禁止演绎 4.0 国际许可协议进行许可。