LLM 原理:工程师必懂的核心概念

目标读者:不需要自己训练模型,但需要深刻理解 LLM 能做什么、不能做什么、为什么会出错的工程师和产品人。


一、LLM 是什么

1.1 一句话定义

LLM(Large Language Model,大语言模型)本质上是一个概率预测机器

给定前面所有的文字,预测下一个词(Token)最可能是什么。

就这么简单。但正是这个看似简单的任务,在超大规模数据和参数下,涌现出了推理、写作、编程、翻译等复杂能力。

1.2 Token 是什么

LLM 处理的基本单位不是"字",而是 Token

英文:
  "Hello, world!" → ["Hello", ",", " world", "!"]  → 4 个 Token

中文:
  "你好世界"      → ["你好", "世界"]               → 2 个 Token
  (中文通常 1~2 个汉字对应 1 个 Token)

代码:
  "print('hello')" → ["print", "('", "hello", "')"] → 4 个 Token

关键数字

  • GPT-4 的上下文窗口:128K Token ≈ 约 10 万个汉字
  • Claude 3.5:200K Token ≈ 约 15 万个汉字
  • 1000 Token ≈ 750 个英文单词 ≈ 500 个汉字

二、Transformer:LLM 的核心架构

2.1 架构全景

输入文本(字符串)
   ↓
[Tokenizer]              文本 → Token ID 序列
                         "今天天气" → [1234, 5678, 9012]
   ↓
[Embedding 层]           Token ID → 向量
                         每个 ID 映射为一个高维浮点数向量
                         [1234] → [0.12, -0.34, 0.87, ...]
   ↓
[N × Transformer Block]
   ├── [Self-Attention]  每个 Token 关注其他所有 Token,更新向量表示
   └── [FFN]            前馈网络,进一步提取特征
   ↓
[输出层(Linear + Softmax)]
                         最后一个位置的向量 → 词表上每个 Token ID 的概率分布
                         {1234: 0.01, 5678: 0.60, 9012: 0.02, ...}
   ↓
[采样]                   根据概率分布选出下一个 Token ID
                         → 选出 ID: 5678
   ↓
[Detokenizer]            Token ID → 文本
                         5678 → "很"
   ↓
输出文本(字符串)"很"
   ↓
[将新 Token 追加到输入,循环执行,直到生成结束符 <EOS>]

2.2 Self-Attention:最关键的机制

用一个完全不涉及数学的例子来理解。


大白话例子:开会投票决定谁说了算

想象你在处理这句话:

"小明把球给了小红,因为他很累了。"

现在的问题是:"他" 指的是谁?小明还是小红?


第一步:每个词都有一张"名片"和一张"问卷"

句子里每个词,系统给它准备两样东西:

  • 名片(Key):写着"我是谁,我有什么特征"
  • 问卷(Query):写着"我想找什么样的词"
小明  的名片:男性、人名、主语、做了动作
小红  的名片:女性、人名、宾语、接受了球
他    的问卷:我想找 → 男性、人名、主语

这张"名片"是人工打的吗?

不是。没有任何人手动标注"小明是男性、人名、主语"。

名片(Key)、问卷(Query)、内容(Value)全部是训练出来的

每个词先变成一串数字(Embedding 向量),然后乘以三个矩阵(W_Q、W_K、W_V),
分别得到 Q、K、V。这三个矩阵是模型在训练过程中自动学习出来的参数。

模型在训练时看了海量文本,发现"他/她/它"后面接的内容,往往和前面某个名词有关——
不断调整这三个矩阵,让"他的问卷"和"小明的名片"自然而然地匹配度更高。

所以"男性、人名、主语"只是我们用人话描述的直觉,
真实的名片是一串几百维的浮点数,没有人能直接读懂,
但它的效果等价于"描述了这个词的语言特征"。


第二步:拿着问卷去匹配所有名片,打分

"他"拿着自己的问卷,挨个去和其他词的名片对照:

"他"的问卷 vs 小明的名片 → 男性✓ 人名✓ 主语✓ → 匹配度:90分
"他"的问卷 vs 小红的名片 → 男性✗ 人名✓ 主语✗ → 匹配度:20分
"他"的问卷 vs 球的名片  → 男性✗ 人名✗ 主语✗ → 匹配度:2分
"他"的问卷 vs 因为的名片 → ...              → 匹配度:1分

第三步:按分数决定"借多少信息"

分数高的词,"他"就多借一点它的信息;分数低的词,少借或者不借:

"他"最终的理解 =
  90% × 小明的含义
  + 20% × 小红的含义(归一化后)
  + 2%  × 球的含义
  + ...

归一化之后大概是:
  72% × 小明  +  16% × 小红  +  2% × 球  + ...

结果:"他"这个词现在已经带上了大量"小明"的信息。

后续模型继续生成文字时,就"知道"了——他 = 小明。


第四步:每个词都同时做这件事

不只是"他"在做,句子里每一个词都同时在做同样的事:

小明  → 看了所有词,更新自己(知道自己是主语,给了球)
球   → 看了所有词,更新自己(知道自己被传递了)
小红  → 看了所有词,更新自己(知道自己是接球的人)
他   → 看了所有词,更新自己(知道自己指的是小明)
累了  → 看了所有词,更新自己(知道是小明累了)

所有词同时互相"看"一遍,每个词都吸收了整个句子的上下文信息。

这就是"Self"(自身序列内部互相关注)的含义。


第五步:不止看一个角度(Multi-Head)

上面的例子只从"指代关系"这一个角度看。现实中模型同时开 32~128 个"视角",每个视角关注不同的语言关系:

视角1(指代关系):他 → 小明
视角2(动作关系):累了 → 小明(谁累了?)
视角3(位置关系):因为 → 连接前后两个事件
视角4(语气关系):...
...
视角N:...

最终把所有视角的结果拼在一起 → 全面理解这个句子

用一句话总结 Self-Attention

句子里的每个词,都去"问"其他所有词:"你跟我有多相关?",
然后按相关程度,从每个词那里借一点信息,
把这些信息混合在一起,更新自己的理解。
所有词同时做这件事,一轮结束后,每个词都"看懂"了整个句子的上下文。


Q、K、V 对应关系

现在再看这三个术语,就很好理解了:

术语 大白话 例子中对应
Q(Query,查询) 我的问卷,我在找什么 "他"想找:男性、人名、主语
K(Key,键) 我的名片,我有什么特征 小明的名片:男性、人名、主语
V(Value,值) 我的实际内容 小明这个词完整的语义信息

公式 Attention(Q, K, V) = softmax(QK^T / √d) × V 做的就是:
用 Q 和 K 算匹配分数 → softmax 变成百分比 → 按百分比加权取 V。


Causal Mask:生成时不能"偷看未来"

还有一个重要限制:模型生成文字时,是一个词一个词往后写的,后面的词还没生成,所以不能用。

正在生成第5个词时:
  能看到:词1 词2 词3 词4 [正在生成词5]
  不能看:词6 词7 词8 ...(还不存在)

就像考试时:
  你只能参考已经写下的答案,不能看还没写的部分

系统通过把"未来位置"的匹配分数强制设为负无穷,让它们经过 softmax 后权重变成 0,等效于视而不见。


训练期原理:W_Q/W_K/W_V 是怎么一步步生成的

用一个极小的例子,数字都是手算得出的,方便理解完整过程。


场景设定

词表只有 3 个词:喜欢

