Token 计算与成本优化
各厂商 Token 计算方式、计费规则和成本控制策略。
什么是 Token?
Token 是大模型处理文本的基本单位,可以理解为”词片段”。模型不直接处理文字,而是先将文本切分成 Token 序列,再进行计算。
理解 Token 很重要,因为:
- 计费依据:API 按 Token 数量收费
- 上下文限制:模型有最大 Token 限制
- 性能影响:Token 越多,响应越慢
Token 的本质:
Token 不是字符,也不是单词,而是 Tokenizer(分词器)切分出的”词片段”。不同模型使用不同的 Tokenizer,所以同样的文本在不同模型中可能有不同的 Token 数。
为什么不直接用字符?
使用 Token 而非字符有几个原因:
- 减少序列长度,提高计算效率
- 捕捉语义单元(如常见词汇)
- 处理多语言和特殊字符
Token 概念
flowchart LR
A[文本] --> B[Tokenizer]
B --> C[Token 序列]
subgraph 示例
D["Hello world"] --> E["Hello, world → 2 tokens"]
F["你好世界"] --> G["你好世界 → 2-4 tokens"]
end估算规则
| 语言 | 估算 | 说明 |
|---|---|---|
| 英文 | 1 token ≈ 4 字符 ≈ 0.75 词 | 常见词通常是 1 token |
| 中文 | 1 token ≈ 1-2 汉字 | 常见词可能是 1 token |
| 代码 | 变化大 | 符号和关键字各占 1 token |
Token 切分示例:
"Hello, world!" → ["Hello", ",", " world", "!"] → 4 tokens
"你好世界" → ["你好", "世界"] → 2 tokens (GPT-4)
"def hello():" → ["def", " hello", "(", "):", ] → 4 tokens
影响 Token 数的因素:
| 因素 | 影响 |
|---|---|
| 语言 | 中文通常比英文消耗更多 Token |
| 词汇 | 常见词 Token 少,罕见词 Token 多 |
| 格式 | 空格、换行、标点都占 Token |
| 代码 | 缩进和符号会增加 Token |
计费模型
大模型 API 采用”输入 + 输出”分开计费的模式。输出 Token 通常比输入贵 2-4 倍,因为生成文本比理解文本需要更多计算。
这意味着:
- 长 Prompt + 短回答 = 相对便宜
- 短 Prompt + 长回答 = 相对昂贵
优化成本时,应同时关注输入和输出两端。
flowchart TD
A[总费用] --> B[输入费用]
A --> C[输出费用]
B --> D[输入 Token × 输入单价]
C --> E[输出 Token × 输出单价]输出通常比输入贵 2-4 倍
成本计算公式:
总成本 = (输入 Token × 输入单价) + (输出 Token × 输出单价)
成本估算示例:
假设使用 GPT-4o(输入 $2.50/M,输出 $10.00/M):
- 系统提示:500 tokens
- 用户输入:100 tokens
- 模型回复:300 tokens
单次成本 = (600 × $2.50/1M) + (300 × $10.00/1M) = $0.0015 + $0.003 = $0.0045
每天 10,000 次调用:$45/天 ≈ $1,350/月
价格对比
旗舰模型(每百万 Token)
| 模型 | 输入 | 输出 | 性价比 |
|---|---|---|---|
| GPT-4o | $2.50 | $10.00 | 综合最强 |
| Claude Sonnet | $3.00 | $15.00 | 长文本优势 |
| Gemini 1.5 Pro | $1.25 | $5.00 | 最便宜 |
轻量模型(每百万 Token)
| 模型 | 输入 | 输出 | 适用场景 |
|---|---|---|---|
| GPT-4o-mini | $0.15 | $0.60 | 简单对话 |
| Claude Haiku | $0.80 | $4.00 | 快速响应 |
| Gemini Flash | $0.075 | $0.30 | 成本敏感 |
⚠️ 价格变动频繁,以官网为准
价格差异分析:
旗舰模型和轻量模型的价格差距可达 10-20 倍。对于简单任务,使用轻量模型可以大幅降低成本,同时保持足够的质量。
Token 计算工具
OpenAI (tiktoken)
tiktoken 是 OpenAI 官方的 Token 计算库,速度快、准确:
import tiktoken
enc = tiktoken.encoding_for_model("gpt-4o")
tokens = enc.encode("Hello world")
print(len(tokens)) # 2
# 查看具体的 Token
print(tokens) # [9906, 1917]
print([enc.decode([t]) for t in tokens]) # ['Hello', ' world']
Anthropic
Claude 没有公开的 Tokenizer,但提供了 API 计算:
# 使用 API 计算
resp = client.messages.count_tokens(
model="claude-sonnet-4-20250514",
messages=[{"role": "user", "content": "Hello"}]
)
print(resp.input_tokens)
通用估算
当无法使用精确工具时,可以用经验公式估算:
def estimate_tokens(text, language="en"):
if language == "en":
return len(text) // 4 # 英文:约 4 字符/token
else:
return int(len(text) * 1.5) # 中文:约 0.7 字符/token
在线工具:
- OpenAI Tokenizer: https://platform.openai.com/tokenizer
- tiktoken playground: 各种在线版本
成本优化策略
成本优化不是简单地”少用”,而是”用得更聪明”。以下是经过验证的优化策略。
flowchart TD
A[成本优化] --> B[减少输入]
A --> C[减少输出]
A --> D[选择模型]
A --> E[使用缓存]
B --> B1[压缩上下文]
B --> B2[精简 Prompt]
C --> C1[限制 max_tokens]
C --> C2[要求简洁回答]
D --> D1[简单任务用小模型]
D --> D2[复杂任务用大模型]
E --> E1[Prompt Caching]
E --> E2[响应缓存]优化效果对比:
| 策略 | 节省比例 | 实施难度 |
|---|---|---|
| 选择合适模型 | 50-90% | 低 |
| Prompt Caching | 50-90% | 中 |
| 压缩上下文 | 20-50% | 中 |
| 响应缓存 | 取决于命中率 | 中 |
| 限制输出长度 | 10-30% | 低 |
优化技巧
1. 上下文压缩
多轮对话中,历史消息会不断累积,导致输入 Token 快速增长。需要定期”瘦身”。
| 方法 | 说明 |
|---|---|
| 滑动窗口 | 只保留最近 N 轮对话 |
| 摘要压缩 | 定期总结历史对话 |
| 选择性保留 | 只保留关键信息 |
2. Prompt Caching(Claude)
Claude 支持缓存重复的系统提示词。如果你的 System Prompt 很长且固定,开启缓存可以节省高达 90% 的输入成本。
缓存有效期为 5 分钟,适合高频调用场景。
flowchart LR
A[首次请求] --> B[缓存 System Prompt]
C[后续请求] --> D[复用缓存]
D --> E[节省 90% 输入成本]# Claude 缓存示例
messages.create(
system=[{
"type": "text",
"text": "长系统提示...",
"cache_control": {"type": "ephemeral"}
}],
...
)
3. 模型路由
不是所有任务都需要最强的模型。简单任务用小模型,复杂任务用大模型,可以在保证质量的同时大幅降低成本。
建议建立任务分类机制,自动路由到合适的模型。
flowchart TD
A[请求] --> B{任务复杂度}
B -->|简单| C[GPT-4o-mini / Haiku / Flash]
B -->|复杂| D[GPT-4o / Sonnet / Pro]
C --> E[低成本]
D --> F[高质量]成本监控
关键指标
| 指标 | 说明 |
|---|---|
| 日均 Token | 每日消耗量 |
| 单次成本 | 每次请求平均成本 |
| 输入/输出比 | 优化方向参考 |
计算公式
单次成本 = (输入Token × 输入单价 + 输出Token × 输出单价) / 1,000,000
成本对比示例
假设:1000 次请求,每次输入 500 token,输出 200 token
| 模型 | 输入成本 | 输出成本 | 总计 |
|---|---|---|---|
| GPT-4o | $1.25 | $2.00 | $3.25 |
| GPT-4o-mini | $0.075 | $0.12 | $0.195 |
| Claude Sonnet | $1.50 | $3.00 | $4.50 |
| Gemini Flash | $0.0375 | $0.06 | $0.0975 |
Gemini Flash 成本约为 GPT-4o 的 3%
月度成本估算:
假设每天 10,000 次请求:
| 模型 | 日成本 | 月成本 |
|---|---|---|
| GPT-4o | $32.50 | $975 |
| GPT-4o-mini | $1.95 | $58.50 |
| Gemini Flash | $0.975 | $29.25 |
图像 Token 计算
多模态输入中,图像也会消耗 Token。不同厂商的计算方式不同,图像通常比文本消耗更多 Token。
OpenAI 图像 Token
OpenAI 根据图像尺寸和 detail 参数计算:
| detail | 计算方式 | 适用场景 |
|---|---|---|
low | 固定 85 tokens | 不需要细节 |
high | 按 512x512 切片计算 | 需要识别细节 |
auto | 自动选择 | 默认 |
高清模式计算公式:
高清图像 Token = ceil(宽/512) × ceil(高/512) × 170 + 85
示例:
- 512x512 图像:1 × 1 × 170 + 85 = 255 tokens
- 1024x1024 图像:2 × 2 × 170 + 85 = 765 tokens
- 2048x1024 图像:4 × 2 × 170 + 85 = 1445 tokens
Claude 图像 Token
Claude 按图像尺寸估算:
Token ≈ (宽 × 高) / 750
示例:
- 500x500 图像 ≈ 333 tokens
- 1000x1000 图像 ≈ 1333 tokens
- 2000x1000 图像 ≈ 2667 tokens
Gemini 图像 Token
Gemini 对图像 Token 计算相对宽松:
- 每张图像约 258 tokens(固定)
图像成本优化
| 策略 | 说明 | 节省比例 |
|---|---|---|
| 压缩图像 | 降低分辨率到够用即可 | 50-80% |
| 使用 low detail | 不需要细节时 | 70-90% |
| 裁剪关键区域 | 只发送必要部分 | 取决于裁剪比例 |
| 批量处理 | 多图合并为一张 | 减少 base token |
# 图像压缩示例
from PIL import Image
import io
import base64
def compress_image(image_path, max_size=1024, quality=85):
"""压缩图像以减少 Token 消耗"""
img = Image.open(image_path)
# 调整尺寸
if max(img.size) > max_size:
ratio = max_size / max(img.size)
new_size = (int(img.size[0] * ratio), int(img.size[1] * ratio))
img = img.resize(new_size, Image.LANCZOS)
# 压缩质量
buffer = io.BytesIO()
img.save(buffer, format="JPEG", quality=quality)
return base64.b64encode(buffer.getvalue()).decode()
批量处理优化
大量请求时,批量处理可以显著降低成本和提高效率。
OpenAI Batch API
OpenAI 提供 Batch API,成本降低 50%,但延迟增加(24 小时内完成)。适合非实时任务。
import json
# 1. 准备批量请求文件
requests = [
{
"custom_id": f"request-{i}",
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": "gpt-4o-mini",
"messages": [{"role": "user", "content": f"问题 {i}"}]
}
}
for i in range(100)
]
# 保存为 JSONL 文件
with open("batch_requests.jsonl", "w") as f:
for req in requests:
f.write(json.dumps(req) + "\n")
# 2. 上传文件
file = client.files.create(
file=open("batch_requests.jsonl", "rb"),
purpose="batch"
)
# 3. 创建批量任务
batch = client.batches.create(
input_file_id=file.id,
endpoint="/v1/chat/completions",
completion_window="24h"
)
# 4. 查询状态
status = client.batches.retrieve(batch.id)
print(f"状态: {status.status}, 完成: {status.request_counts.completed}")
# 5. 获取结果
if status.status == "completed":
result_file = client.files.content(status.output_file_id)
# 解析结果...
并发优化
实时场景下,使用异步并发提高吞吐量:
import asyncio
from openai import AsyncOpenAI
client = AsyncOpenAI()
async def batch_process(prompts, concurrency=10):
"""并发处理多个请求"""
semaphore = asyncio.Semaphore(concurrency)
async def process_one(prompt):
async with semaphore:
return await client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
return await asyncio.gather(*[process_one(p) for p in prompts])
# 使用
prompts = ["问题1", "问题2", "问题3", ...]
results = asyncio.run(batch_process(prompts))
成本监控与预警
监控指标
| 指标 | 说明 | 预警阈值 |
|---|---|---|
| 日均 Token | 每日消耗量 | 超过预算 80% |
| 单次成本 | 每次请求平均成本 | 异常高于平均 |
| 输入/输出比 | 优化方向参考 | 输出占比过高 |
| 错误率 | 失败请求比例 | 超过 5% |
成本追踪代码
import time
from dataclasses import dataclass
from typing import Optional
@dataclass
class UsageRecord:
timestamp: float
model: str
input_tokens: int
output_tokens: int
cost: float
class CostTracker:
def __init__(self):
self.records = []
self.daily_budget = 100.0 # 日预算
# 价格表(每百万 Token)
PRICES = {
"gpt-4o": {"input": 2.50, "output": 10.00},
"gpt-4o-mini": {"input": 0.15, "output": 0.60},
}
def record(self, model: str, input_tokens: int, output_tokens: int):
prices = self.PRICES.get(model, {"input": 0, "output": 0})
cost = (input_tokens * prices["input"] + output_tokens * prices["output"]) / 1_000_000
record = UsageRecord(
timestamp=time.time(),
model=model,
input_tokens=input_tokens,
output_tokens=output_tokens,
cost=cost
)
self.records.append(record)
# 检查预算
daily_cost = self.get_daily_cost()
if daily_cost > self.daily_budget * 0.8:
print(f"⚠️ 警告:日成本已达 ${daily_cost:.2f},接近预算上限")
return cost
def get_daily_cost(self) -> float:
today_start = time.time() - (time.time() % 86400)
return sum(r.cost for r in self.records if r.timestamp >= today_start)
# 使用
tracker = CostTracker()
response = client.chat.completions.create(...)
tracker.record(
model="gpt-4o-mini",
input_tokens=response.usage.prompt_tokens,
output_tokens=response.usage.completion_tokens
)
成本优化清单
| 优化项 | 检查点 | 优先级 |
|---|---|---|
| 模型选择 | 简单任务是否用了大模型? | ⭐⭐⭐⭐⭐ |
| Prompt 长度 | System Prompt 是否过长? | ⭐⭐⭐⭐ |
| 历史管理 | 是否定期清理对话历史? | ⭐⭐⭐⭐ |
| 输出限制 | 是否设置了 max_tokens? | ⭐⭐⭐ |
| 缓存 | 是否启用了 Prompt Caching? | ⭐⭐⭐⭐ |
| 响应缓存 | 相同问题是否重复调用? | ⭐⭐⭐ |
| 图像优化 | 图像是否压缩到合适大小? | ⭐⭐⭐ |
| 批量处理 | 非实时任务是否用 Batch API? | ⭐⭐⭐ |
相关文档
- 01-大模型API-协议概述
- 08-大模型-SDK使用指南
- 04-Claude-Messages-API - Prompt Caching
- 14-大模型-生产环境最佳实践