最近很多人讨论“Token 卖爆了”“半年涨 20 倍”,第一反应往往是价格又涨了。稍微接过大模型 API 的人会发现,问题没那么简单:真正上涨的,是 Token 消耗量,以及开发者对 Token 成本的敏感度。Token 现在是大模型 API、智能体、AI IDE、RAG 项目里最基础的计量单位。这篇内容按我平时踩坑的顺序,把 Token 是什么、为什么消耗涨得快、如何控制、报错怎么排查拆开讲。
不管你是刚准备接入大模型 API 的新手,还是已经上线了智能体、批量任务、代码助手的老手,只要你的产出和 Token 有关,这篇文章都值得往下看。
1. Token 到底是什么,为什么大模型拿它当计价单位
1.1 Token 是模型看到的最小文本切片
很多人把 Token 理解成“一个词”,这个理解有偏差。模型在接收文本之前,会把输入拆成小块,每个小块可能是一个单词、半个单词、一个汉字、一个标点,甚至一个空格。这种小块就是 Token。
简单说,Token 是模型真正“读取”和“生成”的最小单位。模型不是按字符处理文本的,而是按 Token 处理。不同模型用不同 tokenizer,同一个字符串在不同模型下的 Token 数量也可能不同,不能直接拿一个字等于一个 Token 去套所有平台。
举个例子:
Token这个英文单词,在大多数 tokenizer 里会被切成 1 到 2 个 Token。- “什么是 Token” 这句话,中文部分可能切出 3 到 6 个 Token。
- 一段 200 行的代码,保守估计也有几百到上千 Token。
这个数字会因为模型、语言、标点、空格、换行方式不同而变化。实际测试时不要凭感觉猜,应该看 API 返回的 usage 字段。
1.2 为什么按 Token 收费,而不是按字数
大模型生成文本时,是一个 Token 一个 Token 往外蹦的。输入时,模型也要把整个上下文重新读一遍。也就是说,模型的时间、显存、算力消耗,都跟 Token 数量强相关。
输入多少个 Token,决定模型要“看多久”;输出多少个 Token,决定模型要“想多久”。所以平台按 Token 计费,本质是把算力成本换算成一个相对公平的计量单位。
这也解释了很多人的一个疑问:为什么同样一句话,英文模型和中文模型价格看起来不一样?不只是语言问题,Token 切分方式不同、上下文窗口不同、推理成本不同,最终都会反映到价格和消耗上。
1.3 对话里有很多“看不见”的 Token
很多新手第一次跑通 API 之后很兴奋,因为只输入了几个字,输出也几十个字,感觉 Token 消耗应该很少。一看到账单就懵了。
问题通常出在这里:你发给模型的请求不只是你输入的那几个字,还包括:
- system prompt,也就是系统提示词。
- few-shot 示例,也就是你为了让模型模仿而写出的样例。
- 历史对话记录,每次请求都会原样带过去。
- 搜索引擎或 RAG 检索回来的资料块。
- 工具调用的 schema 和模型返回的工具调用指令。
这些内容统统会被算进输入 Token。也就是说,你看到的“界面输入框”里只有一短句,但模型实际收到的可能是几千 Token 的上下文。
| 内容类型 | 大致 Token 范围 | 说明 |
|---|---|---|
| 50 个汉字的短句 | 60~100 Token | 中文按实际 tokenizer 切分 |
| 200 字的代码片段 | 100~300 Token | 缩进、符号、空行都会占 |
| 1000 词的英文文档 | 1000~1500 Token | 英文按子词切分 |
| 一张图片输入 | 数百到上千 Token | 视觉模型会把图片转成视觉 Token |
很多“为什么我什么都没干就没了几百万 Token”的案例,原因就是系统提示词太长、历史消息没清理、RAG 检索片段重复发送。
2. Token 计费口径:输入、输出、缓存和上下文占位不能搞混
2.1 四个容易让账单翻倍的计费项
和传统接口按次数计费不同,大模型 API 的计费口径更细。你要关注的至少有三个维度:输入 Token、输出 Token、缓存 Token。
输入 Token 是每次请求里所有文本的总和,包括 system prompt、历史对话、用户消息、检索资料。输出 Token 是模型生成内容的 Token 总数,包括最终回答,也包括模型在内部推理过程里生成的“思考”内容。如果模型支持上下文缓存,相同前缀重复请求时,平台可能按缓存价计费,但缓存也不是所有平台、所有模型都有。
很多项目跑了一段时间后成本起飞,不是因为输出太长,而是因为输入里塞了太多重复内容。多轮对话越积越长,RAG 每次把同样的文章片段重复发给模型,智能体每执行一个工具调用就把前面所有历史重新发一遍。最后真正读到的答案很短,但每次请求都背着很重的上下文。
2.2 Credit、Token、字数之间没有固定换算
热搜里经常出现“2500 credits 相当于多少 token”“3 亿 token 能跑多久”“一积分多少 token”这类问题。这类问题的标准答案只有一个:没有固定换算。
Credit 是平台的额度包装,Token 是模型的实际计量单位。同一个模型、同一个请求,消耗的 Token 相对稳定;但同一个 Credit 在不同模型、不同输入输出比例、不同缓存策略下,能兑换的 Token 数量差异很大。
想估算消耗,至少要按这个公式理解:
总 Token = 输入 Token + 输出 Token + 缓存命中 Token 成本 = 输入 Token × 输入单价 + 输出 Token × 输出单价 + 缓存 Token × 缓存单价如果平台用 Credit 展示,那只是把上面这个成本乘以一个平台换算系数,不是简单的“1 Credit = 1000 Token”。接入新平台时,第一件事是去官方文档找计费公式,而不是自己猜。
2.3 最终以 API 响应的 usage 字段为准
不管前面怎么估算,每次调用结束后,API 响应里通常会返回一个 usage 对象,里面包含 prompt_tokens、completion_tokens、total_tokens 等字段。这才是真正计算费用的依据。
建议在自己代码里把这个字段打到日志里:
resp = client.chat.completions.create( model="your-model", messages=messages, max_tokens=200 ) usage = resp.usage print("prompt_tokens:", usage.prompt_tokens) print("completion_tokens:", usage.completion_tokens) print("total_tokens:", usage.total_tokens)我接入任何大模型 API,第一件事就是确认 usage 能不能打印出来。如果响应里没有 usage,也要通过平台控制台或日志系统去查。没有 usage 数据,你后面所有成本优化都是盲人摸象。
3. 半年涨 20 倍的消耗,到底被谁吃掉了
3.1 三类最烧 Token 的任务
第一类是多轮智能体。智能体每执行一步工具调用,都会把当前对话历史、工具定义、工具返回结果重新发给模型。一个看起来只有三步的任务,实际上可能触发 10 次甚至 20 次模型调用,每次调用都是几百到几千 Token。
第二类是代码类和 AI IDE 场景。代码模型要同时读入项目文件、选中代码、终端报错、对话历史。每次补全和修改都是一个不小的上下文请求。很多人觉得“我就让它改一处 bug”,结果它把整个文件内容和报错信息都吃进去了。
第三类是 RAG 检索问答。检索回来的资料如果每次不分段、不裁剪,直接把几千字的文章塞给模型,用户只问一句话,模型却要读两千 Token 的资料。这个问题在知识库问答里最常见。
3.2 一个 RAG 查询的真实消耗拆解
我拿最常见的知识库问答举例。一次普通查询,大致长这样:
| 请求组成部分 | 预估 Token |
|---|---|
| 系统提示词 | 300~500 |
| 检索回来的资料块 | 800~1500 |
| 最近 5 轮历史对话 | 1000~2000 |
| 用户当前问题 | 30~80 |
| 模型输出 | 200~600 |
也就是说,一次普通查询可能就要消耗 3000 到 5000 Token。这还只是一次请求。如果一天跑 1000 次,就是 300 万到 500 万 Token。加上并发任务、失败重试、长时间会话,量级很容易再翻几倍。
所以“半年涨 20 倍”这种说法放在 Token 消耗上,并不是没有可能。稳定业务量没有涨,但调用频率、上下文长度、模型复杂度只要同时上涨,消耗就会非线性地变大。
3.3 为什么涨得快:重复读取、长上下文和循环调用
传统接口的计费逻辑是“你调用一次,我执行一次”。大模型 API 的计费逻辑是“你调用一次,我按你带过来的上下文大小执行一次”。
关键在于重复读取。搜索引擎时代,网页抓取一次可以在索引里反复使用。大模型 API 不行,每次请求都是独立的,都要把上下文重新读一遍。即使两个请求的问法非常接近,模型也不会记住上一次的内容,除非你把历史一起发过去。
再加上 Agent 的循环调用,一个简单任务可能会被拆成“理解问题、调用工具、看返回结果、修正答案、重新回答”多个环节。每一步都是一次新的 Token 消耗,总消耗自然上去了。
4. 怎么把 Token 消耗压下来:参数、上下文和任务拆分
4.1 调参先分清“控制长度”和“控制随机性”
很多人一看 Token 消耗高,第一反应是调低 temperature,以为这样能省钱。其实 temperature 影响的是输出的随机性,不是 Token 数量。你把它从 1 调到 0,模型该生成多少字还是多少字。
真正控制生成长度的是另外两个东西:
- max_tokens 或 max_output_tokens,限制输出上限。
- stop 序列,让模型在遇到指定内容时提前结束。
如果你的目标只是“让回答短一点”,直接在请求里限制输出长度,并在 system prompt 里写清楚要求。
{ "model": "your-model", "messages": [ { "role": "system", "content": "只输出结论,不超过三句话。" }, { "role": "user", "content": "Token 是什么?" } ], "max_tokens": 200, "temperature": 0.2, "stream": true }这个例子里,max_tokens 设成 200,绝大多数短回答都够用。如果经常出现回答被截断,再逐步加到 300、500,而不是一上来就设 4096。输出 Token 是成本里最直观的部分,宁可给一个可接受的下限,也不要让它无限生成。
4.2 上下文裁剪:省下最多的一步
我一般会把上下文优化放在所有成本优化措施的第一位,因为省下来的通常非常明显。
首先是 system prompt。很多项目的 system prompt 写到上千字,每次请求都要带着,这等于每个请求都背上一个长期税。把不必要的内容删掉,把规则写短,保留最关键的约束即可。
其次是历史消息。不要每次都把所有历史消息原样发给模型。可以只保留最近几轮,或者把更早的对话总结成一段摘要,再放进 system prompt。
例如一个最简单的裁剪逻辑:
def trim_messages(messages, keep_recent=10): system_msgs = [m for m in messages if m["role"] == "system"] recent_msgs = messages[-keep_recent:] return system_msgs + recent_msgs注意,这只是“按条数裁剪”的示意。真正稳定的做法是根据 usage 数据计算历史消息实际占了多少 Token,再决定保留多少轮。按条数裁剪虽然简单,但遇到超长对话时仍然可能超限。
第三是 RAG 检索块。检索回来的内容不要整篇塞进去,先判断问题需要哪一段,再截取最相关的几百字。top_k 也不要拉得太高,返回 3 个片段能回答的问题,就没必要检索 10 个片段。
4.3 任务拆分:比省提示词更有效的成本控制
省提示词只能降低单次请求的 Token,真正的量级控制还是要靠任务拆分。
一个很长的任务,不要全部塞进一次对话。比如“先读取文件,再总结内容,再写报告”,可以拆成多个独立步骤,每步只带必要输入。这样每一步的上下文都很短,整体 Token 消耗会小很多。
批量任务也不要盲目开并发。先把单条任务跑通,确认输入、输出、日志都正常,再逐步提高批量大小。遇到失败时,优先做有限次数的失败重试,并且只重试失败的那一步,不要重试整个任务。
最关键的是把每次调用产生的 usage 落到日志或数据库里。你可以按任务类型、用户、日期分组,看看哪个场景消耗最高。没有这个数据,所谓的优化都只是拍脑袋。
5. 常见 Token 报错怎么排查:超限、登录失败和输出截断
5.1 “model token limit”和“output token limit”要分开看
接入大模型 API 最常见的报错是“exceeded model token limit”或“maximum context length”。这类问题的核心原因是:你发送的输入 Token 数量加模型可能生成的输出 Token 数量,已经超过了模型的上下文窗口。
排查顺序很固定:
- 先看 usage 或日志里的 prompt_tokens 是多少。
- 再算系统提示词、历史消息、RAG 片段各自占了多少。
- 最后看 max_tokens 设置是否过高。
如果输入已经接近窗口上限,优化方向就是裁剪历史消息、压缩 system prompt、缩小检索块。如果输入正常,但 max_tokens 设得太大,模型在计算时也会认为可能超限,需要适当调低。
另一种常见现象是“已达到输出 token 上限,回答被截断”。这个报错不是模型坏了,而是你设置的 max_tokens 小于模型想生成的完整内容。要么提高 max_tokens,要么让模型输出更短的答案,要么把任务拆成多个子任务分步完成。
5.2 登录时报 token exchange failed,其实是身份认证问题
很多人在登录 IDE、CLI 工具或第三方平台时,看到类似“sign-in could not be completed token exchange failed”的报错,第一反应是“Token 不够了”。
这里的 Token 和计费 Token 完全不是一个东西。登录报错里的 token 是 OAuth 登录令牌,是身份凭证,和模型计费单位同名但不同语境。
这类错误通常和这几件事有关:
- 系统时间不准,导致令牌签名校验失败。
- 浏览器或客户端缓存了旧登录态。
- 账号权限不足,服务端拒绝发放新令牌。
- API 网关临时不可用,请求没有到达认证服务。
处理顺序建议是先检查系统时间,再清理旧登录态,然后重新按官方流程登录。如果返回 401 或 403,先确认账号权限和密钥状态,不要反复重试,更不要尝试绕过限制。按服务商的官方支持渠道处理是最稳妥的方案。
5.3 API token 和 access token 失效,和模型 Token 无关
如果你在 Git 客户端、CI/CD、命令行工具里看到“check api token”“access token could not be refreshed”“invalid token”,这些都属于身份凭证问题。
常见原因包括:
- API Token 过期,需要重新生成。
- 刷新令牌过期,需要重新登录。
- 请求头里没有正确携带 Token,比如少了 Authorization 字段。
- Token 中间有换行或空格,复制时被截断。
- 服务端校验规则变化,旧 Token 不再被接受。
如果你在自己系统里实现 JWT 续签,也要额外关注 access token 有效期、refresh token 存储、服务端时钟一致性和黑名单机制。但大模型 API 场景下,多数平台不需要你实现完整鉴权,你只需要把服务商签发的密钥或令牌保存好,并按文档传给对应字段。
总之,遇到 token 相关报错时,先判断这是“模型 Token”还是“身份 Token”。一个是成本问题,一个是权限问题。混在一起排查,很容易浪费时间。
6. 给学习者和生产项目不同的用量控制建议
6.1 学习阶段:先设上限,再跑通能力
学习阶段用免费额度或少量充值跑 Demo,收益很高,但一样要控制预算。免费额度通常有时效、速率限制和并发限制,不要把它当成无限资源。
建议从最小用例开始:
- 先用短文本跑通单次请求。
- 输出 max_tokens 设低一点。
- 不保留超长历史,每次只发必要消息。
- 每次调用后打印 usage,建立“一次请求大概耗多少 Token”的基本感觉。
- API Key 放在环境变量里,不要硬编码在代码仓库中。
不要一上来就跑几百个文件、几十个并发。低配置能跑通一个 Demo,不代表这样适合批量跑。先把稳定的单条请求跑稳,再逐步放大。
6.2 生产阶段:把 Token 当成预算系统来管
生产环境里,Token 不是“技术参数”,而是“成本单位”。你要像管云资源预算一样管 Token。
至少要落地这几件事:
- 把每次 API 响应的 usage 落库,不能只打印到控制台。
- 按任务类型、用户、日期做聚合统计。
- 设置每日或单任务 Token 上限,超过就告警。
- 失败重试要加退避,不能无限重发同一个请求。
- 不同环境使用不同 API Key,方便定位是哪个项目超支。
我给生产项目做接入时,通常会先搭一个非常小的日志中间层,把所有模型的调用次数、输入 Token、输出 Token、耗时记录到同一张表里。之后所有成本分析和问题排查都从这张表出发,比临时看控制台有效得多。
| 对比项 | 学习环境 | 生产环境 |
|---|---|---|
| 模型选择 | 默认模型或免费模型 | 按任务选择大小模型 |
| max_tokens | 尽量短 | 按场景单独配置 |
| 日志 | 控制台打印 | 落库加监控告警 |
| 失败重试 | 手动重试 | 指数退避加死信队列 |
| 成本控制 | 少量免费额度 | 预算阈值和配额管理 |
6.3 本地模型和开源模型做兜底,但不算零成本
如果 API 成本太高,或者数据不适合传到外部服务,可以考虑本地部署开源模型。本地模型没有“按 Token 计费”这一说,但占用的是显存、内存、GPU 时间和电力成本,同时还要花时间维护模型版本、推理服务和并发性能。
我的建议是,把本地模型当兜底方案,而不是唯一方案。简单重复任务可以走小模型或本地模型,复杂推理、长上下文、需要更强能力的任务再走大模型 API。混合使用比单一方案更容易控制成本。
但要注意,本地模型同样有上下文窗口限制,同样需要做输入裁剪和任务拆分。不要以为本地部署就不用管 Token 了,只是 Token 从“账单上的数字”变成了“显存里的压力”。
6.4 踩过几次坑之后,我优先看的三个点
如果你准备在项目里认真控制 Token 成本,我先说三个最值得抓的点。
第一,usage 必须落日志。没有 usage,你连问题在哪都看不到。
第二,输入 Token 通常比输出 Token 先爆炸。多轮对话、RAG 检索、系统提示词,这些都是输入开销的大头,先处理它们。
第三,报错时要先分清是“模型 Token”还是“身份 Token”。前者看上下文和参数,后者看密钥、权限和登录状态。
把这三件事做好,再谈模型选型、批量任务、并发优化。Token 这个计费单位会一直存在,早点把它纳入工程管理,后面能省掉不少折腾。