我们每次调用大模型 API 时,真正在花钱的并不是“输入多少字”,而是“输入多少个 token”。很多开发者把 Token 当作一个抽象概念,直到账单翻倍、上下文窗口报错、长对话突然截断,才开始回头审视提示词里到底塞了多少无效内容。
Token 优化不是让模型更聪明的手段,而是让每次请求更便宜、更快、更不容易出错的手段。同一套业务逻辑,提示词写得省一点,成本可以下降 30% 到 50%;上下文管理做得好一点,长文档处理不再需要开新会话重来;批量任务设计合理一点,一天的调用量能压缩到原来的三分之一。
这篇文章围绕《token优化手册》的核心思路,把 Token 的计数方式、提示词压缩、上下文管理、成本估算、批量调用、问题排查和最佳实践完整串起来。你可以把它当作一份可执行的优化清单,不需要额外安装复杂框架,也不需要改业务代码结构,只调整提示词组织方式和调用策略,就能直接看到效果。
1. 核心能力速览:Token 优化到底管什么
Token 优化并不是某一款软件,而是一套覆盖“拆解、压缩、管理、监控”四个环节的方法集合。先看清楚它解决的问题范围,再决定从哪里入手。
| 能力项 | 说明 |
|---|---|
| 优化对象 | 提示词、上下文历史、检索结果、模型输出约束 |
| 核心目标 | 降低单次请求 Token 占用量,提升上下文利用效率 |
| 适用模型 | OpenAI 系、Anthropic 系、Gemini、DeepSeek、本地开源模型等所有按 Token 计价的模型 |
| 前置条件 | 能查看 Token 计数结果即可,不需要专用服务器或显卡 |
| 主要手段 | 提示词压缩、结构化输出、滚动窗口、摘要压缩、检索增强、缓存复用 |
| 支持批量任务 | 是,批量任务里 Token 复用和失败重试的收益更明显 |
| 接口能力 | 可与 OpenAI 兼容接口配合,通常只需要改请求参数 |
| 直接收益 | 降低成本、减少超时、降低截断率、提升响应稳定性 |
| 适合人群 | 调用大模型 API 的开发者、提示词工程师、自动化脚本维护者、使用 AI 工具处理长文档的内容团队 |
本手册的重点不是概念科普,而是可落地的操作方式。下面每章都会从问题触发,然后给出具体的压缩或管理方案,最后提供验证方法。
2. 先理解 Token:计数方式、计费单位和窗口限制
2.1 Token 是语言模型的“最小识别单位”
Token 是语言模型处理文本时的最小离散单元。模型并不是逐字阅读你的提示词,而是先把文本切分成 Token 序列,再按序列预测输出。
英文文本中,一个 Token 通常对应 0.7 到 1 个单词。中文文本中,模型的分词差异较大,一般一个汉字约等于 1 到 2 个 Token,具体取决于使用的 Tokenizer。这也是为什么同样字数的提示词,中文请求的 Token 消耗通常比英文更高。
2.2 计费公式
几乎所有大模型平台的计费都是按总 Token 数计算:
单次请求费用 = 输入 Token 数 × 输入单价 + 输出 Token 数 × 输出单价很多平台输入和输出的单价不一致,输出 Token 通常更贵。因此,优化输出长度往往比压缩输入提示词更省钱,但压缩输入能减少上下文重新处理的时间,两者需要同时考虑。
2.3 上下文窗口不是“内存越大越好”
上下文窗口是模型一次能容纳的最大 Token 量。例如 128K 窗口,意味着输入和输出总共不能超过 128K Token。一旦超过,请求会直接报错;没有报错时,模型也会优先记住前后部分,中段内容容易被忽略。
所以,常见的长文本问题并不是“模型记不住”,而是“你一次性塞进去的 Token 太多,超过了注意力能覆盖的范围”。Token 优化要解决的正是这种超载场景。
上下文窗口 = 系统提示 + 历史对话 + 检索内容 + 工具定义 + 本次用户输入 + 最大输出 Token窗口是固定上限,减掉一个部分,其他部分才能有更多空间。
3. 环境准备与 Token 计量工具
优化 Token 之前,先建立一个能“看到 Token 数量”的环境。没有量化,就没有优化。
3.1 使用官方 Tokenizer 工具
常用方案:
| 平台 | 工具 | 说明 |
|---|---|---|
| OpenAI | tiktoken | Python 库,可精确计数 |
| Anthropic | claude-tokenizer | Python 或 Node 均可调用 |
| Hugging Face | tokenizers | 兼容多种开源模型的分词器 |
| 各家网页端 | Token 计数器页面 | 适合快速测试,不适合脚本批量调用 |
以 Python 为例,安装 tiktoken:
pip install tiktoken对应 OpenAI 模型的使用示例:
import tiktoken encoding = tiktoken.encoding_for_model("gpt-4o") text = "欢迎使用 Token 优化手册,这里是一段中文测试文本。" tokens = encoding.encode(text) print("token 数量:", len(tokens)) print("解码验证:", encoding.decode(tokens[:5]))这里要注意,不同模型的 Tokenizer 并不通用,同一段文字在不同模型下的 Token 数量可能存在差异。统计时要以实际调用模型对应的 Tokenizer 为准。
3.2 在调用日志里记录 Token 用量
真实 API 响应中通常带有 Token 使用字段。把每次请求的 Token 数据写入日志,是成本分析的基础。
以 OpenAI 风格接口为例,把响应中的 usage 字段落盘:
import json response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": "你好"}], ) usage = response.usage log_entry = { "prompt_tokens": usage.prompt_tokens, "completion_tokens": usage.completion_tokens, "total_tokens": usage.total_tokens, } print(json.dumps(log_entry, ensure_ascii=False))日志里至少应该包含 prompt_tokens、completion_tokens、total_tokens 三项。后续做成本归因、请求耗时分析,都依赖这笔记录。
3.3 本地模型场景怎么观察 Token
如果使用本地部署的模型,比如 Ollama、vLLM 或 llama.cpp,可以先通过官方服务日志查看 token/s 和 prompt eval 速度。Token 消耗不直接影响真金白银,但会显著影响响应时间:输入 Token 越多,前置处理阶段越长,首字延迟就越高。
4. 提示词设计与压缩:用更少 Token 表达相同意图
提示词压缩是 Token 优化里见效最快的一环。多数用户的第一版提示词里,至少有 20% 到 40% 是重复、铺垫和无效限制。
4.1 减少“角色扮演式”前缀
很多提示词开头会写“你是一个优秀的人工智能助手,拥有丰富的知识储备,请帮我……”。这类描述不会显著提升模型效果,却占了系统提示的固定 Token。
更优写法是把角色限定压缩成功能关键词,例如:
旧写法(占 30 token): 你是一个优秀的人工智能助手,拥有丰富的知识储备,能够回答各种领域的问题, 并且回答总是详细完整。请帮我总结下面这段文字的核心观点: {文本} 新写法(占 8 token): 总结以下文本的核心观点: {文本}第二种写法在效果上通常不弱于第一种,却省下了大量 Token。如果担心模型输出不够详细,应该限制输出长度,而不是在系统提示里反复强调“详细”。
4.2 精简 few-shot 示例
少样本示例(few-shot)是有效提升输出格式稳定性的手段,但也是 Token 消耗大户。一条示例可能包含问题、标准答案、格式说明,动辄几百 Token。
压缩原则:
- 每个示例只保留能体现格式差异的最小片段。
- 示例数量控制在 1 到 3 条,不要堆多条相似例子。
- 能用 JSON Schema 描述的输出结构,优先用结构化输出,不用示例演示。
例如,要模型输出 JSON,可以只给一个关键约束:
{ "instruction": "只输出 JSON,不要输出任何其他文字。", "schema": { "type": "object", "properties": { "title": {"type": "string"}, "summary": {"type": "string"} } } }结构化的 schema 比几条自然语言示例更省 Token,同时更适合程序解析。
4.3 固定模板与变量分离
在批量任务中,提示词模板应把固定部分和可变部分分开。固定部分提前合并,可变部分只保留本次任务真正需要的字段。不要在每条请求里重复粘贴大段公共说明。
template = "提取以下文档中的关键信息:\n处理要求:{rules}\n文档内容:{document}" payload = template.format( rules="只返回 json,不要解释。", document=document_content, )这里需要注意的是,rules 如果对所有请求都一样,可以直接拼到系统提示里,只让每条请求携带 document。这样可以避免每条请求都重复传送相同的规则描述。
4.4 指示模型“不用复述问题”
很多模型会习惯性复述用户的问题,或者在回答开头重复一遍要求。这类输出会白白消耗输出 Token。在系统提示里加入一行“直接给出结果,不要复述问题”,往往能显著减少输出 Token。
验证方法:用同一批测试问题,分别使用压缩前和压缩后的提示词,统计 total_tokens 和输出质量,取 20 条左右的平均值。
5. 上下文窗口管理:长文档、多轮对话与摘要压缩
5.1 滚动窗口思路
对于多轮对话,把所有聊天历史全部发给模型,Token 消耗会随轮次线性增长。更稳妥的做法是只保留最近的 N 轮内容,更早的历史转换成摘要。
当前请求上下文 = 早期对话摘要 + 最近 10 轮完整对话 + 当前用户问题摘要的生成可以由模型完成,也可以直接截断。注意,摘要本身也需要消耗 Token,如果摘要占比太高,效果反而不好。通常摘要长度控制在完整历史的 10% 到 20% 比较合理。
5.2 长文档分块与分层总结
处理长文档时,不要直接把整篇文档塞给模型。推荐的做法是“先分块、再分块总结、最后汇总”。
例如一份 50 页的 PDF:
- 按章节或固定长度切成 5 到 10 个片段。
- 每个片段单独调用模型,输出摘要和关键信息。
- 把所有摘要合并,再让模型基于摘要输出最终结论。
这种方法下,每次请求的 Token 不会超过窗口限制,同时每段内容都能获得足够的注意力权重。
5.3 历史消息的 Token 预算分配
建议把上下文窗口划分为多个预算区:
| 区域 | 建议占比 | 用途 |
|---|---|---|
| 系统提示与固定规则 | 5% - 10% | 稳定指令、输出格式 |
| 历史摘要 | 5% - 10% | 早期对话、已处理文档 |
| 检索内容 | 30% - 50% | 当前任务真正需要的资料 |
| 最近对话 | 20% - 30% | 用户最新意图和上下文 |
| 输出缓冲 | 5% - 10% | 留给生成结果 |
如果某个区域超出预算,优先削减历史完整对话,而不是压缩检索内容。当前任务相关的核心资料保留优先级最高。
5.4 使用截断与压缩策略组合
实际工程中,可以采用“先截断、再摘要、最后全文”的降级策略:
- 如果原文 Token 总数低于窗口阈值的 50%,直接全文传入。
- 如果超过阈值,先做关键段落抽取,只传入与问题相关的段落。
- 如果抽取后仍然超限,先生成每段摘要,再把摘要传入模型。
这个策略能在尽量保留关键信息的前提下,把每次请求的 Token 控制到稳定区间。
6. Token 计数、成本估算与预算监控
6.1 建立成本计算公式
在业务代码里,可以封装一个简单的成本计算函数:
MODEL_PRICE = { "gpt-4o": {"input": 0.0000025, "output": 0.00001}, # 单位:美元 / token } def estimate_cost(prompt_tokens, completion_tokens, model): price = MODEL_PRICE[model] return prompt_tokens * price["input"] + completion_tokens * price["output"]不要写死价格,因为模型价格会变动。更好的做法是把价格表放到配置文件中,定期更新。
6.2 为调用量设置预警
在调用 API 的公共入口处,对累计 Token 做统计并设置预警线。例如每 1000 次请求统计一次:
total_prompt_tokens = 0 total_completion_tokens = 0 BUDGET_WARNING = 100_000_000 # 例如累计达到 1 亿 token 时预警 def log_usage(prompt_tokens, completion_tokens): global total_prompt_tokens, total_completion_tokens total_prompt_tokens += prompt_tokens total_completion_tokens += completion_tokens if total_prompt_tokens + total_completion_tokens > BUDGET_WARNING: send_warning_email()预警的意义是让你在账单爆炸前就有感知,而不是等到月账单出来后才后悔。
6.3 用抽样估算代替全量统计
如果调用量很大,无法逐条记录,可以按百分比抽样。例如记录每 100 条中的 1 条,估算整体 Token 消耗和成本。抽样统计在工程上更容易实现,误差通常在 5% 以内。
7. 批量调用与 API 请求层面的 Token 优化
7.1 合并同类请求
如果多个任务共用相同的系统提示和检索材料,可以把它们合并到一条请求中,要求模型一次输出多个结果。这样可以复用输入 Token。
例如,一次请求完成 10 条文本的分类,而不是调用 10 次:
texts = [text_1, text_2, ..., text_10] prompt = f""" 对以下每段文本进行分类,输出 JSON 数组,数组顺序与输入顺序一致: {json.dumps(texts, ensure_ascii=False)} """注意,合并请求的输出长度会变长,如果输出超限,可以按每组 3 到 5 条文本分批处理。合并后的单次请求如果失败,重试成本也更高,因此需要额外关注稳定性。
7.2 缓存重复请求结果
对于相同或相似的输入,使用缓存可以完全避免重复计费。常见做法:
- 以文本哈希值作为缓存键,命中缓存时直接返回历史结果。
- 对检索增强型应用,缓存文档向量和文本摘要。
- 对多轮对话应用,只对最近几轮做精确哈希,不要缓存过长历史。
import hashlib def request_with_cache(text, model, cache: dict): key = hashlib.sha256(text.encode("utf-8")).hexdigest() if key in cache: return cache[key] result = call_model(text, model) cache[key] = result return result缓存不仅能降低 Token 成本,还能显著降低接口延迟。
7.3 批量失败重试要控制重复请求次数
批量任务里最容易出现的浪费是“失败后直接重新请求”。如果接口返回超时错误,但并不确定模型是否成功处理,重发会产生重复计费。建议:
- 为每条请求生成唯一 request_id,服务端做幂等处理。
- 重试时使用指数退避,间隔从 1 秒开始逐步递增。
- 对同一批次任务记录已用 Token,失败超过 3 次就转人工检查。
7.4 考虑使用模型自带的缓存机制
部分平台提供提示词缓存或上下文缓存功能,例如相同的前缀内容在短期内多次重复时,缓存部分的计价会更低。如果调用场景中前缀高度一致,比如固定系统提示加固定知识库前缀,可以优先使用这类能力。具体缓存策略与价格,以各平台官方文档为准。
8. 常见问题与排查方法
Token 优化过程中,大部分问题集中在超限、截断和成本飙升。下面给出问题现象、可能原因和排查方案。
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 请求报上下文长度超限 | 输入加输出超过窗口上限 | 检查 usage.total_tokens 与 max_tokens | 压缩历史、缩小检索范围、调低 max_tokens |
| 输出被截断 | 预留的 max_tokens 不够 | 查看响应是否包含 finish_reason=length | 调大 max_tokens,或改用流式输出分批处理 |
| Token 成本持续上涨 | 历史对话无限累积且重复传入 | 检查日志中 prompt_tokens 的变化趋势 | 启用滚动窗口和摘要压缩 |
| 批量任务响应太慢 | 单次请求输入 Token 过多 | 观察首字延迟和总耗时 | 合并或拆分请求,权衡输入复用 |
| 输出格式不稳定 | 提示词中示例过多或格式约束混乱 | 测试不同示例数量下的格式通过率 | 使用结构化输出或精简示例 |
| 缓存命中率低 | 缓存键包含无关变量 | 统计缓存命中率数据 | 归一化缓存键,移除时间戳等无关字段 |
| 提示词压缩后效果变差 | 压缩掉了必要的约束条件 | 对比相同任务压缩前后的输出质量 | 保留与任务强相关的约束,去掉空泛修饰 |
排查时最有效的方法是先看 usage 日志,再分析失败请求的公共模式。常见的优化错误是把所有压缩手段同时套上,导致输出质量明显下降,然后又全部回滚。正确做法是每次只改一个变量,用同一批测试集做 A/B 对比。
8.1 上下文超限时的紧急处理
如果线上环境已经出现大量超限错误,先立刻降低 max_tokens。这一步能让部分请求先恢复。然后再压缩历史或检索内容。不要在生产环境直接换模型,换模型的 Tokenizer 差异可能引起更多格式问题。
8.2 截断问题不一定要靠扩大窗口
输出截断时,很多人的第一反应是“加 max_tokens”。但更省 Token 的做法是让模型输出更精简。例如:
- 限制输出为 Markdown 列表,不要长段落。
- 要求先输出结论,再按需展开。
- 对于超长内容输出,改用流式输出并分段保存。
9. 为不同任务设计 Token 预算
不是所有任务都需要相同规模的上下文预算。把任务按复杂度分级,可以避免高成本模型处理简单任务。
9.1 轻量任务
典型:文本分类、关键词提取、情感判断、格式转换。这类任务上下文需求小,系统提示应该极短,输入直接是核心文本,输出结构固定。预算建议控制在 1000 到 3000 Token 以内。
9.2 中度任务
典型:文档摘要、内容改写、信息抽取。需要传入足够相关上下文,但不需要全文。预算建议控制在 8000 到 20000 Token,按文档大小动态调整。
9.3 重度任务
典型:多轮深度问答、复杂业务推理、长文档跨章节分析。需要合理使用摘要和分段检索,预算可能达到 30000 Token 以上。此时必须配置缓存和成本监控,不能裸调接口。
预算优先分配顺序: 当前问题相关检索 > 当前输入文本 > 最近对话 > 早期摘要 > 长尾上下文10. 最佳实践与合规提醒
10.1 工程落地清单
- 每次请求都在日志中记录 usage 字段。
- 所有提示词模板参数化,禁止在代码里硬编码长文本。
- 为每条提示词模板设置 Token 预估上限,超出时自动截断或告警。
- 批量任务使用批次记录、唯一请求 ID 和指数退避重试。
- 缓存键要稳定,定期清理过期缓存,避免内存无限增长。
- 上线前用同一批测试样本对比提示词压缩前后的输出质量,保留质量达标且成本更低的版本。
- 成本监控报表按天和按模型维度统计,设置周环比增长率预警。
10.2 数据与合规边界
Token 优化过程中,不可避免要把业务文本发送给模型。无论使用云端 API 还是本地模型,都应遵守以下原则:
- 用户敏感信息、隐私数据、未公开业务信息,不得随意发送到第三方模型服务。
- 涉及人脸、声音、版权素材等内容的场景,必须确认已获得授权,并评估传播范围。
- 企业环境优先使用私有化部署或签署数据协议的 API 服务。
- 内部测试数据使用脱敏样本,避免在日志中保留完整敏感内容。
- 模型输出可能包含幻觉内容,关键业务场景需要人工复核,不能完全自动化放行。
Token 优化只是让工具更高效的工程技术,不能突破数据合规与内容安全的底线。
11. 总结与下一步
Token 优化手册解决的核心问题只有一个:让每一分 Token 预算都花在刀刃上。
值得最先动手验证的是提示词压缩。把自己最常用的提示词拿出来,删掉角色铺垫、压缩示例数量、限制输出格式,对比压缩前后的 Token 用量和输出质量。通常这一小步就能看到 20% 以上的成本下降。
最容易踩的坑是一次性改造过多。压缩提示词后输出质量下降,不代表方向错误,而是变量太多。建议保留旧版本提示词,建立小规模测试集,每次只改一个环节,量化记录。
后续可以继续扩展的方向包括:接入 RAG 后按检索相关度动态裁剪上下文、针对不同模型固定一套提示词模板规范、建立更细粒度的 Token 成本报表,以及在服务端实现多层缓存。Token 优化不是一次性的优化动作,而应该沉淀为团队或个人的提示词工程规范。好的规范会随着调用量增长,自动让成本曲线变得更平缓。