Skip to content

大模型从Token到训练与推理完整原理

大语言模型不是一个“存满标准答案的数据库”,也不是先理解整篇文章再一次性写出回答。它本质上是一个经过大规模数据训练的参数化函数:接收已有 Token 序列,计算下一个 Token 的概率分布,选出一个 Token,再把它放回输入继续预测。

这句定义很短,但真正学懂必须把整个过程串起来:原始文本怎样变成 Token,Token 怎样变成向量,Transformer 怎样交换上下文信息,训练时参数怎样更新,推理时为什么逐 Token 生成,Temperature 为什么改变随机性,KV Cache 为什么提速又占显存,模型为什么会幻觉,以及商业系统怎样控制质量、延迟和成本。

学习目标

完成本页后,你应该能够:

  1. 区分 AI、机器学习、深度学习、基础模型和大语言模型。
  2. 解释 Token、Token ID、Vocabulary、Tokenizer 和 Embedding 的关系。
  3. 解释预训练为什么通常使用“预测下一个 Token”的目标。
  4. 从输入文本一路讲到 Logits、Softmax 和下一个 Token。
  5. 用直觉和公式解释 Self-Attention 中的 Q、K、V。
  6. 解释 Causal Mask、Residual、Normalization、FFN 和多层堆叠的作用。
  7. 区分预训练、继续预训练、SFT、偏好对齐和推理。
  8. 区分 Greedy、Temperature、Top-k、Top-p 和 Beam Search。
  9. 解释 Prefill、Decode、TTFT、TPOT、吞吐量和 KV Cache。
  10. 判断幻觉、知识缺失、上下文缺失、检索失败和指令冲突的区别。
  11. 写出 Token 估算、采样和模型 API 调用 Demo。
  12. 在商业场景中完成模型选型、成本预算、监控和故障排查。

一、AI、大模型和应用系统是什么关系

mermaid
flowchart TD
    A["人工智能AI"] --> B["机器学习"]
    B --> C["深度学习"]
    C --> D["Transformer等神经网络架构"]
    D --> E["基础模型"]
    E --> F["大语言模型LLM"]
    F --> G["Prompt、RAG、Agent和业务应用"]
概念含义例子
AI让机器表现出感知、决策或生成能力的总称规则系统、搜索、推荐、视觉、LLM
机器学习从数据中学习参数,而不是把每条规则都手写分类、回归、排序
深度学习使用多层神经网络学习复杂表示CNN、Transformer
基础模型在大规模数据上训练、可适配很多任务的模型语言、视觉、多模态基础模型
LLM主要处理和生成语言 Token 的大规模模型问答、摘要、代码、抽取
AI应用把模型与权限、数据、工具、评估和业务流程组合企业知识库、客服、采集异常助手

商业系统不会因为接入 LLM 就删除规则、数据库和搜索引擎。金额计算、权限判断和事务状态应由确定性代码负责;模型适合语言理解、非结构化信息处理、解释和候选方案生成。

二、全过程总览:一句话怎样变成回答

mermaid
flowchart TD
    A["用户输入文本"] --> B["Tokenizer切分Token"]
    B --> C["Token映射为整数ID"]
    C --> D["Embedding查表得到向量"]
    D --> E["加入位置信息"]
    E --> F["多层Transformer Block"]
    F --> G["最后位置的隐藏向量"]
    G --> H["LM Head投影到词表维度"]
    H --> I["得到每个候选Token的Logit"]
    I --> J["Softmax与解码策略"]
    J --> K["选出下一个Token"]
    K --> L{"是否遇到停止条件"}
    L -->|"否"| C
    L -->|"是"| M["Detokenize为最终文本"]

关键点:模型每轮通常只确定下一个 Token。你看到一句完整回答,是这个循环执行几十、几百甚至几千次的结果。

三、Tokenizer:文本怎样变成模型能处理的数字

3.1 Token不等于汉字或单词

模型只能接收有限词表中的整数 ID。Tokenizer 先把文本拆成词表中存在的片段:

text
原文:unbelievable
可能切分:un + believe + able

原文:数据库连接失败
可能切分:数据库 + 连接 + 失败

原文:order_id=1024
可能切分:order + _ + id + = + 102 + 4

