1. 背景与核心概念
最近科技圈有一个说法讨论度很高:AI 跑得太快,有观点建议开征“token 税”。很多人第一反应是“AI 怎么收税?”或者“token 不是区块链里的代币吗?”。
这里要先把概念对齐一下:AI 领域的 token,并不是加密货币代币,而是大语言模型处理文本时的最小计量单位。它可以粗略理解成模型“读到的”或“写出的”一小段文本块。所谓“token 税”,本质上是建议根据 AI 使用的 token 消耗量来收取费用,让大规模调用 AI 的行为承担对应的成本。
需要说明的是,这类建议目前更接近公共讨论和治理方向,并不是已经正式落地的政策,国内外也都没有统一标准。本文不评价具体政策立场,只从技术开发视角做两件事:
- 把“token 税”背后的逻辑翻译成工程问题;
- 演示在实际系统中,如何做 token 计量、配额、预算与审计。
即便未来“税”本身不一定出现,Token 消耗的计量也已经是每个公司接入大模型时必须面对的核心问题。做过 AI 应用开发的同学应该都有印象:上线一个月,最容易被追问的不是模型效果,而是“我们这个月 token 花了多少,哪个功能消耗最大,有没有办法控制成本”。
1.1 “税”字背后的三类动机
为什么有人会把“税”和 AI 放到一起讨论?大致有三类背景。
第一类:资源稀缺性。
大规模训练和在线推理需要大量 GPU、电力、内存和网络资源。尤其是现代大模型,一次推理可能消耗的算力远超传统接口。当一个系统被海量用户无限制调用时,总资源消耗会非常惊人。为了控制资源被滥用,服务商逐渐引入按 token 计费、按次限制、模型分级等手段。
第二类:外部性问题。
经济学里的“外部性”指的是某个行为让第三方承担了成本,却没有被计入价格。AI 的推理成本虽然由服务商或使用者直接承担,但仍然存在共享基础设施压力、电力消耗、硬件淘汰速度加快等间接影响。如果所有成本都由“所有人”共同承担,使用者就不会有动力去做优化。按用量付费,本质上是一种“谁使用、谁分担”的思路。
第三类:公平分摊。
在同一套 API 网关下,不同业务对 token 的消耗差异可能达到百倍。比如一个内部知识库问答机器人可能每天消耗几百万 token,而一个简单的文本分类接口每天只消耗几万 token。如果没有一个精确的计量体系,账目就很难透明分摊。这不只是税的问题,更是企业内部成本核算问题。
1.2 开发者视角:把讨论翻译成工程任务
如果抛开“税”这个字眼,从开发人员的工作看,这个建议包含了一串真实存在的工程需求:
- 统一计量单位;
- 每分钟或每天对每个应用进行配额限制;
- 根据模型、模块、部门生成成本报表;
- 设置预算上限和预警;
- 审计每个请求的输入输出 token 数量。
这些需求在云厂商和大模型 API 服务中已经有雏形。比如“按 token 计费”“上下文窗口限制”“速率限制”都属于这一方向。更进一步的企业级实践,是在自己的网关层做统一的 token 计量与配额管理,把不同模型、不同供应商的 token 口径归一化。
所以,与其只去讨论“税”会不会落地,不如先掌握 token 计量这把尺子。这也是本文的主题。
2. 认识 Token:AI 世界的“最小计量单位”
2.1 Token 到底是什么
大模型不是直接读字符,而是先把文本切分成 token。分词规则来自模型训练时使用的分词器。有些 token 对应一个完整的英文单词,有些 token 对应单词的一部分,中文则可能对应单个字、词组或整句,具体要看词汇表设计。
例如一句话:
AI is developing fast.可能被分成类似下面的形式:
AI is developing fast .其中 “AI” 可能是一个 token,“is” 加前面的空格可能是一个 token。这就是为什么不能用简单的“字符数量除以 4”来准确估算 token 数量。
Token 数量直接影响两件事:
- 上下文窗口大小;
- API 计费成本。
上下文窗口的意思是模型一次最多能处理多少 token。比如一个模型支持 128K token,那么输入和输出加在一起不能超过这个限制。输出阶段一次推理可以产出的 token 上限也需要单独限制,否则一个问答接口可能因为生成太长而超时、超预算。
2.2 为什么不能用字符数代替 Token 数
先看一个直观例子。同样是 1000 个字符:
- 如果是英文,可能对应两百多个 token;
- 如果是中文,可能对应三百到六百个 token,不同模型差异很大;
- 如果夹杂大量代码、JSON、Markdown 表格,token 分布更不均匀。
因此,对文本做 token 估算时,最可靠的方法是使用该模型对应的分词器。社区里常见的两个工具是transformers和tiktoken。
下面是一个最小代码示例,使用tiktoken统计一段英文文本的 token 数量。
# 文件:token_count_example.py import tiktoken # cl100k_base 是 OpenAI 部分模型使用的 BPE 编码之一 enc = tiktoken.get_encoding("cl100k_base") text = "AI is developing fast. Let's talk about token tax." tokens = enc.encode(text) print("原始文本:", text) print("token 数量:", len(tokens))运行后,输出大致是:
原始文本: AI is developing fast. Let's talk about token tax. token 数量: 12如果你在本地没有安装tiktoken,可以先安装:
pip install tiktoken如果项目里使用的是 Hugging Face 生态,也可以这样:
# 文件:token_count_hf.py from transformers import AutoTokenizer tokenizer = AutoTokenizer.from_pretrained("gpt2") text = "AI is developing fast. Let's talk about token tax." tokens = tokenizer.tokenize(text) print("切分结果:", tokens) print("token 数量:", len(tokens))注意,gpt2只是示例,实际生产中要选择与模型完全一致的分词器。不同模型使用不同词表,同一个句子的 token 数量不能跨模型通用。
2.3 一次请求中 Token 到底消耗在哪里
一个普通的对话请求,token 消耗不只是用户输入的那一句话。它通常包括:
| 组成 | 说明 |
|---|---|
| 系统提示词 | 定义人设、回答规则、输出格式的指令 |
| 多轮历史 | 前几轮对话记录,越长越消耗输入 token |
| 检索上下文 | 知识库检索结果、文档片段 |
| 用户问题 | 当前请求中的用户输入 |
| 工具定义 | 部分请求会携带函数调用入参、schema 定义 |
| 模型输出 | 回答文本,计费时可以单独统计 |
常见误区是只统计“用户问题”那几十个 token,却忘了系统提示词和检索上下文可能有几千 token。一个典型的知识库问答请求,输入 token 可能被系统提示词和检索片段占掉 70% 以上。
2.4 Token 不只是“输入”,还包括“输出”
很多初学者以为 token 只算输入,这是不对的。在实际计费中,输出 token 往往单价更高,而且大模型在流式输出时,用户看到的内容越长,消耗越大。
因此,做成本治理时,输入输出是分开看的。输入侧靠压缩上下文来降本,输出侧靠控制回答长度、设置max_tokens上限、使用更简洁的提示词来降本。
3. 为什么会有“按量计费”和“税”的讨论
3.1 算力账单从来都不低
训练一个现代大模型需要大量 GPU,推理阶段也要持续占用算力资源。很多公司买了一张高配 GPU 卡回来,跑几个大模型推理任务,就能感受到电费和资源占用的压力。
从工程角度看,AI 推理的成本不是一次性的。用户每个请求都会触发前向计算:
- 计算所有输入 token 的注意力权重;
- 逐 token 生成输出;
- 每生成一个 token 都要做一次完整的模型前向推理。
这意味着,一个回答两三百字的请求,实际计算量可能是一个简短回答的十几倍。这也是按 token 计费在商业上合理的原因之一。
3.2 “以价制量”是一种治理思路
如果某种共享资源容易被滥用,常见治理方式有这几种:
| 方式 | 原理 | 缺点 |
|---|---|---|
| 直接限流 | 超出配额就拒绝 | 用户感受差,可能误伤正常请求 |
| 排队等待 | 高峰期降速 | 实时性差 |
| 按量计费 | 谁用谁付费 | 对小用户成本感知太强 |
| 配额分摊 | 部门/应用各自有预算 | 需要完善计量系统 |
“token 税”讨论的底层思路,更接近“用量作为计价基础”。假设每个请求都被准确计量,那么流量越大的应用,分摊的成本越高;反过来,应用方就有动力去优化提示词、减少重复调用、合并请求、引入缓存。
这套逻辑在平台内部同样适用。很多中大型团队已经在 API 网关里做 token 计量,再按业务线分摊成本,本质上就是一套“内部 token 税”系统。
3.3 统一计量的真正难点
想法说起来简单,落到技术上很难。最大的难点是:不同模型、不同供应商对 token 的定义并不一致。
- OpenAI 有 cl100k_base、o200k_base 等不同编码;
- 开源社区的 LLaMA、Qwen、DeepSeek 各有自己的分词器;
- 如果直接使用“字符长度除某个常数”的方式估算,误差会很大;
- 同一个请求经过网关转发到不同模型时,前后两次可能无法直接比较。
所以,如果一个平台想实现“对所有 AI 请求统一扣税”,必须先做一层抽象:把不同供应商返回的 token 数量归一化。这个归一化怎么做、按什么模型基准算,本身就是一个工程问题,而不是政策问题。
4. 生产环境中 Token 计量与配额设计
接下来我们进入实战。本文演示一个最小可运行的 token 计量和配额管理模块。重点不是第三方 SDK 的具体调用方式,而是计量、记录、预算控制这套思路。
4.1 创建项目结构
先创建一个简单目录:
token-budget/ ├── usage_tracker.py # token 台账记录 ├── budget_checker.py # 预算与配额控制 ├── usage_demo.py # 演示脚本 └── requirements.txt # 依赖文件4.2 定义 token 台账表
首先实现usage_tracker.py,使用 SQLite 记录每次调用的 token 消耗。生产环境建议替换为 PostgreSQL、ClickHouse 等更适合统计分析的表结构。
# 文件:usage_tracker.py import sqlite3 from datetime import datetime, timezone class UsageTracker: def __init__(self, db_path="usage.db"): self.conn = sqlite3.connect(db_path) self._init_table() def _init_table(self): self.conn.execute(""" CREATE TABLE IF NOT EXISTS token_usage ( id INTEGER PRIMARY KEY AUTOINCREMENT, request_id TEXT NOT NULL, app_id TEXT NOT NULL, model TEXT NOT NULL, prompt_tokens INTEGER NOT NULL, completion_tokens INTEGER NOT NULL, total_tokens INTEGER NOT NULL, created_at TEXT NOT NULL ) """) self.conn.commit() def record(self, request_id, app_id, model, prompt_tokens, completion_tokens): total_tokens = prompt_tokens + completion_tokens created_at = datetime.now(timezone.utc).isoformat() self.conn.execute( """ INSERT INTO token_usage ( request_id, app_id, model, prompt_tokens, completion_tokens, total_tokens, created_at ) VALUES (?, ?, ?, ?, ?, ?, ?) """, ( request_id, app_id, model, prompt_tokens, completion_tokens, total_tokens, created_at, ), ) self.conn.commit() return total_tokens def sum_tokens_by_app(self, app_id): """统计某个应用累计消耗的 token 数量。""" cursor = self.conn.execute( """ SELECT SUM(total_tokens) FROM token_usage WHERE app_id = ? """, (app_id,), ) row = cursor.fetchone() return row[0] if row and row[0] else 0这里只展示了两个核心方法:
record:记录一次请求的 token 消耗;sum_tokens_by_app:按应用统计累计消耗。
如果需要按天、按小时统计,可以在 SQL 中增加对created_at的日期截断函数,也可以增加window_start时间字段。
4.3 实现预算配额控制
有了台账,下一层就是配额控制。我们可以实现一个简单的日预算检查器,代码如下。
# 文件:budget_checker.py class BudgetChecker: def __init__(self, tracker, daily_quota=50000): self.tracker = tracker self.daily_quota = daily_quota def remaining_budget(self, app_id): used = self.tracker.sum_tokens_by_app(app_id) return self.daily_quota - used def can_proceed(self, app_id, estimated_tokens): remaining = self.remaining_budget(app_id) if remaining < estimated_tokens: return False, remaining return True, remaining这个模块会根据当前应用已经消耗的 token 总量,判断这次请求是否还有“预算额度”。
需要注意:真实的配额系统不会只查一次总用量,而是会配合 Redis、数据库事务、计数器等手段保证并发安全。上面的代码更偏向教学演示。
4.4 模拟一次请求并记录用量
下面写一个演示脚本。为了不依赖具体大模型 API,这里用tiktoken模拟计算输入 token,然后模拟输出 token 数量。
# 文件:usage_demo.py import uuid import tiktoken from usage_tracker import UsageTracker from budget_checker import BudgetChecker enc = tiktoken.get_encoding("cl100k_base") def count_tokens(text: str) -> int: return len(enc.encode(text)) def mock_llm_call(messages, max_output_tokens=200): """ 模拟一次大模型调用。 真实场景中,应该读取模型 API 返回的 usage 字段, 而不是自己主观估算。 """ prompt_text = "".join(item.get("content", "") for item in messages) prompt_tokens = count_tokens(prompt_text) # 以下只是演示,不能作为真实计费依据。 completion_tokens = max_output_tokens return { "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens, "content": "这是一段模拟输出。", } def main(): tracker = UsageTracker() checker = BudgetChecker(tracker, daily_quota=100000) app_id = "rag_service" messages = [ {"role": "system", "content": "你是一个技术助手,回答尽量简洁。"}, {"role": "user", "content": "请解释 token 计量在生产环境中的价值。"}, ] can_run, remaining = checker.can_proceed(app_id, estimated_tokens=500) if not can_run: print("预算不足,建议降级或拒绝请求。剩余预算:", remaining) return resp = mock_llm_call(messages, max_output_tokens=200) request_id = str(uuid.uuid4()) tracker.record( request_id=request_id, app_id=app_id, model="mock-model", prompt_tokens=resp["prompt_tokens"], completion_tokens=resp["completion_tokens"], ) total_from_log = tracker.sum_tokens_by_app(app_id) print("请求 ID:", request_id) print("输入 token:", resp["prompt_tokens"]) print("输出 token:", resp["completion_tokens"]) print("当前应用累计消耗:", total_from_log) if __name__ == "__main__": main()运行命令:
pip install tiktoken python usage_demo.py预期输出类似:
请求 ID: 86a84a5f-xxxx-xxxx-xxxx-xxxxxxxxxxxx 输入 token: 34 输出 token: 200 当前应用累计消耗: 234这个脚本解决了三个最小问题:
- 如何记录每次调用的 token 消耗;
- 如何按应用统计总量;
- 如何在做请求前判断预算是否充足。
真实项目中,你还需要处理并发写入、异步场景、失败重试、按时间窗口统计等细节。
5. 真实 API 中的 Token 计量参考
不同大模型服务商返回的用量字段不完全一样,但主流模式基本一致。以常见的 ChatCompletions 接口为例,响应中通常会包含类似字段:
| 字段 | 含义 |
|---|---|
prompt_tokens | 请求输入侧消耗的 token 数 |
completion_tokens | 模型返回内容消耗的 token 数 |
total_tokens | 本次请求总消耗 token 数 |
cached_tokens | 命中缓存的 token 数 |
在使用第三方 SDK 时,代码类似下面这样,但字段名会根据 SDK 版本有所变化。
# 伪代码:不同 SDK 版本字段可能不同,以官方文档为准 response = client.chat.completions.create( model=model_name, messages=messages, ) usage = response.usage tracker.record( request_id=request_id, app_id=app_id, model=model_name, prompt_tokens=usage.prompt_tokens, completion_tokens=usage.completion_tokens, )生产环境有几个细节格外重要:
- 不要通过“输出字符数”反推输出 token,准确值以 API 返回为准;
- 流式输出模式下,部分服务商在最后一条响应中才会携带完整 usage;
- 重试请求要在业务层做好幂等,否则一次失败重发,账单上会出现两次消耗。
如果你使用的是公司内部自建模型,可能没有现成的 usage 字段。这种情况下,可以用模型对应的 tokenizer 自行统计,然后写入日志系统。
6. 常见问题与排查思路
做 token 计费和配额管理时,比较容易踩到下面这些坑。
| 问题现象 | 常见原因 | 解决思路 |
|---|---|---|
| 实际账单比预估多很多 | 只统计用户输入,没统计检索内容和系统提示词 | 对完整请求做日志,统计 prompt_tokens |
| 同一个文本 token 数不稳定 | 使用了错误的分词器 | 确认模型对应的 tokenizer 版本 |
| 上下文窗口超限 | 历史消息和检索内容堆积 | 做消息裁剪、摘要、滑动窗口 |
| 请求失败后重复计费 | 重试逻辑没有做幂等 | 增加 request_id,失败重放时复用同一 ID |
| 输出 token 超出预期 | 没有设置 max_tokens 上限 | 明确限制单次输出长度 |
| 缓存不生效 | 请求文本细微变化导致缓存无法命中 | 对历史命中做规范化处理,比如空白字符统一 |
| 日志表越来越大 | 每一条请求都全量落库 | 按天做分区表,保留最近 90 天明细,历史数据做聚合表 |
6.1 上下文窗口超限怎么处理
上下文窗口超限,本质上是输入 token 总量超过了模型限制。最常见于 RAG 应用。
推荐的处理顺序:
- 先裁剪系统提示词,删除不必要的规则;
- 对多轮历史做摘要;
- 限制检索文档的返回条数;
- 如果还是超限,考虑升级到支持更大上下文窗口的模型。
6.2 预估 Token 与实际不一致
很多团队喜欢用“字符数除以 4”来估算 token,在文字以英文为主时勉强接近,但遇到代码、JSON、中文时误差较大。正确的做法是引入模型对应的 tokenizer,并在真实请求后回写 usage 数据。
6.3 “税”如果真落地,技术上需要什么
如果有一天真的需要做全链路 token 计量,从技术角度看至少需要:
- 统一度量基准;
- 网关层记录每个请求的来源应用、用户、模型、token 明细;
- 定期对账;
- 审计日志保留足够长的周期;
- 敏感内容脱敏后再入库。
这些能力其实和普通 API 网关的审计功能没有本质区别,只是把计费单位从“调用次数”变成“token 数”。
7. 最佳实践与工程建议
下面这些建议适合已有 AI 应用,或正准备接大模型 API 的团队参考。
7.1 先计量,再优化
很多团队第一个版本只关注功能和效果,等账单出来后才发现成本失控。正确的节奏是:上线第一天就埋好 token 计量日志。没有计量,就不可能知道该优化谁的提示词、裁剪谁的上下文。
7.2 分层配额,避免互相挤占
不要把总预算放在一个池子里。建议按业务线、应用、甚至用户维度拆分:
- 高优业务使用大模型,配额充足;
- 内部工具使用中型模型,配额适中;
- 个人体验类功能使用小模型,配额有限。
预算不足时,优先做“降级”,而不是直接拒绝。比如从 128K 窗口模型降到 32K 窗口模型,或者从大参数模型换成小参数模型。
7.3 缓存是所有优化里收益最大的
同样的用户问题,如果没有缓存,每个请求都会完整走一次模型推理。引入语义缓存或文本缓存后,相同或相似问题可以直接复用历史答案。这样能大幅降低 token 消耗。
缓存设计时要注意:
- 缓存不适用于需要实时数据的场景;
- 对敏感信息要做好权限隔离;
- 缓存结果要记录版本,避免模型提示词升级后返回旧答案。
7.4 安全与隐私边界
记录 token 消耗时,不要把用户的完整对话明文写入日志。你可以记录:
- 用户哈希 ID;
- 请求来源;
- 应用编号;
- prompt token 数量;
- completion token 数量;
- 用时和状态。
至于消息内容本身,应该只保留在会话系统里,并且按公司数据安全规范设置有效期。任何对日志的查询,都应当遵循最小权限原则,先申请,再授权,再访问。
7.5 报表与预警要自动化
月度报表是事后数据,真正能省钱的是实时预警。建议对这三类指标设阈值:
- 单个应用日消耗 token 突增;
- 单次请求的平均 token 数量上升;
- 输入 token 占总量比例过高。
出现异常时,通过企业微信、钉钉、短信等渠道通知负责人,第一时间定位是提示词膨胀、检索策略异常还是用户流量暴增。
8. 总结与下一步学习方向
回到开头的“token 税”话题。无论这个提法最终走向哪里,它背后都有一个不变的技术问题:AI 应用正在从“免费试玩”走向“精细化运营”,每个团队都需要回答这几个问题:
- 一个请求消耗了多少 token?
- 哪个业务消耗得最多?
- 如果 budget 只剩一半,先砍谁?
本文给了一个最小可运行的 token 记账与配额模块,也解释了 token 计量、上下文窗口、输入输出成本、缓存和配额控制的基本思路。接下来你可以继续学习:
- 不同模型的 tokenizer 原理与 BPE 分词算法;
- API 网关中的流式计费方案;
- 基于 Nebenwelten 的语义缓存与相似度检索;
- 大规模日志分析中的 token 报表设计;
- 多模型路由与自动降级架构。
如果你最近也在做 AI 应用的接入和成本治理,建议先花半天时间,把项目里的 token 日志补上。账清楚了,后面怎么优化都会顺手很多。