别让 LLM 再胡说了——RAG 检索增强生成完全指南

RAG 从入门到实践:系统学习与复习指南

本文整理自 RAG 学习笔记,覆盖从 LLM 基础原理到 RAG 完整实战的全链路知识体系,适合作为系统复习参考。

一、前置基础:LLM 是怎么工作的

在理解 RAG 之前,需要先搞清楚大模型到底是怎么"说话"的。

1.1 核心逻辑:看上文,猜下文

LLM 的本质是一个自回归语言模型 ------它不会一次性输出整段话,而是一个字一个字往外蹦

arduino 复制代码
输入:"我今天很"
  ↓ 模型处理
输出概率:"开" 62%、"难" 18%、"累" 8%、其他...
  ↓ 选最高的
输出:"开" → 拼成 "我今天很开心"
  ↓ 再喂进去,猜下一个字...

每次只多输出一个 token(≈一个字或一个词),你觉得它在流畅对话,其实它把"猜下一个字"这个游戏玩了成千上万次。

1.2 猜字的核心机制:QKV 自注意力

Transformer 架构里,每个 token 怎么知道应该关注前文哪些字?靠的是 QKV 自注意力机制

概念 全称 通俗含义 一句话
Q Query(查询) "我想找谁" 当前 token 发出的"寻人启事"
K Key(键) "我是谁" 每个 token 给自己的身份标签
V Value(值) "我有什么" 每个 token 携带的实际信息

1.3 打比方------图书馆查资料

你写论文要查"Transformer 注意力机制":

  • Q:你脑子里的研究问题------"注意力到底怎么算的?"
  • K:每本书的书名、标签、分类号------帮你定位哪本书
  • V:书里的实际内容------你真正要读的东西

1.4 完整计算过程(举例)

输入 "我爱吃苹",模型要猜下一个字。以最后一个 token "苹" 为例(它负责猜下一个字):

css 复制代码
第 1 步:每个字通过训练好的 W_Q、W_K、W_V 分别生成自己的 Q、K、V
(矩阵乘法,这里直接展示结果)

  token      Q               K               V
  ─────────────────────────────────────────────────
  "我"    [0.2, 0.5, 0.1]  [0.3, 0.1, 0.8]  [1.0, 0.2, 0.5]
  "爱"    [0.4, 0.1, 0.6]  [0.5, 0.7, 0.2]  [0.1, 0.9, 0.3]
  "吃"    [0.7, 0.3, 0.4]  [0.6, 0.2, 0.5]  [0.4, 0.1, 1.0]
  "苹"    [0.6, 0.2, 0.7]  [0.4, 0.5, 0.3]  [0.3, 0.4, 0.8]
less 复制代码
第 2 步:
  Q("苹") · K("吃") = 0.92  ← 最高!动词"吃"是猜食物的最强信号
  Q("苹") · K("我") = 0.76
  Q("苹") · K("苹") = 0.55
  Q("苹") · K("爱") = 0.58

为什么"吃"得分最高? 因为训练数据里"吃苹果"、"吃香蕉"大量出现,梯度下降让 W_K("吃") 和 W_Q(食物相关 token) 的向量方向越来越对齐。Q·K 的分数就是训练出来的统计规律。

arduino 复制代码
第 3 步:softmax 把分数变成权重(总和为 1)

  原始分数:[0.92,  0.76,  0.58,  0.55]
  softmax  → [0.31,  0.26,  0.22,  0.21]

  "吃" 31% | "我" 26% | "爱" 22% | "苹" 21%
scss 复制代码
第 4 步:用权重加权平均所有字的 V,融合整句话的信息

  输出 = 0.31 × V("吃") + 0.26 × V("我") + 0.22 × V("爱") + 0.21 × V("苹")
       = 0.31×[0.4, 0.1, 1.0]
       + 0.26×[1.0, 0.2, 0.5]
       + 0.22×[0.1, 0.9, 0.3]
       + 0.21×[0.3, 0.4, 0.8]
       ─────────────────────────
       = [0.47, 0.37, 0.67]    ← 融合了全文信息的向量