这只是示意。真实结果完全由目标模型配套的 Tokenizer、词表和规范化规则决定。不能用“一个汉字约等于一个 Token”作为精确计费依据。

3.2 为什么不用每个完整单词做词表

如果把每个完整单词都设为 Token:

  • 词表会极大,Embedding 和输出层参数暴涨。
  • 新人名、产品名、拼写变化和代码标识符无法覆盖。
  • 多语言和组合词会制造大量低频项。

如果只按单个字符:

  • 词表小,但序列过长。
  • 模型需要更多层才能组合出词义。
  • 上下文和推理成本增加。

子词 Tokenization 在“词表大小”和“序列长度”之间折中。常见思想包括 BPE、WordPiece 和 Unigram/SentencePiece;它们训练词表的方法不同,但目标都是用有限子词表示开放文本。

3.3 Vocabulary、Token和Token ID

名称含义
Vocabulary模型认识的全部 Token 集合
Token文本被切出的一个词表单元
Token IDToken 在词表中的整数编号
Special TokenBOS、EOS、PAD、角色边界等特殊标记
Detokenize把生成的 Token ID 恢复成文本

同一段文本使用不同模型的 Tokenizer,Token 数和 ID 都可能不同。模型权重和 Tokenizer 必须配套;换错词表相当于把完全不同的编号含义交给模型。

3.4 可运行Demo:观察真实Tokenizer

安装:

bash
python -m pip install transformers
python
from transformers import AutoTokenizer


MODEL_DIR = "替换为本地模型目录或允许访问的模型标识"
tokenizer = AutoTokenizer.from_pretrained(MODEL_DIR)

texts = [
    "Redis为什么快?",
    "ConcurrentHashMap",
    "order_id=1024",
]

for text in texts:
    ids = tokenizer.encode(text, add_special_tokens=False)
    tokens = tokenizer.convert_ids_to_tokens(ids)
    restored = tokenizer.decode(ids)
    print({
        "text": text,
        "token_count": len(ids),
        "tokens": tokens,
        "ids": ids,
        "restored": restored,
    })

这个实验用于理解切分和预算,不应在每次线上请求中重新加载 Tokenizer。生产通常在进程启动时加载一次,或使用 Provider 返回的 usage 作为最终计费事实。

四、Embedding:整数ID为什么能表达语义

Token ID 只是索引,不带大小或语义关系。ID 1000 并不比 ID 10“更重要”。模型维护一个可训练矩阵:

text
Embedding矩阵形状 = 词表大小 V × 隐藏维度 d

用 Token ID 查矩阵的一行,就得到该 Token 的 d 维向量。训练过程中,反向传播不断调整这些数字,让在相似上下文中发挥相似作用的 Token 获得可利用的表示。

mermaid
flowchart TD
    A["Token:数据库"] --> B["Token ID:示意为4812"]
    B --> C["Embedding矩阵查第4812行"]
    C --> D["d维初始向量"]
    D --> E["经过多层上下文计算"]
    E --> F["带有当前语境的隐藏表示"]

输入 Embedding 只是起点。同一个“苹果”在“苹果很好吃”和“苹果发布手机”中的查表向量起初相同,但经过 Attention 与不同上下文交互后,最终隐藏表示不同。

这里的 Token Embedding 与 RAG 的句子/文档 Embedding 有联系但用途不同:前者是模型内部逐 Token 表示,后者通常把整段文本压缩成一个向量用于相似度检索。详见 Embedding与向量库

五、为什么必须加入位置信息

纯 Self-Attention 只看元素之间的内容关系,本身没有天然顺序。如果没有位置:

text
我打你
你打我

包含相同 Token 集合,却无法可靠区分主客体。模型需要位置编码或旋转位置表示等机制,让注意力计算感知顺序和相对距离。

位置方法会影响:

  • 模型能支持的上下文长度。
  • 长距离关系的表达。
  • 超出训练长度后的外推表现。
  • KV Cache 和推理实现。

“配置支持 128K 上下文”不等于模型在整个 128K 范围内都同样准确,也不等于把无关资料全部塞满窗口会提高质量。

六、Self-Attention:每个Token怎样读取上下文

6.1 Q、K、V的直觉

每个输入向量通过三个可训练矩阵变换为:

