RAG 技术详解:概念、场景、优势、微调对比与代码示例
检索增强生成(Retrieval-Augmented Generation,简称 RAG)是当前大语言模型(LLM)领域的重要架构模式。它通过结合信息检索技术与生成式模型,有效解决了大模型知识滞后、幻觉严重及私有数据无法利用等问题。
01 概念与原理
2020 年,Facebook AI Research (FAIR) 团队在论文《Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks》中首次提出了 RAG 概念。该架构旨在让语言模型能够访问外部知识库,从而在不更新模型参数的情况下获取最新或特定领域的知识。
核心工作流
RAG 的核心流程包含三个关键步骤:
- 检索(Retrieval):当用户提出查询时,系统首先从庞大的文档集合中检索出与查询最相关的片段。这通常涉及将查询转换为向量,并在向量数据库中进行相似度搜索。
- 利用(Augmentation):将检索到的相关文档片段作为上下文信息,拼接到大模型的提示词(Prompt)中。这一步相当于为模型提供了'参考资料'。
- 生成(Generation):大模型基于原始查询和提供的参考上下文,生成最终的回答。由于有了外部知识的约束,生成的内容更加准确且可追溯。
架构组成
典型的 RAG 系统包含以下组件:
- 索引器(Indexer):负责将非结构化数据(如 PDF、网页文本)进行清洗、分块(Chunking),并通过嵌入模型(Embedding Model)转换为向量存储到向量数据库中。
- 检索器(Retriever):负责接收用户查询,将其向量化,并从向量库中召回 Top-K 个相关文档。
- 生成器(Generator):即大语言模型,负责根据 Prompt 中的上下文生成自然语言回答。
02 应用场景
RAG 技术适用于多种需要结合外部知识或私有数据的自然语言处理任务:
- 问答系统(QA Systems):构建企业级智能客服或内部知识库助手,能够基于公司文档回答员工或客户的具体问题,无需针对每个问题重新训练模型。
- 文档生成与摘要:自动生成报告、会议纪要或长文档摘要。模型可以依据检索到的原始资料填充内容,确保信息的真实性和完整性。
- 智能助手与虚拟代理:在聊天机器人中集成 RAG,使其能够引用实时数据或特定业务规则来执行任务,减少胡编乱造的情况。
- 信息检索增强:改进传统搜索引擎,不仅匹配关键词,还能理解语义意图,返回更精准的内容片段。
- 知识图谱构建:辅助识别实体关系,通过检索文档自动填充或更新知识图谱中的节点和边。
- 代码辅助与解释:在开发环境中,RAG 可以检索项目内部的 API 文档或历史代码库,帮助开发者理解现有逻辑或生成符合规范的代码。
03 核心优势
相比于直接微调(Fine-tuning)或使用纯大模型,RAG 具有以下显著优势:
- 外部知识利用:模型不再受限于预训练时的截止时间和数据范围,可以实时接入最新的行业报告、法律法规或企业内部数据。
- 数据更新及时性:知识库的更新是独立的。只需更新向量数据库中的文档,无需重新训练昂贵的模型参数,极大降低了维护成本和时间延迟。
- 回复可解释性:RAG 生成的答案可以直接追溯到具体的检索来源。用户可以查看引用的原文,验证答案的准确性,减少了大模型'幻觉'带来的风险。
- 高度定制能力:针对不同垂直领域(如医疗、法律),只需准备该领域的专用语料库并建立索引,即可快速部署专属助手,无需大量标注数据进行微调。
- 安全与隐私控制:可以通过权限管理限制检索范围,确保敏感数据不会被泄露给未授权的用户,同时避免模型记忆并输出敏感信息。
- 降低训练成本:避免了全量微调所需的高昂 GPU 算力消耗和漫长的训练周期,适合中小规模团队快速落地应用。
04 RAG 与微调的对比
在实际业务中,选择 RAG 还是微调取决于具体需求。以下是两者的详细对比:
| 维度 | RAG (检索增强生成) | Fine-tuning (微调) |
|---|
| 知识时效性 | 高,依赖知识库更新 | 低,需重新训练才能更新知识 |
| 通用性 | 强,一套系统可服务多类任务 | 弱,通常针对特定任务优化 |
| 成本 | 较低,主要是存储和推理成本 | 较高,涉及训练算力和时间成本 |
| 可解释性 | 强,答案有明确出处 | 弱,黑盒模型难以追溯依据 |
| 适用场景 | 知识密集型、动态数据、私有数据 | 风格模仿、特定格式输出、专业术语规范化 |
| 数据依赖 | 依赖高质量的外部文档库 | 依赖高质量的指令微调数据集 |
决策建议:如果主要痛点是知识过时或私有数据保护,优先选择 RAG;如果需要改变模型的说话风格、遵循特定的输出格式或学习复杂的推理逻辑,则考虑微调。两者也可以结合使用,例如先微调模型提升指令遵循能力,再配合 RAG 提供专业知识。
05 项目实现示例
下面以 Python 为例,展示如何基于 ERNIE Bot SDK 和 LangChain 搭建一个简单的个人知识库 RAG 系统。
环境准备
首先需要安装必要的依赖库:
pip install erniebot langchain chromadb
1. 初始化 ERNIE Bot
配置 API 密钥并测试 Embedding 功能,用于将文本转换为向量。
import erniebot
erniebot.api_type = "aistudio"
erniebot.access_token = "<YOUR_ACCESS_TOKEN>"
response = erniebot.Embedding.create(
model="ernie-text-embedding",
input=["这是测试文本,用于验证 embedding 功能是否正常"]
)
print(response.get_result())
2. 引入 ChromaDB 向量数据库
ChromaDB 是一个轻量级的本地向量数据库,适合演示和小规模应用。
import chromadb
from chromadb.api.types import Documents, Embeddings
chroma_client = chromadb.PersistentClient(path="./chroma_db")
3. 自定义嵌入函数
封装 ERNIE Bot 的 Embedding 接口,使其符合 LangChain 的 EmbeddingFunction 接口规范。
from typing import List, Optional
class ErnieEmbeddingFunction:
def __call__(self, input: Documents) -> Embeddings:
embeddings = []
for text in input:
try:
response = erniebot.embedding.create(
model="ernie-text-embedding",
input=[text]
)
result = response.get_result()
if isinstance(result, list):
embeddings.append(result[0])
else:
embeddings.append(result)
except Exception as e:
print(f"Error processing text: {text}, Error: {e}")
embeddings.append([0.0] * 1024)
return embeddings
embedding_func = ErnieEmbeddingFunction()
collection = chroma_client.create_collection(
name="knowledge_base",
embedding_function=embedding_func
)
4. 数据加载与分块
读取本地文档并进行切分。合理的分块大小对检索效果至关重要。
from langchain.text_splitter import CharacterTextSplitter
from langchain.document_loaders import TextLoader
loader = TextLoader('./AI_课程逐字稿.txt', encoding='utf-8')
documents = loader.load()
splitter = CharacterTextSplitter(chunk_size=600, chunk_overlap=50)
docs = splitter.split_documents(documents)
print(f"共分割出 {len(docs)} 个文档片段")
5. 添加数据到向量库
将分割后的文档及其元数据存入 ChromaDB。
import uuid
docs_list = [doc.page_content for doc in docs]
metadatas = [{"source": "AI_Course", "page": i} for i, _ in enumerate(docs)]
ids = [str(uuid.uuid4()) for _ in range(len(docs))]
collection.add(
documents=docs_list,
metadatas=metadatas,
ids=ids
)
6. 检索与生成
实现核心的查询逻辑:检索相关片段 -> 构造 Prompt -> 调用大模型生成回答。
def query_rag(query: str, n_results: int = 2):
results = collection.query(
query_texts=[query],
n_results=n_results
)
context = "\n".join(results['documents'][0])
prompt = f"""
你是一个专业的 AI 助手。请根据以下背景知识回答问题。
如果背景知识中没有相关信息,请直接说明不知道。
<context>
{context}
</context>
用户问题:{query}
"""
response = erniebot.ChatCompletion.create(
model="ernie-4.0",
messages=[{"role": "user", "content": prompt}]
)
return response.get_result()['result']
if __name__ == "__main__":
user_input = input("请输入您的问题:")
answer = query_rag(user_input)
print("\n回答:", answer)
06 高级 RAG 优化策略
基础 RAG 虽然有效,但在复杂场景下可能面临检索不准或回答质量不高的问题。业界已发展出一系列高级优化技术:
1. 混合检索(Hybrid Search)
单纯依赖向量相似度(Dense Retrieval)可能会丢失精确关键词匹配的能力。混合检索结合了稀疏检索(如 BM25)和稠密检索(Vector Search)。BM25 擅长匹配专有名词和精确术语,而 Vector Search 擅长语义理解。通过加权融合两者的得分,可以显著提升召回率。
2. 重排序(Re-ranking)
检索阶段召回的 Top-K 文档可能包含噪声。引入一个专门的 Re-ranker 模型(Cross-Encoder),对候选文档与查询进行精细的相关性打分,然后取前 K 个作为最终上下文。虽然增加了计算开销,但能大幅提高生成质量。
3. 查询改写(Query Rewriting)
用户的原始提问往往简略或缺乏上下文。通过 LLM 对查询进行重写,例如将多轮对话中的追问还原为独立问题,或补充缺失的主语/宾语,可以使检索结果更精准。例如,用户问'它的价格是多少?',改写后变为'文心一言大模型的价格是多少?'。
4. 递归检索(Recursive Retrieval)
对于复杂的多跳问题(Multi-hop Question),单次检索可能无法找到所有必要信息。递归检索允许模型先检索一部分信息,基于此生成中间结论,再基于中间结论进行二次检索,直到收集到足够信息为止。
5. 评估体系(Evaluation)
为了持续优化 RAG 系统,需要建立评估指标。常用的包括:
- 检索准确率(Recall@K):衡量是否找到了正确答案所在的文档。
- 生成忠实度(Faithfulness):衡量回答是否严格基于检索内容,有无幻觉。
- 答案相关性(Answer Relevance):衡量回答是否直接解决了用户问题。
07 最佳实践与注意事项
- 数据清洗:入库前的数据质量决定上限。去除 HTML 标签、特殊符号、乱码,统一编码格式。
- 分块策略:不要盲目使用固定字符数。应根据文档结构(如段落、标题)进行语义分块,保持内容的完整性。
- 元数据过滤:利用元数据(如日期、作者、分类)进行过滤,可以在检索前缩小范围,提高精度。
- 缓存机制:对于高频重复问题,应建立缓存层,直接返回历史答案,降低 API 成本和响应延迟。
- 人工审核:在关键业务场景(如医疗、金融),建议引入人工审核环节,对 RAG 生成的内容进行抽检。
通过上述优化与实践,RAG 技术能够构建出既具备大模型语言能力,又拥有精准知识掌控力的智能系统,是企业数字化转型中不可或缺的技术组件。