erlang 复制代码
第 5 步:这个向量过全连接层 + softmax → 词表概率

  "苹果" → 62%   ← ✅ 最高!
  "果"   → 18%
  "面包" → 12%
  "香蕉" →  5%
  其他    →  3%

  模型输出:"果",和前面的"苹"拼成完整的 "苹果"

二、LLM 的死穴:幻觉从哪来

2.1 什么是幻觉(Hallucination)

大模型知道的知识,完全取决于训练时喂给它的数据集。有两个盲区:

  • 知识有截止日期:训练数据之后发生的事,它不知道
  • 没见过私有数据:企业内部文档、产品手册、规范,它也没见过

关键问题是:LLM 不会说"我不知道" 。它是概率生成模型,面对不知道的问题不会拒绝回答,而是基于学到的语言模式"编造"一个听起来合理但实际错误的答案------这就是幻觉

2.2 幻觉的场景危害

场景 幻觉表现 后果
医疗问答 编造不存在的治疗方案 危及生命
法律咨询 引用虚构的法条和判例 法律纠纷
企业客服 给出错误的退换货政策 客诉、退款损失
金融分析 捏造财报数据 投资决策失误

三、解决幻觉的两条路:微调 vs RAG

3.1 微调(Fine-tuning)

用新数据继续训练大模型,让模型"学会"新知识。

缺点:

问题 说明
成本高 需要大量标注数据 + GPU 算力
周期长 训练一次几天到几周
不可溯源 记住了知识,但说不清从哪学的
更新困难 知识过期 → 重新训练

适用场景:大公司、专业垂直领域(医疗影像诊断、法律文书生成)------需要模型"学会一种能力",而不只是"记住一堆事实"。

3.2 RAG(检索增强生成)

不训练模型,而是给它配一个外部知识库。 每次提问先查资料,把资料塞进 Prompt,再让模型对着资料回答。

复制代码
传统 LLM:
  用户问 → LLM 凭训练记忆回答 → 可能幻觉 ❌

RAG:
  用户问 → 知识库检索 → 找到相关文档 → 拼入 Prompt → LLM 对着资料回答 ✅

3.3 对比总结

维度 微调 RAG
成本 高(GPU + 标注 + 时间) 低(只需 Embedding + 存储)
知识更新 重新训练 增删文档即可
可解释性 黑盒 可溯源到具体文档
适用方向 学"能力"(推理风格、格式) 学"知识"(事实、规范、政策)
幻觉控制 不一定,可能学到错误信息 显著降低(有据可查)

四、RAG 核心机制深度拆解

4.1 三个字母的含义

字母 全称 含义 通俗理解
R Retrieval 检索 去知识库搜相关文档
A Augmented 增强 把搜到的文档塞进 Prompt
G Generation 生成 LLM 根据增强后的 Prompt 生成答案

4.2 检索阶段:怎么找到相关文档?

方案一:关键词搜索(传统方案,效果差)

直接在文档里做字符串匹配。

arduino 复制代码
用户问:"那种红色脆脆的水果是什么?"
文档里有:"红富士苹果是一种..."

关键词匹配:"红色"✗、"脆脆"✗、"水果"✗ → 搜不到 ❌

根本缺陷:只做字面匹配,不理解语义。换个说法、同义替换就搜不到了。

方案二:向量语义搜索(现代 RAG 标配)

把文本转成向量(一串数字),用向量间的夹角衡量语义相似度。

css 复制代码
用两个维度直观理解(真实模型 768~4096 维):

        可食用性          硬度
水果 →  [0.9,             0.3]
苹果 →  [0.9,             0.5]
香蕉 →  [0.8,             0.3]
石头 →  [0.1,             0.9]

水果 vs 苹果:夹角 ≈ 10°  → 强相关 ✅
水果 vs 香蕉:夹角 ≈ 12°  → 强相关 ✅
水果 vs 石头:夹角 ≈ 75°  → 基本无关 ❌

即使用户问"红色脆脆的水果",它的向量也和"红富士苹果"的向量夹角很小------搜的是意思 ,不是字面


五、嵌入模型与向量化

5.1 什么是嵌入模型(Embedding Model)

专门把文本(图片/语音/PDF)转成向量的模型。 和生成式 LLM 是两回事:

对比维度 嵌入模型 生成式大模型
任务 文本 → 向量 文本 → 文本
模型大小 小(几百 MB) 大(几百 GB)
推理成本 低(便宜) 高(贵)
推理速度
代表模型 text-embedding-3-small GPT-4o、Claude

5.2 向量化的意义

把非结构化文本变成计算机能计算的数字:

arduino 复制代码
"你好啊" → [0.21, -0.53, 0.78, ..., 0.09]  (768 维)
"今日は" → [0.02, -0.61, 0.81, ..., 0.12]  (日语"你好",意思相近,向量也接近)

两个向量的语义相似度用余弦相似度(Cosine Similarity)计算,范围 -1 到 1,越接近 1 越相似。

5.3 支持的数据类型

不只是文本------只要嵌入模型支持,都可以向量化做语义搜索:文本、PDF、图片(多模态嵌入模型)、语音(转文字后向量化)、代码文件。


六、向量数据库

6.1 为什么需要专门的向量数据库

传统数据库(MySQL、PostgreSQL)存的是结构化数据(数字、字符串),索引基于 B-Tree / Hash,擅长精确匹配。但高维向量的相似度计算需要 ANN(近似最近邻)索引,传统数据库做不到。

向量数据库专门为此设计,核心三板斧:

能力 说明
海量高维向量持久化存储
毫秒级 ANN(近似最近邻)检索
结合 metadata 做标量过滤("只查第 3 章的文档")

6.2 常见向量数据库

产品 定位 特点
Chroma 轻量开源 适合学习和小项目,Python/JS 双 SDK
Milvus 企业级开源 分布式、高性能、多索引类型
Pinecone 云服务 全托管、开箱即用
Weaviate 开源 内置向量化模块,支持混合搜索
Qdrant 开源 Rust 编写,性能优秀
PGVector PostgreSQL 插件 在 PG 里直接存向量,降低架构复杂度

6.3 相似度检索流程

ini 复制代码
1. 用户问:"光光和东东是怎么成为朋友的?"
       ↓
2. 嵌入模型把问题向量化 → [0.34, -0.12, 0.78, ...]
       ↓
3. 向量库用 ANN 算法找最相似的 Top-K 个文档向量
       ↓
4. 返回相似度最高的 K 篇文档(如 K=3)

七、文档切分策略

7.1 为什么必须切分

两个原因:

  1. 上下文窗口有限:一整本书塞不进 Prompt
  2. 检索精度:一段 5000 字的文档,用户问其中一个小细节,无关内容会稀释向量相似度

7.2 切分策略对比

策略 适用场景 优点 缺点
按章节 结构化强的长文 语义完整 粒度可能太粗
按段落 一般文章 自然语义边界 段落长度不均
按固定字数 通用 简单可控 可能切断语义
递归字符分割 LangChain 默认 按分隔符优先级逐级切 需调 chunk_size
语义分块 高质量要求 用模型判断自然断点 成本高

7.3 核心原则

每个切片保持完整的自然语义,不要拦腰砍断一句话。

7.4 hello-rag 中的实际切分

故事按章节切成了 7 个 Document,每段是一个完整情节:

复制代码
第 1 章:角色介绍(光光)
第 2 章:角色介绍(东东)
第 3 章:友情情节(光光邀请东东踢球)
第 4 章:友情情节(互相帮助练习)
第 5 章:高潮转折(赢得比赛)
第 6 章:结局(友谊升华)
第 7 章:尾声(多年以后)

8.1 环境配置

arduino 复制代码
import 'dotenv/config'

自动读取 .env,把敏感信息加载到 process.env

ini 复制代码
OPENAI_API_KEY=sk-xxxxxxxx
OPENAI_API_BASE_URL=https://your-proxy.com/v1
MODEL_NAME=gpt-4o
EMBEDDING_MODEL_NAME=text-embedding-3-small

8.2 初始化 LLM(生成式模型)

arduino 复制代码
import { ChatOpenAI } from 'langchain/openai'

const model = new ChatOpenAI({
  temperature: 0,                          // RAG 场景必须为 0
  model: process.env.MODEL_NAME,           // 如 gpt-4o
  apiKey: process.env.OPENAI_API_KEY,
  configuration: {
    baseURL: process.env.OPENAI_API_BASE_URL,  // 代理/中转地址
  }
})

