我用 30 行代码,搞懂了大模型是怎么"读"中文的
💡 你知道吗?你发给 ChatGPT 的每一句话,它其实根本看不懂。它看到的是一串数字,而你看到的文字,在它眼里只是"Token"。
写在前面
前两天有个朋友问我:"为什么我让 AI 帮我翻译一篇文章,明明没几个字,API 调用花了好几块钱?"
我当时就想,这哥们肯定不知道 Token 这玩意儿。
后来我花了一下午,从 GPT 之父 Karpathy 的视频啃到源码,终于把 Token 和 Embedding 这俩概念彻底搞明白了。今天分享给大家,保证看完你就知道:
- 为什么中文比英文"贵"
- 为什么 AI 能理解"猫"和"狗"是相似的东西
- 为什么你写的 Prompt 越长,钱包越疼
- 为什么网传的"文言文省钱法"其实是个坑
一、先说结论:AI 是个"文盲"
你没看错,大模型本质上是个文盲。
它不认识字,不认识单词,不认识任何人类语言。它只认识数字。
这就很离谱了------一个不认识字的东西,怎么写出那么流畅的文章?
秘密就在于:在它"看"文字之前,先把文字变成数字了。
这个过程叫 Tokenization(分词) ,而变出来的数字就叫 Token。
二、Token 到底是啥?
你可以把 Token 理解为大模型的"词汇单位"。
但这里有个坑:Token ≠ 单词,Token ≠ 汉字
举个例子:
javascript
// "Hello" → 1 个 token
// "Tokenization" → 可能被拆成 3-4 个 token
// "你好世界" → 大概 2-4 个 token
为什么不能直接用一个字/单词当一个 token 呢?
因为:
- 词表会爆炸:英语几十万单词,中文几万个汉字,再加上各种组合,词表会大到离谱
- 没见过的词怎么办:你总不能说"这个词不在词表里,我就不处理了吧"
- 效率问题:token 太细,一句话变成几百个 token,计算量扛不住
所以 GPT 用了一个叫 BPE(Byte Pair Encoding) 的算法,简单说就是:
高频出现的字符组合,会被合并成一个 token
就像"的"字在中文里出现频率超高,它就会被单独编码成一个 token。而一些生僻词,可能会被拆成几个常见的 token 组合。
三、动手试试:30 行代码搞懂分词
光说不练假把式,直接上代码:
javascript
import { getEncoding } from 'js-tiktoken';
// GPT-4 用的编码器
const enc = getEncoding('cl100k_base');
// 试试中英文混合
const text = "Hello, tiktoken! 你好,世界!";
const tokens = enc.encode(text);
console.log("原始文本:", text);
console.log("Token IDs:", tokens);
console.log("Token 数量:", tokens.length);
// 反向解码验证
console.log("解码回来:", enc.decode(tokens));
输出大概长这样:
yaml
原始文本: Hello, tiktoken! 你好,世界!
Token IDs: [9906, 11, 65402, 9039, 0, 57668, 3837, 35148, 120657, 30982]
Token 数量: 10
解码回来: Hello, tiktoken! 你好,世界!
看到了吧,一句看起来很短的话,已经用了 10 个 token。
而且中文特别吃亏------一个汉字大概 0.6-1 个 token,而英文一个字母才 0.3 个 token。所以同样的意思,中文表达会更贵。
这就是为什么很多程序员写 Prompt 喜欢用英文的原因之一(不是耍帅,是真的省钱)。
四、网传的"文言文省钱大法",靠谱吗?
最近网上有个特别火的说法:用文言文写 Prompt,token 数直接腰斩。
我一开始也觉得离谱,直到我亲自测了一下......结果发现事情没那么简单。
实测:文言文常用字反而更贵!
javascript
import { getEncoding } from 'js-tiktoken';
const enc = getEncoding('cl100k_base');
// 单字对比
console.log("我:", enc.encode("我").length); // 1 token
console.log("吾:", enc.encode("吾").length); // 2 tokens!
console.log("你:", enc.encode("你").length); // 1 token
console.log("汝:", enc.encode("汝").length); // 2 tokens!
console.log("看:", enc.encode("看").length); // 1 token
console.log("观:", enc.encode("观").length); // 2 tokens!
// 生僻字更离谱
console.log("矣:", enc.encode("矣").length); // 3 tokens!
console.log("兮:", enc.encode("兮").length); // 2 tokens
文言文那些"古风"字,很多反而要 2-3 个 token!
那为什么整体看起来省了?
因为文言文句子更短。虽然单字贵,但字数少,总 token 数可能还是少一些:
| 场景 | 现代中文 | 文言文 | 差异 |
|---|---|---|---|
| 简单指令 | "帮我翻译成英文" (11) | "译为英文" (5) | -55% |
| 复杂指令 | "请帮我分析这段代码的性能问题..." (28) | "析此码之性能..." (22) | -21% |
| 带专业术语 | "用 React hooks 实现防抖" (11) | "以 React hooks 作防抖" (11) | 0% |
但问题来了:省的那点钱,值吗?
不值。 原因如下:
- 歧义风险:文言文表达可能让 AI 理解偏差,回答不对 → 多轮追问 → 总成本更高
- 专业术语省不了:React、TypeScript、API 这些该几个 token 还是几个
- 维护成本:你写 Prompt 要先"翻译"成文言文,累不累?
真正省钱的方法:结构化格式
javascript
// 啰嗦版:47 tokens
"请帮我分析一下这段代码的性能问题,我想知道有哪些地方可以优化,能不能给出具体的优化建议和代码示例。"
// 文言文版:24 tokens(但有歧义风险)
"析此码之性能,寻可优化之处,予策并示其码。"
// 结构化版:32 tokens(清晰 + 省钱)✅ 推荐
"分析代码性能:\n- 找出瓶颈\n- 给出优化建议\n- 附代码示例"
// 极简版:15 tokens(最省,但可能丢信息)
"性能分析 + 优化建议 + 代码示例"
结论:用现代中文 + 结构化格式(bullet points、markdown headers),既省 token 又保证质量。
别迷信文言文,那是噱头。真正省 token 的是精简表达 + 结构化。
五、Embedding:让 AI 看懂语义
好,现在我们知道文字变成 Token ID 了。但问题来了:
javascript
// "猫" → token ID: 1234
// "狗" → token ID: 5678
这两个数字在数学上毫无关系,模型怎么知道它们都是动物?
答案是 Embedding(向量化)。
什么是 Embedding?
简单说,就是把每个 token 映射到一个高维空间里的一个点。
arduino
"猫" → [0.2, -0.5, 0.8, 0.1, ...] // 1024 维向量
"狗" → [0.3, -0.4, 0.7, 0.2, ...] // 很接近!
"汽车" → [-0.8, 0.1, 0.3, -0.6, ...] // 差很远
在这个空间里,语义相近的词,距离也相近。
这就像你在地图上找位置------北京和天津很近,和纽约很远。Embedding 就是给每个词画了一张"语义地图"。
实测:用 API 算语义相似度
javascript
import OpenAI from 'openai';
const client = new OpenAI({
apiKey: process.env.DASHSCOPE_API_KEY,
baseURL: process.env.DASHSCOPE_baseURL,
});
async function getEmbedding(text) {
const res = await client.embeddings.create({
model: 'text-embedding-v4',
input: text,
dimensions: 1024
});
return res.data[0].embedding;
}
// 余弦相似度:算两个向量有多"像"
function cosineSimilarity(a, b) {
let dot = 0, magA = 0, magB = 0;
for (let i = 0; i < a.length; i++) {
dot += a[i] * b[i];
magA += a[i] ** 2;
magB += b[i] ** 2;
}
return dot / (Math.sqrt(magA) * Math.sqrt(magB));
}
// 测试!
const vec1 = await getEmbedding("Karpathy 讲解大模型分词原理");
const vec2 = await getEmbedding("GPT 之父解释 Tokenization");
const vec3 = await getEmbedding("今天中午吃什么");
console.log(cosineSimilarity(vec1, vec2)); // 0.85+ ← 语义相似
console.log(cosineSimilarity(vec1, vec3)); // 0.15- ← 毫不相干
神奇吧? 两句话用词完全不同,但 Embedding 知道它们"意思差不多"。
六、这玩意儿有啥用?
你可能觉得"哦知道了,然后呢?"
其实 Token + Embedding 是很多 AI 应用的基石:
1. 语义搜索
传统搜索只能关键词匹配,搜"苹果手机"找不到"iPhone"。
有了 Embedding,你可以算语义相似度,搜"苹果手机"也能找到"iPhone 15 Pro Max 评测"。
2. RAG(检索增强生成)
就是让大模型先在你的文档里搜相关内容,再基于搜到的内容回答问题。
没有 Embedding,RAG 就是个笑话。
3. 智能推荐
用户喜欢看"JavaScript 性能优化",你就可以用 Embedding 找语义相似的文章推荐给他。
4. 去重
一堆文章里有很多重复内容?Embedding + 相似度阈值,轻松去重。
七、一些踩坑经验
1. Token 数量决定成本
调 API 是按 Token 计费的。你发给模型的 Prompt 越长,返回的内容越多,钱包越疼。
所以写 Prompt 的时候:
- 能用一句说清的,别写三句
- 别把无关的上下文塞进去
- 善用 System Prompt 复用
- 用结构化格式,别用文言文
2. 中文真的贵
同样的意思,英文表达比中文便宜 30-50%。如果你的场景对语言没要求,用英文 Prompt 能省不少钱。
3. 不是维度越高越好
Embedding 维度越高,理论上表达能力越强,但计算成本也越高。1024 维对于大多数场景已经够用了。
最后
Token 和 Embedding 这两个概念,说难不难,说简单也不简单。但理解它们之后,你会对大模型的工作原理有质的认知。
下次再看到 API 按 Token 计费,你就知道为什么了。
下次再写 Prompt,你会更注意精简,用结构化格式而不是啰嗦的长句。
下次再做 RAG,你会知道底层是怎么检索的。
这就是底层知识的价值------它不会让你立刻变强,但会让你理解"为什么"。