向量直觉
Query当前 Token 想寻找什么信息
Key当前 Token 能被什么查询匹配
Value当前 Token 真正提供的内容

以“订单支付失败,因为它超时了”为例,处理“它”时,Query 会与“订单”“支付”“失败”“超时”等位置的 Key 计算匹配程度,再按权重汇总对应 Value,使“它”的新表示带上相关上下文。

6.2 计算步骤

对输入矩阵 X

text
Q = XWq
K = XWk
V = XWv

Scores = QKᵀ / √dk
Weights = Softmax(Scores + Mask)
Attention = WeightsV

逐步理解:

  1. XWq/XWk/XWv:用可学习矩阵从同一输入提取三种视角。
  2. QKᵀ:每个 Query 与每个 Key 做点积,得到相关性分数。
  3. 除以 √dk:避免维度增大后点积数值过大,使 Softmax 梯度过度饱和。
  4. 加 Mask:禁止读取不应看到的位置。
  5. Softmax:把一行分数变成和为 1 的权重。
  6. WeightsV:按相关性加权汇总 Value。
mermaid
flowchart TD
    A["输入隐藏向量X"] --> B["线性变换得到Q"]
    A --> C["线性变换得到K"]
    A --> D["线性变换得到V"]
    B --> E["Q与K点积并缩放"]
    C --> E
    E --> F["加入Causal Mask"]
    F --> G["Softmax得到注意力权重"]
    G --> H["按权重汇总V"]
    D --> H
    H --> I["上下文化表示"]

Attention 权重不是天然可靠的“人类解释”。它是模型计算的一部分,不能只展示一张权重图就断言模型的真实推理原因。

6.3 为什么需要Causal Mask

自回归模型训练时可以一次输入完整句子,但位置 i 不能偷看右侧未来 Token,否则训练时知道答案、推理时却没有未来内容,产生训练—推理不一致。

text
输入:今天 天气 很 好

预测“天气”时可看:今天
预测“很”时可看:今天 天气
预测“好”时可看:今天 天气 很

Causal Mask 把未来位置的分数设为负无穷,Softmax 后权重接近 0。

6.4 多头注意力为什么存在

一个注意力头的表示空间有限。多个头使用不同投影矩阵,可同时学习不同关系,例如语法依赖、指代、局部搭配和长距离关联,再把各头结果拼接并投影。

多头不保证每个头都有清晰的人类标签,也不是简单把同一计算重复多次取平均。各头参数不同,会形成不同子空间。

七、一个Transformer Block还有什么

典型 Decoder Block 不只有 Attention:

mermaid
flowchart TD
    A["输入隐藏状态"] --> B["Normalization"]
    B --> C["Masked Multi-Head Attention"]
    C --> D["与残差相加"]
    D --> E["Normalization"]
    E --> F["前馈网络FFN"]
    F --> G["与残差相加"]
    G --> H["输出给下一层"]
组件作用缺少后的问题
Attention在不同位置间交换信息Token 难以利用上下文
FFN对每个位置做非线性特征变换表达能力不足
Residual保留原信息并改善深层梯度传播深层网络难训练、信息易损失
Normalization稳定数值尺度和训练梯度和激活更不稳定
多层堆叠逐层形成更抽象表示单层难表达复杂模式

不同模型可能使用 Pre-Norm、RMSNorm、SwiGLU、GQA、MQA、RoPE、MoE 等变体,核心链路相似但实现和性能边界不同。完整拆解见 Transformer原理

八、LM Head、Logits和Softmax

经过最后一层后,取当前生成位置的隐藏向量,通过 LM Head 投影到词表维度。假设词表有 100000 个 Token,就得到 100000 个分数,这些未归一化分数叫 Logits。

Softmax 将 Logits 转为概率:

text
P(token_i) = exp(logit_i) / Σ exp(logit_j)

示意:

候选TokenLogitSoftmax后概率
成功4.20.62
失败3.50.31
超时1.90.06
香蕉-0.40.01

概率表示在当前模型、参数和上下文下的相对偏好,不等于事实正确率。模型可能非常自信地生成错误事实。

九、解码策略:模型怎样从概率中选Token

9.1 Greedy

每次选概率最高的 Token。稳定、简单,但可能重复、僵硬,并且局部最优不保证整段最优。