训练样本:给模型看 "猫 喜欢" → 让它预测下一个词是 "鱼"

向量维度设为 2(现实中是几千维,这里为了手算用 2)。


第一步:把词变成向量(Embedding)

先给每个词一个初始向量(随机数,训练会调整):

猫   → [1.0,  0.5]
喜欢  → [0.3,  0.8]
鱼   → [0.9,  0.1]

输入 "猫 喜欢" 时,得到两个向量:

x_猫  = [1.0, 0.5]
x_喜欢 = [0.3, 0.8]

第二步:随机初始化 W_Q、W_K、W_V

训练开始时,这三个矩阵都是随机数:

W_Q = [[0.1, 0.2],   W_K = [[0.4, 0.1],   W_V = [[0.3, 0.5],
       [0.3, 0.4]]          [0.2, 0.3]]          [0.1, 0.2]]

(现实中这些矩阵是几千×几千维,这里用 2×2 示意)


第三步:计算 Q、K、V(前向传播)

用每个词的 Embedding 乘以三个矩阵,得到 Q/K/V:

Q_猫  = x_猫 × W_Q = [1.0, 0.5] × W_Q = [0.25, 0.40]
K_猫  = x_猫 × W_K = [1.0, 0.5] × W_K = [0.50, 0.25]
V_猫  = x_猫 × W_V = [1.0, 0.5] × W_V = [0.35, 0.60]

Q_喜欢 = x_喜欢 × W_Q = [0.3, 0.8] × W_Q = [0.27, 0.38]
K_喜欢 = x_喜欢 × W_K = [0.3, 0.8] × W_K = [0.28, 0.27]
V_喜欢 = x_喜欢 × W_V = [0.3, 0.8] × W_V = [0.17, 0.31]

第四步:计算 Attention 权重("喜欢"该关注谁?)

用 Q_喜欢 分别和 K_猫、K_喜欢 做点积,算匹配分数:

score(喜欢→猫)  = Q_喜欢 · K_猫
               = 0.27×0.50 + 0.38×0.25 = 0.135 + 0.095 = 0.230

score(喜欢→喜欢) = Q_喜欢 · K_喜欢
               = 0.27×0.28 + 0.38×0.27 = 0.076 + 0.103 = 0.179

经过 Softmax 变成权重(两个加起来=1):

weight(喜欢→猫)  = 0.513
weight(喜欢→喜欢) = 0.487

"喜欢""猫" 的关注度略高(51% vs 49%)。


第五步:加权求和 V,得到"喜欢"的新向量

output_喜欢 = 0.513 × V_猫 + 0.487 × V_喜欢
           = 0.513 × [0.35, 0.60] + 0.487 × [0.17, 0.31]
           = [0.180, 0.308] + [0.083, 0.151]
           = [0.263, 0.459]

这个 [0.263, 0.459] 就是"喜欢"经过 Attention 后的新向量,
它已经融合了"猫"的信息


第六步:用新向量预测下一个词

把 output_喜欢 经过最后一层,得到词表上每个词的概率:

预测结果:
  猫   → 15%
  喜欢  → 10%
  鱼   → 75%   ← 模型预测"鱼"的概率最高

第七步:和正确答案对比,算误差(Loss)

正确答案是 "鱼",对应的目标是:

目标:猫=0%, 喜欢=0%, 鱼=100%
预测:猫=15%, 喜欢=10%, 鱼=75%

Loss(误差)= 预测和目标的差距
           = 用交叉熵公式算出一个数,比如 0.288

Loss 越大,说明预测越不准,需要调整。


第八步:反向传播,调整 W_Q、W_K、W_V

这是训练的核心。根据 Loss,计算每个参数"调多少能让 Loss 变小":

调整方向:让 Loss 减小的方向(梯度下降)

W_Q 的某个数原来是 0.1
梯度告诉我们:这个数应该增大一点,Loss 才会减小
→ 调整后变成 0.1 + 学习率 × 梯度 = 0.1 + 0.01 × 0.5 = 0.105

W_K、W_V 同理,每个数字都被微调一点点

这一次调整完,模型预测"鱼"的概率可能从 75% 变成 76%。


第九步:重复几千亿次

换一条新的训练数据,重复第一步到第八步:

样本1:"猫 喜欢" → 预测"鱼"   → 调整参数
样本2:"我 吃" →   预测"饭"   → 调整参数
样本3:"天气 很" → 预测"好"   → 调整参数
...
重复几千亿次

每次调整都是微小的,但经过几千亿次后:

W_Q 从随机数 → 变成"擅长提取查询特征"的矩阵
W_K 从随机数 → 变成"擅长描述词的特征"的矩阵
W_V 从随机数 → 变成"擅长携带语义内容"的矩阵

训练结束,推理期怎么用

训练完 W_Q/W_K/W_V 就固定了。用户输入新句子时:

用户输入:"小明把球给了小红,因为他"

1. 每个词 → Embedding 向量
2. × 固定的 W_Q → Q(每个词的"问卷")
3. × 固定的 W_K → K(每个词的"名片")
4. × 固定的 W_V → V(每个词的"内容")
5. 计算 Attention 权重
   → "他"的 Q 和"小明"的 K 匹配度高
   → "他"的新向量融合大量"小明"信息
6. 预测下一个词:"很"
7. 继续生成,直到结束

W_Q/W_K/W_V 没有变化,只是被"用"了一次。


一句话总结训练过程

训练 = 不断喂数据 → 预测 → 算误差 → 微调参数 → 重复
     = 让 W_Q/W_K/W_V 从一堆废话随机数
       慢慢变成"懂语言"的参数矩阵

大模型的"参数"在例子里指什么

人们常说 GPT-3 有 1750 亿参数、Claude 有几百亿参数,这些参数就是上面例子里那些矩阵里的每一个数字

回到我们的小例子,把所有矩阵展开数一数:

Embedding 表(每个词一个向量):
  猫   → [1.0,  0.5]   ← 2 个数
  喜欢  → [0.3,  0.8]   ← 2 个数
  鱼   → [0.9,  0.1]   ← 2 个数
  小计:3 个词 × 2 维 = 6 个参数

W_Q 矩阵:
  [[0.1, 0.2],
   [0.3, 0.4]]           ← 4 个数 = 4 个参数

W_K 矩阵:
  [[0.4, 0.1],
   [0.2, 0.3]]           ← 4 个参数

W_V 矩阵:
  [[0.3, 0.5],
   [0.1, 0.2]]           ← 4 个参数

最后一层(输出层,把向量变成词的概率):
  也是一个矩阵          ← 假设 6 个参数

────────────────────────────────
这个玩具模型总参数量 ≈ 6+4+4+4+6 = 24 个参数

这 24 个数字,就是这个玩具模型的全部"知识"

训练过程就是反复调整这 24 个数字,让它们从随机值变成能预测"猫 喜欢 → 鱼"的值。


放大到真实大模型

真实模型和玩具模型的区别,只是数字的数量不同:

玩具模型(上面例子):
  词表 3 个词,向量维度 2
  → 参数量:约 24 个

GPT-2(2019年):
  词表 50257 个词,向量维度 768
  Transformer 层数 12,每层有多个矩阵
  → 参数量:15 亿个数字

GPT-3(2020年):
  向量维度 12288,层数 96
  → 参数量:1750 亿个数字

Claude Sonnet 4.6(现在):
  → 参数量:未公开,估计数百亿

