← 返回 Learning
Learning · LLM API

Token 数量是怎么算出来的?

如果你没参与前面那次调试,直接看结论可能会一愣:问同一个问题「什么是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?714
什么是RAG?详细解释它的原理、流程和优缺点。2324

看明白没?7 个字的句子被切成了 14 个 token;23 个纯中文句子才 24 个 token。规律很直白:常见汉字大约 1 字 ≈ 1 token,而英文、数字、标点往往要拆成更多碎片,所以「什么是RAG?」这种中英混排就「吃亏」,token 数翻倍。

三、这个 token 数,到底谁在算?

你可能会以为:是 Python 代码在数字数吧?不是。

那个 296 / 564 / 576 里的每个数,都是 模型服务端(你本机的 Ollama,或 DeepSeek 的云端)用自己的分词器把文字切碎后数出来的。数完,它把结果塞进 API 返回的 usage 字段还给你。你的代码只做了一件事:把它读出来显示。

记住:Token 数 = 输入(prompt) + 输出(completion),由「模型的分词器」决定,你的代码只是读取。换一个模型,同一句话的 token 数就可能不同——因为分词器换了把「菜刀」。

四、为什么三个模型差这么多?

把三个 total 拆开(prompt 含系统提示约 37 token,可反推 completion):

模型total≈ 输入≈ 输出(答案)差在哪
DeepSeek296~37~259分词器不同 + 答案最简短
qwen3:8b564~37~527答案较长
qwen3:4b576~37~539答案略长于 8b

两个原因叠在一起:

  • 分词器不同:DeepSeek 和 qwen3 是两把不同的「菜刀」,同一句中文切出来的 token 数本就不同。
  • 答案长短(主因):回头看第二节的实验输出——qwen3:8bqwen3:4b同样的输入,切出的 token 数完全一样(都是 14 和 24)。这说明它俩共用同一把菜刀。那 564 和 576 差的那 12 个 token,纯粹是它俩生成的答案长短差了一点点。而 DeepSeek 只有 296,主要是因为它这次回答得更精简。

五、你也可以自己跑一遍

上面第二节那段代码就是「自查 token 数」的最小工具:把 model 换成任意本地或云端模型,把 content 换成你想测的句子,跑完看 r.usage.prompt_tokens 即可。想连生成的答案一起数,把 max_tokens=1 改大,再读 r.usage.completion_tokensr.usage.total_tokens

速记四句话:
  • token ≠ 字符,常见汉字约 1 字 = 1 token。
  • Token 数 = 输入 + 输出,由模型分词器算出,代码只读取。
  • 不同模型数字不同 = 分词器不同 + 答案长短不同。
  • 同系列模型(如 8b / 4b)分词器相同,差异只来自答案长度。