9.2 Temperature

在 Softmax 前将 Logit 除以温度 T

text
P(token_i) = softmax(logit_i / T)
  • T < 1:分布更尖锐,高概率候选更占优势。
  • T > 1:分布更平坦,低概率候选更容易被采样。
  • 温度接近 0 的具体处理由 Provider 决定,通常接近确定性选择。

低温不等于事实正确,高温也不等于模型获得新知识。它只改变现有概率分布的采样方式。

9.3 Top-k

只保留概率最高的 k 个候选,再重新归一化采样。k 固定,无法随分布“很确定”或“很不确定”自适应。

9.4 Top-p

从高到低累加概率,保留累计概率达到 p 的最小候选集合。模型很确定时集合小,分布平坦时集合大。

保留若干条累计分数较高的候选序列,常用于翻译等更强调全局序列概率的任务。开放式聊天中可能导致输出过于保守或相似,也增加计算量。

9.6 可运行Demo:观察温度如何改变概率

python
import math
import random


TOKENS = ["成功", "失败", "超时", "香蕉"]
LOGITS = [4.2, 3.5, 1.9, -0.4]


def softmax(logits: list[float], temperature: float) -> list[float]:
    if temperature <= 0:
        raise ValueError("temperature必须大于0")
    scaled = [value / temperature for value in logits]
    maximum = max(scaled)
    exps = [math.exp(value - maximum) for value in scaled]
    total = sum(exps)
    return [value / total for value in exps]


def sample_once(temperature: float) -> str:
    probabilities = softmax(LOGITS, temperature)
    return random.choices(TOKENS, weights=probabilities, k=1)[0]


for temperature in [0.2, 1.0, 2.0]:
    probabilities = softmax(LOGITS, temperature)
    counts = {token: 0 for token in TOKENS}
    for _ in range(10_000):
        counts[sample_once(temperature)] += 1
    print("temperature=", temperature)
    print("probabilities=", dict(zip(TOKENS, probabilities)))
    print("sample counts=", counts)

减去最大 Logit 是数值稳定技巧,避免 exp 溢出;它不会改变 Softmax 结果,因为所有项同时减去同一常数。

十、模型是怎样训练出来的

10.1 预训练数据链路

mermaid
flowchart TD
    A["原始网页、书籍、代码等语料"] --> B["授权、过滤和数据治理"]
    B --> C["清洗、去重、质量分类"]
    C --> D["隐私和有害内容处理"]
    D --> E["Tokenizer编码"]
    E --> F["切成固定长度训练样本"]
    F --> G["批量送入模型"]
    G --> H["计算下一个Token损失"]
    H --> I["反向传播计算梯度"]
    I --> J["优化器更新参数"]
    J --> K{"是否完成训练计划"}
    K -->|"否"| G
    K -->|"是"| L["评估和保存Checkpoint"]

训练数据不是越多越好。重复、污染、隐私、错误事实和低质量模板会进入模型行为。数据溯源和许可还涉及版权、合规与可追溯性。

10.2 下一个Token训练目标

例如训练序列:

text
输入:Redis 是 一个
目标:是 一个 内存

模型在每个位置输出词表概率,训练用正确 Token 的负对数概率计算交叉熵损失。若正确 Token 概率低,损失大;反向传播计算每个参数应向哪个方向调整,优化器执行更新。

模型不是把训练文本逐条写进数据库。知识和模式分布式地编码在大量参数中,但模型也可能记忆部分训练片段;因此不能认为“参数化”天然消除隐私和版权风险。

10.3 为什么预测Token能产生通用能力

要降低海量语料上的预测误差,模型必须学习词法、语法、上下文关系、常见事实、代码结构和一定的问题求解模式。规模、数据和训练使这些能力组合出来。

但训练目标仍是预测序列,不是内置事实数据库或形式化证明器。这解释了它为什么语言流畅,也解释了它为什么可能把“统计上合理”误当成“事实正确”。

10.4 预训练、SFT和对齐的区别

阶段输入目标典型作用
预训练大规模通用语料下一个Token预测获得语言和通用模式
继续预训练领域原始语料保持语言建模目标适应领域术语和分布
SFT指令—回答样本学习按指令输出形成助手行为和任务格式
偏好对齐多个回答及偏好/奖励更符合人类或安全偏好改善有用性和安全性
推理用户当前输入不更新基础参数生成实际结果