结构完全一样:Embedding 表 + N 层(W_Q/W_K/W_V + 其他矩阵)+ 输出层。

只是维度更大、层数更多,参数量从 24 个变成了几百亿个。


参数里存的是什么

参数不是人写进去的规则,是训练出来的数字,但这些数字里隐含了语言知识:

W_Q/W_K/W_V 里的数字
  → 隐含了"什么词应该关注什么词"的规律
  → 比如代词倾向于关注前面的名词

Embedding 表里的数字
  → 隐含了词与词之间的语义关系
  → "国王"和"王后"的向量距离,接近"男人"和"女人"的向量距离

输出层矩阵里的数字
  → 隐含了"什么上下文后面跟什么词"的概率规律
  → 看到"我饿了,想吃",下一个词大概率是食物

所有这些"知识",都压缩在这几百亿个浮点数里。

模型文件之所以几十 GB,就是因为要存下这几百亿个数字。


这些数字如何和文字对应起来?

分两张表,串联起来完成"文字 ↔ 数字"的转换。


第一张表:词表(Vocabulary)— 文字 → ID

这是一张固定的查找表,训练前就确定好了,训练期间不变:

词表(Tokenizer 内置):

  ID    词
  ───────────────
  0  →  [PAD]       (填充符)
  1  →  [UNK]       (未知词)
  2  →  [BOS]       (句子开始)
  3  →  [EOS]       (句子结束)
  4  →  "猫"
  5  →  "喜欢"
  6  →  "鱼"
  7  →  "我"
  8  →  "你"
  ...
  50256 → "hello"

用户输入 "猫 喜欢 鱼" 时:

Tokenizer 查这张表:
  "猫"  → ID 4
  "喜欢" → ID 5
  "鱼"  → ID 6

得到 ID 序列:[4, 5, 6]

这张表是人工构建的(统计语料中的高频词/子词),GPT 系列约有 50257 个词,Claude 更多。


第二张表:Embedding 表 — ID → 向量

这是一张可训练的矩阵,每一行对应一个 ID 的向量:

Embedding 表(训练过程中不断调整):

  ID   向量(2维示意,实际几千维)
  ──────────────────────────────
  0  → [0.00,  0.00]   (PAD)
  1  → [0.01,  0.02]   (UNK)
  2  → [0.11,  0.33]   (BOS)
  3  → [0.22,  0.44]   (EOS)
  4  → [1.00,  0.50]   (猫)
  5  → [0.30,  0.80]   (喜欢)
  6  → [0.90,  0.10]   (鱼)
  ...

ID 序列 [4, 5, 6] 查这张表:

ID 4 → [1.00, 0.50]   (猫的向量)
ID 5 → [0.30, 0.80]   (喜欢的向量)
ID 6 → [0.90, 0.10]   (鱼的向量)

这张表里的数字,就是参数的一部分,训练时会不断调整,让语义相近的词向量距离更近。


输出时反向查表:向量 → 文字

模型生成下一个词时,输出的是词表上每个 ID 的概率,再查回词表:

输出层计算结果(每个 ID 的概率):
  ID 4(猫)   → 5%
  ID 5(喜欢) → 8%
  ID 6(鱼)   → 80%
  ID 7(我)   → 3%
  ...

选概率最高的 → ID 6
查词表        → ID 6 = "鱼"
输出文字      → "鱼"

完整的文字 ↔ 数字流程

输入侧:
  文字 "猫 喜欢"
    ↓ 词表(固定查找表)
  ID 序列 [4, 5]
    ↓ Embedding 表(可训练矩阵)
  向量序列 [[1.0,0.5], [0.3,0.8]]
    ↓ Transformer(W_Q/W_K/W_V 等参数)
  新向量(融合了上下文)

输出侧:
  新向量
    ↓ 输出层矩阵(可训练参数)
  每个 ID 的概率 [5%, 8%, 80%, 3%, ...]
    ↓ 采样/取最大值
  ID 6
    ↓ 反查词表(固定查找表)
  文字 "鱼"

两张表的区别

词表(Vocabulary) Embedding 表
内容 文字 ↔ ID 的映射 ID ↔ 向量的映射
大小 约 5~10 万行 约 5~10 万行 × 向量维度
是否可训练 不变,训练前固定 会变,训练中不断调整
存储位置 Tokenizer 文件里 模型参数文件里
作用 文字和数字的桥梁 让数字携带语义信息

词表是死的查找表,Embedding 表是活的参数矩阵。两者配合,完成"文字→语义向量→文字"的完整转换。

2.3 为什么叫"大"语言模型

参数量决定了模型的记忆和推理能力:

模型 参数量 能力水平
GPT-2(2019) 15 亿 能写连贯文章,推理较弱
GPT-3(2020) 1750 亿 涌现出 few-shot 学习能力
GPT-4(2023) 未公开(估计万亿级) 接近人类专家水平
Claude 3.5 Sonnet 未公开 顶级推理和代码能力

涌现(Emergence):参数量超过某个阈值后,模型突然获得之前没有的能力。这不是线性增长,而是质变。


三、训练过程(知道即可)

3.1 预训练

数据来源:互联网文本、书籍、代码、论文...
   ↓
任务:给定前 N 个 Token,预测第 N+1 个 Token
   ↓
损失函数:预测错了就调整参数(梯度下降)
   ↓
重复数万亿次
   ↓
模型学会了语言规律、世界知识、推理模式

关键理解:预训练完的模型是一个"知识压缩器",把互联网上的信息压缩进了参数里。但它只会续写文本,不会"对话"。

3.2 RLHF:让模型变得有用

Reinforcement Learning from Human Feedback(人类反馈强化学习):

Step 1:监督微调(SFT)
  人类写出"好的"对话示例
  → 模型学习如何回答问题

Step 2:奖励模型训练
  人类对多个回答排序(A > B > C)
  → 训练一个"评分模型"

Step 3:PPO 强化学习
  用评分模型给 LLM 的回答打分
  → 强化好回答,惩罚坏回答
  → 模型越来越符合人类期望

这一步产生了"对齐":模型从"续写机器"变成"乐于助人的助手"。

3.3 知识截止日期

模型的知识来自训练数据,训练数据有截止时间:

Claude Sonnet 4.6 的知识截止:2025 年 8 月
  → 2025 年 8 月之后发生的事,模型不知道
  → 模型可能会"自信地"给出错误的新信息(幻觉)

工程含义:涉及实时信息的场景,必须通过 RAG 或工具调用补充最新知识。


四、推理过程:模型如何生成文字

4.1 自回归生成

LLM 每次只生成一个 Token,然后把这个 Token 加入输入,再生成下一个:

输入:"今天天气"
  → 模型预测下一个 Token:{"很": 0.6, "不": 0.2, "真": 0.1, ...}
  → 采样选出"很"
  → 输入变成"今天天气很"
  → 模型预测:{"好": 0.5, "差": 0.3, "热": 0.15, ...}
  → 采样选出"好"
  → 输入变成"今天天气很好"
  → ...直到生成结束符

工程含义

  • 生成速度与输出长度成正比,输出越长越慢
  • 流式输出(Streaming)就是每生成一个 Token 就立刻返回

4.2 Temperature:控制随机性

Temperature = 0    → 每次都选概率最高的 Token(确定性,适合代码/数学)
Temperature = 0.7  → 适度随机(平衡创意和准确,适合对话)
Temperature = 1.5  → 高度随机(创意写作,可能胡言乱语)

