大模型幻觉的“特效药“:RAG 检索增强生成原理与工程实战
关键词:RAG、检索增强生成、大语言模型、Embedding、向量数据库、LangChain、NLP
本文系统讲解 RAG 为什么能缓解大模型幻觉,拆解"索引—检索—生成"全链路的关键技术(分块、Embedding、重排序、Prompt 组装),并给出一段可以直接跑的代码实战。全文约 4500 字。
一、大模型为什么需要 RAG?
预训练大语言模型(LLM)有三个绕不开的痛点:
- 知识截止(Knowledge Cutoff):模型的知识冻结在训练数据截止的那一天,之后发生的事情它一无所知;
- 幻觉(Hallucination):模型本质是"下一个词预测器",对不知道的问题也会一本正经地编造答案;
- 私有知识缺失:公司内部文档、个人笔记从未出现在训练语料里,再微调也难以精确记住。
继续用预训练/微调往模型里"灌"知识,成本高、见效慢、还容易出现灾难性遗忘。RAG(Retrieval-Augmented Generation,检索增强生成)换了一个思路:
开卷考试代替闭卷考试。 先把相关知识从外部知识库里检索出来,塞进 Prompt,让模型"看着资料作答"。
这个由 Facebook AI Research 在 2020 年提出的范式,如今已成为大模型落地(企业知识库问答、智能客服、垂直领域助手)事实上的标准方案。
二、RAG 的核心架构
RAG 分为离线索引(Indexing)和在线查询(Query)两个阶段:
离线文档 → 分块(Chunking) → Embedding → 存入向量数据库
在线问题 → Embedding → 向量检索(Top-K) → [重排序] → 拼接Prompt → LLM生成答案
2.1 分块(Chunking)
长文档不能直接整体向量化(会稀释语义,也超上下文窗口),需要切成小块。常见策略:
- 固定长度分块:每 512 token 切一刀,块间保留 50~100 token 重叠(overlap),避免答案被切断;
- 语义分块:按标题、段落、句子边界切,保持语义完整;
- 递归分块:LangChain 的
RecursiveCharacterTextSplitter,优先按\n\n→\n→ 句号的层级切分,兼顾长度与语义。
经验法则:块太大则检索噪声多,块太小则上下文不完整。一般 256~1024 token 之间调优,配合 10%~20% 的 overlap。
2.2 Embedding:把文本变成向量
Embedding 模型把每个文本块映射为一个稠密向量(如 768/1024 维),语义相近的文本在向量空间中距离也近:
(余弦相似度)
常用选择:
| 模型 | 特点 |
|---|---|
OpenAI text-embedding-3-small/large | 效果好、需调 API、按量计费 |
| BGE-M3(智源) | 中英双语强、开源免费、支持多粒度 |
| M3E / GTE | 中文场景轻量级选择 |
注意:索引和查询必须用同一个 Embedding 模型,换模型等于重建整个索引。
2.3 向量检索与向量数据库
查询时也把问题 Embedding 成向量,在库中找余弦相似度最高的 Top-K 个块。小规模(<10 万向量)直接用 numpy 暴力算即可;大规模则需要 ANN(近似最近邻)索引:
- HNSW:基于图的索引,召回率和速度的性价比之王,是大多数向量库的默认选择;
- IVF / PQ:聚类 + 量化压缩,适合千万级以上。
常用向量数据库:FAISS(本地库)、Chroma(轻量)、Milvus / Qdrant / Weaviate(生产级)、pgvector(Postgres 插件,少维护一个组件)。
2.4 重排序(Rerank)
向量检索是"粗排",Embedding 模型对细粒度相关性区分能力有限。可以再用一个交叉编码器(Cross-Encoder,如 bge-reranker)对粗排结果的 Top-20~50 做精排,把 query 和文档拼接后一起过模型算相关分,精度显著提升,代价是只能对少量候选打分。
2.5 Prompt 组装与生成
最后把检索到的上下文填进模板:
请基于以下参考资料回答问题。如果资料中没有相关信息,请明确说明"根据现有资料无法回答",不要编造。
【参考资料】
[1] {chunk_1}
[2] {chunk_2}
...【问题】{query}
最后这条"不知道就说不知道"的指令,是工程上抑制幻觉成本最低、收益最高的一招。
三、代码实战:50 行搭一个本地文档问答系统
下面的例子用 sentence-transformers 本地 Embedding + numpy 暴力检索 + 任意 LLM API,不依赖 LangChain,方便看清每一步发生了什么:
import numpy as np
from sentence_transformers import SentenceTransformer
# ---------- 1. 离线索引 ----------
embedder = SentenceTransformer("BAAI/bge-small-zh-v1.5") # 中文 Embedding
docs = [
"RAG(检索增强生成)由 Facebook AI Research 于 2020 年提出,核心思想是先检索后生成。",
"向量数据库通过 ANN 索引(如 HNSW)加速高维向量的相似度搜索。",
"大模型的幻觉指模型编造看似合理但与事实不符的内容,RAG 可以显著缓解该问题。",
"Transformer 架构由 Vaswani 等人在 2017 年的论文《Attention Is All You Need》中提出。",
"重排序(Rerank)使用交叉编码器对粗排结果精排,能显著提升检索精度。",
]
# BGE 系列建议给查询加指令前缀,文档侧不用
doc_vecs = embedder.encode(docs, normalize_embeddings=True) # (5, 512)
# ---------- 2. 在线检索 ----------
def retrieve(query: str, top_k: int = 2):
q = embedder.encode("为这个句子生成表示以用于检索相关文章:" + query,
normalize_embeddings=True)
scores = doc_vecs @ q # 归一化后点积 = 余弦相似度
top_idx = np.argsort(scores)[::-1][:top_k]
return [(docs[i], float(scores[i])) for i in top_idx]
# ---------- 3. 组装 Prompt 并生成 ----------
def build_prompt(query: str, contexts: list) -> str:
ctx = "\n".join(f"[{i+1}] {c}" for i, (c, _) in enumerate(contexts))
return f"""请基于以下参考资料回答问题。若资料中没有相关信息,请回答"根据现有资料无法回答"。
【参考资料】
{ctx}
【问题】{query}"""
query = "大模型幻觉是什么意思?怎么解决?"
contexts = retrieve(query, top_k=2)
prompt = build_prompt(query, contexts)
print(prompt)
# 之后把 prompt 发给任意 LLM(OpenAI / DeepSeek / 本地 Qwen)即可:
# answer = llm.chat(prompt)
运行后可以看到,retrieve 准确地捞出了"幻觉"和"RAG"两条相关文档。真实项目中把 numpy 部分换成 FAISS/Chroma,把 llm.chat 接到你的模型 API,就是一个可上线的最小 RAG。
生产级简化版(LangChain + Chroma)
from langchain_community.document_loaders import DirectoryLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.embeddings import HuggingFaceBgeEmbeddings
from langchain_community.vectorstores import Chroma
docs = DirectoryLoader("./my_docs", glob="**/*.md").load()
chunks = RecursiveCharacterTextSplitter(chunk_size=512, chunk_overlap=64).split_documents(docs)
emb = HuggingFaceBgeEmbeddings(model_name="BAAI/bge-small-zh-v1.5")
vectordb = Chroma.from_documents(chunks, emb, persist_directory="./chroma_db")
retriever = vectordb.as_retriever(search_kwargs={"k": 4})
relevant = retriever.invoke("大模型幻觉怎么解决?")
四、RAG 常见翻车点与优化清单
| 症状 | 可能原因 | 优化手段 |
|---|---|---|
| 检索不到相关内容 | 分块太碎/太大;Embedding 不适合领域 | 调整 chunk 策略;换领域 Embedding 或微调 |
| 检索到了但答错 | Prompt 没约束;上下文太多稀释了重点 | 加"只依据资料回答"指令;控制 Top-K |
| 专有名词检索差 | 向量检索对精确匹配弱 | 混合检索:BM25(关键词)+ 向量,RRF 融合 |
| 问题依赖上下文指代 | 多轮对话中"它""这个"检索不到 | 先用 LLM 改写问题(query rewriting)再检索 |
| 需要跨块推理 | 答案分散在多个块中 | 增大 Top-K;Parent-Child 索引;或用 GraphRAG |
| 需要评估效果 | 没有指标体系 | 用 RAGAS 评 faithfulness / answer relevancy |
进阶方向还包括:HyDE(先让 LLM 生成假想答案再检索)、Self-RAG(模型自主判断何时检索、自我反思)、Agentic RAG(把检索作为 Agent 的一个工具,多步规划调用)。
五、小结
- RAG = 检索(外脑)+ 生成(表达),用"开卷考试"思路缓解幻觉与知识过时;
- 核心链路:分块 → Embedding → 向量检索 → 重排序 → Prompt → 生成,每一环都有调优空间;
- 原型一天能搭出来,但做好需要持续打磨分块策略、混合检索、Rerank 与评估体系;
- RAG 不会消失:上下文窗口再大,"把最相关的资料给模型"依然是成本与效果的最优平衡点。
参考资料
- Lewis et al. Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. NeurIPS 2020.
- Karpukhin et al. Dense Passage Retrieval for Open-Domain Question Answering. EMNLP 2020.
- Chen et al. BGE M3-Embedding: Multi-Lingual, Multi-Functionality, Multi-Granularity Text Embeddings. 2024.
- Gao et al. Retrieval-Augmented Generation for Large Language Models: A Survey. 2023.
- Asai et al. Self-RAG: Learning to Retrieve, Generate, and Critique through Self-Reflection. ICLR 2024.
更多推荐

所有评论(0)