RAG 不属于训练:它在推理时把检索资料放进上下文。微调会更新参数或适配器权重,适合行为、格式和领域模式,不适合替代频繁更新且要求可引用的事实库。

十一、训练为什么能并行,生成为什么仍逐Token

训练样本的正确后续 Token 已经存在。配合 Causal Mask,GPU 可以一次计算序列中多个位置的预测损失。

推理时下一个 Token 尚未产生:

text
必须先生成token 1
才能把token 1作为上下文生成token 2
再用token 1、2生成token 3

同一请求的 Decode 具有串行依赖,但不同请求、不同样本和矩阵运算仍可批处理并行。这也是推理引擎持续批处理和调度优化的重要来源。

十二、Prefill、Decode和KV Cache

12.1 Prefill

模型先处理系统提示词、历史、用户问题和 RAG 资料等全部输入 Token,生成各层隐藏状态以及后续需要的 Key/Value。输入越长,Prefill 计算通常越重。

12.2 Decode

之后逐步生成输出 Token。每一步读取已有上下文的缓存并计算新 Token。Decode 常受显存带宽、批处理和序列长度影响。

12.3 KV Cache为什么存在

如果每生成一个 Token 都重新计算之前所有 Token 的 K、V,会重复大量工作。KV Cache 保存每层历史 Token 的 Key 和 Value,新一步只计算新 Token 并读取历史缓存。

mermaid
flowchart TD
    A["Prefill处理全部输入"] --> B["保存每层历史K和V"]
    B --> C["生成第一个输出Token"]
    C --> D["只计算新Token的Q、K、V"]
    D --> E["Q读取历史KV Cache"]
    E --> F["选出下一个Token"]
    F --> G["把新K、V追加到Cache"]
    G --> H{"是否结束"}
    H -->|"否"| D
    H -->|"是"| I["释放请求Cache"]

KV Cache 用空间换计算:上下文越长、层数越多、并发越高,占用显存越多。模型权重能放进显存,不代表高并发长上下文一定能运行。

简化估算思路:

text
KV Cache约与以下因素成正比:
层数 × KV头数 × 每头维度 × 序列长度 × 2(K和V) × 每元素字节 × 并发

具体模型可能使用 MQA/GQA、分页 KV Cache、量化 Cache 等优化,必须按模型配置和推理引擎公式计算。

十三、延迟指标到底看什么

指标含义用户感受
排队时间等待 GPU/配额请求尚未处理
TTFT从请求到首个Token多久开始看到回答
TPOT后续每个Token平均时间打字速度快慢
总耗时从请求到结束完整任务多久完成
吞吐量单位时间处理Token/请求数系统整体能力
并发同时在途请求影响排队和显存

流式输出主要改善用户感知的 TTFT,不自动降低 Prefill、总 Decode 时间或 Token 成本。只监控平均耗时会掩盖 P95/P99 长尾。

请求慢的分层判断:

mermaid
flowchart TD
    A["AI请求慢"] --> B{"首Token是否慢"}
    B -->|"是"| C["检查排队、输入长度、RAG、网络和Prefill"]
    B -->|"否"| D{"后续生成是否慢"}
    D -->|"是"| E["检查模型大小、批处理、显存带宽和输出长度"]
    D -->|"否"| F["检查网关缓冲和前端渲染"]
    C --> G["按阶段耗时定位"]
    E --> G
    F --> G

十四、上下文窗口不是无限记忆

一次请求的上下文通常包含:

text
System Prompt
+ 开发者规则
+ 历史对话
+ 当前用户问题
+ RAG片段
+ 工具定义
+ 工具结果
+ 已生成输出

必须满足模型和 Provider 的窗口限制。输入占得越多,可生成输出空间通常越少。

预算示例:

python
def check_token_budget(
    context_limit: int,
    system_tokens: int,
    history_tokens: int,
    rag_tokens: int,
    tool_tokens: int,
    user_tokens: int,
    max_output_tokens: int,
    safety_margin: int = 512,
) -> dict:
    input_tokens = (
        system_tokens
        + history_tokens
        + rag_tokens
        + tool_tokens
        + user_tokens
    )
    required = input_tokens + max_output_tokens + safety_margin
    return {
        "input_tokens": input_tokens,
        "required_tokens": required,
        "context_limit": context_limit,
        "allowed": required <= context_limit,
        "remaining": context_limit - required,
    }