直觉:Temperature 越高,模型越"天马行空";越低,越"保守准确"。

4.3 Context Window:模型的"工作记忆"

┌──────────────────────────────────────────────┐
│                  Context Window               │
│                                               │
│  System Prompt │ 历史对话 │ 当前输入 │ 输出   │
│  (系统设定)   │          │          │        │
└──────────────────────────────────────────────┘
                              ↑
                    模型只能看到这个窗口内的内容

关键限制

  • 超出 Context Window 的内容,模型完全看不到(不是变模糊,是彻底消失)
  • Context Window 越大,计算成本越高,速度越慢
  • 长文本处理时,模型对"中间"部分的注意力往往弱于"开头"和"结尾"(Lost in the Middle 问题)

五、LLM 的能力边界

5.1 LLM 擅长的事

能力 原因 典型场景
文本生成与改写 训练目标本身就是预测文本 写作、翻译、摘要
代码生成 训练数据包含大量代码 写函数、修 Bug、解释代码
模式识别与分类 见过大量样本 情感分析、意图识别
知识问答 压缩了大量世界知识 百科问答、概念解释
格式转换 见过各种格式互转的例子 JSON↔表格、Markdown↔HTML
少样本学习(Few-shot) 涌现能力 给几个例子,模型举一反三
推理(有限范围) CoT 能力 数学题、逻辑推理

5.2 LLM 不擅长的事(能力边界)

① 精确计算

问:1234567 × 9876543 = ?
LLM 的做法:根据训练数据中的"数字模式"猜一个看起来合理的数
→ 可能是错的,而且模型说得很自信

正确做法:通过工具调用真正的计算器

原理:LLM 处理数字和处理文字一样,都是 Token 预测,没有真正的"计算"能力。

② 实时信息

问:今天比特币价格是多少?
LLM 的做法:给出训练截止日前的价格,或者编造一个
→ 必然是错的

正确做法:工具调用实时 API

③ 精确记忆长文本

给模型一份 10 万字的文档,然后问文档第 5000 行的第 3 个数字
→ 模型大概率回答错误

原理:"Lost in the Middle" —— 模型对上下文中间部分的注意力显著弱于头尾

④ 稳定的逻辑推理链

简单推理:模型能做
  → "如果 A > B,B > C,那么 A > C?" → 正确

复杂推理链:容易出错
  → 多步骤、多条件的推理,错误会累积
  → 特别是反事实推理("如果历史上没有发生X,Y会怎样")

⑤ 自我认知

问:你确定这个答案是对的吗?
LLM 的问题:模型不知道自己"知道"什么,"不知道"什么
→ 可能对错误的答案表现出高度自信
→ 也可能对正确的答案表示怀疑

这就是"幻觉"的根源之一

⑥ 持久记忆

对话结束后,下次对话模型完全不记得你
→ 每次对话都从零开始
→ 需要工程手段(数据库 + RAG)实现跨会话记忆

六、幻觉(Hallucination):最重要的工程挑战

6.1 什么是幻觉

模型自信地给出错误信息,叫做幻觉。

典型例子:
  问:爱因斯坦在哪所大学获得博士学位?
  错误回答(幻觉):"爱因斯坦在柏林洪堡大学获得博士学位"(编造的)
  正确答案:苏黎世大学

  更危险的幻觉:
  问:引用几篇关于 XXX 的论文
  → 模型可能给出看起来真实的论文标题、作者、期刊,但完全是编造的

6.2 幻觉的根本原因

LLM 的本质:预测下一个 Token 的概率分布
           ≠ 检索真实事实

当模型"不知道"答案时:
  → 它不会说"我不知道"(这不是训练的主要目标)
  → 它会生成一个"看起来合理"的答案(因为这是训练的目标)
  → 这个"合理"但错误的答案 = 幻觉

6.3 减少幻觉的工程手段

手段 原理 适用场景
RAG(检索增强生成) 先检索真实文档,再让模型基于文档回答 知识库问答、文档分析
工具调用(Tool Use) 让模型调用外部 API 获取真实数据 实时信息、精确计算
降低 Temperature 减少随机性,避免"发挥" 需要准确性的场景
要求引用来源 Prompt 中要求模型给出依据 研究类、事实类问题
输出验证 用另一个模型或规则校验输出 高风险场景
思维链(CoT) 让模型"逐步思考",减少跳跃 推理类问题

七、Prompt 工程:如何"驾驶"LLM

7.1 模型如何理解 Prompt

完整的输入结构:

┌─────────────────────────────────┐
│ System Prompt(系统提示)        │  定义模型角色、规则、约束
├─────────────────────────────────┤
│ Few-shot 示例(可选)            │  示范期望的输入输出格式
├─────────────────────────────────┤
│ User Message(用户输入)         │  当前任务描述
├─────────────────────────────────┤
│ Assistant Prefix(可选)         │  引导模型以特定方式开始回答
└─────────────────────────────────┘

7.2 高质量 Prompt 的要素

① 角色设定

差:帮我写代码
好:你是一个有 10 年经验的 Python 后端工程师,
    擅长写可维护、有完善错误处理的生产级代码。

② 上下文完整

差:这个函数有 Bug,帮我修
好:以下是一个 Python 函数,输入是用户 ID(整数),
    预期输出是用户名(字符串)。
    当用户不存在时应该抛出 UserNotFoundError。
    现在当输入 -1 时会崩溃,帮我修复:
    [代码]

③ 输出格式明确

差:分析一下这个需求
好:分析以下需求,用 JSON 格式输出,包含字段:
    - risks: 风险列表(数组)
    - complexity: 复杂度(low/medium/high)
    - estimatedDays: 估算工作日(整数)

④ 思维链(Chain of Thought)

在 Prompt 末尾加:
  "请一步步思考,展示你的推理过程"

→ 显著提升复杂推理的准确性
→ 原理:迫使模型在 Token 层面"写出"中间步骤,减少跳跃

7.3 Prompt 的常见陷阱

陷阱 现象 解决方法
指令冲突 System Prompt 和 User 要求矛盾 明确优先级,减少歧义
上下文污染 历史对话影响当前回答 关键任务用新会话
负面指令无效 "不要做X"往往不如"做Y" 用正面指令替代负面指令
过长 Prompt 失效 指令太多,模型"忘记"部分指令 精简核心指令,辅以示例
格式不稳定 同样 Prompt 输出格式不一致 用 Few-shot 示例固定格式

八、RAG:给 LLM 接上外部知识库

8.1 为什么需要 RAG

LLM 的知识:
  ✓ 截止训练数据的时间点
  ✗ 你的公司内部文档
  ✗ 实时信息
  ✗ 超过 Context Window 的长文档

RAG 解决:让模型基于"当下检索到的真实内容"回答

8.2 RAG 工作流程

离线阶段(建立知识库):
  文档
   ↓ 切片(Chunking)
  文档片段(每段 200~500 Token)
   ↓ Embedding 模型
  向量(每段文字变成一串数字,语义相近的向量距离近)
   ↓ 存入向量数据库(Pinecone/Weaviate/pgvector)

在线阶段(用户查询):
  用户问题
   ↓ Embedding 模型
  问题向量
   ↓ 向量数据库相似度检索
  最相关的 N 个文档片段
   ↓ 拼入 Prompt
  LLM 基于检索到的内容生成回答

8.3 RAG 的关键参数

