大模型应用开发:从原理到实践的全面指南
文章借助AI撰写,作者已阅读,介意者勿阅!!!
2026年,大语言模型(LLM)应用开发已经形成完整的生态体系。本文从原理出发,系统讲解LLM应用开发的各个方向,帮助开发者既了解原理,又掌握实践,更能自主创新。
一、大模型基础:从文字到智能
在开始学习大模型应用开发之前,我们需要先理解大模型本身是如何工作的。本章将从最基础的概念开始,讲解文字是如何进入大模型、被处理、然后变成我们能看懂的输出的。
本章核心问题:
- 一段文字是如何进入大模型的?
- 大模型是如何"理解"文字的?
- 大模型是如何生成文字的?
- Embedding到底是什么?它在大模型中扮演什么角色?
1.1 大模型是如何处理文字的?
1.1.1 文字进入大模型的全过程
当你向ChatGPT发送一条消息时,背后经历了以下完整流程:
用户输入: "什么是RAG?"
↓
┌─────────────────────────────────────────────────────────────┐
│ 第1步:Tokenization(分词) │
│ 将文字拆分成更小的单元(Token) │
│ "什么是RAG?" → ["什么", "是", "R", "A", "G", "?"] │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2步:Token to ID(转为数字) │
│ 将每个Token映射到一个数字ID │
│ ["什么", "是", "R", "A", "G", "?"] → [1234, 5678, 45, 32, 38, 3456] │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第3步:Embedding(向量化) │
│ 将每个Token ID转换为一个高维向量 │
│ [1234, 5678, 45, 32, 38, 3456] → [[0.1, 0.2, ...], [0.3, 0.4, ...], ...] │
│ 每个Token变成一个4096维的向量 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第4步:Transformer推理 │
│ 向量经过多层自注意力网络处理 │
│ 输入向量 → 32/64层Transformer → 输出向量 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第5步:输出层(Output Layer) │
│ 将输出向量转换为每个Token的分数 │
│ 输出向量 → 线性层 → Logits(50000个分数) │
│ 如:{"检索": 0.8, "生成": 0.1, "增强": 0.05, ...} │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第6步:Decoding(解码) │
│ 从分数中选择一个Token,转换回文字 │
│ 选择"检索" → "检索" │
└─────────────────────────────────────────────────────────────┘
↓
模型输出: "RAG是检索增强生成..."
关键概念:
| 步骤 | 输入 | 输出 | 说明 |
|---|---|---|---|
| Tokenization | 文字 | Token列表 | 将文字拆分成更小的单元 |
| Token to ID | Token列表 | 数字列表 | 将Token映射到数字 |
| Embedding | 数字列表 | 向量列表 | 将数字转换为高维向量 |
| Transformer | 向量列表 | 向量列表 | 处理向量,提取语义 |
| 输出层 | 向量 | 分数 | 计算每个Token的概率 |
| Decoding | 分数 | 文字 | 选择Token,转换回文字 |
1.1.2 Tokenization:文字如何变成Token
Tokenization是将文本分割成更小的单元(Token)的过程。
为什么要分词?
| 原因 | 说明 |
|---|---|
| 词汇表有限 | 模型不可能包含所有可能的词 |
| 处理未知词 | 可以将未知词拆分为已知的子词 |
| 多语言支持 | 不同语言可以共享子词 |
| 效率 | 固定大小的词汇表便于计算 |
分词示例:
# 使用tiktoken(OpenAI的分词器)
import tiktoken
encoder = tiktoken.encoding_for_model("gpt-4")
# 文本 → Token
text = "什么是RAG?"
tokens = encoder.encode(text)
print(tokens) # [1234, 5678, 45, 32, 38, 3456]
# Token → 文本
decoded_text = encoder.decode(tokens)
print(decoded_text) # "什么是RAG?"
# 查看每个Token是什么
for token_id in tokens:
token_text = encoder.decode([token_id])
print(f"ID {token_id}: '{token_text}'")
# 输出:
# ID 1234: '什么'
# ID 5678: '是'
# ID 45: 'R'
# ID 32: 'A'
# ID 38: 'G'
# ID 3456: '?'
主流分词算法详解
现代大模型主要使用以下几种分词算法:
| 算法 | 核心思想 | 方向 | 代表模型 |
|---|---|---|---|
| BPE (Byte Pair Encoding) | 自底向上,合并频率最高的字符对 | 合并 | GPT系列、Claude |
| WordPiece | 自底向上,合并似然度最高的字符对 | 合并 | BERT |
| Unigram | 自顶向下,删除贡献最小的子词 | 删除 | T5、ALBERT |
| SentencePiece | 语言无关的库,支持BPE和Unigram | 库 | Llama、Qwen |
| Tiktoken | OpenAI的BPE实现,Rust编写 | 库 | GPT-3.5、GPT-4 |
BPE算法原理(最常用)
BPE(Byte Pair Encoding) 是当前最主流的分词算法,被GPT系列、Claude、DeepSeek等模型使用。
核心思想:从单个字符开始,迭代地合并语料中最频繁出现的相邻字符对,直到达到目标词汇表大小。
训练过程:
初始语料: "low low low low low lowest lowest newer newer newer"
初始词汇: {l, o, w, e, s, t, n, r}
第1轮: 统计所有相邻字符对
"l o" 出现 7 次
"o w" 出现 7 次
"w e" 出现 2 次
"e r" 出现 3 次
...
合并频率最高的 "l o" → "lo"
第2轮: 重新统计
"lo w" 出现 7 次
"w e" 出现 2 次
...
合并 "lo w" → "low"
第3轮: 继续合并
"low e" 出现 2 次
"e r" 出现 3 次
...
合并 "e r" → "er"
... 重复直到达到目标词汇表大小
Byte-level BPE(GPT-2及之后的模型使用):
- 基础词汇表为256个字节值(而非Unicode字符)
- 任何UTF-8文本都能被表示,永远不会出现未知Token
- 基础词汇表固定,不随语言变化
代码示例:
# 使用HuggingFace Tokenizers训练BPE分词器
from tokenizers import Tokenizer
from tokenizers.models import BPE
from tokenizers.trainers import BpeTrainer
from tokenizers.pre_tokenizers import ByteLevel
# 1. 初始化BPE分词器
tokenizer = Tokenizer(BPE())
# 2. 设置预分词器(Byte-level)
tokenizer.pre_tokenizer = ByteLevel(add_prefix_space=True)
# 3. 配置训练器
# vocab_size: 词汇表大小,决定分词粒度
trainer = BpeTrainer(
vocab_size=50000, # 词汇表大小
min_frequency=2, # 最小出现频率
special_tokens=["<unk>", "<s>", "</s>", "<pad>"],
initial_alphabet=ByteLevel.alphabet() # 包含所有256字节
)
# 4. 训练
tokenizer.train(files=["corpus.txt"], trainer=trainer)
# 5. 使用
result = tokenizer.encode("Hello, 你好世界!")
print(f"Tokens: {result.tokens}") # ['Hello', ',', 'Ġä½ł', '好', 'ä¸ç', 'ķ', '¼', '!']
print(f"IDs: {result.ids}")
不同模型使用的分词算法
| 模型系列 | 分词算法 | 分词器类型 | 词汇表大小 | 特殊说明 |
|---|---|---|---|---|
| GPT-2 | Byte-level BPE | tiktoken | 50,257 | 首个Byte-level BPE |
| GPT-3 | Byte-level BPE | tiktoken | 50,257 | 同GPT-2 |
| GPT-3.5-turbo | Byte-level BPE | tiktoken (cl100k_base) | 100,277 | 扩展词汇表 |
| GPT-4 | Byte-level BPE | tiktoken (cl100k_base) | 100,277 | 同GPT-3.5 |
| GPT-4o / o1 / o3 | Byte-level BPE | tiktoken (o200k_base) | ~200,000 | 更好多语言支持 |
| Claude系列 | BPE变体 | 自定义 | ~200,000 | 专有实现 |
| Llama 1/2 | SentencePiece (BPE) | SentencePiece | 32,000 | byte_fallback=True |
| Llama 3 | SentencePiece (BPE) | SentencePiece | 128,256 | 显著扩展 |
| Llama 4 | SentencePiece (BPE) | SentencePiece | 256,000+ | 多模态优化 |
| Qwen系列 | Byte-level BPE | HuggingFace | 151,643 | 中文优化 |
| DeepSeek | Byte-level BPE | HuggingFace | 100,015 | 代码优化 |
| BERT | WordPiece | HuggingFace | 30,522 | 需预分词 |
| T5 / mT5 | Unigram | SentencePiece | 32,128 / 250,000 | 经典Unigram |
词汇表大小趋势:
2018-2019: 30K-50K
BERT: 30,522
GPT-2: 50,257
T5: 32,128
2020-2022: 50K-100K
GPT-3: 50,257
GPT-3.5: 100,277
2023-2024: 100K-130K
GPT-4: 100,277
Llama 3: 128,256
Qwen 2: 151,643
2025-2026: 200K+
GPT-4o: ~200,000
Llama 4: 256,000+
为什么词汇表越来越大?
| 原因 | 说明 |
|---|---|
| 多语言支持 | 更大词汇表能覆盖更多语言的字符和子词 |
| 分词效率 | 更大词汇表 → 更少Token表示相同文本 → 更低成本 |
| 嵌入矩阵占比小 | 嵌入矩阵只占总参数的很小比例(如Llama-7B仅1.9%) |
多语言分词效率对比:
| 语言 | GPT-4 (cl100k) | GPT-4o (o200k) | Llama 3 (128K) | Qwen 2 (152K) |
|---|---|---|---|---|
| 英文 | 1x (基准) | 1x | 1x | 1x |
| 中文 | ~3x 更贵 | ~2x 更贵 | ~1.5x 更贵 | ~1.2x 更贵 |
| 日文 | ~4x 更贵 | ~2.5x 更贵 | ~2x 更贵 | ~1.5x 更贵 |
关键洞察:使用大词汇表的模型(如Qwen 2)在处理中文时更高效。
1.1.3 Embedding:Token如何变成向量
Embedding(向量化)是将Token ID转换为高维向量的过程。
什么是向量?
向量就是一组数字,比如:
[0.1, -0.2, 0.5, 0.8, -0.3, 0.4, ...] # 4096个数字
为什么需要向量?
| 原因 | 说明 |
|---|---|
| 计算机只能处理数字 | 文字无法直接参与数学运算 |
| 语义表示 | 相似含义的Token,向量也相似 |
| 可计算 | 可以用数学运算处理语义关系 |
Embedding是如何工作的?
Embedding本质上是一个查找表。模型内部存储了一个大矩阵:
词汇表大小: 50000个Token
向量维度: 4096
Embedding矩阵: [50000 × 4096]
- 第0行: Token ID 0 的向量
- 第1行: Token ID 1 的向量
- ...
- 第49999行: Token ID 49999 的向量
当输入 Token ID = 1234 时:
→ 查找第1234行
→ 返回该行的4096个数字
代码示例:
import torch
import torch.nn as nn
# 参数说明:
# vocab_size: 词汇表大小,即模型能识别的不同Token的数量
# d_model: 模型维度,即每个Token用多少个数字表示
vocab_size = 50000 # 词汇表大小(如GPT-2使用50,257)
d_model = 4096 # 模型维度(如Llama-2-7B使用4,096)
# 创建Embedding层(这就是大模型中的Embedding层)
embedding_layer = nn.Embedding(vocab_size, d_model)
# Token ID → 向量
token_ids = torch.tensor([1234, 5678, 45, 32, 38, 3456])
vectors = embedding_layer(token_ids)
print(vectors.shape) # torch.Size([6, 4096])
# 6个Token ID,每个变成了4096维的向量
不同模型的Embedding层参数
每个模型都有自己独立的Embedding查找表,参数各不相同:
| 模型 | 词汇表大小 | Embedding维度 | 位置编码 | 权重共享 | 特点 |
|---|---|---|---|---|---|
| GPT-2 | 50,257 | 768–1,600 | 可学习绝对位置 | ✅ 是 | 输入Embedding = 输出投影 |
| GPT-3 | 50,257 | 12,288 | 可学习绝对位置 | ❌ 否 | 独立的LM Head |
| GPT-4 | ~100,000 | 未公开 | 推测RoPE | 未公开 | MoE架构 |
| Llama 2 | 32,000 | 4,096–8,192 | RoPE | ❌ 否 | 开源,可学习 |
| Llama 3 | 128,256 | 4,096–8,192 | RoPE (θ=500,000) | ❌ 否 | 显著扩展词汇表 |
| Qwen 2 | 151,936 | 4,096–8,192 | RoPE | ❌ 否 | 中文优化,词汇表最大 |
| DeepSeek-V3 | 102,400 | 7,168 | RoPE + YaRN | ❌ 否 | MoE架构 |
| BERT | 30,522 | 768–1,024 | 可学习绝对位置 + Token Type | ✅ 是 | Encoder架构 |
| T5 | 32,128 | 512–1,024 | 相对位置桶编码 | ✅ 是 | Encoder-Decoder架构 |
关键发现:
- 每个模型都有独立的Embedding查找表:GPT、Llama、Qwen、DeepSeek的Embedding矩阵完全不同
- 维度选择影响表达能力:维度越高,能表示的语义越丰富,但计算成本也越高
- 词汇表大小影响分词效率:词汇表越大,分词越细,但Embedding参数也越多
Embedding参数量对比:
| 模型 | Embedding参数量 | 占总参数比例 |
|---|---|---|
| BERT-base (110M) | 30K × 768 = 23M | 21% |
| Llama 2-7B (6.7B) | 32K × 4,096 = 131M | 1.9% |
| Llama 3-8B (8B) | 128K × 4,096 = 524M | 6.6% |
| Qwen 2-7B (7.6B) | 152K × 4,096 = 622M | 8.2% |
| DeepSeek-V3 (671B) | 102K × 7,168 = 734M | 0.11% |
洞察:词汇表越大,Embedding参数占比越高。对于小模型(如BERT),Embedding占比可达21%;对于超大模型(如DeepSeek-V3),占比仅0.11%。
位置编码方式对比:
| 模型 | 位置编码方式 | 原理 | 优点 | 缺点 |
|---|---|---|---|---|
| BERT | 可学习绝对位置 | 为每个位置学习一个向量 | 简单直观 | 无法泛化到训练时未见过的位置 |
| GPT-2/3 | 可学习绝对位置 | 同上 | 简单直观 | 同上 |
| T5 | 相对位置桶编码 | 将相对位置映射到固定数量的桶 | 支持一定程度的长度外推 | 实现复杂 |
| Llama/Qwen/DeepSeek | RoPE (旋转位置编码) | 通过旋转Q/K向量编码位置 | 无需额外参数,支持长上下文 | 计算复杂 |
代码示例:不同模型的Embedding层实现:
import torch
import torch.nn as nn
# ===== GPT-2风格:可学习绝对位置编码 =====
class GPT2Embedding(nn.Module):
def __init__(self, vocab_size=50257, d_model=768, max_seq_len=1024):
super().__init__()
# Token Embedding: 将Token ID转换为向量
self.token_embedding = nn.Embedding(vocab_size, d_model)
# Position Embedding: 为每个位置学习一个向量
self.position_embedding = nn.Embedding(max_seq_len, d_model)
def forward(self, input_ids):
# input_ids: [batch_size, seq_len] - Token ID序列
seq_len = input_ids.size(1)
positions = torch.arange(seq_len, device=input_ids.device)
# Token Embedding + Position Embedding
token_embeds = self.token_embedding(input_ids) # [batch, seq_len, d_model]
position_embeds = self.position_embedding(positions) # [seq_len, d_model]
return token_embeds + position_embeds
# ===== BERT风格:Word + Position + Token Type =====
class BertEmbedding(nn.Module):
def __init__(self, vocab_size=30522, d_model=768, max_seq_len=512, type_vocab_size=2):
super().__init__()
# Word Embedding: 将Token ID转换为向量
self.word_embedding = nn.Embedding(vocab_size, d_model)
# Position Embedding: 为每个位置学习一个向量
self.position_embedding = nn.Embedding(max_seq_len, d_model)
# Token Type Embedding: 区分句子对(如问答任务)
self.token_type_embedding = nn.Embedding(type_vocab_size, d_model)
def forward(self, input_ids, token_type_ids=None):
seq_len = input_ids.size(1)
positions = torch.arange(seq_len, device=input_ids.device)
if token_type_ids is None:
token_type_ids = torch.zeros_like(input_ids)
# 三种Embedding相加
word_embeds = self.word_embedding(input_ids)
position_embeds = self.position_embedding(positions)
type_embeds = self.token_type_embedding(token_type_ids)
return word_embeds + position_embeds + type_embeds
# ===== Llama/Qwen风格:RoPE位置编码 =====
class LlamaEmbedding(nn.Module):
def __init__(self, vocab_size=32000, d_model=4096):
super().__init__()
# 只有Token Embedding,没有Position Embedding
# 位置信息通过RoPE在注意力层中编码
self.token_embedding = nn.Embedding(vocab_size, d_model)
def forward(self, input_ids):
# 只返回Token Embedding,位置信息在RoPE中处理
return self.token_embedding(input_ids)
向量的语义特性:
相似含义的Token,向量也相似:
import numpy as np
def cosine_similarity(a, b):
"""计算两个向量的相似度(0~1,越接近1越相似)"""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))
# 假设我们有以下向量(简化为3维便于理解)
cat_vector = np.array([0.2, -0.1, 0.5]) # "猫"
kitten_vector = np.array([0.3, -0.2, 0.4]) # "小猫"
car_vector = np.array([-0.5, 0.8, -0.1]) # "汽车"
# 计算相似度
print(cosine_similarity(cat_vector, kitten_vector)) # 0.95 (高,因为都是猫)
print(cosine_similarity(cat_vector, car_vector)) # 0.12 (低,因为语义不同)
总结:
| 概念 | 说明 |
|---|---|
| Embedding层 | 模型内部的组件,将Token ID转换为向量 |
| 每个模型独立 | 不同模型有不同的Embedding查找表 |
| 参数不同 | 词汇表大小、维度、位置编码方式都不同 |
| 权重共享 | 部分模型(如BERT、T5)的输入Embedding和输出投影共享权重 |
1.1.4 Transformer推理:向量如何被处理
Transformer是大模型的核心架构。它通过自注意力机制处理向量序列。
自注意力的直觉理解:
句子: "小明喜欢小红,因为她很可爱"
当处理"她"时,模型会"关注"句子中的其他词:
- "小红":高权重(因为"她"指的是"小红")
- "小明":低权重
- "喜欢":低权重
- "可爱":中等权重
这样模型就能理解"她"指的是谁
Transformer的处理过程:
# 简化的Transformer处理
def transformer_forward(input_vectors):
"""
input_vectors: [seq_len, d_model] # 输入向量序列
output_vectors: [seq_len, d_model] # 输出向量序列
"""
# 1. 自注意力:每个向量关注所有其他向量
attention_output = self_attention(input_vectors)
# 2. 前馈网络:非线性变换
ff_output = feed_forward(attention_output)
# 3. 残差连接 + 层归一化
output = layer_norm(input_vectors + ff_output)
return output
# 大模型会重复这个过程32次、64次甚至更多
for i in range(num_layers):
output_vectors = transformer_forward(output_vectors)
1.1.5 输出层:向量如何变成文字
输出层将Transformer的输出向量转换为每个Token的分数(Logits)。
class OutputLayer(nn.Module):
def __init__(self, d_model, vocab_size):
super().__init__()
self.linear = nn.Linear(d_model, vocab_size)
def forward(self, x):
# x: [seq_len, d_model]
# logits: [seq_len, vocab_size]
logits = self.linear(x)
return logits
# 假设词汇表大小为50000
output_layer = OutputLayer(d_model=4096, vocab_size=50000)
# Transformer输出
transformer_output = torch.randn(10, 4096) # 10个Token,每个4096维
# 转换为logits
logits = output_layer(transformer_output)
print(logits.shape) # torch.Size([10, 50000])
# 每个Token都有50000个分数,对应词汇表中的每个Token
Logits是什么?
Logits是每个Token的"原始分数",需要经过Softmax转换为概率:
# Logits → 概率
logits = torch.tensor([2.0, 1.0, 0.5, -1.0])
probs = torch.softmax(logits, dim=0)
print(probs) # tensor([0.57, 0.21, 0.12, 0.10])
# Token 0 的概率是57%,Token 1 是21%,...
1.1.6 解码策略:如何选择下一个Token
从概率分布中选择Token有多种策略:
def greedy_decoding(probs):
"""贪心解码:总是选择概率最高的"""
return torch.argmax(probs)
def temperature_sampling(probs, temperature=1.0):
"""温度采样:控制随机性"""
# temperature < 1: 更确定(选择高概率的)
# temperature > 1: 更随机(给低概率的更多机会)
adjusted_probs = torch.softmax(torch.log(probs) / temperature, dim=0)
return torch.multinomial(adjusted_probs, num_samples=1)
def top_k_sampling(probs, k=50):
"""Top-K采样:只在概率最高的K个中选择"""
top_k_probs, top_k_indices = torch.topk(probs, k)
# 重新归一化
top_k_probs = top_k_probs / top_k_probs.sum()
selected = torch.multinomial(top_k_probs, num_samples=1)
return top_k_indices[selected]
def top_p_sampling(probs, p=0.9):
"""Top-P采样(核采样):累积概率达到P时停止"""
sorted_probs, sorted_indices = torch.sort(probs, descending=True)
cumulative_probs = torch.cumsum(sorted_probs, dim=0)
# 找到累积概率超过P的位置
mask = cumulative_probs - sorted_probs >= p
sorted_probs[mask] = 0
# 重新归一化
sorted_probs = sorted_probs / sorted_probs.sum()
selected = torch.multinomial(sorted_probs, num_samples=1)
return sorted_indices[selected]
解码策略对比:
| 策略 | 特点 | 适用场景 |
|---|---|---|
| 贪心解码 | 总是选择概率最高的 | 需要确定性输出 |
| 温度采样 | 控制随机性 | 创意写作、对话 |
| Top-K | 只在前K个中选 | 平衡质量和多样性 |
| Top-P | 累积概率达到P | 动态调整候选集 |
1.2 Embedding到底是什么?
在上一节中,我们多次提到了"Embedding"这个词。但Embedding这个词有两层含义,很多人会混淆。本节将彻底讲清楚。
1.2.1 Embedding的两层含义
Embedding这个词在不同语境下有不同的含义:
| 含义 | 类型 | 说明 | 示例 |
|---|---|---|---|
| Embedding(过程) | 动词 | 将Token转换为向量的过程 | “对这个Token做Embedding” |
| Embedding层(组件) | 名词 | 大模型内部的一个组件 | “GPT-4的Embedding层” |
| Embedding模型(独立模型) | 名词 | 专门生成向量的独立模型 | “text-embedding-3-large” |
关键区别:
Embedding(过程)
↓
┌─────────────────────────────────────────────────────────────┐
│ 两种实现方式: │
├─────────────────────────────────────────────────────────────┤
│ 1. 大模型中的Embedding层(模型内部组件) │
│ - 是大模型的一部分 │
│ - 只能用于Token → 向量 │
│ - 向量维度固定(如4096) │
│ - 不能单独使用 │
├─────────────────────────────────────────────────────────────┤
│ 2. 独立的Embedding模型(专门的模型) │
│ - 是独立的模型 │
│ - 可以用于文本 → 向量 │
│ - 向量维度可选(如256、1024、3072) │
│ - 可以单独使用 │
└─────────────────────────────────────────────────────────────┘
1.2.2 大模型中的Embedding层
Embedding层是大模型内部的一个组件,负责将Token ID转换为向量。
class LargeLanguageModel(nn.Module):
def __init__(self, vocab_size, d_model, num_layers):
super().__init__()
# Embedding层:Token ID → 向量
self.token_embedding = nn.Embedding(vocab_size, d_model)
# 位置编码
self.position_embedding = nn.Embedding(max_seq_len, d_model)
# Transformer层
self.layers = nn.ModuleList([
TransformerLayer(d_model) for _ in range(num_layers)
])
# 输出层:向量 → Logits
self.output_layer = nn.Linear(d_model, vocab_size)
def forward(self, input_ids):
# 1. Embedding:Token ID → 向量
token_vectors = self.token_embedding(input_ids)
# 2. 添加位置信息
positions = torch.arange(len(input_ids))
position_vectors = self.position_embedding(positions)
vectors = token_vectors + position_vectors
# 3. Transformer处理
for layer in self.layers:
vectors = layer(vectors)
# 4. 输出层
logits = self.output_layer(vectors)
return logits
Embedding层的特点:
| 特点 | 说明 |
|---|---|
| 是模型的一部分 | 不能单独使用 |
| 输入是Token ID | 不是文本 |
| 输出维度固定 | 由模型决定(如4096) |
| 训练目标不同 | 为了"预测下一个Token" |
1.2.3 独立的Embedding模型
Embedding模型是专门用于生成文本向量的独立模型。
from sentence_transformers import SentenceTransformer
# 加载Embedding模型
model = SentenceTransformer('all-MiniLM-L6-v2')
# 文本 → 向量(直接输入文本,不需要分词)
texts = ["什么是RAG?", "RAG是检索增强生成技术"]
vectors = model.encode(texts)
print(vectors.shape) # (2, 384)
# 每个文本变成了384维的向量
Embedding模型的特点:
| 特点 | 说明 |
|---|---|
| 是独立的模型 | 可以单独使用 |
| 输入是文本 | 不需要手动分词 |
| 输出维度可选 | 可以选择256、1024、3072等 |
| 训练目标不同 | 为了"语义相似度" |
1.2.4 它们的联系与区别
核心区别:
| 维度 | 大模型的Embedding层 | 独立的Embedding模型 |
|---|---|---|
| 本质 | 模型内部组件 | 独立模型 |
| 输入 | Token ID | 文本 |
| 输出 | 向量(维度固定) | 向量(维度可选) |
| 训练目标 | 预测下一个Token | 语义相似度 |
| 使用方式 | 嵌入在大模型中 | 单独调用 |
| 主要用途 | 大模型推理 | 检索、分类、聚类 |
它们的联系:
- 技术原理相同:都是将离散数据转换为向量
- 都可以表示语义:相似的文本,向量也相似
- 可以互相转换:理论上可以用大模型的Embedding层作为Embedding模型
为什么不能直接用大模型的Embedding层作为Embedding模型?
| 问题 | 说明 |
|---|---|
| 训练目标不同 | 大模型的Embedding层是为了"预测下一个Token",而不是"语义相似度" |
| 效率问题 | 大模型的Embedding层很大(几十GB),而独立的Embedding模型很小(几百MB) |
| 使用不便 | 需要先分词,再查表,而独立的Embedding模型可以直接输入文本 |
但是,可以用大模型的Embedding层吗?
可以! 这叫做用大模型作为Embedding模型:
from transformers import AutoModel, AutoTokenizer
# 加载大模型(只使用Embedding层)
tokenizer = AutoTokenizer.from_pretrained("meta-llama/Llama-3-8B")
model = AutoModel.from_pretrained("meta-llama/Llama-3-8B")
# 文本 → Token → Embedding
text = "什么是RAG?"
inputs = tokenizer(text, return_tensors="pt")
# 获取Embedding(不经过Transformer层)
with torch.no_grad():
# 获取第一层的输出(Embedding层的输出)
outputs = model(inputs.input_ids, output_hidden_states=True)
embedding = outputs.hidden_states[0] # 第0层就是Embedding层
print(embedding.shape) # torch.Size([1, 6, 4096])
为什么不推荐这样做?
| 问题 | 说明 |
|---|---|
| 效率低 | 大模型很大,推理慢 |
| 效果差 | 训练目标不同,语义表示不准确 |
| 成本高 | 需要大量GPU内存 |
1.2.5 为什么需要独立的Embedding模型?
独立的Embedding模型是专门为"语义表示"设计的,有更好的效果:
| 优势 | 说明 |
|---|---|
| 训练目标明确 | 专门为语义相似度优化 |
| 效率高 | 模型小,推理快 |
| 使用方便 | 直接输入文本,不需要分词 |
| 维度灵活 | 可以选择不同的维度 |
主流的Embedding模型:
| 模型 | 维度 | 特点 |
|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 综合能力最强 |
| OpenAI text-embedding-3-small | 1536 | 性价比高 |
| Cohere Embed v4 | 256-1536 | 多模态支持 |
| BGE-large-en-v1.5 | 1024 | 开源最佳 |
| all-MiniLM-L6-v2 | 384 | 轻量级 |
1.3 不同大模型的Embedding层
不同的大模型使用不同的Embedding层设计。了解这些差异有助于理解模型的能力和限制。
1.3.1 主流大模型的Embedding层对比
| 模型 | Embedding维度 | 词汇表大小 | 分词算法 | 特点 |
|---|---|---|---|---|
| GPT-4 | 12288 | 100,277 | Tiktoken (BPE) | 高维度,强大语义表示 |
| GPT-4o | 未公开 | 100,277 | Tiktoken (BPE) | 多模态支持 |
| Claude 3 | 未公开 | 未公开 | 自研 | 长上下文支持 |
| Llama 3 | 4096 | 128,256 | SentencePiece | 开源,多语言 |
| Llama 3 70B | 8192 | 128,256 | SentencePiece | 更强语义表示 |
| Qwen 2 | 4096 | 151,936 | SentencePiece | 中英文优化 |
| DeepSeek | 4096 | 100,015 | BPE | 中英文支持 |
1.3.2 维度选择的权衡
| 维度 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| 低维(256-1024) | 计算快、内存小 | 语义表示能力弱 | 简单任务、资源受限 |
| 中维(2048-4096) | 平衡 | 通用选择 | 大多数应用 |
| 高维(8192+) | 语义表示能力强 | 计算慢、内存大 | 复杂推理、高精度需求 |
1.3.3 词汇表大小的影响
| 词汇表大小 | 优点 | 缺点 | 代表模型 |
|---|---|---|---|
| 小(30K-50K) | 训练快、内存小 | 分词粒度粗,长词会被拆分 | BERT |
| 中(100K-150K) | 平衡 | 通用选择 | GPT-4、Llama 3 |
| 大(150K+) | 分词粒度细,少拆分 | 训练慢、内存大 | Qwen 2 |
1.4 Embedding在应用中的作用
Embedding在大模型应用中扮演着关键角色。本节介绍Embedding的主要用途。
1.4.1 语义搜索
语义搜索是Embedding最常用的应用。它通过计算向量相似度来找到语义相关的内容。
from openai import OpenAI
import numpy as np
client = OpenAI()
def get_embedding(text):
"""获取文本的Embedding"""
response = client.embeddings.create(
model="text-embedding-3-large",
input=text
)
return response.data[0].embedding
def cosine_similarity(a, b):
"""计算余弦相似度"""
return np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(a))
# 文档库
documents = [
"RAG是检索增强生成技术",
"大语言模型是AI的一种",
"Python是一种编程语言",
"机器学习是AI的子领域"
]
# 查询
query = "什么是RAG?"
# 获取Embedding
query_embedding = get_embedding(query)
doc_embeddings = [get_embedding(doc) for doc in documents]
# 计算相似度
similarities = [cosine_similarity(query_embedding, doc_emb) for doc_emb in doc_embeddings]
# 找到最相似的文档
most_similar_idx = np.argmax(similarities)
print(f"最相关的文档: {documents[most_similar_idx]}")
print(f"相似度: {similarities[most_similar_idx]:.4f}")
1.4.2 RAG(检索增强生成)
RAG是Embedding和大模型协作的典型应用:
用户问题: "什么是RAG?"
↓
┌─────────────────────────────────────────────────────────────┐
│ 第1步:Embedding模型 │
│ 将问题转为向量: [0.1, 0.2, 0.3, ..., 0.4] │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第2步:向量检索 │
│ 在知识库中找到最相似的文档 │
└─────────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────────┐
│ 第3步:大模型生成 │
│ 基于检索到的文档,生成回答 │
└─────────────────────────────────────────────────────────────┘
↓
模型输出: "RAG是检索增强生成技术..."
代码示例:
from openai import OpenAI
import numpy as np
client = OpenAI()
def rag_answer(question, documents):
"""RAG问答"""
# 1. 获取问题的Embedding
question_embedding = get_embedding(question)
# 2. 获取文档的Embedding
doc_embeddings = [get_embedding(doc) for doc in documents]
# 3. 找到最相关的文档
similarities = [cosine_similarity(question_embedding, doc_emb) for doc_emb in doc_embeddings]
most_similar_idx = np.argmax(similarities)
relevant_doc = documents[most_similar_idx]
# 4. 使用大模型生成回答
response = client.chat.completions.create(
model="gpt-4",
messages=[
{"role": "system", "content": "基于以下上下文回答问题。"},
{"role": "user", "content": f"上下文:{relevant_doc}\n\n问题:{question}"}
]
)
return response.choices[0].message.content
# 使用
documents = [
"RAG是检索增强生成技术,它结合了检索和生成...",
"大语言模型是AI的一种,它通过预训练学习语言...",
"Python是一种编程语言,广泛用于AI开发..."
]
answer = rag_answer("什么是RAG?", documents)
print(answer)
1.4.3 文本分类
文本分类是将文本分为不同类别的任务。Embedding可以将文本转换为向量,然后用分类器进行分类。
from sklearn.linear_model import LogisticRegression
from openai import OpenAI
import numpy as np
client = OpenAI()
# 训练数据
train_texts = [
"这部电影太好看了",
"剧情很无聊",
"演员演技很棒",
"浪费时间",
"推荐大家去看",
"不值得票价"
]
train_labels = [1, 0, 1, 0, 1, 0] # 1=正面, 0=负面
# 获取Embedding
train_embeddings = [get_embedding(text) for text in train_texts]
# 训练分类器
classifier = LogisticRegression()
classifier.fit(train_embeddings, train_labels)
# 预测新文本
new_text = "这个电影还不错"
new_embedding = get_embedding(new_text)
prediction = classifier.predict([new_embedding])
print(f"预测结果: {'正面' if prediction[0] == 1 else '负面'}")
1.4.4 文本聚类
文本聚类是将相似文本分组的任务。Embedding可以将文本转换为向量,然后用聚类算法进行分组。
from sklearn.cluster import KMeans
from openai import OpenAI
import numpy as np
client = OpenAI()
# 文本列表
texts = [
"苹果发布了新iPhone",
"华为发布了新手机",
"今天天气很好",
"明天会下雨",
"iPhone销量创新高",
"气温将达到30度"
]
# 获取Embedding
embeddings = [get_embedding(text) for text in texts]
# 聚类
kmeans = KMeans(n_clusters=2, random_state=0)
clusters = kmeans.fit_predict(embeddings)
# 输出聚类结果
for i, (text, cluster) in enumerate(zip(texts, clusters)):
print(f"文本: {text} → 类别: {cluster}")
# 文本: 苹果发布了新iPhone → 类别: 0
# 文本: 华为发布了新手机 → 类别: 0
# 文本: 今天天气很好 → 类别: 1
# 文本: 明天会下雨 → 类别: 1
# 文本: iPhone销量创新高 → 类别: 0
# 文本: 气温将达到30度 → 类别: 1
1.5 总结:大模型与Embedding的关系
1.5.1 关键概念回顾
| 概念 | 说明 | 用途 |
|---|---|---|
| Tokenization | 将文字拆分成Token | 大模型输入 |
| Embedding(过程) | 将Token转换为向量 | 大模型内部处理 |
| Embedding层 | 大模型内部的组件 | Token → 向量 |
| Embedding模型 | 独立的模型 | 文本 → 向量 |
| Transformer | 处理向量的网络 | 语义理解 |
| 输出层 | 将向量转换为分数 | 生成Token |
| Decoding | 从分数中选择Token | 生成文字 |
1.5.2 它们如何协作
┌─────────────────────────────────────────────────────────────┐
│ 应用场景:RAG │
├─────────────────────────────────────────────────────────────┤
│ 1. Embedding模型:将文档和问题转为向量 │
│ 2. 向量数据库:存储和检索向量 │
│ 3. 大模型:基于检索结果生成回答 │
└─────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────┐
│ 应用场景:对话 │
├─────────────────────────────────────────────────────────────┤
│ 1. 大模型内部的Embedding层:将输入Token转为向量 │
│ 2. Transformer:处理向量,理解语义 │
│ 3. 输出层 + Decoding:生成回答 │
└─────────────────────────────────────────────────────────────┘
1.5.3 给开发者的建议
| 场景 | 推荐方案 |
|---|---|
| 语义搜索 | 使用独立的Embedding模型(如text-embedding-3-large) |
| 文本分类 | 使用独立的Embedding模型 + 分类器 |
| 文本聚类 | 使用独立的Embedding模型 + 聚类算法 |
| RAG | Embedding模型 + 向量数据库 + 大模型 |
| 对话 | 直接使用大模型(内部使用Embedding层) |
二、大模型应用概述
传统AI应用开发遵循"任务特定"范式:为每个任务训练专门的模型,需要大量标注数据和特征工程。大语言模型的出现带来了根本性转变:
| 维度 | 传统AI | 大语言模型 |
|---|---|---|
| 模型形态 | 任务特定模型 | 通用基础模型 |
| 开发方式 | 训练-部署-迭代 | 提示工程-微调-部署 |
| 数据需求 | 大量标注数据 | 少量示例或零样本 |
| 能力边界 | 单一任务 | 多任务通用 |
| 迭代周期 | 周-月级 | 小时-天级 |
2.1 LLM应用的核心特征
LLM应用与传统软件应用有显著差异,主要体现在以下四个维度:
| 特征 | 说明 | 设计要点 |
|---|---|---|
| 概率性输出 | 相同输入可能产生不同结果 | 输出验证、多次采样、用户预期管理 |
| 上下文依赖 | 推理能力高度依赖上下文质量 | 提示词设计、动态注入、记忆管理 |
| 工具增强 | 通过工具调用与外部系统交互 | API调用、代码执行、系统控制 |
| 迭代优化 | 需要持续评估和优化 | 提示词迭代、检索策略、模型切换 |
2.2 2026年应用生态全景图
经过两年多的发展,LLM应用生态已经形成清晰的分层架构:
┌─────────────────────────────────────────────────────────────┐
│ 应用层 (Applications) │
│ Chatbot · 代码助手 · 内容创作 · 数据分析 · 游戏NPC ... │
├─────────────────────────────────────────────────────────────┤
│ 编排层 (Orchestration) │
│ LangChain · LangGraph · Dify · Flowise · n8n │
├─────────────────────────────────────────────────────────────┤
│ 协议层 (Protocol) │
│ MCP (Model Context Protocol) │
├─────────────────────────────────────────────────────────────┤
│ 能力扩展层 (Capabilities) │
│ RAG · Agent · Skill · Memory · Tools │
├─────────────────────────────────────────────────────────────┤
│ 基础设施层 (Infrastructure) │
│ 向量数据库 · 可观测性 · 模型路由 · 安全防护 · 成本控制 │
├─────────────────────────────────────────────────────────────┤
│ 模型层 (Models) │
│ OpenAI · Claude · DeepSeek · Qwen · Llama · 本地模型 │
└─────────────────────────────────────────────────────────────┘
按成熟度划分,当前LLM应用可分为三个梯队:
| 阶段 | 应用类型 | 代表产品 |
|---|---|---|
| 成熟应用 | 对话式AI | ChatGPT、Claude、文心一言 |
| 代码助手 | GitHub Copilot、Cursor、Windsurf | |
| 内容创作 | Jasper、Notion AI、Writesonic | |
| 企业知识库 | 基于RAG的内部知识问答系统 | |
| 新兴应用 | 自主Agent | 能独立完成复杂任务的AI代理 |
| 多智能体系统 | 多个Agent协作完成任务 | |
| AI工作流 | Dify、n8n等自动化编排平台 | |
| 游戏NPC | 具有动态对话能力的游戏角色 | |
| 探索阶段 | AI科学家 | 自主进行科学研究的AI系统 |
| 具身智能 | 与物理世界交互的机器人 | |
| 长期记忆 | 具有持久记忆的个人AI助手 |
三、RAG:检索增强生成
3.1 RAG的核心原理
RAG(Retrieval-Augmented Generation) 是将LLM与外部知识库结合的技术。其核心思想是:在LLM生成回答之前,先从知识库中检索相关文档,将其作为上下文注入提示词。
为什么需要RAG?
| 问题 | 说明 | RAG解决方案 |
|---|---|---|
| 知识截止 | LLM训练数据有时间限制 | 检索最新文档 |
| 幻觉问题 | LLM可能生成错误信息 | 基于真实文档生成 |
| 私有知识 | LLM不了解企业内部信息 | 检索企业知识库 |
| 领域专业 | LLM在专业领域可能不准确 | 检索专业文档 |
RAG的工作流程:
用户问题
↓
┌─────────────┐
│ 查询理解 │ → 问题改写、查询扩展
└─────────────┘
↓
┌─────────────┐
│ 检索器 │ → 向量检索 + 关键词检索
└─────────────┘
↓
┌─────────────┐
│ 重排序器 │ → Cross-Encoder精排
└─────────────┘
↓
┌─────────────┐
│ 上下文构建 │ → 组装提示词
└─────────────┘
↓
┌─────────────┐
│ LLM生成 │ → 基于上下文生成回答
└─────────────┘
↓
最终回答
3.2 混合检索与重排序
混合检索结合了向量检索(语义匹配)和关键词检索(精确匹配),能显著提升检索质量:
from langchain.retrievers import EnsembleRetriever
from langchain_community.retrievers import BM25Retriever
from langchain.vectorstores import Chroma
# BM25检索器(关键词匹配)
bm25_retriever = BM25Retriever.from_documents(documents)
bm25_retriever.k = 50
# 向量检索器(语义匹配)
vectorstore = Chroma.from_documents(documents, embedding_model)
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 50})
# 混合检索(RRF融合)
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, vector_retriever],
weights=[0.4, 0.6] # BM25权重0.4,向量权重0.6
)
重排序是检索后的精排步骤,使用Cross-Encoder模型对检索结果进行重新排序:
from langchain.retrievers import ContextualCompressionRetriever
from langchain_cohere import CohereRerank
# Cohere Rerank 3.5
compressor = CohereRerank(model="rerank-v3.5", top_n=5)
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
3.3 如何自己开发一个RAG系统
步骤1:文档加载与分块
from langchain_community.document_loaders import DirectoryLoader, TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载文档
loader = DirectoryLoader("./docs", glob="**/*.md", loader_cls=TextLoader)
documents = loader.load()
# 分块
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separators=["\n\n", "\n", " ", ""]
)
chunks = text_splitter.split_documents(documents)
步骤2:向量化与存储
from langchain_openai import OpenAIEmbeddings
from langchain.vectorstores import Chroma
# 初始化Embedding模型
embeddings = OpenAIEmbeddings(model="text-embedding-3-large")
# 创建向量数据库
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db"
)
# 创建检索器
retriever = vectorstore.as_retriever(
search_type="mmr", # 最大边际相关性
search_kwargs={"k": 5}
)
步骤3:构建RAG链
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 提示词模板
template = """
基于以下上下文回答问题。如果上下文中没有相关信息,请说"我不知道"。
上下文:
{context}
问题:
{question}
回答:
"""
prompt = ChatPromptTemplate.from_template(template)
# LLM
llm = ChatOpenAI(model="gpt-4o")
# RAG链
def format_docs(docs):
return "\n\n".join(doc.page_content for doc in docs)
rag_chain = (
{"context": retriever | format_docs, "question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
# 使用
answer = rag_chain.invoke("什么是RAG?")
四、Agent:智能体系统
4.1 Agent的核心原理
Agent是能自主感知环境、做出决策、执行行动的AI系统。与传统的LLM调用不同,Agent具有自主性和目标导向性。
Agent的核心组件:
| 组件 | 功能 | 实现方式 |
|---|---|---|
| 感知 | 获取环境信息 | 工具调用、API查询 |
| 推理 | 分析信息、制定计划 | LLM推理 |
| 行动 | 执行操作 | 工具调用、代码执行 |
| 记忆 | 存储历史信息 | 向量数据库、消息列表 |
ReAct架构是最经典的Agent架构,通过"思考-行动-观察"循环解决问题:
Thought: 我需要搜索今天的天气
Action: search_weather("北京")
Observation: 北京今天晴,25°C
Thought: 我已经知道天气了,可以回答用户
Action: respond("北京今天晴,25°C")
4.2 多智能体协作
多智能体系统让多个Agent协作完成复杂任务:
| 模式 | 结构 | 适用场景 |
|---|---|---|
| 顺序协作 | Agent A → Agent B → Agent C | 流水线任务 |
| 并行协作 | 多个Agent同时执行,汇总结果 | 独立子任务 |
| 层级协作 | 管理Agent分配任务给子Agent | 复杂项目管理 |
| 对话协作 | 多Agent共享记忆,相互对话 | 需要讨论的任务 |
4.3 如何自己开发一个Agent
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_react_agent
from langchain_core.tools import tool
from langchain_core.prompts import ChatPromptTemplate
# 定义工具
@tool
def search_database(query: str) -> str:
"""搜索数据库中的信息"""
return f"搜索结果:{query}"
@tool
def send_email(to: str, subject: str, body: str) -> str:
"""发送邮件"""
return f"邮件已发送到 {to}"
# LLM
llm = ChatOpenAI(model="gpt-4o")
# 提示词
prompt = ChatPromptTemplate.from_messages([
("system", "你是一个有用的助手,可以使用工具来完成任务。"),
("human", "{input}"),
("placeholder", "{agent_scratchpad}")
])
# 创建Agent
agent = create_react_agent(llm, [search_database, send_email], prompt)
# 创建执行器
executor = AgentExecutor(
agent=agent,
tools=[search_database, send_email],
verbose=True,
max_iterations=10
)
# 运行
result = executor.invoke({"input": "帮我搜索数据库中的用户信息"})
五、MCP:模型上下文协议
5.1 MCP的核心原理
MCP(Model Context Protocol) 是由Anthropic于2024年11月发布的开放协议,定义了AI应用如何连接外部工具和数据源。
MCP架构:
┌─────────────────┐ ┌─────────────────┐
│ MCP Client │ ←──→ │ MCP Server │
│ (AI应用) │ │ (工具/数据源) │
└─────────────────┘ └─────────────────┘
三种能力类型:
| 能力 | 用途 | 示例 |
|---|---|---|
| Tools | Agent可调用的函数(有副作用) | API调用、数据库查询 |
| Resources | Agent可读取的数据(只读) | 配置文件、数据源 |
| Prompts | 可复用的提示模板 | 代码审查、日报生成 |
5.2 如何自己开发一个MCP服务器
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
const server = new McpServer({
name: "my-mcp-server",
version: "1.0.0"
});
// 定义工具
server.tool(
"calculate",
{ expression: z.string() },
async ({ expression }) => {
try {
const result = eval(expression);
return {
content: [{ type: "text", text: String(result) }]
};
} catch (error) {
return {
content: [{ type: "text", text: `错误: ${error.message}` }],
isError: true
};
}
}
);
// 启动服务器
const transport = new StdioServerTransport();
await server.connect(transport);
六、Harness工程与性能优化
6.1 Harness工程概述
Harness工程是围绕LLM调用构建的外部编排层,包括系统提示、工具选择、执行流、记忆系统和中间件钩子。
Harness的三个核心设计维度:
| 维度 | 说明 | 示例 |
|---|---|---|
| System Prompt | 系统提示词设计 | 角色定义、行为约束 |
| Tools | 工具选择和配置 | API、数据库、文件系统 |
| Middleware | 模型和工具调用周围的钩子 | 日志、缓存、重试 |
6.2 Token压缩:Headroom
Headroom是一个上下文优化层,在LLM调用前压缩所有输入内容。
三阶段压缩流水线:
Stage 1: CacheAligner → 稳定消息前缀,使KV Cache命中
Stage 2: ContentRouter → 自动检测内容类型,路由到最优压缩器
Stage 3: IntelligentContext → 按重要性评分,删除低价值消息
6种压缩算法:
| 内容类型 | 压缩器 | 典型节省 | 原理 |
|---|---|---|---|
| JSON数组 | SmartCrusher | 70-90% | 统计分析:保留错误、异常、边界 |
| 源代码 | CodeCompressor | 40-70% | AST感知(tree-sitter) |
| 纯文本 | Kompress | 30-50% | ModernBERT token分类 |
| 日志 | LogCompressor | 80-95% | 保留失败/错误/警告 |
| 搜索结果 | SearchCompressor | 60-80% | 按相关性排序 |
| Diff | DiffCompressor | 高 | 保留变更块 |
6.3 模型路由
模型路由根据请求特征自动选择最合适的模型:
class ModelRouter:
"""基于规则的模型路由"""
def route(self, query: str) -> str:
complexity = self.classify_complexity(query)
if complexity == "simple":
return "gpt-4o-mini" # 便宜
elif complexity == "medium":
return "gpt-4o" # 平衡
else:
return "claude-opus-4" # 强大
6.4 缓存策略
| 策略 | 适用场景 | 实现 |
|---|---|---|
| 精确匹配 | FAQ、固定问答 | 哈希输入messages |
| 语义相似度 | 近似问题 | Embedding + 相似度阈值 |
| TTL缓存 | 时效性数据 | Redis TTL |
| Provider KV Cache | 前缀稳定 | CacheAligner(Headroom) |
6.5 流式输出
流式输出基于**Server-Sent Events(SSE)**协议:
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
import json
app = FastAPI()
async def generate_tokens(prompt: str):
response = await openai_client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}],
stream=True
)
async for chunk in response:
if chunk.choices[0].delta.content:
yield f"data: {json.dumps({'content': chunk.choices[0].delta.content})}\n\n"
yield "data: [DONE]\n\n"
@app.post("/chat/stream")
async def chat_stream(prompt: str):
return StreamingResponse(
generate_tokens(prompt),
media_type="text/event-stream"
)
6.6 错误处理与重试
错误分类:
| 错误类型 | HTTP码 | 重试策略 |
|---|---|---|
| 速率限制 | 429 | 指数退避 + jitter |
| 服务器过载 | 500, 502, 503, 504 | 指数退避;N次后切换provider |
| 网络超时 | Timeout | 立即重试一次;然后指数退避 |
| 上下文超限 | 400 | 不重试:截断输入 |
| 内容策略拒绝 | 400 | 不重试:重写prompt |
七、Agent Skills系统
7.1 Skills的核心原理
Skills是一种模块化、文件系统抽象,让Agent按需加载领域专业知识。
SKILL.md文件格式:
---
name: code-review
description: |
代码审查技能,用于审查代码质量、安全性和最佳实践。
触发条件:
- 当用户请求审查代码时
- 当PR需要审查时
allowed-tools:
- read
- lsp_diagnostics
- grep
---
# 代码审查技能
## 审查维度
| 维度 | 检查项 |
|------|--------|
| **代码质量** | 命名规范、代码结构、注释质量 |
| **安全性** | 输入验证、SQL注入防护、XSS防护 |
| **性能** | 算法复杂度、资源使用、缓存策略 |
## 审查流程
1. 使用`read`工具读取代码文件
2. 使用`lsp_diagnostics`检查语法错误
3. 使用`grep`搜索潜在问题模式
4. 生成审查报告
八、LLM Gateway与可观测性
8.1 LLM Gateway原理
LLM Gateway是位于应用和LLM提供商之间的控制平面:
| 功能 | 说明 |
|---|---|
| 统一API | 一个接口访问多个LLM提供商 |
| 智能路由 | 根据请求特征选择最优模型 |
| 故障转移 | 主模型失败时自动切换备用模型 |
| 成本追踪 | 监控每个请求的成本 |
| 缓存 | 缓存相同请求的响应 |
代表产品对比:
| 网关 | 许可 | 自托管 | 最佳场景 |
|---|---|---|---|
| LiteLLM | MIT | ✅ | 自托管、全控制 |
| OpenRouter | 闭源 | ❌ | 零运维、快速实验 |
| Portkey | Apache 2.0 | ✅ | 企业治理、guardrails |
8.2 AI可观测性
AI可观测性通过捕获每条trace、token、成本和评估分数,让团队看到AI做了什么以及为什么。
代表产品对比:
| 工具 | 最佳场景 | 许可 | 自托管 |
|---|---|---|---|
| Langfuse | 大多数团队、任何框架 | MIT | ✅ |
| LangSmith | LangChain/LangGraph栈 | 闭源 | 仅企业 |
| Arize Phoenix | RAG评估和漂移检测 | Elastic License | ✅ |
九、向量数据库与Embedding
9.1 向量数据库原理
向量数据库是RAG应用的核心基础设施,存储和检索高维向量嵌入。
代表产品对比:
| 数据库 | 许可 | 最佳规模 | 最佳场景 |
|---|---|---|---|
| Qdrant | Apache 2.0 | 10M-1B向量 | 性能、负载过滤 |
| Milvus | Apache 2.0 | 100M-10B+ | 企业级大规模 |
| Chroma | Apache 2.0 | <10M向量 | 原型开发 |
| pgvector | PostgreSQL扩展 | <50M | 已有PostgreSQL |
9.2 Embedding模型
Embedding模型将文本/图像转换为高维向量表示。
代表模型对比:
| 模型 | MTEB分数 | 维度 | 特点 |
|---|---|---|---|
| Qwen3-Embedding-8B | 70.6 | 4096 | 开源最强 |
| Cohere Embed v4 | 65.2 | 256-1536 | 多模态、100+语言 |
| OpenAI text-embedding-3-large | 64.6 | 3072 | 生态最广 |
| BGE-M3 | 63.2 | 1024 | 开源、混合搜索 |
十、垂直领域应用
10.1 代码生成与软件开发
| 阶段 | 工具 | 能力 |
|---|---|---|
| V1 | GitHub Copilot | 代码补全 |
| V2 | Cursor | 上下文感知、多文件编辑 |
| V3 | Devin | 自主完成开发任务 |
10.2 内容创作与媒体
| 应用 | 说明 | 代表产品 |
|---|---|---|
| 文案写作 | 营销文案、广告语 | Jasper、Copy.ai |
| 内容创作 | 博客、文章、故事 | Notion AI、Writesonic |
| 翻译 | 多语言翻译 | DeepL、Google Translate |
| 摘要 | 文档摘要、会议纪要 | Otter.ai、Fireflies |
10.3 医疗健康
| 场景 | 具体应用 |
|---|---|
| 诊断辅助 | 症状分析、疾病预测、医学影像识别 |
| 药物发现 | 分子结构生成、药物相互作用预测 |
| 医学研究 | 文献综述生成、数据分析辅助 |
| 患者管理 | 电子病历自动生成、患者咨询机器人 |
10.4 金融领域
| 场景 | 具体应用 |
|---|---|
| 风险控制 | 欺诈检测、信用评估、反洗钱监控 |
| 投资研究 | 财报分析、行业研究、投资建议 |
| 客户服务 | 智能客服、理财顾问、投诉处理 |
| 合规管理 | 合同审查、法规解读、报告生成 |
10.5 教育领域
| 场景 | 具体应用 |
|---|---|
| 个性化学习 | 自适应学习路径、智能题目推荐 |
| 智能辅导 | 问题解答、作文批改、编程辅导 |
| 内容生成 | 教案生成、题目生成、课件制作 |
| 评估分析 | 学习效果评估、知识掌握分析 |
十一、游戏领域的LLM应用
11.1 NPC智能交互
| 维度 | 传统NPC | LLM NPC |
|---|---|---|
| 对话方式 | 预写脚本 | 动态生成 |
| 响应范围 | 固定选项 | 自由对话 |
| 记忆能力 | 无 | 有 |
| 情感表达 | 固定表情 | 动态情感 |
| 开发成本 | 低 | 高 |
| 运行成本 | 无 | 有 |
NVIDIA ACE Game Agent SDK:
| 组件 | 功能 |
|---|---|
| Agent API | 推理和行动能力 |
| Chat API | 对话管理 |
| RAG API | 知识检索 |
实际应用案例:
| 游戏 | 应用 | 技术 |
|---|---|---|
| PUBG: BATTLEGROUNDS | AI队友"Ella" | NVIDIA Mistral-Nemo-Minitron 2B |
| Total War: PHARAOH | AI法老顾问 | RAG架构,查询1200+游戏数据表 |
11.2 剧情与内容生成
| 场景 | 输入 | 输出 |
|---|---|---|
| 任务生成 | 玩家等级、职业、当前区域 | 护送商队穿越森林 |
| 对话生成 | NPC性格、玩家关系 | “今天的战斗真是精彩!” |
| 世界观构建 | 游戏类型、主题 | 背景故事、地理描述 |
| 支线剧情 | 主线进度、玩家选择 | 村民回报,隐藏任务 |
11.3 案例研究
Maicraft:LLM驱动的Minecraft代理
| 组件 | 说明 |
|---|---|
| LLM | 提供智能决策和任务规划 |
| Mineflayer | 操控MC游戏 |
| Agent | 采用ReAct逻辑的Agent |
AgentGal:开放世界多Agent角色扮演
| 特点 | 说明 |
|---|---|
| 开放世界式推进 | 非固定剧情树 |
| 自主生成角色 | 系统自动生成新角色 |
| 适合慢热游玩 | 长期沉浸式体验 |
十二、未来趋势与挑战
12.1 技术瓶颈
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 幻觉问题 | LLM生成看似合理但错误的内容 | RAG、事实检查、引用来源 |
| 上下文长度限制 | 上下文窗口有限 | RAG、摘要压缩、分层记忆 |
| 推理能力不足 | 复杂推理任务仍有局限 | Chain-of-Thought、Tree-of-Thought |
12.2 伦理与安全
| 问题 | 说明 | 解决方案 |
|---|---|---|
| 偏见问题 | LLM继承训练数据中的偏见 | 数据清洗、对抗训练 |
| 隐私保护 | LLM可能泄露隐私信息 | 差分隐私、联邦学习 |
| 可控性 | LLM输出难以精确控制 | 系统提示词、输出解析 |
12.3 Agentic AI的未来
根据Forrester 2026年报告:
| 状态 | 比例 |
|---|---|
| 探索中 | 30% |
| 试点 | 38% |
| 准备就绪 | 14% |
| 生产运行 | 11% |
未来趋势:
| 趋势 | 说明 |
|---|---|
| 自主Agent | 能独立完成复杂任务的AI |
| 多Agent协作 | 多个Agent协同工作 |
| 人机协作 | 人类和Agent无缝配合 |
| 持续学习 | Agent能从经验中学习 |
总结
2026年,大模型应用开发已经从"能用"进入"好用"的工程化阶段。RAG、Agent、MCP、Skill等技术栈日趋成熟,游戏、医疗、金融、教育等垂直领域应用蓬勃发展。
| 要点 | 说明 |
|---|---|
| RAG是基础 | 几乎所有LLM应用都需要RAG来增强知识 |
| Agent是核心 | Agent让LLM能自主完成复杂任务 |
| MCP是标准 | MCP成为AI应用与外部工具交互的标准协议 |
| Skill是扩展 | Skills让Agent能力可模块化、可共享 |
| Harness是优化 | Harness工程让Agent更高效、更可靠 |
| 工程化是关键 | 生产级应用需要可观测性、安全性、成本控制 |
给开发者的建议:
| 建议 | 说明 |
|---|---|
| 从RAG开始 | 先掌握RAG技术,这是最实用的技能 |
| 学习Agent框架 | LangGraph是目前最成熟的Agent框架 |
| 关注MCP生态 | MCP正在成为行业标准 |
| 实践为主 | 理论学习要结合实际项目 |
| 持续跟进 | LLM技术迭代很快,要持续学习 |