print(check_token_budget(
    context_limit=32_768,
    system_tokens=800,
    history_tokens=6_000,
    rag_tokens=12_000,
    tool_tokens=2_000,
    user_tokens=500,
    max_output_tokens=3_000,
))

超限不能只从最前面机械截断,否则可能删掉系统规则或最近问题。应按类型设计裁剪:保留安全规则;摘要旧历史;RAG 去重和重排;工具返回只保留必要字段;为输出预留明确空间。

十五、参数量、精度、显存和成本

模型参数权重的粗略存储:

text
权重显存约等于 参数量 × 每参数字节

例如 7B 参数仅权重粗估:

精度每参数理论字节仅权重粗估
FP324约28GB
FP16/BF162约14GB
INT81约7GB
4-bit0.5约3.5GB

真实运行还要增加:

  • 量化元数据和分组尺度。
  • KV Cache。
  • 激活和临时工作区。
  • 推理引擎、CUDA Context和通信缓冲。
  • 并发请求。
  • 多卡切分开销。

量化降低权重和带宽压力,但可能损失精度,且不同层和任务敏感度不同。不能只看模型成功加载,必须用业务评估集验证质量。

训练显存远高于推理,因为还要保存梯度、优化器状态和大量中间激活。LoRA/QLoRA 通过冻结基础权重、训练低秩适配器等方式降低成本,详见 LoRA与QLoRA

十六、幻觉为什么发生

幻觉不是一个单一故障,至少要区分:

类型例子根因方向
参数知识错误编造不存在的版本或论文训练知识不足、冲突或陈旧
上下文无依据材料没写却给出具体结论生成目标偏向流畅回答
RAG检索错误找到相似但不适用的制度切分、召回、权限、重排问题
引用幻觉引用了不存在的文件和页码让模型自由生成引用标识
工具结果误读把失败状态解释为成功Schema、Prompt或结果裁剪错误
指令冲突用户诱导忽略系统规则信任边界和提示注入问题

模型优化的是条件概率,不是数据库约束中的事实一致性。面对信息不足时,“生成一个常见且语言合理的后续”可能比“拒绝回答”概率更高。

治理流程:

mermaid
flowchart TD
    A["用户问题"] --> B["判断是否需要实时或私有事实"]
    B -->|"需要"| C["受权限控制的RAG或工具"]
    B -->|"不需要"| D["模型参数知识"]
    C --> E["返回可追溯资料"]
    D --> F["标注知识时效和不确定性"]
    E --> G["要求基于资料回答"]
    F --> G
    G --> H["结构、引用和规则校验"]
    H --> I{"证据是否充分"}
    I -->|"否"| J["拒答、澄清或人工处理"]
    I -->|"是"| K["返回并记录版本证据"]

降低温度只能减少采样发散,不能给模型补充缺失事实。高风险医疗、支付、法律和生产变更必须使用确定性校验与人工审批。

十七、模型记忆、会话历史和RAG的区别

能力数据在哪里是否实时适合什么
参数知识模型权重通常不是通用语言和稳定模式
当前上下文本次Prompt当前任务、短期规则和资料
Chat Memory应用保存后重新放入Prompt取决于应用多轮会话连续性
RAG外部知识库检索可更新私有、可引用、频繁变化知识
Tool Calling实时业务系统订单状态、计算、执行动作

模型 API 不会因为你上一次调用过就天然记住用户。所谓 Memory 通常是应用保存历史并在下一次请求重新发送,因此会占上下文和成本,也必须做租户隔离、过期和隐私删除。

十八、云API调用Demo与工程边界

python
import os
import time
from typing import Any

import requests


API_URL = os.environ["MODEL_API_URL"]
API_KEY = os.environ["MODEL_API_KEY"]
MODEL_NAME = os.environ["MODEL_NAME"]


class ModelCallError(RuntimeError):
    pass


