全部笔记All notes

大模型 Token 计算与成本优化

阅读 7m 48s7m 48s read

Token 计算与成本优化

各厂商 Token 计算方式、计费规则和成本控制策略。


什么是 Token?

Token 是大模型处理文本的基本单位,可以理解为”词片段”。模型不直接处理文字,而是先将文本切分成 Token 序列,再进行计算。

理解 Token 很重要,因为:

  • 计费依据:API 按 Token 数量收费
  • 上下文限制:模型有最大 Token 限制
  • 性能影响:Token 越多,响应越慢

Token 的本质:

Token 不是字符,也不是单词,而是 Tokenizer(分词器)切分出的”词片段”。不同模型使用不同的 Tokenizer,所以同样的文本在不同模型中可能有不同的 Token 数。

为什么不直接用字符?

使用 Token 而非字符有几个原因:

  1. 减少序列长度,提高计算效率
  2. 捕捉语义单元(如常见词汇)
  3. 处理多语言和特殊字符

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

在线工具:


成本优化策略

成本优化不是简单地”少用”,而是”用得更聪明”。以下是经过验证的优化策略。

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 Caching50-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?⭐⭐⭐

相关文档