ANALYSIS
概述
RAG(Retrieval-Augmented Generation,检索增强生成)是由 Meta AI 研究团队于 2020 年提出的技术架构,其核心思想是让 LLM 在回答问题前先检索相关资料,再基于检索结果生成答案。
RAG 完美解决了 LLM 的两大痛点:
- 知识过时:模型知识截止于训练时间点
- 幻觉问题:模型可能生成看似合理但错误的内容
为什么需要 RAG
LLM 的知识局限
| 问题 | 说明 | RAG 解决方案 |
|---|---|---|
| 知识截止 | 训练数据有时效性 | 实时检索最新信息 |
| 领域知识不足 | 垂直领域专业性弱 | 接入专业知识库 |
| 幻觉 | 生成错误但看似合理的内容 | 答案有据可查 |
| 私有数据 | 敏感数据不能用于训练 | 本地知识库检索 |
RAG vs 传统方案
| 方案 | 知识更新 | 成本 | 准确性 | 适用场景 |
|---|---|---|---|---|
| 纯 LLM | 需重训练 | 高 | 中 | 通用知识 |
| RAG | 更换文档 | 低 | 高 | 知识问答 |
| Fine-tuning | 需重训练 | 高 | 中高 | 风格定制 |
| RAG + Fine-tuning | 更换文档 + 微调 | 高 | 最高 | 专业领域 |
工作流程详解
PRTCL // PLAINTEXT
┌──────────────────────────────────────────────────────────────┐│ RAG 系统架构 │├──────────────────────────────────────────────────────────────┤│ ││ 【离线阶段:知识库构建】 ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ 文档 │ → │ 文档 │ → │ 向量 │ → │ 向量 │ ││ │ 加载 │ │ 切分 │ │ 嵌入 │ │ 存储 │ ││ └──────────┘ └──────────┘ └──────────┘ └──────────┘ ││ ││ 【在线阶段:检索生成】 ││ ┌──────────┐ ┌──────────┐ ┌──────────┐ ┌──────────┐ ││ │ 用户问题 │ → │ 问题嵌入 │ → │ 向量检索 │ → │ 上下文 │ ││ └──────────┘ └──────────┘ └──────────┘ │ 组装 │ ││ └────┬─────┘ ││ ┌──────────┐ ┌──────────┐ │ ││ │ 最终回答 │ ← │ LLM 生成 │ ←──────────────────────┘ ││ └──────────┘ └──────────┘ │└──────────────────────────────────────────────────────────────┘核心组件详解
文档加载器(Document Loader)
支持多种格式:
| 格式 | 常用库 | 说明 |
|---|---|---|
| PyPDF2, pdfplumber | 需要处理布局和表格 | |
| Word | python-docx | 结构化文档 |
| Markdown | 直接读取 | 天然结构化 |
| HTML | BeautifulSoup | 网页内容 |
| CSV | pandas | 表格数据 |
| Notion | notion-api | 在线文档 |
| Confluence | atlassian-api | 企业知识库 |
文档切分(Chunking)
切分是 RAG 效果的关键环节。
切分策略对比:
| 策略 | 方法 | 优缺点 |
|---|---|---|
| 固定大小 | 按字数 /Token 切分 | 简单,但可能切断语义 |
| 句子级别 | 按句子边界切分 | 保语义,但块太小 |
| 段落级别 | 按段落切分 | 语义完整,大小适中 |
| 递归切分 | 按层级结构递归切分 | 平衡大小和语义 |
| 语义切分 | 用模型判断切分点 | 效果最好,成本高 |
关键参数:
| 参数 | 说明 | 建议值 |
|---|---|---|
| chunk_size | 每块 Token 数 | 500-1000 |
| chunk_overlap | 块间重叠 Token | 50-100 |
重叠的重要性:
PRTCL // PLAINTEXT
块 1: [句子 1][句子 2][句子 3]块 2: [句子 3][句子 4][句子 5] ← 句子 3 重复,避免语义断裂向量化(Embedding)
将文本转换为向量。
主流 Embedding 模型:
| 模型 | 维度 | 特点 | 适用场景 |
|---|---|---|---|
| text-embedding-ada-002 | 1536 | OpenAI 官方 | 通用 |
| text-embedding-3-large | 3072 | 高精度 | 英文为主 |
| bge-large-zh-v1.5 | 1024 | 中文最强 | 中文为主 |
| m3e-large | 1024 | 多语言支持 | 混合语言 |
| Jina-embeddings-v3 | 1024 | Late Chunking | 长文档 |
向量数据库
存储和检索向量。
| 数据库 | 类型 | 特点 | 适用场景 |
|---|---|---|---|
| Pinecone | 云服务 | 托管,开箱即用 | 快速上线 |
| Weaviate | 云 / 自托管 | 混合搜索强 | 多模态 |
| Milvus | 自托管 | 大规模首选 | 企业级 |
| Qdrant | 自托管 | 性能优秀 | 高性能 |
| Chroma | 本地 | 轻量,开发友好 | 原型 / 学习 |
| FAISS | 库 | 离线处理 | 单机 |
检索策略
策略 1:向量检索(语义检索)
PRTCL // PYTHON
query_embedding = embed_model.encode("用户问题")results = vector_db.search(query_embedding, top_k=5)策略 2:关键词检索(BM25)
PRTCL // PYTHON
from rank_bm25 import BM25Okapibm25 = BM25Okapi(tokenized_chunks)scores = bm25.get_scores(tokenized_query)策略 3:混合检索
PRTCL // PYTHON
# 综合向量相似度和 BM25 分数hybrid_score = 0.7 * vector_score + 0.3 * keyword_score策略 4:重排序(Rerank)
PRTCL // PYTHON
# 初筛后精排from sentence_transformers import CrossEncoderreranker = CrossEncoder('cross-encoder/ms-marco-MiniLML-12-v2')reranked = reranker.predict(query_doc_pairs)上下文组装
将检索结果注入 Prompt。
组装策略:
| 策略 | 说明 | 适用场景 |
|---|---|---|
| 直接拼接 | 简单拼接所有检索结果 | 检索质量高 |
| 窗口拼接 | 围绕相关片段扩大上下文 | 上下文可能不足 |
| 摘要压缩 | 对检索结果先摘要再注入 | 长文档 |
| 元数据过滤 | 用元数据先过滤再检索 | 结构化知识 |
Prompt 模板:
PRTCL // PLAINTEXT
你是一个问答助手。请根据以下参考资料回答用户问题。如果参考资料中没有相关信息,请如实告知。
参考资料:{context}
用户问题:{question}
回答要求:1. 基于参考资料回答,不要编造信息2. 如涉及多个参考资料,请综合整理3. 引用时注明来源进阶技术
Query 改写
将用户口语化问题转换为检索友好表达。
PRTCL // PYTHON
# 原始问题可能模糊或口语化query = "那个关于 Python 的文档在哪里"
# 改写为更明确的检索表达rewritten = llm.invoke( "将以下问题改写为适合检索的表述,保留核心信息:" f"原问题:{query}")# 结果:"Python 编程语言文档"HyDE(假设性文档嵌入)
用 AI 生成假设性答案来辅助检索。
PRTCL // PYTHON
# 让 AI 生成假设性答案hypothetical = llm.invoke(f"回答以下问题:{query}")
# 用假设性答案做检索results = vector_db.search(embed(hypothetical))父子检索(Parent Document Retrieval)
大文档用小 chunk 检索,用大 chunk 提供上下文。
PRTCL // PLAINTEXT
文档层级:├── 父文档(完整章节,2000 tokens)│ ├── 子块 1(200 tokens)← 检索用│ ├── 子块 2(200 tokens)← 检索用│ └── 子块 3(200 tokens)← 检索用
检索:找到相关子块组装:返回对应父文档Self-RAG
让 LLM 自我评估检索必要性。
PRTCL // PYTHON
# LLM 判断是否需要检索is_retrieval_needed = llm.invoke( f"判断是否需要检索外部知识来回答:{query}")
if is_retrieval_needed: results = retrieve(query) rag_output = llm.invoke(f"基于以下资料回答:{results}\n\n 问题:{query}")else: direct_output = llm.invoke(query)知识图谱增强
结合知识图谱提升结构化推理能力。
PRTCL // PLAINTEXT
用户问:"张三的上司是谁?"
知识图谱:张三 --同事--> 李四李四 --下属--> 王五张三 --下属--> [?]
推理:直接检索无法回答知识图谱推理:张三的下司的下司是王五?→ 需要补充信息工程实践
RAG 框架选择
| 框架 | 语言 | 特点 | 适用场景 |
|---|---|---|---|
| LangChain | Python/JS | 功能全面 | 生产级 |
| LlamaIndex | Python | 数据优先 | 数据密集型 |
| RAGFlow | Python | UI 友好 | 快速原型 |
| Dify | 多语言 | 低代码 | 非技术用户 |
| Flowise | 多语言 | 低代码 | 可视化 |
评估方法
| 维度 | 指标 | 说明 |
|---|---|---|
| 检索质量 | HitRate@K, MRR | 检索是否命中相关文档 |
| 生成质量 | RAGAS, BERTScore | 回答是否准确相关 |
| 端到端 | Task Success | 用户问题是否解决 |
常见问题与解决
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 检索不到相关内容 | Chunking 不合理 | 调整 chunk_size 和 overlap |
| 上下文太长 | 检索结果过多 | 增加 rerank 或摘要 |
| 幻觉仍然存在 | Prompt 设计不当 | 强化”基于资料回答”的约束 |
| 回答不够精准 | 检索质量差 | 优化 embedding 模型或检索策略 |
总结
RAG 是当前将大模型落地最成熟的技术路径之一。它让 AI 的回答有了”据可查”的基础,大幅提升了回答的准确性和可信度。
关于我
| 项目 | 内容 |
|---|---|
| 编辑 | echowang |
| 来源 | echospace |
| 邮箱 | echohaoran@gmail.com |
| 简介 | AI 爱好者,专注于大语言模型应用与智能体开发,分享技术与实践心得 |
| 社交 | 欢迎交流讨论,共同成长 |
R P
Rhine Lab Pioneer Division
Auth_Verified: 2026.03.21
Auth_Verified: 2026.03.21
