如果你没参与前面那次调试,直接看结论可能会一愣:问同一个问题「什么是RAG?」,DeepSeek 显示 296、qwen3:8b 显示 564、qwen3:4b 显示 576。这三个数到底从哪冒出来的?
这篇文章不假设你看过任何实验。我会把代码和真实跑出来的结果一段段贴出来,你边看边能在自己机器上复现。
一、先复现现象:三个数字从哪来
这是一个对比多个大模型的小工具 multi_llm_client.py。装好环境后,运行它并问一个问题:
# 进入项目目录后用自带的虚拟环境运行
.venv\Scripts\python.exe multi_llm_client.py --prompt "什么是RAG?"
终端里每个模型会打印一行 Tokens: total=...。你实际看到的大致是这样:
模型 Tokens (total)
DeepSeek 296
qwen3:8b 564
qwen3:4b 576
问题一模一样,凭什么三个数差这么多?下面一个个拆。
二、Token 到底是什么?先甩一个实验
先破除一个错觉:模型不认识「字」,它只认「token」。你可以把 token 想象成乐高的最小颗粒——一段话被「菜刀」(分词器)剁成大小不一的小块,模型只吃这些小块。
我们直接用 qwen3 的分词器数一下,看「几个字」和「几个 token」差多少。下面这段代码只发问题、不让模型生成(max_tokens=1),专门读它切出了几个 token:
from openai import OpenAI
client = OpenAI(api_key="ollama", base_url="http://localhost:11434/v1")
texts = ["什么是RAG?", "什么是RAG?详细解释它的原理、流程和优缺点。"]
for model in ["qwen3:8b", "qwen3:4b"]:
print(f"=== {model} 分词器 ===")
for t in texts:
r = client.chat.completions.create(model=model,
messages=[{"role": "user", "content": t}],
max_tokens=1, temperature=0)
print(f" {t!r} -> prompt token 数: {r.usage.prompt_tokens}")
真实输出
=== qwen3:8b 分词器 ===
'什么是RAG?' -> prompt token 数: 14
'什么是RAG?详细解释它的原理、流程和优缺点。' -> prompt token 数: 24
=== qwen3:4b 分词器 ===
'什么是RAG?' -> prompt token 数: 14
'什么是RAG?详细解释它的原理、流程和优缺点。' -> prompt token 数: 24
| 文本 | 字符数 | token 数 |
|---|---|---|
什么是RAG? | 7 | 14 |
什么是RAG?详细解释它的原理、流程和优缺点。 | 23 | 24 |
看明白没?7 个字的句子被切成了 14 个 token;23 个纯中文句子才 24 个 token。规律很直白:常见汉字大约 1 字 ≈ 1 token,而英文、数字、标点往往要拆成更多碎片,所以「什么是RAG?」这种中英混排就「吃亏」,token 数翻倍。
三、这个 token 数,到底谁在算?
你可能会以为:是 Python 代码在数字数吧?不是。
那个 296 / 564 / 576 里的每个数,都是 模型服务端(你本机的 Ollama,或 DeepSeek 的云端)用自己的分词器把文字切碎后数出来的。数完,它把结果塞进 API 返回的 usage 字段还给你。你的代码只做了一件事:把它读出来显示。
四、为什么三个模型差这么多?
把三个 total 拆开(prompt 含系统提示约 37 token,可反推 completion):
| 模型 | total | ≈ 输入 | ≈ 输出(答案) | 差在哪 |
|---|---|---|---|---|
| DeepSeek | 296 | ~37 | ~259 | 分词器不同 + 答案最简短 |
| qwen3:8b | 564 | ~37 | ~527 | 答案较长 |
| qwen3:4b | 576 | ~37 | ~539 | 答案略长于 8b |
两个原因叠在一起:
- 分词器不同:DeepSeek 和 qwen3 是两把不同的「菜刀」,同一句中文切出来的 token 数本就不同。
- 答案长短(主因):回头看第二节的实验输出——
qwen3:8b和qwen3:4b对同样的输入,切出的 token 数完全一样(都是 14 和 24)。这说明它俩共用同一把菜刀。那 564 和 576 差的那 12 个 token,纯粹是它俩生成的答案长短差了一点点。而 DeepSeek 只有 296,主要是因为它这次回答得更精简。
五、你也可以自己跑一遍
上面第二节那段代码就是「自查 token 数」的最小工具:把 model 换成任意本地或云端模型,把 content 换成你想测的句子,跑完看 r.usage.prompt_tokens 即可。想连生成的答案一起数,把 max_tokens=1 改大,再读 r.usage.completion_tokens 和 r.usage.total_tokens。
- token ≠ 字符,常见汉字约 1 字 = 1 token。
- Token 数 = 输入 + 输出,由模型分词器算出,代码只读取。
- 不同模型数字不同 = 分词器不同 + 答案长短不同。
- 同系列模型(如 8b / 4b)分词器相同,差异只来自答案长度。