Token 与上下文窗口

Token 是 LLM 的计费单位,上下文窗口是能力边界。理解这两个概念是成本控制和架构设计的基础。


Token 基础

什么算一个 Token?

内容约等于 Token 数
1 个英文字符0.25
1 个中文字符1-2
1 个英文单词1.5
1 个代码 Token~4 字符
一个中文字(中文)1-2

粗略估算:中文 1 Token ≈ 1 个汉字;英文 1 Token ≈ 0.75 个单词。

Token 计数的实际影响

1
2
3
4
5
6
7
8
GPT-4o 定价(2025年参考):
- 输入:$2.5 / 1M Token
- 输出:$10 / 1M Token

实际例子:
一篇 2000 字的中文文章 ≈ 2000 Token 输入
一次回复 500 字 ≈ 500 Token 输出
每次 API 调用成本 ≈ $2.5/1M × 2500 ≈ $0.00625 ≈ 0.6分钱

代码 Token 效率

代码重复率高,同样的 Token 数能容纳更多代码:

1
2
Python 代码:1 Token ≈ 4 字符(高度重复,压缩率高)
自然语言:1 Token ≈ 0.75 词

上下文窗口(Context Window)

什么是上下文窗口?

上下文窗口 = 输入 + 输出能容纳的最大 Token 数(不含模型参数)。

模型上下文窗口说明
GPT-4o128K最大众
GPT-4o-mini128K性价比
Claude 3.5 Sonnet200K最大之一
Claude 3 Opus200K旗舰
Claude 3 Haiku200K便宜
Llama 3.1 8B128K开源
Qwen2.5-7B32K(可扩展)国产
DeepSeek-V364K国产

上下文耗尽时会发生什么?

1
2
3
4
5
6
7
8
9
问题:上下文窗口满了 LLM 会怎么做?

答案:取决于具体实现

1. 截断(Truncation,最常见)

2. 滑动窗口

3. 主动摘要(部分系统支持)

上下文管理策略

策略 1:简单截断

1
2
3
4
5
6
7
8
9
10
# LangChain MessagesHistory 简单实现
messages = []
MAX_TOKENS = 16000 # 留 2K 给输出

def add_message(messages, role, content):
messages.append({"role": role, "content": content})
# 简单截断:超过则丢弃最早的
while count_tokens(messages) > MAX_TOKENS:
messages.pop(0) # 丢弃最早的
return messages

策略 2:摘要压缩(Summarize)

1
2
3
4
5
# 对话太长时,先摘要再继续
def compress_history(messages):
summary_prompt = "将以下对话压缩为50字摘要:\n" + str(messages)
summary = llm.invoke(summary_prompt) # 用 LLM 生成摘要
return [{"role": "system", "content": f"对话摘要:{summary}"}]

策略 3:选择性保留(Semantic)

1
2
3
4
5
6
7
8
9
10
# 只保留与当前问题语义相关的历史
def filter_relevant_history(messages, current_query):
query_emb = embedding.embed(current_query)
scored = []
for msg in messages:
msg_emb = embedding.embed(msg["content"])
score = cosine_similarity(query_emb, msg_emb)
scored.append((score, msg))
# 只保留相关性 > 0.7 的历史
return [msg for score, msg in scored if score > 0.7]

Token 计数工具

1
2
3
4
5
6
7
8
9
10
11
# Tiktoken(OpenAI 官方分词器)
import tiktoken

enc = tiktoken.get_encoding("cl100k_base") # GPT-4 用这个
tokens = enc.encode("你好,世界")
print(len(tokens)) # 5

# 计算成本
prompt_tokens = len(tokens)
cost = prompt_tokens / 1_000_000 * 2.5 # $2.5 per 1M input
print(f"成本: ${cost:.4f}")
1
2
3
4
# 中文模型计 Token(近似)
def count_chinese_tokens(text: str) -> int:
"""中文字符粗略估算"""
return len(text) * 1.5 # 留余量

生产环境 Token 管理

分级降级策略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
def answer_with_budget(question: str, budget: float):
"""
根据预算自动选择方案
budget: 允许的最大成本(美元)
"""
estimated_tokens = estimate_tokens(question)

if budget < 0.001:
# 极低成本:用 embedding 搜索直接返回,不调 LLM
return vector_db.similarity_search(question)

if budget < 0.01:
# 低成本:gpt-4o-mini + 短上下文
return llm_mini.invoke(question)

if budget < 0.1:
# 中成本:gpt-4o + 标准 RAG
return rag_pipeline(question)

# 高成本:Claude 3.5 + 长上下文 + 多轮
return claude_long_context(question)

上下文窗口的架构影响

问题影响解决方案
对话越来越长,成本增加每次调用都带入全部历史定期摘要 / 滑动窗口
长文档 RAG 超限无法同时处理全文分块检索 + 二次排序
Agent 记忆过长关键信息被稀释重要记忆存入外部存储

最佳实践

  1. 对话系统:设置 Token 上限,用摘要或截断管理
  2. 长文档 RAG:先分块,检索 Top-K 后再让 LLM 读
  3. 代码生成:用代码专用模型(如 Cursor 用 GPT-4o-mini),Token 成本更低
  4. 成本控制:API 调用前先估算 Token 数