参数 含义 调优方向
Chunk Size 每段文字的长度 太小丢失上下文,太大检索精度低
Top-K 检索几个片段 太少信息不足,太多噪音增加
Embedding 模型 把文字转向量的模型 影响检索精度,中文需专用模型
重排序(Rerank) 对检索结果二次排序 显著提升检索准确性

RAG小结

 RAG = Retrieval-Augmented Generation

  - Retrieval:检索
  - Augmented:增强
  - Generation:生成

  直译:检索增强生成。

  RAG 是指先根据离线文档查找出一些内容,然后作为提示词和用户问题一起给llm?这样做好处是啥?

⏺ 是的,理解完全正确。用一张图说清楚:

  没有 RAG:
    用户问题 → LLM → 回答
    (LLM 只靠训练时学到的知识,可能幻觉)

  有 RAG:
    用户问题
        ↓
    向量检索(从你的文档库里找最相关的片段)
        ↓
    组装 Prompt:
      "根据以下资料回答问题:
       [检索到的文档片段]
       用户问题:XXXX"
        ↓
    LLM → 回答
    (基于真实文档内容,有据可查)

  ---
  好处

  1. 解决知识截止问题
  LLM 训练数据有截止日期,你的内部文档、最新资料它根本不知道。RAG 让模型能"看到"这些内容。

  2. 减少幻觉
  模型有参考资料时,倾向于基于资料回答,而不是自己发挥。可以进一步要求"只根据提供的资料回答,资料里没有
  的就说不知道"。

  3. 可追溯
  回答可以附上"来源:第X段",用户可以去验证原文,企业场景下非常重要。

  4. 不需要重新训练模型
  更新知识库只需要更新文档,不需要重新训练或微调模型,成本极低。

  5. 控制回答范围
  客服机器人只回答产品相关问题,把产品文档放进知识库,模型自然只在这个范围内回答。

  ---
  核心代价

  检索质量决定回答质量——如果检索到的片段不相关,模型的回答也会跑偏。所以 RAG 的难点不在
  LLM,在怎么把文档切好、检索准。

九、Function Calling 协议:LLM 突破文本边界的关键设计

9.1 为什么需要 Function Calling

LLM 有一个根本限制:它只能输出文本

LLM 的本质:
  输入:一段文字(Prompt)
  输出:一段文字(预测的下一个 Token 序列)

  仅此而已。

这意味着:

  • 它无法执行代码
  • 它无法读写文件
  • 它无法调用 API 获取实时数据
  • 它无法控制任何外部系统

如果没有任何扩展,LLM 就是个"纯文字生成机",用户问"北京现在天气怎样",它只能靠训练数据里的知识猜测一个合理的答案——幻觉不可避免。

Function Calling(函数调用)协议就是为了解决这个问题而诞生的:让 LLM 在需要时,用文本的形式"声明"它想调用某个函数,再由外部代码真正执行,把结果反馈给模型。


9.2 协议的核心思想:LLM 输出结构化指令

关键洞察是:

LLM 虽然只能输出文本,但文本可以是结构化的(JSON)。
把 JSON 当成"指令",由外部程序解析并执行,就相当于 LLM "调用"了外部功能。

传统对话:
  用户问题 → LLM → 自然语言回答

Function Calling:
  用户问题 + 工具列表 → LLM → 结构化调用指令(JSON)
                                     ↓
                               外部代码解析并执行
                                     ↓
                               执行结果注入 Context
                                     ↓
                        LLM 读取结果 → 生成最终回答

LLM 自己什么都没有"执行",它只是输出了一段 JSON,外部系统帮它完成了真正的动作。


9.3 完整协议流程

以"查北京天气"为例,走一遍完整的 Function Calling 协议:


第一步:开发者定义工具

在调用 LLM API 时,把可用工具的"说明书"传给模型:

tools: [
  {
    "name": "get_weather",
    "description": "获取指定城市的实时天气。当用户询问天气信息时调用此工具。",
    "parameters": {
      "type": "object",
      "properties": {
        "city": {
          "type": "string",
          "description": "城市名称,如'北京'、'上海'"
        },
        "unit": {
          "type": "string",
          "enum": ["celsius", "fahrenheit"],
          "description": "温度单位,默认 celsius"
        }
      },
      "required": ["city"]
    }
  }
]

关键点description 是给 LLM 看的,模型根据这段文字决定"什么时候该调哪个工具"。写得越准确,模型的判断越正确。


第二步:LLM 决策——输出调用指令

用户问:"北京今天天气怎么样?"

LLM 的回应不是自然语言,而是一段结构化指令(以 Claude API 为例):

{
  "type": "tool_use",
  "id": "toolu_01A09q90qw90lq917835lq9",
  "name": "get_weather",
  "input": {
    "city": "北京",
    "unit": "celsius"
  }
}

这就是 LLM 输出的"文本",只不过格式是 JSON。LLM 本身并没有调用任何东西。


第三步:外部代码执行工具

你的代码接收到这个 JSON,解析出函数名和参数,真正去调 API:

# 伪代码
if response.type == "tool_use":
    tool_name = response.name          # "get_weather"
    tool_input = response.input        # {"city": "北京", "unit": "celsius"}

    # 真正执行 API 调用
    result = call_weather_api(city="北京", unit="celsius")
    # result = {"temp": 22, "unit": "celsius", "weather": "晴", "humidity": "45%"}

第四步:把结果注入 Context,再次调用 LLM

把工具执行结果写入对话历史,重新发给 LLM:

messages: [
  {"role": "user", "content": "北京今天天气怎么样?"},
  {"role": "assistant", "content": [{"type": "tool_use", "name": "get_weather", ...}]},
  {
    "role": "user",
    "content": [
      {
        "type": "tool_result",
        "tool_use_id": "toolu_01A09q90qw90lq917835lq9",
        "content": "{\"temp\": 22, \"weather\": \"晴\", \"humidity\": \"45%\"}"
      }
    ]
  }
]

第五步:LLM 基于真实数据生成回答

LLM 读取到工具返回的结果,生成最终自然语言回答:

"北京今天天气晴朗,气温 22°C,湿度 45%,出门不需要带伞。"

这次回答有真实数据支撑,不是幻觉。


整个流程图

用户:"北京今天天气怎么样?"
        ↓
[LLM] 读取:用户问题 + 工具列表
        ↓
[LLM] 输出:{"type":"tool_use","name":"get_weather","input":{"city":"北京"}}
        ↓                          (LLM 只输出了这段文字)
[外部代码] 解析 JSON,真正调用天气 API
        ↓
[外部代码] 把结果 {"temp":22,"weather":"晴"} 注入 Context
        ↓
[LLM] 读取真实数据,生成自然语言回答
        ↓
"北京今天晴,22°C"

9.4 LLM 如何"决定"调不调工具

LLM 并没有专门的"决策模块",它的决策机制和生成文字完全一样:预测下一个 Token

训练时,Anthropic / OpenAI 给模型大量"什么时候该调工具"的示例数据,模型学会了:

Context 里有工具列表 + 用户问实时信息
  → 下一个 Token 大概率是 {"type":"tool_use"...}

Context 里有工具列表 + 用户问一般知识
  → 下一个 Token 大概率是自然语言回答

本质上,LLM 只是在"续写"一段包含工具调用格式的对话,和写普通文字没有区别。


9.5 多工具串联:一次任务,多次调用

Function Calling 的真正威力在于多工具串联:LLM 可以连续调用多个工具,每次把上一步的结果作为下一步的输入。