temperature 参数(OpenAI 范围 0~2):

效果 RAG 适用性
0 每次回答几乎一致,严格按输入 RAG 必选
0.7~1 有一定创造性 ❌ 可能偏离文档
>1.5 创造性极强 ❌ 容易编造

8.3 初始化嵌入模型

arduino 复制代码
import { OpenAIEmbeddings } from 'langchain/openai'

const embeddings = new OpenAIEmbeddings({
  apiKey: process.env.OPENAI_API_KEY,
  model: process.env.EMBEDDING_MODEL_NAME,      // 如 text-embedding-3-small
  configuration: {
    baseURL: process.env.OPENAI_API_BASE_URL,
  }
})

关键区分ChatOpenAI(LLM,负责生成回答)和 OpenAIEmbeddings(嵌入模型,负责文本→向量)是两个独立实例,各司其职。

8.4 构建文档数组

javascript 复制代码
import { Document } from 'langchain/core/documents'

const document = [
  new Document({
    pageContent: `光光是一个活泼开朗的小男孩...`,  // 正文,参与向量化
    metadata: {                                     // 标签,不参与向量化
      chapter: 1,
      character: "光光",
      type: "角色介绍",
      mood: "活泼"
    },
  }),
  // ... 共 7 个 Document
]

Document 的两个字段:

字段 作用 参与向量化? 用途
pageContent 文档正文 ✅ 是 检索匹配的主体
metadata 附加标签 ❌ 否 过滤、溯源、权限控制

metadata 的三种实际用途:

  • 过滤 :搜完后只留 type === "角色介绍" 的结果
  • 溯源:回答附上"参考自第 3 章"
  • 权限:按文档密级过滤

8.5 定义查询

ini 复制代码
const question = [
  "光光和东东的友谊是怎样的?"
]

后续流程:向量化问题 → 相似度检索 → 返回最相关 Document → pageContent 拼 Prompt → LLM 生成答案。

当前代码到这里为止还是"准备阶段",真正的检索 + 增强 + 生成逻辑需要后续补全。


九、RAG 完整流程总结

markdown 复制代码
═══════════════════════════════════════════════════════════════
                        RAG 完整数据流
═══════════════════════════════════════════════════════════════

【离线阶段:知识入库  ---  只在文档更新时做一次】
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ 1.加载文档 │ → │ 2.切分    │ → │ 3.向量化   │ → │ 4.存入向量库│
│ Document  │    │ Chunking │    │ Embedding│    │ VectorDB  │
└──────────┘    └──────────┘    └──────────┘    └──────────┘

【在线阶段:问答  ---  每次用户提问都执行】
┌──────────┐    ┌──────────┐    ┌──────────┐    ┌──────────┐
│ 5.用户提问 │ → │ 6.向量化   │ → │ 7.相似度   │ → │ 8.拼入    │
│ Question  │    │ Embedding│    │ 检索Top-K │    │ Prompt   │
└──────────┘    └──────────┘    └──────────┘    └─────┬────┘
                                                       ↓
                                                ┌──────────┐
                                                │9.LLM生成  │
                                                │  最终答案  │
                                                └──────────┘
═══════════════════════════════════════════════════════════════

关键三步总结:

步骤 做什么 核心工具
R 检索 问题向量化 → 向量库相似度计算 → 返回 Top-K 文档 Embedding Model + VectorDB
A 增强 将检索到的文档拼接进 Prompt 作为背景知识 Prompt 模板
G 生成 LLM 阅读参考资料,生成有据可查的回答 ChatOpenAI / LLM

十、如何系统学习和复习 RAG

10.1 七层递进学习路线

css 复制代码
第 1 层:LLM 基础
  ├── 自回归生成(token 级别,一个字一个字蹦)
  ├── Transformer 注意力(QKV 计算过程,手推一遍)
  └── LLM 局限性(幻觉、知识截止、上下文窗口有限)

第 2 层:为什么要 RAG
  ├── 幻觉产生的根本原因(概率生成 + 知识盲区)
  ├── 微调 vs RAG 对比(成本、可解释性、更新方式)
  └── RAG 的核心思想:检索 → 增强 → 生成

