告别大模型瞎编!RAG保姆级实战指南:原理、架构与代码,建议收藏
本文详解RAG(检索增强生成)如何解决大模型知识截止与幻觉难题。涵盖架构原理、文档分块、向量化、混合检索及重排序等核心技术,并提供完整代码实现与生产环境最佳实践,助你打造私有知识库。
前排提示,文末有大模型AGI-CSDN独家资料包哦!
- 开篇:为什么 LLM 需要 RAG?
「用户问:我们公司的报销流程是什么?」
你把这个问题丢给 ChatGPT,它会一本正经地编造一套通用流程。问题是——它根本不知道你们公司的报销制度。
这就是大语言模型的先天缺陷:
•知识截止:训练数据有时间边界,不知道最新信息•无法访问私有数据:公司文档、内部知识库它看不到•幻觉问题:不知道就瞎编,而且编得很像真的
怎么办?两条路:
| 方案 | 做法 | 代价 |
|---|---|---|
| 微调 (Fine-tuning) | 在预训练模型基础上用私有数据继续训练 | 成本高、周期长、数据更新要重新训练 |
| RAG | 检索相关文档,拼到 Prompt 里 | 成本低、实时更新、无需重新训练 |
RAG(Retrieval-Augmented Generation,检索增强生成)的思路很直接:先找资料,再回答问题。
就像你写报告时会先查文档一样,RAG 让 LLM 在回答前先「查阅」相关资料,基于真实信息生成答案。
读完这篇文章,你将掌握:
•RAG 系统的完整架构和工作流程•文档解析、分块、向量化的核心技术•检索、重排序、生成的实现方案•生产环境的最佳实践和避坑指南
- 核心概念:RAG 的两个阶段
RAG 系统分为两个阶段:索引阶段(离线)和 检索生成阶段(在线)。
2.1 整体架构
┌─────────────────────────────────────────────────────────────┐│ 索引阶段(离线) │├─────────────────────────────────────────────────────────────┤│ 文档 → 解析 → 分块 → 向量化 → 存入向量数据库 │└─────────────────────────────────────────────────────────────┘ ↓┌─────────────────────────────────────────────────────────────┐│ 检索生成阶段(在线) │├─────────────────────────────────────────────────────────────┤│ 用户问题 → 向量化 → 检索 → 重排序 → 构造Prompt → LLM生成 │└─────────────────────────────────────────────────────────────┘
2.2 索引阶段
把文档「翻译」成向量,存到数据库里,方便后续检索。
直觉理解:就像给图书馆的每本书做索引卡片。
传统索引是按书名、作者分类。向量索引是按「内容语义」分类——讲同一个主题的书,不管标题怎么写,都会被放在一起。
索引阶段的核心步骤:
原始文档(PDF/Word/Markdown) ↓文档解析(提取文本) ↓文本分块(切成小段) ↓向量化(转成数字) ↓存入向量数据库
2.3 检索生成阶段
用户提问时,找到相关文档,让 LLM 基于这些文档回答。
直觉理解:就像开卷考试。
LLM 本来是「闭卷」答题,只能靠记忆(训练数据)。RAG 让它变成「开卷」——可以翻阅参考资料再作答。
检索生成阶段的核心步骤:
用户问题:"报销流程是什么?" ↓向量化(把问题转成向量) ↓向量检索(找语义相近的文档块) ↓可选:重排序(精细化排序) ↓构造 Prompt(问题 + 相关文档) ↓LLM 生成答案
2.4 术语对照表
| 英文 | 中文 | 说明 |
|---|---|---|
| Embedding | 向量化/嵌入 | 把文本转成数字向量 |
| Chunk | 文本块/分块 | 文档切分后的小段 |
| Vector Store | 向量数据库 | 存储和检索向量的数据库 |
| Retriever | 检索器 | 负责找相关文档的组件 |
| Reranker | 重排序器 | 对检索结果精细化排序 |
| Context | 上下文 | 提供给 LLM 的参考信息 |
- 原理:每个环节在做什么?
了解了整体流程,接下来深入每个环节的原理。
3.1 文档解析
不同格式的文档需要不同的解析方式:
| 格式 | 解析难度 | 常用工具 | 注意事项 |
|---|---|---|---|
| TXT/Markdown | ★ | 直接读取 | 注意编码 |
| ★★★ | PyMuPDF, pdfplumber | 扫描件需要 OCR | |
| Word | ★★ | python-docx | 注意表格和图片 |
| Excel | ★★ | openpyxl, pandas | 保留表格结构 |
关键点:解析质量直接影响后续效果。垃圾进,垃圾出。
3.2 文本分块(Chunking)
为什么要分块?两个原因:
1.LLM 上下文有限:不能把整本书塞进去2.检索需要粒度:整篇文档太粗,找不准
分块策略对比:
| 策略 | 做法 | 适用场景 |
|---|---|---|
| 固定长度 | 按字符数切分 | 简单场景 |
| 递归分割 | 按段落→句子→字符逐级切分 | 通用场景(推荐) |
| 语义分块 | 按语义边界切分 | 高质量要求 |
| 按标题分块 | 按文档结构切分 | 结构化文档 |
最佳实践:
Chunk 大小:500-1000 字符(中文)重叠大小:100-200 字符(保持上下文连贯)
为什么要重叠?避免关键信息被切断。
原文:「...报销需要提交发票。发票必须是正规发票...」
无重叠切分: Chunk1: 「...报销需要提交发票。」 Chunk2: 「发票必须是正规发票...」 → 问"发票要求"可能只找到 Chunk2,丢失了"需要提交"的信息
有重叠切分: Chunk1: 「...报销需要提交发票。发票必须...」 Chunk2: 「...提交发票。发票必须是正规发票...」 → 关键信息在两个 Chunk 都有
3.3 向量化(Embedding)
把文本转成高维向量,让计算机能计算「语义相似度」。
直觉理解:给每段文字一个「语义坐标」。
「报销流程」和「费用报销步骤」虽然用词不同,但语义坐标很接近。检索时,找坐标相近的文档块。
主流 Embedding 模型:
| 模型 | 提供商 | 维度 | 特点 |
|---|---|---|---|
| text-embedding-3-small | OpenAI | 1536 | 性价比高,推荐 |
| text-embedding-3-large | OpenAI | 3072 | 效果更好,成本更高 |
| voyage-3 | Voyage AI | 1024 | 多语言效果好 |
| bge-large-zh | BAAI | 1024 | 中文效果好,开源 |
3.4 向量检索
向量数据库的核心能力:快速找到最相似的向量。
如果有 100 万个文档块,逐个计算相似度太慢。向量数据库用 ANN(近似最近邻)算法加速:
| 算法 | 原理 | 特点 |
|---|---|---|
| HNSW | 分层图索引 | 速度快,内存占用大 |
| IVF | 聚类+倒排 | 内存友好,需要训练 |
| PQ | 向量压缩 | 节省存储,精度有损 |
主流向量数据库:
| 数据库 | 类型 | 特点 |
|---|---|---|
| Qdrant | 开源/云服务 | 功能全面,推荐 |
| Weaviate | 开源/云服务 | 支持混合检索 |
| Pinecone | 云服务 | 全托管,易上手 |
| Milvus | 开源 | 大规模场景 |
| pgvector | PostgreSQL 扩展 | 已有 PG 可直接用 |
3.5 混合检索
单纯的向量检索有局限:对精确关键词不敏感。
比如用户问「ERR-4012 怎么解决」,向量检索可能找到一堆「错误处理」相关的文档,但不一定包含 ERR-4012。
混合检索的思路:向量检索 + 关键词检索,两手都要抓。
用户问题 ↓┌───────────────────────────────────────┐│ 并行执行两路检索 │├─────────────────┬─────────────────────┤│ 向量检索 │ BM25检索 ││ (语义匹配) │ (关键词匹配) │├─────────────────┴─────────────────────┤│ RRF 融合算法 │└───────────────────────────────────────┘ ↓融合后的结果
3.6 重排序(Reranking)
检索返回的结果可能不够精准。重排序器对候选结果做精细化排序。
检索结果 Top-20 → Reranker → 最终 Top-5
为什么不直接用 Reranker 检索?因为 Reranker 需要对每个候选单独计算,太慢。所以先用检索器快速召回,再用 Reranker 精排。
主流 Reranker:
•Cohere Rerank•Jina Reranker•BGE Reranker(开源)
3.7 Prompt 构造与生成
把检索到的文档和用户问题组合成 Prompt:
你是一个专业的助手。请根据以下参考资料回答用户问题。
参考资料:{检索到的文档块}
用户问题:{用户的问题}
请基于参考资料回答,如果资料中没有相关信息,请说明无法回答。
关键点:
•明确指示 LLM 基于资料回答•处理「找不到」的情况•控制上下文长度,避免超出限制
- 实践:生产级 RAG 系统实现
理论讲完,来看代码如何实现。
4.1 技术栈选型
| 组件 | 推荐方案 | 备选方案 |
|---|---|---|
| 框架 | LangChain / LlamaIndex | 自研 |
| Embedding | OpenAI text-embedding-3-small | Voyage, BGE |
| 向量数据库 | Qdrant | Weaviate, pgvector |
| LLM | GPT-4o / Claude | 国产模型 |
| Reranker | Cohere Rerank | Jina, BGE |
4.2 环境准备
# 核心依赖pip install langchain langchain-openai langchain-communitypip install qdrant-clientpip install rank-bm25 # BM25 检索pip install cohere # 重排序(可选)
# 文档解析pip install pymupdf # PDFpip install python-docx # Wordpip install openpyxl # Excel
4.3 索引管道实现
"""RAG 索引管道 - 文档解析、分块、向量化、存储"""from typing import Optionalfrom pathlib import Path
from langchain.text_splitter import RecursiveCharacterTextSplitterfrom langchain_openai import OpenAIEmbeddingsfrom langchain_community.vectorstores import Qdrantfrom qdrant_client import QdrantClient
# === 第一步:文档解析 ===def load_document(file_path: str) -> str: """ 解析文档,提取文本
支持格式:txt, md, pdf, docx """ path = Path(file_path) suffix = path.suffix.lower()
if suffix in ['.txt', '.md']: return path.read_text(encoding='utf-8')
elif suffix == '.pdf': import fitz # PyMuPDF doc = fitz.open(file_path) text = "" for page in doc: text += page.get_text() return text
elif suffix == '.docx': from docx import Document doc = Document(file_path) return "/n".join([p.text for p in doc.paragraphs])
else: raise ValueError(f"不支持的文件格式: {suffix}")
# === 第二步:文本分块 ===def split_text( text: str, chunk_size: int = 800, chunk_overlap: int = 150,) -> list[str]: """ 递归分块 - 按段落→句子→字符逐级切分
参数: text: 原始文本 chunk_size: 块大小(字符数) chunk_overlap: 重叠大小 """ splitter = RecursiveCharacterTextSplitter( chunk_size=chunk_size, chunk_overlap=chunk_overlap, separators=["/n/n", "/n", "。", "!", "?", ";", " ", ""], ) return splitter.split_text(text)
# === 第三步:向量化并存储 ===def create_vector_store( texts: list[str], collection_name: str = "documents", qdrant_url: str = "http://localhost:6333",) -> Qdrant: """ 创建向量存储
参数: texts: 文本块列表 collection_name: 集合名称 qdrant_url: Qdrant 服务地址 """ # 初始化 Embedding 模型 embeddings = OpenAIEmbeddings( model="text-embedding-3-small", )
# 创建向量存储 client = QdrantClient(url=qdrant_url)
vector_store = Qdrant.from_texts( texts=texts, embedding=embeddings, collection_name=collection_name, url=qdrant_url, )
return vector_store
# === 完整索引流程 ===def index_document(file_path: str, collection_name: str = "documents"): """ 索引单个文档的完整流程 """ # 1. 解析 text = load_document(file_path) print(f"解析完成,文本长度: {len(text)}")
# 2. 分块 chunks = split_text(text) print(f"分块完成,共 {len(chunks)} 个块")
# 3. 向量化并存储 vector_store = create_vector_store(chunks, collection_name) print(f"索引完成,已存入 {collection_name}")
return vector_store
4.4 检索生成实现
"""RAG 检索生成 - 检索、重排序、生成答案"""from typing import Optionalfrom langchain_openai import ChatOpenAI, OpenAIEmbeddingsfrom langchain_community.vectorstores import Qdrantfrom qdrant_client import QdrantClient
# === 第一步:初始化检索器 ===def get_retriever( collection_name: str = "documents", qdrant_url: str = "http://localhost:6333", top_k: int = 5,): """ 获取向量检索器 """ embeddings = OpenAIEmbeddings(model="text-embedding-3-small") client = QdrantClient(url=qdrant_url)
vector_store = Qdrant( client=client, collection_name=collection_name, embeddings=embeddings, )
return vector_store.as_retriever(search_kwargs={"k": top_k})
# === 第二步:检索相关文档 ===def retrieve_documents(query: str, retriever) -> list[str]: """ 检索与查询相关的文档 """ docs = retriever.invoke(query) return [doc.page_content for doc in docs]
# === 第三步:可选 - 重排序 ===def rerank_documents( query: str, documents: list[str], top_n: int = 3,) -> list[str]: """ 使用 Cohere 重排序 """ import cohere
co = cohere.Client() # 需要设置 COHERE_API_KEY
response = co.rerank( model="rerank-english-v3.0", query=query, documents=documents, top_n=top_n, )
return [documents[r.index] for r in response.results]
# === 第四步:构造 Prompt 并生成 ===def generate_answer( query: str, context_docs: list[str], model: str = "gpt-4o",) -> str: """ 基于检索到的文档生成答案 """ # 构造上下文 context = "/n/n---/n/n".join(context_docs)
# 构造 Prompt prompt = f"""你是一个专业的助手。请根据以下参考资料回答用户问题。
参考资料:{context}
用户问题:{query}
请基于参考资料回答。如果资料中没有相关信息,请明确说明"根据现有资料无法回答该问题"。"""
# 调用 LLM llm = ChatOpenAI(model=model, temperature=0) response = llm.invoke(prompt)
return response.content
# === 完整 RAG 查询流程 ===def rag_query( query: str, collection_name: str = "documents", use_rerank: bool = True,) -> dict: """ 完整的 RAG 查询流程
返回: { "answer": 生成的答案, "sources": 参考的文档块, } """ # 1. 获取检索器 retriever = get_retriever(collection_name, top_k=10 if use_rerank else 5)
# 2. 检索 docs = retrieve_documents(query, retriever)
# 3. 重排序(可选) if use_rerank and len(docs) > 0: docs = rerank_documents(query, docs, top_n=3)
# 4. 生成答案 answer = generate_answer(query, docs)
return { "answer": answer, "sources": docs, }
# === 使用示例 ===if __name__ == "__main__": # 索引文档 index_document("company_policy.pdf", "company_docs")
# 查询 result = rag_query( "报销流程是什么?", collection_name="company_docs", use_rerank=True, )
print("答案:", result["answer"]) print("/n参考来源:") for i, source in enumerate(result["sources"], 1): print(f"{i}. {source[:100]}...")
4.5 常见问题
Q: 检索结果不相关怎么办?
排查顺序:
1.检查分块是否合理(太大或太小都不好)2.检查 Embedding 模型是否适合你的数据(中文用中文模型)3.尝试混合检索(向量 + BM25)4.加入重排序
Q: 生成的答案有幻觉怎么办?
1.Prompt 中明确要求「基于资料回答」2.要求 LLM 引用来源3.降低 temperature(设为 0)4.检索更多文档,提供更充分的上下文
Q: 上下文太长超出限制怎么办?
1.减少检索数量(top/_k 调小)2.用重排序筛选最相关的3.对检索结果做摘要压缩4.使用支持长上下文的模型
- 最佳实践
5.1 分块策略
| 场景 | 推荐配置 |
|---|---|
| 通用文档 | chunk/_size=800, overlap=150 |
| 技术文档 | chunk/_size=1000, overlap=200 |
| FAQ/问答 | chunk/_size=300, overlap=50 |
| 法律合同 | 按条款分块 |
原则:一个 Chunk 应该包含完整的语义单元,能独立回答一个问题。
5.2 Embedding 选择
| 场景 | 推荐模型 |
|---|---|
| 英文为主 | text-embedding-3-small |
| 中文为主 | bge-large-zh 或 text-embedding-3-small |
| 多语言 | voyage-3 |
| 成本敏感 | text-embedding-3-small(性价比最高) |
5.3 检索优化
混合检索权重:
| 场景 | 向量权重 | BM25权重 |
|---|---|---|
| 通用问答 | 0.7 | 0.3 |
| 技术文档 | 0.5 | 0.5 |
| 精确搜索 | 0.3 | 0.7 |
5.4 避坑指南
陷阱 1:Chunk 太大或太小
太大(>2000字符): - 检索不精准,噪音多 - 浪费上下文空间
太小(<200字符): - 语义不完整 - 检索结果碎片化
陷阱 2:忽略文档结构
错误:把表格当普通文本切分 → 表格被切碎,信息丢失
正确:识别表格,整体保留或转为描述性文本
陷阱 3:Prompt 没有边界约束
错误 Prompt: "请回答用户问题:{query}" → LLM 可能瞎编
正确 Prompt: "请基于以下资料回答,如果资料中没有相关信息,请说明无法回答。" → 有边界约束
陷阱 4:没有处理空结果
# 错误:直接生成docs = retrieve(query)answer = generate(query, docs) # docs 可能为空
# 正确:检查结果docs = retrieve(query)if not docs: return "抱歉,没有找到相关信息"answer = generate(query, docs)
- 进阶:生产环境优化
6.1 查询改写
用户的问题可能表述不清。查询改写可以提升检索效果:
def rewrite_query(original_query: str) -> list[str]: """ 将用户问题改写成多个检索友好的查询 """ prompt = f"""将以下问题改写成3个不同的表述,用于检索:
原问题:{original_query}
要求:1. 保持原意2. 使用不同的关键词3. 每行一个改写结果""" # 调用 LLM 改写 ...
6.2 缓存策略
# 查询缓存 - 相同问题直接返回query_cache = {}
def cached_rag_query(query: str): cache_key = hash(query) if cache_key in query_cache: return query_cache[cache_key]
result = rag_query(query) query_cache[cache_key] = result return result
# Embedding 缓存 - 避免重复计算# 使用 Redis 或本地缓存存储已计算的 embedding
6.3 监控指标
生产环境需要监控:
| 指标 | 说明 | 告警阈值 |
|---|---|---|
| 检索延迟 | 向量检索耗时 | P99 > 500ms |
| 生成延迟 | LLM 生成耗时 | P99 > 5s |
| 空结果率 | 检索无结果的比例 | > 20% |
| 用户满意度 | 点赞/点踩比例 | < 70% |
6.4 评估指标
| 指标 | 说明 | 计算方式 |
|---|---|---|
| Recall@K | 相关文档被检索到的比例 | 相关文档数 / 总相关文档数 |
| MRR | 第一个相关结果的排名倒数 | 1 / 第一个相关结果排名 |
| Faithfulness | 答案是否基于检索内容 | LLM 评估 |
| Answer Relevancy | 答案与问题的相关性 | LLM 评估 |
6.5 架构演进
阶段1:单体架构 所有组件在一个服务中 适合:POC、小规模
阶段2:服务拆分 索引服务 + 检索服务 + 生成服务 适合:中等规模
阶段3:分布式架构 向量数据库集群 + LLM 网关 + 缓存层 适合:大规模生产
- 总结
RAG 的核心价值:让 LLM 基于真实数据回答问题,而不是靠「记忆」瞎编。
关键要点:
两阶段架构:索引阶段(离线)准备数据,检索生成阶段(在线)回答问题
分块是关键:Chunk 大小影响检索精度,推荐 500-1000 字符,带 100-200 字符重叠
混合检索更稳健:向量检索擅长语义,BM25 擅长关键词,两者结合效果更好
重排序提升精度:检索后加一层 Reranker,能显著提升相关性
Prompt 要有边界:明确要求 LLM 基于资料回答,处理好「找不到」的情况
读者福利:倘若大家对大模型感兴趣,那么这套大模型学习资料一定对你有用。
针对0基础小白:
如果你是零基础小白,快速入门大模型是可行的。
大模型学习流程较短,学习内容全面,需要理论与实践结合
学习计划和方向能根据资料进行归纳总结
包括:大模型学习线路汇总、学习阶段,大模型实战案例,大模型学习视频,人工智能、机器学习、大模型书籍PDF。带你从零基础系统性的学好大模型!
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓


👉AI大模型学习路线汇总👈
大模型学习路线图,整体分为7个大的阶段:(全套教程文末领取哈)
第一阶段: 从大模型系统设计入手,讲解大模型的主要方法;
第二阶段: 在通过大模型提示词工程从Prompts角度入手更好发挥模型的作用;
第三阶段: 大模型平台应用开发借助阿里云PAI平台构建电商领域虚拟试衣系统;
第四阶段: 大模型知识库应用开发以LangChain框架为例,构建物流行业咨询智能问答系统;
第五阶段: 大模型微调开发借助以大健康、新零售、新媒体领域构建适合当前领域大模型;
第六阶段: 以SD多模态大模型为主,搭建了文生图小程序案例;
第七阶段: 以大模型平台应用与开发为主,通过星火大模型,文心大模型等成熟大模型构建大模型行业应用。
👉大模型实战案例👈
光学理论是没用的,要学会跟着一起做,要动手实操,才能将自己的所学运用到实际当中去,这时候可以搞点实战案例来学习。

👉大模型视频和PDF合集👈
这里我们能提供零基础学习书籍和视频。作为最快捷也是最有效的方式之一,跟着老师的思路,由浅入深,从理论到实操,其实大模型并不难。

👉学会后的收获:👈
• 基于大模型全栈工程实现(前端、后端、产品经理、设计、数据分析等),通过这门课可获得不同能力;
• 能够利用大模型解决相关实际项目需求: 大数据时代,越来越多的企业和机构需要处理海量数据,利用大模型技术可以更好地处理这些数据,提高数据分析和决策的准确性。因此,掌握大模型应用开发技能,可以让程序员更好地应对实际项目需求;
• 基于大模型和企业数据AI应用开发,实现大模型理论、掌握GPU算力、硬件、LangChain开发框架和项目实战技能, 学会Fine-tuning垂直训练大模型(数据准备、数据蒸馏、大模型部署)一站式掌握;
• 能够完成时下热门大模型垂直领域模型训练能力,提高程序员的编码能力: 大模型应用开发需要掌握机器学习算法、深度学习框架等技术,这些技术的掌握可以提高程序员的编码能力和分析能力,让程序员更加熟练地编写高质量的代码。
👉获取方式:
😝有需要的小伙伴,可以保存图片到wx扫描二v码免费领取【保证100%免费】🆓
AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。
更多推荐


所有评论(0)