用户:"帮我查一下北京和上海的天气,然后推荐周末去哪个城市旅游。"

第一轮:
  LLM 输出:调用 get_weather(city="北京")
  外部执行:返回北京天气数据

第二轮(结果注入 Context):
  LLM 输出:调用 get_weather(city="上海")
  外部执行:返回上海天气数据

第三轮(两个结果都在 Context 里):
  LLM 综合两个城市的数据,生成推荐:
  "上海周末晴天 24°C,北京有轻度雾霾,建议去上海。"

这种模式,就是 AI Agent 的雏形——LLM 自主规划多个步骤,逐步完成目标。


9.6 工程注意事项

注意点 说明
description 写清楚 模型根据 description 决定是否调用;描述模糊会导致误调用或漏调用
工具数量适中 工具越多,模型"选错"的概率越高;单次对话建议不超过 20 个
做好错误处理 工具调用可能失败(网络超时等),要有 fallback,不能让整个对话挂起
危险操作加人工确认 删除、支付、发送邮件等不可逆操作,不能由模型直接触发
工具结果控制大小 工具返回内容太长会占满 Context Window,要提前裁剪或摘要
幂等性 工具可能被重复调用(模型重试),设计时考虑幂等性

9.7 Function Calling 是 AI Agent 的基石

没有 Function Calling,LLM 永远困在文字的世界里。

有了 Function Calling,LLM 可以:

  • 查询实时数据(不再幻觉)
  • 写入文件、执行命令(从"说"到"做")
  • 调用任意 API(连接整个互联网)
  • 串联多步骤工具调用(完成复杂任务)

这正是下一章 AI Agent 的技术基础:Agent = LLM 大脑 + Function Calling 协议 + 外部工具集合。


十、LLM 的成本与速度

10.1 计费模型

主流 LLM API 按 Token 计费:

费用 = 输入 Token 数 × 输入单价 + 输出 Token 数 × 输出单价

Claude Sonnet 4.6(参考价):
  输入:$3 / 百万 Token
  输出:$15 / 百万 Token

一次对话(1000 输入 + 500 输出):
  费用 = 1000/1,000,000 × $3 + 500/1,000,000 × $15
       = $0.003 + $0.0075
       = $0.0105 ≈ 约 7 分钱人民币

输出比输入贵:因为输出是自回归逐个生成,计算量更大。

10.2 速度瓶颈

影响响应速度的因素:

TTFT(首 Token 时间)
  = 处理输入 Token 的时间
  → 输入越长,TTFT 越长

TPS(每秒输出 Token 数)
  = 模型生成速度
  → 通常 50~100 Token/秒
  → 输出 1000 Token ≈ 10~20 秒

总延迟 = TTFT + 输出 Token 数 / TPS

10.3 降低成本和延迟的工程手段

手段 效果 代价
使用小模型 成本降低 10x,速度提升 5x 能力下降
缩短 Prompt 减少输入 Token 可能降低质量
限制输出长度(max_tokens) 控制输出成本 可能截断
缓存(Prompt Cache) 相同前缀只计算一次 需要管理缓存
流式输出(Streaming) 用户感知延迟降低 处理复杂度增加
批处理(Batching) 吞吐量提升 响应延迟增加

十一、多模态:超越文字

11.1 现状

主流 LLM 已支持多种模态输入:

模态 代表模型 典型用途
文字 → 文字 所有 LLM 对话、写作、代码
图片 → 文字 GPT-4o, Claude 3.5 图片描述、OCR、图表分析
文字 → 图片 DALL-E, Midjourney 图片生成
语音 → 文字 Whisper 语音转录
文字 → 语音 ElevenLabs, TTS 语音合成
视频 → 文字 Gemini 1.5 Pro 视频理解

11.2 工程含义

  • 图片处理比文字贵:一张图片通常消耗 1000~2000 Token
  • 不同模态有不同的能力边界:模型看图的能力远弱于读文字
  • 多模态输入需要特殊的 API 格式(base64 编码或 URL)

十二、AI Agent:让 LLM 从"问答"变成"行动"

以下分析基于两个有源码依据的真实系统:Claude Code(Anthropic 官方 CLI)和 OpenClaw(开源个人 AI 助手)。

12.1 什么是 AI Agent

纯 LLM 是一个"问答机器":输入 → 输出,一问一答,不能主动做事。

AI Agent 在 LLM 之上加了一个执行循环,让模型可以:

用户目标
   ↓
[Agent Loop 循环执行]
   ├── 思考:现在应该做什么?       ← LLM 负责
   ├── 行动:调用工具(读文件/搜索/写代码/执行命令)← 外部工具执行
   ├── 观察:工具返回了什么结果?   ← 结果注入 Context 给 LLM
   └── 判断:目标完成了吗?→ 没有 → 继续循环  ← LLM 负责
   ↓
目标达成,输出结果

核心差异

纯 LLM AI Agent
交互方式 一问一答 自主多步执行
工具使用 可调用外部工具
状态保持 无(单次对话) 有(跨步骤记忆)
主动性 被动响应 可主动规划和执行

12.2 LLM 在 Agent 中扮演的角色

Agent 系统中 LLM 是大脑,工具是四肢,两者缺一不可。

┌──────────────────────────────────────────────────────┐
│                    Agent 系统                          │
│                                                        │
│   ┌──────────────────────────────────────────────┐    │
│   │                  LLM(大脑)                  │    │
│   │                                              │    │
│   │  ① 理解用户意图                               │    │
│   │    "帮我重构这个函数" → 拆解成具体步骤         │    │
│   │                                              │    │
│   │  ② 决策:下一步调用哪个工具?参数是什么?      │    │
│   │    → 输出 Function Call 指令                  │    │
│   │                                              │    │
│   │  ③ 解读工具结果                               │    │
│   │    → 工具返回文件内容,LLM 理解并继续规划      │    │
│   │                                              │    │
│   │  ④ 判断任务是否完成                           │    │
│   │    → 生成最终回答或继续下一轮循环              │    │
│   └──────────────────────────────────────────────┘    │
│                        ↕ Function Call / Tool Result   │
│   ┌──────────────────────────────────────────────┐    │
│   │                 工具层(四肢)                 │    │
│   │                                              │    │
│   │  读文件 / 写文件 / 执行命令 / 搜索 / 调 API   │    │
│   │  LLM 本身无法做这些,必须通过工具完成          │    │
│   └──────────────────────────────────────────────┘    │
└──────────────────────────────────────────────────────┘

LLM 具体负责哪些决策

Agent 阶段 LLM 做的事 工具做的事
意图理解 把自然语言拆解成可执行计划
工具选择 决定调用哪个工具、传什么参数
工具执行 实际执行(读写文件、命令等)
结果解读 理解工具返回的内容,判断下一步
错误处理 工具失败时决定重试还是换方案
任务完成判断 判断目标是否达成

关键理解:LLM 每次只处理当前 Context 内的信息,它看不到"过去的步骤",只能看到被注入 Context 的工具结果。Agent 框架的核心工作之一就是把每步的工具结果正确地写入 Context,让 LLM 能"记住"已经做过什么。


12.3 案例一:Claude Code

依据:CLAUDE_CODE_ARCHITECTURE.md(基于官方 CLI 架构分析)

是什么

Claude Code 是 Anthropic 官方的命令行 AI 编程助手,采用工具增强的 Agent 架构,在本地运行,直接操作你的代码仓库。

核心架构