def ask_llm(question: str, request_id: str) -> dict[str, Any]:
    if not question.strip():
        raise ValueError("question不能为空")
    if len(question) > 4_000:
        raise ValueError("question过长")

    payload = {
        "model": MODEL_NAME,
        "messages": [
            {
                "role": "system",
                "content": "你是企业技术助手。不确定时必须明确说明。",
            },
            {"role": "user", "content": question},
        ],
        "temperature": 0.2,
        "max_tokens": 800,
    }

    started = time.perf_counter()
    try:
        response = requests.post(
            API_URL,
            headers={
                "Authorization": f"Bearer {API_KEY}",
                "Content-Type": "application/json",
                "X-Request-Id": request_id,
            },
            json=payload,
            timeout=(3.0, 30.0),
        )
    except requests.Timeout as exc:
        raise ModelCallError(f"模型调用超时 requestId={request_id}") from exc
    except requests.RequestException as exc:
        raise ModelCallError(f"模型网络调用失败 requestId={request_id}") from exc

    elapsed_ms = int((time.perf_counter() - started) * 1000)

    if response.status_code == 429:
        retry_after = response.headers.get("Retry-After")
        raise ModelCallError(
            f"模型限流 requestId={request_id} retryAfter={retry_after}"
        )
    if response.status_code in {401, 403}:
        raise ModelCallError(f"模型认证或权限失败 requestId={request_id}")
    if response.status_code >= 500:
        raise ModelCallError(f"模型服务异常 requestId={request_id}")
    response.raise_for_status()

    data = response.json()
    choices = data.get("choices") or []
    if not choices:
        raise ModelCallError(f"模型没有返回候选结果 requestId={request_id}")

    answer = choices[0]["message"]["content"]
    return {
        "answer": answer,
        "usage": data.get("usage", {}),
        "model": data.get("model", MODEL_NAME),
        "elapsed_ms": elapsed_ms,
        "request_id": request_id,
    }

这个 Demo 比“直接 requests.post”多做了参数边界、连接/读取超时、错误分类、空候选检查、usage 和 requestId,但仍不是完整生产实现。生产还需要身份权限、请求/并发/Token/金额限额、Retry-After、退避抖动、熔断、脱敏日志、评估和降级。

十九、商业模型选型

不要只看排行榜总分。模型必须按实际业务评估:

维度要问的问题
任务质量分类、抽取、问答、代码或推理是否达到阈值
上下文实际有效长上下文表现如何,而不只是标称长度
结构化输出JSON Schema和枚举约束是否稳定
Tool Calling参数生成、并行工具和错误恢复是否可靠
多模态支持哪些图片、音频、文件格式和大小
延迟TTFT、TPOT、P95/P99是否满足SLO
成本输入、输出、缓存、图片和失败重试如何计费
安全合规数据驻留、训练使用、删除、审计和合同边界
可用性配额、Region、SLA、限流和故障历史
可迁移性API兼容是否覆盖真实使用能力

同一系统可以按任务路由:低风险分类走小模型,复杂推理走强模型,敏感数据走本地模型,高风险决策进入人工审核。模型路由必须记录实际模型、Prompt、RAG、工具和安全版本,便于复现与回滚。

二十、生产故障排查Runbook

20.1 回答突然变差

先固定一组失败样本并保存脱敏证据,然后对比:

text
实际模型及版本
Prompt模板和参数
历史对话裁剪
RAG召回与Rerank结果
工具输入输出
Temperature/Top-p/输出上限
finish reason和内容安全状态
应用发布和Provider变更时间

不要只反复问同一问题看“这次好了没有”。使用评估集比较变更前后正确率、引用、拒答、安全、延迟和成本。

20.2 首Token突然变慢

检查排队时间、输入 Token、RAG/Rerank、网络、Provider Region、模型负载和 Prefill。若输入增长,继续拆 System、历史、RAG 和工具定义分别占多少 Token。

20.3 生成速度慢

检查 TPOT、输出长度、模型大小、量化、批处理、GPU 利用率、显存带宽、KV Cache 和并发。TTFT 正常但 TPOT 变差时,不要优先优化向量检索。

20.4 显存OOM

判断是加载模型时 OOM,还是并发增长后 OOM:

  • 启动即 OOM:权重、精度、并行切分和工作区问题。
  • 长上下文才 OOM:KV Cache 随序列增长。
  • 并发后 OOM:每个请求 Cache 与批处理叠加。
  • 运行一段时间后持续增长:请求未释放、引擎或应用泄漏。