第 3 层:嵌入与向量
  ├── 嵌入模型 vs 生成式 LLM(任务不同、成本不同)
  ├── 向量的语义含义(多维数字 = 语义坐标)
  ├── 余弦相似度计算原理
  └── 多模态向量化(文本、图片、语音)

第 4 层:向量数据库
  ├── 为什么 MySQL 干不了(B-Tree 无法索引高维向量)
  ├── ANN 近似最近邻(牺牲微小精度换百倍速度)
  ├── 主流选型(Chroma 入门 → Milvus/Pinecone 生产)
  └── 混合搜索(向量相似度 + metadata 标量过滤)

第 5 层:文档处理
  ├── 切分策略(按章节/段落/固定字数/语义/递归)
  ├── Chunk Size 与 Overlap 调参
  ├── metadata 设计(为过滤和溯源服务)
  └── 多格式加载(PDF、网页、数据库、API)

10.2 核心概念速查表

概念 一句话解释 关键词
LLM 幻觉 模型不知道但不会拒绝,基于概率编造答案 知识截止、概率生成
RAG 先查资料再回答,给 LLM 加上"外脑" Retrieval + Augmented + Generation
微调 继续训练模型来学新知识 成本高、GPU、标注数据
嵌入模型 文本→向量的专用模型 便宜、快、小、和 LLM 不同
向量 一段文本的"语义坐标" 多维数组、余弦相似度
余弦相似度 两个向量夹角的余弦,衡量语义相关度 -1~1,越近 1 越相似
向量数据库 存向量 + 快速 ANN 检索 Chroma、Milvus、Pinecone
Chunk 切分后的文本片段 保持完整语义
Document pageContent(正文)+ metadata(标签) 正文向量化,标签不向量化
metadata 文档的标签属性 过滤、溯源、权限
temperature LLM 输出的随机度 RAG 设为 0
Top-K 检索返回最相似的 K 篇 通常 3~10
QKV Transformer 注意力核心机制 Q 找、K 标、V 给
LangChain AI 应用开发框架 封装 LLM、向量库、文档操作

10.4 自检清单

  • LLM 的幻觉是什么?为什么会幻觉?
  • 微调和 RAG 的优缺点?各自适用什么场景?
  • RAG 三个字母代表什么?每一步具体做了什么?
  • 关键词搜索比向量语义搜索差在哪?
  • 嵌入模型和 LLM 有什么区别?
  • 向量数据库解决了什么问题是 MySQL 解决不了的?
  • Document 的 pageContentmetadata 分别干嘛用?
  • 文档切分的核心原则是什么?
  • 从用户提问到 LLM 回答,RAG 完整链路是怎样的?
  • Q、K、V 分别代表什么?在 Attention 中如何配合?
  • 余弦相似度怎么算?为什么用余弦而不是欧氏距离?
  • temperature 在 RAG 场景应该设多少?为什么?

本文基于 LangChain + OpenAI Embeddings 实战代码编写,从 LLM 底层 QKV 机制讲到 RAG 完整工程链路,建议收藏作为系统复习手册。

相关推荐
爱勇宝2 小时前
《道德经》第4章,老子写得很像在描述一个“底层系统”。
前端·后端·程序员
kyriewen5 小时前
我用 AI 写代码快了 3 倍——然后同事的 Code Review 全变成了我的
前端·程序员·ai编程
阿里嘎多学长21 小时前
2026-07-10 GitHub 热点项目精选
开发语言·程序员·github·代码托管
没有了遇见1 天前
AI Agent 是什么?—— 一文理解 LLM、Memory、Skills、Tools、MCP、Workflow,Context
android·前端·程序员
DogDaoDao2 天前
【GitHub】 LLVM Project 深度解析:现代编译器基础设施的基石
java·c++·python·程序员·github·编译器·llvm
SamDeepThinking2 天前
为什么我会主动给一个年轻程序员加薪、晋升?
java·后端·程序员
AI小码2 天前
WAIC 2026前瞻:AI产业进入拼落地的下半场
人工智能·算法·ai·程序员·大模型·编程·智能体
zzzll11112 天前
LangChain实战-RAG问答机器人
学习·计算机·程序员·大模型
Coffeeee2 天前
22道AI Agent 工程师必会的面试题,别啥都不会就出去找工作
程序员·agent·ai编程