用户输入(自然语言)
        ↓
  ┌─────────────────────────────────────────┐
  │            Claude Code Core              │
  │                                          │
  │  会话管理器    → 维护对话历史和上下文      │
  │  权限控制器   → 控制哪些工具可以执行       │
  │  上下文管理器  → 识别工作目录/Git状态      │
  │  记忆系统     → CLAUDE.md + Auto Memory  │
  └──────────────────┬──────────────────────┘
                     ↓
  ┌──────────────────────────────────────────┐
  │           Agent 执行引擎(Agent Loop)    │
  │                                          │
  │  思考 → 规划 → 执行 → 反思 → 迭代        │
  │                                          │
  │  子 Agent 管理:                          │
  │    Explore Agent   → 探索代码库           │
  │    Plan Agent      → 制定实现计划         │
  │    Bash Agent      → 执行 Shell 命令      │
  │    General Purpose → 通用任务            │
  └──────────────────┬──────────────────────┘
                     ↓
  ┌──────────────────────────────────────────┐
  │           工具管理器(34+ 个工具)         │
  │                                          │
  │  本地工具:Read/Write/Edit/Bash/Glob/Grep │
  │  MCP 工具:动态加载外部 MCP Server 工具   │
  │  子 Agent 工具:派发子任务                │
  └──────────────────────────────────────────┘

LLM 在 Claude Code 中的作用

Claude Code 使用 Claude 模型作为唯一的大脑,所有决策都由它完成:

用户说:"帮我把这个函数改成异步的"
        ↓
LLM 接收到:System Prompt(角色设定)
           + CLAUDE.md(项目规范记忆)
           + Git 状态(当前工作目录上下文)
           + 用户指令

LLM 决策输出(Function Call):
  → 先调用 Read 工具读取目标文件
        ↓ 文件内容注入 Context
  → LLM 理解代码结构,规划修改方案
  → 调用 Edit 工具写入修改
        ↓ 修改结果注入 Context
  → LLM 判断:是否需要同步修改调用方?
  → 调用 Grep 搜索所有调用处
        ↓ 搜索结果注入 Context
  → LLM 决策:逐一修改调用处
  → 调用 Bash 运行测试验证
        ↓ 测试结果注入 Context
  → LLM 判断:测试通过,任务完成

LLM 在每一步的角色:理解当前状态 → 决定下一步工具调用 → 解读结果 → 继续或结束

Claude Code 能做什么

✅ 能做
──────────────────────────────────────────
  读取、编写、编辑本地文件(Read/Write/Edit)
  搜索代码库(Glob/Grep)
  执行 Shell 命令(Bash)
  Git 操作(status/diff/commit/push)
  多文件联动修改(理解跨文件依赖)
  派发子 Agent 并行处理子任务
  调用 MCP Server 扩展工具能力
  通过 CLAUDE.md 记忆项目规范
  Hooks 机制:工具执行前后触发自定义脚本

⚠️ 有限制
──────────────────────────────────────────
  不能访问互联网(除非配置了联网 MCP Server)
  长任务中 Context Window 可能耗尽(需要手动压缩)
  复杂架构决策仍需人工判断
  跨会话不自动记忆(需要 CLAUDE.md 显式记录)

❌ 不能做
──────────────────────────────────────────
  运行图形界面程序
  实时监听文件变化(被动触发)
  自主部署到生产环境(需要人工确认)
  理解二进制文件内容

权限控制机制

Claude Code 的关键设计是权限分级,不是所有操作都自动执行:

工具调用权限等级:

自动执行(无需确认):
  → Read(读文件)
  → Glob(文件搜索)
  → Grep(内容搜索)

需要用户确认:
  → Write/Edit(写文件)
  → Bash(执行命令)
  → 网络请求

永远需要确认:
  → 危险命令(rm -rf、git push --force)
  → 生产环境操作

12.4 案例二:OpenClaw

依据:openclaw_architecture.md(基于源码 openclaw-main 分析)

是什么

OpenClaw 是开源的个人 AI 助手网关,核心理念:

你在哪里聊天,AI 就在哪里。

把 Claude/GPT/Gemini 等 AI 接入 WhatsApp、Telegram、Slack、Discord、iMessage 等日常 IM 工具,在用户自己的设备上运行,数据不经第三方。

核心架构

IM 平台(Telegram/Discord/Slack...)
        ↓ Webhook / Bot API / 长连接
  ┌─────────────────────────────────────┐
  │         OpenClaw Gateway            │
  │  (运行在用户本地设备)               │
  │                                     │
  │  接入层   → 消息规范化为 MsgContext  │
  │  路由层   → 7级优先级匹配 → agentId │
  │  调度层   → HTTP/WS服务、认证、Cron  │
  │  Agent层  → 会话管理、Prompt构建     │
  │           → Tool调用循环、流式输出   │
  │  插件层   → Memory/Canvas/联网搜索   │
  │  Provider层→ 多模型统一封装+Failover │
  └──────────────┬──────────────────────┘
                 ↓ API 调用
  外部 AI 模型(Claude/GPT/Gemini/本地Ollama)

LLM 在 OpenClaw 中的作用

OpenClaw 支持多个 LLM Provider(Claude/GPT/Gemini/本地 Ollama),LLM 是可替换的"引擎":

Telegram 用户:"帮我搜一下今天的 AI 新闻并总结"
        ↓
OpenClaw 流程:
  接入层   → 消息规范化为 MsgContext
  路由层   → 匹配到 agentId="default"
  Agent层  → 加载会话历史 + 构建 Prompt:
             System Prompt(人设)
           + Memory 技能注入(长期记忆)
           + 会话历史(短期记忆)
           + 可用工具列表(Web Search 等)
           + 用户消息

  LLM 决策:需要调用 Web Search 工具
        ↓ Function Call 输出
  Web Search 工具执行 → 返回新闻内容
        ↓ 结果注入 Context
  LLM 生成摘要回答
        ↓ 流式输出
  发回 Telegram 用户

LLM 在各层的具体角色

接入层  → 与 LLM 无关,纯消息格式转换
路由层  → 与 LLM 无关,规则匹配
调度层  → 与 LLM 无关,网络服务
        ↓
Agent 层 ← LLM 在这里介入,是唯一的决策者
  │
  ├── Prompt 构建完成后 → 发给 LLM
  │     LLM 读取:系统人设 + 历史对话 + 工具列表 + 用户消息
  │
  ├── LLM 输出①:普通文本回复
  │     → 直接流式发回用户
  │
  └── LLM 输出②:Function Call(工具调用指令)
        → Agent 层解析,执行对应工具
        → 工具结果写回 Context
        → 再次调用 LLM,继续决策
        → 循环直到 LLM 不再发出 Function Call

Provider 层 → 封装各家 LLM 的 API 差异,LLM 对 Agent 层透明

LLM 在 OpenClaw 中的三个核心角色

角色 具体表现
对话者 理解用户自然语言,生成自然语言回复
工具调度员 决定何时调用哪个工具(Web Search/Code Run 等),生成 Function Call 指令
内容生成器 基于工具返回的真实数据,组织成最终回答发给用户

OpenClaw 中 LLM 的特殊点

  • 可热切换:主模型失败时 Failover 自动换备用模型,LLM 对 Agent 层透明
  • 多 Agent 差异化配置:不同 agentId 可配置不同 LLM(如个人助手用 Claude Opus,编程助手用 Claude Sonnet)
  • 本地模型可选:隐私场景可换成 Ollama 本地运行,LLM 推理完全在用户设备上,零数据出网
  • 流式输出:LLM 每生成一个 Token 就立即推送给 IM 平台,用户看到逐字输出效果