临时降低并发和上下文可以止血,但必须结合显存时间线、请求长度分布和引擎指标确认根因。

20.5 成本突然上升

公式化拆分:

text
总成本 = 成功请求成本
       + 失败请求已消耗成本
       + 重试成本
       + RAG/Embedding/Rerank成本
       + 工具和基础设施成本

检查请求量、输入/输出 Token 分布、缓存命中、历史长度、RAG TopK、重试次数、模型路由和异常流量。流式输出不等于成本更低。

20.6 回答出现敏感信息

立即限制传播并保存受控证据,定位敏感内容来自用户输入、系统 Prompt、历史、RAG、工具、模型参数知识还是日志回放。撤销泄露密钥、修复权限过滤、清理缓存和评估集,并审计受影响租户。只修改一句 Prompt 不能代替数据权限修复。

二十一、常见误区

大模型是搜索引擎

错误。模型参数不提供可枚举、实时和可追溯文档集合。实时事实用搜索、RAG或工具。

参数越大一定越适合

错误。任务质量还受数据、训练、上下文、推理策略影响;大模型通常延迟和成本更高。

上下文越长回答越好

错误。无关内容会稀释信号、增加成本和攻击面,长上下文有效利用也可能下降。

模型概率就是答案正确率

错误。下一个 Token 的条件概率不是事实置信度。

Temperature设为0就不会幻觉

错误。它只降低采样随机性,不修复错误知识和缺失上下文。

模型支持JSON就能直接入库

错误。语法正确不等于字段、金额、权限和业务规则正确,必须 Schema 和业务校验。

二十二、面试标准回答

大模型怎样生成一句话

文本先经Tokenizer切成Token并映射为ID,通过Embedding和位置信息得到向量;多层Transformer用Masked Self-Attention和FFN形成上下文表示;最后位置经过LM Head得到整个词表的Logits,Softmax和解码策略选出下一个Token。新Token追加到上下文后重复计算,直到EOS、停止词或长度上限。

Q、K、V是什么

Q表示当前位置想找什么,K表示每个位置可被怎样匹配,V表示实际贡献的信息。Q和K点积、缩放、Mask、Softmax后得到权重,再按权重汇总V。多头使用不同投影学习不同子空间关系。

训练和推理有什么区别

训练有已知目标Token,计算交叉熵损失并反向传播更新参数,配合Causal Mask可并行计算多个位置;推理不更新基础参数,而且下一个Token未知,必须生成后再用于下一步,因此单请求Decode具有逐Token依赖。

KV Cache为什么提速又占显存

自回归生成时历史Token的K和V不会变化。KV Cache保存每层历史K/V,新一步只计算新Token,避免重复计算;但Cache随层数、KV头、序列长度、精度和并发增长,因此长上下文和高并发会显著占用显存。

为什么大模型会幻觉

模型训练目标是生成在上下文中概率较高的后续,不是查询受约束的事实数据库。训练知识缺失或过期、上下文不足、RAG错误、指令冲突都可能让流畅但错误的文本概率更高。治理要结合RAG或工具、权限、引用校验、拒答、评估和高风险人工审核,不能只调低Temperature。

二十三、学习验收

不看答案完成:

  1. 用目标模型 Tokenizer 比较中文、英文和代码的 Token 数。
  2. 解释为什么 Token ID 大小不表示语义远近。
  3. 手算三个 Logit 的 Softmax,并解释减最大值的作用。
  4. 画出 Q、K、V、Mask、Softmax 和加权求和链路。
  5. 解释训练时为什么能同时算多个位置,推理时为什么逐Token。
  6. 运行温度采样 Demo,比较三组分布和计数。
  7. 为32K窗口设计System、历史、RAG、工具和输出预算。
  8. 估算一个模型仅权重显存,并列出遗漏开销。
  9. 区分TTFT变慢和TPOT变慢的排查方向。
  10. 对一个幻觉案例判断根因属于参数、上下文、RAG、工具还是指令。
  11. 设计一个小模型、强模型、本地模型和人工审核的路由规则。
  12. 给模型API Demo补请求限流、Token限额和脱敏审计。

关联知识点