这次我们来看一个正在快速扩散的技术趋势:Tokenmaxxing 退场,AI 应用进入成本收紧期。
Tokenmaxxing 不是什么开箱即用的开源项目,而是过去一年里很多 LLM 应用团队都踩过的开发习惯:能挂多长的上下文就挂多长,能调多大模型就调多大模型,能多开几轮 Agent 循环就多开几轮。表面上效果不错,等到账单出来才发现,单次体验的成本早就把利润吃光了。现在行业明显在往另一个方向走:按 token 算经济账,做瘦身、缓存、路由和降级。
这篇文章适合正在做 LLM 应用、Agent 工作流、RAG 服务的开发者阅读。我会先把 Tokenmaxxing 的特征和死因讲清楚,再给出一套从评估指标到落地动作的成本收紧实践路径,包含模型路由、上下文裁剪、缓存设计、批量任务治理和效果回归方法。文章里的命令和代码都是通用模板,你需要按自己的模型服务地址、接口格式和业务场景调整。
1. Tokenmaxxing 是什么,为什么走到头了
1.1 Tokenmaxxing 的三个典型特征
Tokenmaxxing 这个词在 AI 开发社区指一种高消耗用法:把大量上下文、大量工具调用、大量模型调用叠在一起,换取一点点效果上限。典型表现有三个:
第一,上下文能拉满就拉满。做文档问答时,不先做检索,而是把整本手册、全部聊天记录、全部历史结果一起塞进 prompt,指望模型自己找重点。长上下文模型越来越强,这种方式确实能跑通,但每次请求的 token 成本会随着输入长度线性上升。
第二,大事小事都叫大模型。简单分类、关键词提取、格式清洗这类完全可以用规则或小模型完成的任务,也统一走大模型接口。效果差异很小,费用差异却很大。
第三,Agent 循环没有边界。一个简单任务启动多个 Agent,每个 Agent 都携带完整上下文,中间还要多次调用工具、多次重试。流程看起来完整,实际上大量 token 消耗在重复上下文和无效来回上。
1.2 死因不是模型不行,而是成本模型变了
Tokenmaxxing 之所以被认为“死了”,不是因为它不生效,而是因为它不经济。
当一个应用还在 demo 阶段,调用量小,优化省不了多少钱,多花 token 换效果完全合理。但进入生产环境后,情况完全不同:每个用户每天可能发起几十次请求,每次请求都带着越来越长的上下文,模型的输出 token 也在膨胀。再加上 Agent 循环里的多轮调用,成本是乘积式增长,而不是线性增长。
这个阶段,团队真正关心的指标已经变了:单次请求成本、月账单总额、单位转化成本、响应延迟、利润率。Tokenmaxxing 在这些指标面前没有竞争力。同样一个需求,检索后摘要 800 token 能解决,硬塞全文 8000 token 也能解决,效果可能只差 1%,成本却差 10 倍。谁会继续选后者?
1.3 用户侧和产品侧也在反向施压
用户端同样有感知:上下文越长,首 token 延迟越高,结果也越容易漂移。产品端则是预算有限,一旦广告投放和开发成本已经很高,留给模型调用的钱就更紧了。现在很多团队在立项时就会要求先写清楚“每次会话预期的 token 消耗”,再决定用什么模型、开多少轮循环、缓存怎么做。
所以,结论很清楚:Tokenmaxxing 作为探索阶段的试错方式是合格的,但作为生产环境默认策略是不合格的。接下来的技术动作,都围绕一件事情展开:在保住效果的前提下,把 token 消耗压下来。
2. 成本收紧期的核心能力速览
| 能力项 | 说明 |
|---|---|
| 核心目标 | 降低单次请求 token 消耗和总成本,同时控制效果退化幅度 |
| 主要手段 | 上下文裁剪、检索增强、模型路由、语义缓存、小模型兜底、输出约束 |
| 适用对象 | RAG 应用、Agent 工作流、客服对话、批量信息抽取、长文档总结 |
| 推荐硬件 | 纯 API 方案不依赖显卡;自托管小模型建议先按 8G 到 24G 显存规划 |
| 启动方式 | 代码库集成、API 网关 / 代理层、批处理脚本 |
| 是否支持 API | 是,所有优化动作都围绕模型 API 或本地推理服务展开 |
| 是否支持批量任务 | 是,批量场景收益最明显 |
| 关键风险 | 过度裁剪丢失关键信息、路由判断错误导致效果下降、缓存命中率低 |
| 适合读者 | LLM 应用后端开发者、算法工程师、技术负责人、运维 / 平台工程师 |
这里要说明一点:本文不会给出某个固定的显存占用数字。成本收紧的前半段工作是在 API 层完成的,不依赖本地显卡;后半段如果引入 7B、13B 甚至更小的自托管模型,显存需求才需要按实际模型版本测试。
3. 适用场景与使用边界
3.1 适合做成本收紧的场景
最值得优先投入的是高频、重复结构明显的请求。比如:
- 客服意图识别和答复生成:用户问题高度相似,语义缓存收益很直接。
- 文档问答和长文总结:大部分段落与用户问题无关,检索后只保留相关片段,能省下大量输入 token。
- 批量信息抽取:固定模板字段、固定输出格式,用规则先清洗,再让模型做抽取,比整篇塞给模型更稳。
- 多轮 Agent 任务:优化历史消息截断策略、工具结果裁剪策略,能明显减少每个任务的总 token。
- 内部效率工具:大量短文本改写、分类、翻译,模型路由可以自动分流到不同规格的模型上。
3.2 不适合做的场景
不是所有项目都该立刻压缩 token。下面这些场景要谨慎:
- 对准确率极其敏感的法律、医疗场景,裁剪和缓存可能引入风险,必须先做回归测试。
- 需要完整引用原文的长文档分析,不能只保留摘要,需要保留原文级引用。
- 复杂推理类 Agent,如果强行减少上下文,会导致关键信息丢失。
3.3 使用边界与合规提醒
做上下文裁剪和缓存时,要注意隐私和版权边界。用户对话、内部文档、医疗信息等敏感内容,不能因为要做缓存就明文存到公共存储里。缓存数据要按业务隔离,敏感字段做脱敏,并遵循最小化存储原则。涉及生成内容的发布或商用,还需要确认模型输出是否符合版权合规要求。
4. 实践前置条件与评估指标
成本收紧不是凭感觉删 prompt,而是先建立一套可量化的评估基线。没有基线,你无法判断优化是否有效。
4.1 建立评估样本集
准备一个固定的评测集合,至少覆盖高频问题、长文本问题、多轮对话、边界 case。每一条样本需要包含输入文本、期望输出、可接受的输出范围。这个集合不需要很大,几十条高质量样本就够用,但必须保持稳定,避免每次优化后结果无法对比。
4.2 关键指标
建议把下面的指标记录下来:
| 指标 | 说明 |
|---|---|
| 输入 token 数 | 每次请求的 prompt 长度,优化重点 |
| 输出 token 数 | 模型生成长度,通过 max_tokens 约束 |
| 单次请求成本 | 按模型单价计算 |
| 总延迟 | 首 token 延迟 + 生成延迟 |
| 命中率 / 准确率 | 业务指标,决定优化是否可用 |
| 缓存命中率 | 语义缓存是否有效 |
| 模型路由准确率 | 大小模型分流是否正确 |
| 重试率 | 因为超时、报错导致的重复调用 |
4.3 最小可运行脚本:记录 token 用量
如果项目还没有日志,可以先写一个最简版工具,把每次请求的 token 和耗时记下来。下面的代码是一个通用模板,你需要按实际调用方式调整函数名和返回字段。
import time import json def call_llm_with_log(messages, model="gpt-4o-mini", client=None): start = time.time() resp = client.chat.completions.create( model=model, messages=messages, max_tokens=512, ) cost_time = time.time() - start usage = resp.usage log_item = { "model": model, "input_tokens": usage.prompt_tokens, "output_tokens": usage.completion_tokens, "total_tokens": usage.total_tokens, "latency": round(cost_time, 3), "timestamp": int(start), } print(json.dumps(log_item, ensure_ascii=False)) return resp.choices[0].message.content日志先打出来,后面再接到 Prometheus、阿里云监控、日志服务或者你自己的统计表里。这一步本身不省成本,但它是所有后续优化的前提。
5. 成本收紧的技术落地路线
5.1 请求瘦身:裁剪 prompt 和上下文
第一个动作是减少输入 token。常见做法有三种。
第一种是固定指令精简。把 system prompt 从几百字压缩到几十字,删除重复说明和过多示例。不是所有示例都要保留,只留下负载最高的 1 到 2 个 few-shot。
第二种是历史对话截断。多轮对话里,不需要把全部历史都传给模型。只保留最近 N 轮,加上一轮关键信息摘要,就能维持大部分对话连贯性。
第三种是检索替换。这是 RAG 场景最核心的动作:不把所有文档塞进 prompt,而是先用检索召回 Top-K 片段,再将这些片段拼入上下文。检索策略可以先用关键词和向量混合召回,后续再用重排序模型精排。
下面是一个轻量级上下文裁剪函数示例:
def trim_messages(messages, recent_count=6, max_tokens=2048): """ messages: [{"role": "system", "content": "..."}, ...] 保留 system,再保留最近 recent_count 轮,超出长度则从更早部分截断。 """ system_msgs = [m for m in messages if m["role"] == "system"] history_msgs = [m for m in messages if m["role"] != "system"] recent = history_msgs[-recent_count:] trimmed = list(system_msgs) + recent total_chars = sum(len(m["content"]) for m in trimmed) while total_chars > max_tokens * 3: # 粗略估算:1 token 约等于 3 个中文字符 if len(trimmed) <= len(system_msgs) + 1: break trimmed.pop(len(system_msgs)) # 裁掉最早的一条非 system 消息 total_chars = sum(len(m["content"]) for m in trimmed) return trimmed这个函数只做通用演示,真实场景建议按业务调整截断策略,比如优先保留用户的最后一句话、工具结果的摘要部分。
5.2 模型路由:按任务难度分配模型
模型路由的目标是:简单任务走便宜的小模型,复杂任务才走大模型。路由可以在代码里写规则,也可以在网关层做。常见的判断维度包括:
- 输入长度:超长文本走长上下文模型,短文本走普通模型。
- 任务类型:分类、抽取、改写走小模型;代码生成、复杂推理走大模型。
- 关键词触发:涉及特定风险或特定格式时,升级到强模型。
- 用户等级:免费用户走默认模型,付费用户走高配模型,这是常见的商业策略。
下面的伪代码展示了基础路由思路:
def route_model(user_input: str, task_type: str) -> str: if len(user_input) > 6000: return "long-context-model" if task_type in ("extract", "classify", "rewrite"): return "cheap-small-model" if "代码" in user_input or task_type == "code": return "strong-model" if "合规" in user_input or "风险" in user_input: return "strong-model" return "default-model"路由要定期看日志:如果便宜模型的任务经常重试,说明路由阈值设置太激进;如果强模型承担了大量简单任务,说明路由不够细。
5.3 缓存优先:语义缓存和精确缓存
缓存是这次成本收紧里最直接的省钱手段。同一个问题被问 100 次,如果每次都重新调用模型,成本就是 100 倍;如果只调用一次,后续 99 次走缓存,成本就接近常数。
精确缓存最简单:完全一样的输入直接命中。实现上可以用字典、Redis 或外部存储。但现实里用户表达经常变化,所以需要语义缓存:将用户输入向量化,计算与历史问题的相似度,超过阈值时直接返回历史答案。
需要注意几点:
- 缓存 key 要不只包含用户输入,还要包含 system prompt、模型版本、温度参数。不同模型或不同参数的输出不能混用。
- 缓存要做时间过期,避免推荐、价格、库存类信息过期。
- 缓存结果要经过审核,至少对部分历史输出做人工抽检,防止有毒或错误答案被反复返回。
5.4 小模型与量化:把部分流量切到本地
如果你的服务有稳定、低复杂度、高并发的流量,可以考虑把一部分任务切到本地小模型,比如 7B、13B 级别的开源模型,或者量化后的更小模型。本地推理的边际成本低,隐私风险也可控,但部署运维成本会上升。
量化方式通常有 GGUF、AWQ、GPTQ 等,具体选择依推理框架而定。显存占用要以实际模型版本为准,建议先在测试机验证精度和延迟,再决定放多少流量过去。
典型做法是双轨:路由层先判断任务难度,简单任务调用本地小模型接口,复杂任务继续走云端大模型,形成兜底和分流。
5.5 长任务拆解与压缩
如果一个输入实在太长,比如一本几百页的 PDF,不要一次性全塞进模型。先拆成章节,分块处理,再逐级摘要合并。这个过程可以用一个简单管道:
文档分块 -> 每块摘要 -> 摘要合并 -> 最终输出中间每一步都可以显著降低单次请求的 token 数。分块大小需要测试,通常要保证块与块之间有少量重叠,避免关键信息被切断。
6. 成本削减验证方案
成本优化后的效果要能被验证,否则就是成本转移。下面给出一个可操作的验证矩阵。
6.1 测试步骤
- 先跑基线:使用原始 prompt、原始模型、原始上下文策略,记录准确率和成本。
- 再跑优化版:应用裁剪、路由、缓存、小模型分流后,跑同一份测试集。
- 对比四项指标:成本、延迟、准确率、缓存命中率。
- 如果准确率下降超过可接受范围,回退其中一项,单独验证。
6.2 损失判定标准
不是所有准确率下降都不可接受。建议提前定义三类结果:
- 通过:准确率下降在 1% 以内,且成本下降超过 30%。
- 复审:准确率下降在 1% 到 5%,需要抽样人工检查。
- 回退:准确率下降超过 5%,或出现严重安全 / 合规问题。
阈值需要按业务定,不要照搬。
6.3 成本对比表模板
| 方案 | 输入 token / 请求 | 输出 token / 请求 | 单次成本 | 延迟 | 准确率 | 结论 |
|---|---|---|---|---|---|---|
| 基线方案 | 测量值 | 测量值 | 测量值 | 测量值 | 测量值 | - |
| 裁剪方案 | 测量值 | 测量值 | 测量值 | 测量值 | 测量值 | - |
| 路由方案 | 测量值 | 测量值 | 测量值 | 测量值 | 测量值 | - |
| 缓存方案 | 测量值 | 测量值 | 测量值 | 测量值 | 测量值 | - |
实际数字必须从自己的日志里统计,不要拍脑袋填。
7. 接口 API 与批量任务的成本治理
7.1 批量任务:用集中处理器降低重复调用
批量信息抽取是成本重灾区。常见问题是:每个文件都会重复携带同样的提示词和背景说明,调用端还容易因为超时或限流重试,造成成本翻倍。
建议用一个批量处理器统一管理。它的职责包括:
- 预过滤:文件是否是空文件、是否已有处理结果,先跳过。
- 任务队列:控制并发,避免触发限流。
- 失败重试:只重试失败项,设置最大重试次数,并记录每次重试的 token 消耗。
- 输出校验:用 json 格式要求模型返回结构化内容,并对字段做基础校验。
下面是一个带重试和日志的批量调用模板:
import time import json from tenacity import retry, stop_after_attempt, wait_exponential @retry(stop=stop_after_attempt(3), wait=wait_exponential(min=1, max=10)) def call_model_once(client, messages, model): resp = client.chat.completions.create( model=model, messages=messages, response_format={"type": "json_object"}, ) usage = resp.usage print(json.dumps({ "model": model, "input_tokens": usage.prompt_tokens, "output_tokens": usage.completion_tokens, "cost_estimate_cents": round((usage.prompt_tokens / 1000000) * 0.15, 4), })) return resp.choices[0].message.content def process_batch(client, items): results = [] for item in items: try: messages = [ {"role": "system", "content": "你是信息抽取助手,输出 JSON。"}, {"role": "user", "content": item["text"]}, ] output = call_model_once(client, messages, model="cheap-small-model") results.append({"id": item["id"], "ok": True, "output": output}) except Exception as e: results.append({"id": item["id"], "ok": False, "error": str(e)}) return results这个模板中的调用方式和价格系数只是示例,实际需要按你的模型服务接口和定价调整。重点是设计思路:每个任务带 id、记录成功失败、失败可重试、成本可估算。
7.2 批量任务失败重试的成本控制
批量任务里最容易被忽视的是重试成本。一次超时重试,不仅浪费原请求的 token,还会产生新请求。如果任务本身是长输出任务,重试成本会更高。
建议做到三点:
- 限制重试次数,默认 2 到 3 次。
- 使用指数退避,避免重试风暴。
- 失败任务先落库,分析失败原因后再决定是否重跑,不要无限自动重试。
8. 资源占用与成本观测
成本收紧期的另一个重点是把成本可视化。没有观测,就没有优化。
8.1 为每次请求打标签
在调用日志里增加 project、task_type、user_tier、model 等标签。标签化以后,你才能知道某个业务线一个月花了多少钱,某个模型被谁大量调用。
一个通用记录结构如下:
log_item = { "trace_id": trace_id, "project": "customer_service", "task_type": "intent_cls", "model": "cheap-small-model", "provider": "local_or_cloud", "input_tokens": 1200, "output_tokens": 80, "latency_ms": 320, "cache_hit": False, "retry_count": 0, }8.2 成本统计的两种维度
按模型维度统计,能看出哪些模型吃掉了绝大部分成本。按业务维度统计,能看出哪个功能点最烧钱。这两张表都建议每周看一次。
如果发现某个模型的调用量异常增长,优先排查是否有循环任务或错误重试在反复触发调用。
8.3 显存与本地推理的观察方法
如果你部署了本地小模型,资源占用要单独观察。常见手段是 nvidia-smi 周期性采样,或者用 Prometheus node-exporter 配合 nvidia exporter 采集 GPU 指标。显存占用不是只看加载完模型的静态值,还要看并发推理时的动态峰值。
# 每 2 秒采样一次 GPU 显存和利用率 watch -n 2 nvidia-smi如果本地推理的 P99 延迟刚开始正常、后来越来越慢,要考虑显存换页、上下文长度增长、并发排队等因素。这些需要用实际压测确认。
9. 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 裁剪后效果明显下降 | 把关键背景信息裁掉了 | 对比裁剪前后日志,定位丢失的段落 | 缩短截断窗口,增加关键信息摘要 |
| 语义缓存命中率很低 | 向量化阈值过严或不相关 | 统计最近 1000 个请求的相似度分布 | 调整阈值或改用精确缓存兜底 |
| 路由到小模型后输出格式频繁出错 | 小模型指令遵循能力不足 | 查看小模型返回的 json 错误类型 | 降低分流比例,增加 few-shot 示例 |
| 量化后输出不稳定 | 量化精度损失 | 用同一测试集跑量化前后对比 | 换更高精度量化,或保留大模型兜底 |
| 批量任务失败后成本翻倍 | 自动重试次数过多 | 查看重试日志和同任务调用次数 | 限制重试次数,失败落库后人工处理 |
| 总延迟上升 | 本地推理排队或长上下文未裁剪 | 观察并发数和显存利用率 | 增加缓存、降低并发、压缩上下文 |
| 日志里 token 记录缺失 | 使用 SDK 未开启 usage 返回 | 检查调用参数和 SDK 文档 | 开启 usage_info 或从响应体解析用量 |
| 模型输出与原文不符 | 摘要合并阶段丢失细节 | 检查分块重叠率和摘要 prompt | 增加分块重叠,引用块时保留原文片段 |
10. 最佳实践与工程建议
10.1 先做一个数据驱动的试点
不要一上来就把全部业务切到省钱模式。先选一个高频、低成本、效果容易评估的业务做试点,比如客服意图识别或批量文本分类。记录基线,再逐步叠加裁剪、路由、缓存。试点跑两到三周,数据出来后再推广。
10.2 保留一条可回退的原始路径
成本优化很可能引入连锁问题。建议在路由层保留一个开关,紧急情况下可以一键把流量打回原模型和原 prompt。这个开关不需要很复杂,一个配置项加一个环境变量即可。
ENABLE_COST_SAVING = os.getenv("ENABLE_COST_SAVING", "true").lower() == "true" if ENABLE_COST_SAVING: messages = trim_messages(original_messages) model = route_model(user_input, task_type) else: messages = original_messages model = "strong-default-model"10.3 把成本压进代码评审里
建议在代码评审中增加一个检查项:新增的 LangChain / Agent 链路是否明确设定了最大 token、最大迭代次数、缓存策略、失败重试次数。很多成本问题不是在部署后爆发的,而是在 Agent 链路设计时就已经埋下了。
10.4 合规与安全优先级不要打折
成本收紧不能以降低隐私保护为代价。缓存命中前,要确认用户是否允许缓存;做日志统计时,要脱敏后再存储;自托管模型的数据处理要遵循业务所在地的数据合规要求。另外,凡是涉及人脸、声音、版权素材的功能,授权确认不要省。
10.5 定期做质量回归
成本优化完成后,不是一劳永逸。模型版本更新、prompt 调整、业务数据变化,都会影响优化效果。建议每两周跑一次评估集,把准确率、成本、缓存命中率拉出来对比,发现问题及时回退。
11. 总结
Tokenmaxxing 阶段的思路是“用更多 token 换更好的效果”,这是模型能力快速提升时期的自然选择。但现在生产环境更关心的是“用更少的 token 换足够的收益”,这要求我们把上下文裁剪、模型路由、缓存、小模型分流、批量治理这些工程手段组合起来使用。
最先验证的功能建议从指标采集开始。先把每次请求的 token、延迟、成本打出来,建立基线。随后上线 prompt 裁剪和精确缓存,这两项改动小、见效快、风险低。模型路由和小模型切换放到第二阶段,用评估集验证效果后再放量。
最容易踩的坑有两个:一是为了省钱把关键上下文裁掉,导致业务准确率跌破红线;二是批量任务的重试机制失控,省下的 token 被重试消耗掉。这两个问题都要靠日志和回归测试来控制。
后续可以继续扩展的方向包括:更精细的语义缓存、自动路由训练、针对小模型的指令微调、多级成本预算告警、更智能的 Agent 上下文管理。成本收紧期的本质不是拒绝大模型,而是让每一笔 token 都花在真正必要的位置上。