OpenClaw 能做什么

✅ 能做
──────────────────────────────────────────
  接入 10+ 个 IM 平台(Telegram/Discord/Slack/
  WhatsApp/iMessage/Signal/飞书/Matrix...)
  多 Agent 路由:不同用户/群组路由到不同 Agent
  跨会话记忆(Memory 技能)
  联网搜索(Web Search 技能)
  代码执行(Code Run 技能,沙箱环境)
  Webhook 集成(外部系统注入消息)
  Cron 定时任务(定时触发 Agent)
  多模型自动故障转移(Failover)
  本地模型支持(Ollama,数据不出网)
  语音对话(Voice Call 技能)
  插件扩展(自定义通道/工具/技能)

⚠️ 有限制
──────────────────────────────────────────
  记忆依赖 Memory 技能,默认不持久化
  联网能力依赖 Web Search 插件,非内置
  跨平台会话默认隔离(需配置共享)
  代码执行在沙箱内,无法访问宿主文件系统

❌ 不能做
──────────────────────────────────────────
  直接操作用户设备的文件系统(设计如此)
  访问 IM 平台的历史消息(只能接收新消息)
  主动发起 IM 会话(只能被动响应)
  实时感知用户上线状态

OpenClaw 的路由机制(7 级优先级)

来自源码分析:

消息进来时,按优先级从高到低匹配 Binding 规则:

1. binding.peer        → 精确匹配特定用户/群组(最高优先)
2. binding.peer.parent → 匹配线程父级
3. binding.guild+roles → Discord 服务器 + 角色组合
4. binding.guild       → 整个 Discord 服务器
5. binding.team        → Slack 工作区
6. binding.account     → 账号级别
7. binding.channel     → 通道通配符(最低优先)
   default             → 默认 Agent(兜底)

12.5 两者对比

Claude Code OpenClaw
定位 编程助手(操作代码) 个人助手网关(多平台接入)
运行环境 终端命令行 本地后台服务
核心能力 读写文件、执行命令、Git 多平台消息路由、AI 对话
工具来源 内置34+工具 + MCP Server 插件系统(通道/技能/Provider)
记忆机制 CLAUDE.md + Auto Memory 会话 JSONL + Memory 技能
多模型 单模型(Claude) 多模型 + Failover
数据隐私 本地运行,代码不上传 本地运行,数据不经第三方
扩展方式 MCP Server Plugin SDK(npm 包)
适合场景 软件开发、代码任务 日常助手、自动化、多平台

12.6 AI Agent 的通用能力边界

无论哪种 Agent 系统,以下是共同的能力边界:

✅ Agent 比纯 LLM 强的地方
──────────────────────────────────────────
  可以执行多步骤任务,不需要用户每步都参与
  可以调用真实工具(文件、命令、API)
  可以基于中间结果调整计划
  可以并行派发子任务提高效率
  可以跨会话保持记忆(需要显式设计)

⚠️ Agent 的固有限制
──────────────────────────────────────────
  每一步仍然依赖 LLM 的推理质量
  工具调用失败会导致任务中断(需要容错设计)
  长任务会消耗大量 Context Window
  自主执行有风险:错误操作难以撤回
  调试困难:多步骤执行链路难以追踪

❌ Agent 做不到的事
──────────────────────────────────────────
  真正的"理解"(仍是模式匹配)
  100% 可靠的自主执行(必须有人工监督机制)
  超出工具边界的操作(工具没给的能力,Agent 没有)
  实时感知外部世界变化(被动触发,非主动监听)

12.7 Agent 系统设计的关键原则

原则一:Human-in-the-loop(人在回路)

高风险操作必须人工确认,Claude Code 的权限分级就是最好的实践。Agent 越自主,需要越严格的确认机制。

原则二:工具边界即能力边界

Agent 能做的事,完全由给它的工具决定。设计 Agent 系统 = 设计工具集合 + 设计工具的权限边界。

原则三:失败是常态,容错是必须

工具调用失败率在生产环境中不可忽视,需要:重试机制、降级策略、人工介入触发点。

原则四:Context 是稀缺资源

长任务中 Context Window 会耗尽,需要主动压缩历史、提炼关键信息,而不是等到溢出再处理。


十三、能力边界速查表

✅ LLM 做得好
──────────────────────────────────────────
  文本生成、改写、摘要、翻译
  代码生成、解释、重构
  分类、情感分析、意图识别
  结构化提取(从文字中提取 JSON)
  问答(基于提供的上下文)
  头脑风暴、创意写作
  格式转换

⚠️ LLM 能做但要小心
──────────────────────────────────────────
  数学推理(简单可以,复杂容易出错 → 用工具)
  事实问答(可能幻觉 → 用 RAG + 要求引用)
  长文档理解(中间部分注意力弱 → 分段处理)
  复杂逻辑推理(多步骤 → 用 CoT)
  代码调试(简单 Bug 可以,复杂系统问题不行)

❌ LLM 做不好
──────────────────────────────────────────
  精确计算(→ 调用计算器工具)
  实时信息(→ 调用 API)
  持久记忆(→ 数据库 + RAG)
  确定性输出(同样输入可能不同输出)
  超长文档逐字检索
  真正的"理解"(模型是模式匹配,不是真正理解)

十三、常见误解纠正

误解 真相
"LLM 有意识/情感" 没有。只是复杂的统计模式匹配
"LLM 在搜索互联网" 没有。知识在参数里,截止于训练日期(除非有工具)
"LLM 每次回答一样" 不是。有随机性(Temperature),同样问题可能不同答案
"更长的 Prompt 一定更好" 不是。太长可能稀释关键指令,"Lost in the Middle"
"LLM 知道自己不知道什么" 不知道。这是幻觉的根源
"GPT-4 比 GPT-3.5 在所有任务都强" 不一定。大模型在某些简单任务反而更慢更贵,不必然更准
"Fine-tuning 能解决所有问题" 不是。微调改变行为风格,RAG 才能注入新知识

十四、总结:工程师需要记住的核心认知

1. LLM 是"概率续写机器",不是"知识检索系统"
   → 会幻觉,需要 RAG 和工具调用来补充

2. Context Window 是硬限制
   → 超出即"失忆",设计时必须考虑

3. Token 是一切的基本单位
   → 成本、速度、长度限制都以 Token 计算

4. Temperature 控制随机性
   → 准确性要求高 → 低 Temperature
   → 创意要求高 → 高 Temperature

5. 幻觉是模型的固有特性,不是 Bug
   → 必须在系统层面设计验证机制

6. 工具调用是补充能力边界的正确方式
   → 计算用计算器,实时信息用 API,存储用数据库

7. Prompt 是"编程语言"
   → 清晰、具体、有示例的 Prompt 产生更好的输出

8. 能力在快速进化,边界在持续扩展
   → 今天做不好的事,3 个月后可能就能做好
   → 持续跟踪模型更新

本文档写于 2026 年,基于当前主流 LLM(Claude、GPT-4o、Gemini)的实际工程经验。

©著作权归作者所有,转载或内容合作请联系作者
【社区内容提示】社区部分内容疑似由AI辅助生成,浏览时请结合常识与多方信息审慎甄别。
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

相关阅读更多精彩内容

友情链接更多精彩内容