做AI应用这一年多,我被问得最多的问题,几乎都绕不开三个词:Token、上下文窗口、采样参数。很多人天天在调大模型API,却搞不清“回答到一半被截断”到底是哪里出了问题,也不知道为什么同一个模型在temperature=0.1和0.9下像两个人在说话。这篇文章我想一次性把这三件事拆开揉碎讲清楚,不吹架构、不堆术语,而是把背后的运行逻辑、实测数据和踩坑记录一起放出来。不管你是刚接触LLM的新手,还是正在做RAG应用、Agent开发、AI编程工具的老手,这套认知都能帮你少走很多弯路。
先提醒一句:大模型并不像人一样“读字”。它看到的是一个接一个的数字块,这些数字块由Tokenizer把文本切分而成。理解Token怎么切,你才能明白为什么中文比英文“贵”,为什么同样的文本在不同模型里消耗不同;理解上下文窗口,你才能明白模型为什么“记不住”前面的内容,为什么会报超长错误;理解采样参数,你才能控制模型输出是严谨还是发散。下面我一个一个说。
1. 先看 Token:大模型眼中没有“字”,只有“数字”
1.1 Tokenizer 到底做了什么
大模型的一切输入,最终都会变成数字向量。这中间最关键的一步就是Tokenizer(分词器)。它的任务是把原始字符串切割成Token(词元),每个Token再映射成一个整数ID,模型拿到的其实是这个ID序列,再通过嵌入层转成向量去计算。
当前主流大模型用的分词算法大多是BPE(Byte Pair Encoding,字节对编码)及其变体。核心逻辑一句话概括:先按单个字符切,然后统计语料里哪些字符组合出现频率最高,把它们合并成一个Token,反复迭代,直到达到预设的词表大小。所以英文里高频词“the”“is”通常各占1个Token,低频词或生僻词可能被拆成好几个Token。
写代码验证最直观。用OpenAI开源的tiktoken库,几行就能看到Token切分结果:
python复制import tiktoken
enc = tiktoken.get_encoding("cl100k_base")
text = "I love programming"
tokens = enc.encode(text)
print(tokens)
# 输出类似 [40, 2200, 4996] 这样的 ID 列表
print([enc.decode([t]) for t in tokens])
# 大概率得到 ['I', ' love', ' programming']
注意,Token不是字符,也不完全等于单词。上面例子里的“ love”前面还带着一个空格,这是BPE基于字节合并时的常见现象。很多人在统计Token数量时习惯按“1个英文单词约等于1.3个Token”估算,实际情况要复杂得多:标点、空格、换行、缩写、数字都要单独占Token。
1.2 中英文与代码的 Token 消耗差异
中文在Token消耗上是个让很多人肉疼的问题。英文一个常见单词基本就是1个Token,而中文的常用字经过BPE合并后,一个汉字通常占1到2个Token,两三个字组成的常用词有时能合并成1个Token。粗略估算,同样语义的一句话,中文的Token消耗往往比英文高出30%到80%。
代码更夸张。缩进、空格、换行、各种符号都会产生Token。同样1000个字符,纯英文散文可能只要300个Token,但Python代码因为大量缩进和符号,可能要到400甚至500个Token。这意味着你在写AI编码工具的Prompt时,对代码长度的敏感度要比处理普通文本高得多。
还有一个很容易被忽略的点:不同模型的Tokenizer不同。同一段中文,GPT-4的cl100k_base词表和Claude系列的词表算出来的Token数量可能有明显差异。所以你在A模型上算好的Token预算,放到B模型上直接翻车。最稳妥的办法是每次看API返回的usage字段,那个数字才是计费和限制的真实依据。
1.3 Token 用量、计费与上限
Token是所有大模型API计费的基本单位。一次完整调用的费用,等于输入的Prompt Tokens加上输出的Completion Tokens,两者价格还不一样,通常输出比输入贵2到10倍。很多SaaS产品为了简化用户感知,会
