文章总结: 本文梳理了语言模型从N-gram统计方法到Transformer架构的演化历程,重点解析了GPT采用的Decoder-only自回归生成机制与BPE分词原理。文章通过代码示例阐释了注意力机制、掩码操作等关键技术,揭示了LLM通过简单预测任务与大规模训练获得复杂能力的底层逻辑,为理解Agent核心大脑提供了技术视角。
综合评分: 84
文章分类: AI安全,安全开发,安全培训
从零玩转 AI Agent —— 3:LLM 的大脑是怎么造出来的?
原创
Claude Code
Claude Code
Crush Sec
2026年3月20日 09:04
江苏
上一期我们梳了一遍 Agent 的进化史,最后落在”大规模预训练”这个突破点上。 但”预测下一个词”这件事,背后到底是怎么实现的?为什么这么简单的任务,能让一个模型学会推理和规划?
引子:同一个问题,三代工程师的答案
给你这么一行文字:"今天北京天气___"
50 年前的工程师会去翻语料库——”天气”后面出现最多的词是什么,就填什么。30 年前的工程师会训练一个神经网络——给它喂句子,让它学”天气”后面接什么词概率最高。今天的工程师会调一个 GPT——它不只能接上这句话,还知道北京在哪、今天几号、甚至能帮你规划下雨天的行程。
三代方案,同一个任务,能力差了几个数量级。这一期我们就来把这个进化路径搞清楚——不是为了理解数学公式,而是为了真正明白:LLM 凭什么能成为 Agent 的大脑。
一、从”数词”到”懂词”:语言模型怎么进化的
第一代:N-gram——靠数数
最早的语言模型思路极其直白:一个词出现的概率,取决于它前面的词。
听起来很简单,但这个”简单”背后有一个深刻的假设——马尔可夫假设:我不需要记住整句话,只要知道前面 N-1 个词就够了。这就是 N-gram 模型的本质。
用 Python 算一遍就能感受到:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineimport collections
# 两句话的迷你语料库corpus = "datawhale agent learns datawhale agent works"tokens = corpus.split()
bigrams = list(zip(tokens, tokens[1:]))bigram_counts = collections.Counter(bigrams)
# P(learns | agent) = 出现"agent learns"的次数 / 出现"agent"的次数count_agent_learns = bigram_counts[('agent', 'learns')]count_agent = tokens.count('agent')
print(f"P(learns | agent) = {count_agent_learns}/{count_agent} = {count_agent_learns/count_agent}")# 输出: P(learns | agent) = 1/2 = 0.5
运行效果:
ounter(lineP(learns | agent) = 1/2 = 0.5
三个核心拆解:
① 马尔可夫假设让计算变得可行直接算整句话的概率——P(w₁, w₂, ..., wₘ)——语料里基本找不到完全相同的句子,概率全是 0。马尔可夫假设把复杂的全局问题拆成了一串局部计算,这才有了可操作性。
② 最大似然估计:用计数代替概率P(learns | agent) = Count(agent learns) / Count(agent)——不需要任何高深数学,就是最直白的”在我见过的数据里,agent 后面出现 learns 的比例”。
③ 致命缺陷已经埋下它把每个词当成孤立的符号。如果语料里没有 robot learns,这个概率就是 0——哪怕 agent 和 robot 意思差不多。不认识”类似”,只认识”一模一样”。
第二代:词向量——让词有了”坐标”
问题的根源在于:N-gram 模型里,agent 就是字符串 "agent",跟 robot 没有任何关系。
怎么解决?把词变成向量。
研究者们提出了词嵌入(Word Embedding):把每个词映射到一个高维空间里的点。关键在于,语义相近的词,向量距离也相近。这就把”词之间的模糊关系”变成了”向量之间的数学距离”。
最著名的例子:
ounter(linevector("king") - vector("man") + vector("woman") ≈ vector("queen")
这不是魔法,而是模型在大量语料里学到了一种结构:king 和 man 的差距,跟 queen 和 woman 的差距,是同一种”性别偏移”。
用代码验证:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineimport numpy as np
embeddings = { "king": np.array([0.9, 0.8]), "queen": np.array([0.9, 0.2]), "man": np.array([0.7, 0.9]), "woman": np.array([0.7, 0.3])}
def cosine_similarity(a, b): return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
result = embeddings["king"] - embeddings["man"] + embeddings["woman"]sim = cosine_similarity(result, embeddings["queen"])
print(f"king - man + woman 的结果向量: {result}")print(f"与 queen 的相似度: {sim:.4f}")
运行效果:
ounter(lineounter(lineking - man + woman 的结果向量: [0.9 0.2]与 queen 的相似度: 1.0000
词不再是孤立的符号,它有了”语义坐标”。但新的问题来了——早期的模型(RNN)处理长句子时,前面词的信息会随着序列变长而逐渐”稀释”,就像你读一段话读到后半段,忘了开头说了什么。
二、Transformer:彻底解决”记性不好”的问题
2017 年,Google 提出 Transformer,用**注意力机制(Attention)**取代了 RNN 的循环结构。
核心思想只有一句话:
处理每个词时,同时看整个句子,给每个词动态分配”关注度”权重。
以这句话为例:"The agent learns because it is intelligent."
当模型处理 it 时,它需要知道 it 指代谁。注意力机制会让模型给 agent 分配一个很高的权重,给其他词分配低权重——it 的新表示自然融入了 agent 的信息。没有记性的问题,因为每次都是直接”全局看一遍”。
这个计算过程涉及三个角色:
| 角色 | 含义 | 类比 |
| — | — | — |
| Q(Query) | “我在找什么” | 搜索框里的关键词 |
| K(Key) | “我是谁,能被搜到吗” | 文章的标签 |
| V(Value) | “找到了能拿到什么” | 文章的正文 |
计算公式:
ounter(line注意力输出 = softmax(Q × Kᵀ / √d) × V
不需要背公式,只需要记住这个流程:用 Q 去匹配所有 K,算出相似度,转成权重,再对 V 加权求和。
多头注意力是进一步的增强:同时跑 8 个或 16 个”视角”做注意力,每个视角捕捉不同类型的语义关系(指代关系、时态关系、从属关系……),最后合并。就像读一篇文章,你可以同时关注”谁做了什么”和”发生在哪里”这两个维度,而不是只能关注一个。
三、GPT:砍掉一半,专注生成
原始 Transformer 有编码器(理解输入)和解码器(生成输出)两部分。
OpenAI 造 GPT 时问了一个问题:
语言的核心不就是”预测下一个词”吗?只要解码器就够了。
这就是 Decoder-Only 架构,GPT、LLaMA、DeepSeek 全都是这个结构。
工作方式叫自回归——模型一个词一个词往后生成,每次把自己刚生成的词追加进输入再预测下一个:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line输入: "今天北京天气" → 预测: "晴"输入: "今天北京天气晴" → 预测: ","输入: "今天北京天气晴," → 预测: "气温"…… 一个词一个词地往后接
但这里有个”防作弊”机制:训练时,模型要预测第 N 个词,不能提前看到第 N+1 个词的答案。这通过**掩码(Mask)**实现——把未来位置的注意力分数强制设成负无穷,经过 softmax 后概率归零。
为什么这个简单架构能成功?
ounter(lineounter(lineounter(line训练目标极度统一 → 可以用互联网上全部文字训练结构简单 → 容易扩展到千亿参数天然适合生成 → 对话、代码、规划,全都是"续写"
“预测下一个词”看起来是个极其简单的任务。但当你用足够多的数据、足够大的模型去训练这个任务时,模型需要学会语法、学会事实、学会逻辑——因为只有真正”理解”了语言,才能持续预测出合理的下一个词。这就是 LLM 能力的来源:简单任务 + 极大规模。
四、Token:模型眼里的”最小单位”
模型处理的不是字,也不是词,而是 Token(词元)。
这对 Agent 开发者很重要,因为:
- API 按 Token 计费
- 上下文窗口(8K、128K)是 Token 数,不是字数
- 同样的文字,中英文 Token 数差异巨大
现代模型用的是**子词分词(BPE)**算法,逻辑是:常见词保留完整,罕见词拆成更小的片段。
ounter(lineounter(lineounter(line"tokenization" → ["token", "ization"]"unhappy" → ["un", "happy"]"datawhale" → ["data", "whale"] # 如果是不常见词
为什么不直接按字母或按单词切?两个极端都有问题:
- 按字母:词表很小,但单个字母没语义,模型学习效率极低
- 按单词:语义完整,但词表爆炸,新词全是 OOV(未登录词)
BPE 是个”贪心合并”策略:从单个字符出发,反复把出现频率最高的相邻字符对合并成新词元,直到词表达到目标大小。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineimport re, collections
def get_stats(vocab): pairs = collections.defaultdict(int) for word, freq in vocab.items(): symbols = word.split() for i in range(len(symbols) - 1): pairs[symbols[i], symbols[i+1]] += freq return pairs
def merge_vocab(pair, vocab): out = {} bigram = re.escape(' '.join(pair)) pattern = re.compile(r'(?<!\S)' + bigram + r'(?!\S)') for word in vocab: out[pattern.sub(''.join(pair), word)] = vocab[word] return out
# 迷你语料库vocab = {'h u g </w>': 1, 'p u g </w>': 1, 'p u n </w>': 1, 'b u n </w>': 1}
for i in range(4): pairs = get_stats(vocab) best = max(pairs, key=pairs.get) vocab = merge_vocab(best, vocab) print(f"第{i+1}次合并: {best[0]} + {best[1]} → {''.join(best)}")
运行效果:
ounter(lineounter(lineounter(lineounter(line第1次合并: u + g → ug第2次合并: ug + </w> → ug</w>第3次合并: u + n → un第4次合并: un + </w> → un</w>
三个核心设计:
① get_stats 统计相邻词元对的频率遍历词表里每个词,两两配对计数,找出”最常相邻”的那对。频率越高,说明这两个字符经常一起出现,合并它们最划算。
② merge_vocab 合并最高频的词元对把词表里所有词中出现的那对词元合并成一个新词元。这一步改变了词表本身,让下一轮统计基于新的词表进行。
③ 迭代直到词表大小达到目标GPT-4 的词表约 10 万个 Token,就是这样一步步合并出来的。词表大小是个超参数——越大,每个 Token 携带的信息越多,但词表查找开销也越大。
五、和 LLM 交互的几个关键技巧
理解了 LLM 的结构,再来看怎么用好它。这三个技巧直接影响 Agent 的表现。
5.1 Temperature:控制”保守”还是”创意”
LLM 每步预测时,会给词表里所有词算一个概率。Temperature 决定这个概率分布有多”尖锐”还是多”平坦”:
ounter(lineounter(lineTemperature 低(0~0.3)→ 分布尖锐 → 高概率词独大 → 输出保守、确定Temperature 高(0.7~2)→ 分布平坦 → 低概率词也有机会 → 输出多样、发散
| 场景 | 推荐 Temperature |
| — | — |
| 工具调用、JSON 输出、代码生成 | 0 ~ 0.3 |
| 日常对话、文案写作 | 0.3 ~ 0.7 |
| 创意写作、头脑风暴 | 0.7 ~ 1.5 |
对 Agent 开发的启示:Agent 调用工具时需要格式精确,Temperature 要调低;需要给用户提供多样化方案时,调高。
5.2 Few-shot:用例子”教”模型
不用微调,直接在 Prompt 里给几个例子,模型就能理解你要的格式和风格——这就是”上下文学习(In-context Learning)”的原理。
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(lineounter(line# Zero-shot:不给例子,直接指令prompt = "判断这段评论的情感:Datawhale 的课程非常棒!"
# Few-shot:给几个例子作示范prompt = """文本: 这家餐厅的服务太慢了。情感: 负面
文本: 这部电影的情节很平淡。情感: 中性
文本: Datawhale 的课程非常棒!情感:"""# 模型会补全: 正面
Few-shot 的本质:模型在预训练时见过大量”示例-答案”格式的文本,它学会了”这种格式是在要我模仿前面的例子”。所以你给的例子,就是在激活模型已有的能力,而不是在临时教它新技能。
5.3 Chain-of-Thought:让模型”想清楚再回答”
遇到需要推理的问题,在 Prompt 里加一句”请一步一步地思考”,效果往往比直接问好很多:
ounter(lineounter(lineounter(lineounter(lineounter(lineounter(line问题:一个赛季 80 场赢了 60%,下赛季 15 场赢 12 场,两季总胜率多少?请一步一步地思考并回答。
→ 第一步:第一赛季胜场 = 80 × 60% = 48 场→ 第二步:总场数 = 80 + 15 = 95 场,总胜场 = 48 + 12 = 60 场→ 第三步:总胜率 = 60/95 ≈ 63.16%
为什么有效?因为 LLM 是自回归生成的——它生成的每一个词,都会成为后续预测的上下文。强迫模型先写出推理步骤,相当于给它创造了一个”草稿纸”:写下”第一步:48 场”这个词之后,”48″就进入了上下文,后续计算就不容易算错。
这也是 Agent 框架里 Thought 步骤存在的根本原因——不是装样子,而是真的在帮模型算对。
六、一个绕不开的问题:幻觉
理解了 LLM 是怎么工作的,你就能理解为什么它会”一本正经地胡说八道”。
LLM 的训练目标只有一个:预测下一个词。它没有内置的”事实核查模块”,没有办法知道自己说的是不是真的。当它预测出一个词时,它只是觉得”这个词接在这里,概率最高”——不是因为它查过资料,确认过事实。
这就产生了幻觉(Hallucination):模型会编造不存在的论文引用、写出运行不了的代码、给出听起来合理但实际错误的建议。
对 Agent 开发者来说,幻觉是一个工程约束,不是一个 bug。缓解它的主要手段:
- RAG(检索增强生成):生成前先从外部知识库检索相关内容,把事实”锚定”在检索结果上
- 工具调用:让模型调计算器、搜索引擎来处理事实性问题,而不是全靠自己”记”
- 思维链 + 自我验证:强迫模型把推理过程写出来,再检查每一步
幻觉没法完全消除,但通过合理的 Agent 架构设计,可以把它的影响控制在可接受范围内。
七、下一步
现在我们知道了 LLM 是怎么工作的——从统计到向量,从 RNN 到 Transformer,从双向架构到 Decoder-Only,再到 Token、Temperature、CoT,以及幻觉这个绕不过去的约束。
但掌握了大脑的工作原理,还不够。第一章我们手写的那个 Agent 非常脆弱——工具多了 Prompt 就爆炸,模型输出格式稍微偏一点就解析失败,没有任何容错机制。
下一期,我们从零实现三种经典的 Agent 范式:ReAct(边想边做)、Plan-and-Solve(先规划再执行)、Reflection(自我反思纠错)——这三种范式是今天所有主流 Agent 框架(LangChain、AutoGen)的设计原型,搞清楚它们,才能真正看懂框架背后在做什么。
本系列基于 Hello-Agents 开源课程,结合实践整理而成。代码已在 Python 3.10+ 环境验证。本文由 AI 辅助生成。
免责声明:
本文所载程序、技术方法仅面向合法合规的安全研究与教学场景,旨在提升网络安全防护能力,具有明确的技术研究属性。
任何单位或个人未经授权,将本文内容用于攻击、破坏等非法用途的,由此引发的全部法律责任、民事赔偿及连带责任,均由行为人独立承担,本站不承担任何连带责任。
本站内容均为技术交流与知识分享目的发布,若存在版权侵权或其他异议,请通过邮件联系处理,具体联系方式可点击页面上方的联系我。
本文转载自:Crush Sec Claude Code
Claude Code《从零玩转 AI Agent —— 3:LLM 的大脑是怎么造出来的?》