一、为什么需要 RAG:大模型的“知识困境”
当我们使用像 GPT 这样的大语言模型(LLM) 时,很快会发现一个核心问题:它的知识是静态的、有限的。其知识截止于训练数据的时间点,无法获知最新的新闻、公司内部文档或专业领域的细分知识。直接向它提问,它可能会“一本正经地胡说八道”(即产生幻觉),或者坦诚地表示“我无法获取最新信息”。
RAG(Retrieval-Augmented Generation,检索增强生成) 正是为了解决这个问题而诞生的。它的核心思想非常直观:先检索,再生成。就像我们人类在回答一个不确定的问题时,会先去查阅书籍或搜索资料,然后再基于找到的信息给出回答。RAG 赋予了大模型类似的能力,让它能够利用外部、实时的、特定的知识库来增强其生成回答的准确性和时效性。
提示:你可以将 RAG 理解为给一个博闻强记但不知变通的“大脑”(LLM)配备了一位高效的“图书管理员”(检索器)。管理员快速找到相关书籍的片段,大脑基于这些片段组织语言,生成最终答案。
二、RAG 的核心流程:检索、增强与生成
一个完整的 RAG 流程通常包含三个关键阶段,如下图所示:
- 索引(Indexing):这是离线准备阶段。将你的私有文档(如 PDF、网页、数据库记录)进行处理。核心是将文本分割成较小的
文档块,使用嵌入模型将这些文本块转换为数值向量(嵌入向量),并存入一个向量数据库中。这个过程建立了从语义到向量的映射,为后续的相似度检索打下基础。 - 检索(Retrieval):这是在线查询阶段。当用户提出问题时,首先使用同一个嵌入模型将用户的问题也转换为向量。然后,在向量数据库中执行相似性搜索,找出与问题向量最相似的几个文档块(Top K)。
- 生成(Generation):这是最终回答阶段。将检索到的相关文档块作为上下文,与用户原始问题一起,构造一个提示词,发送给大语言模型。大模型基于这些“证据”生成最终的回答,使其有据可依,大大降低了幻觉风险。
三、关键组件详解:嵌入模型与向量数据库
RAG 的效能高度依赖于两个核心组件:
- 嵌入模型:它将文本转换为高维空间中的向量,语义相似的文本在向量空间中距离更近。选择一个高质量的嵌入模型至关重要,例如
text-embedding-ada-002(OpenAI)、bge-large(智源)或gte(阿里)等。它直接决定了检索的“理解力”。 - 向量数据库:专门用于高效存储和检索向量数据的数据库。它提供了高速的相似性搜索功能(如余弦相似度、欧氏距离)。常见的有
ChromaDB(轻量级,易于上手)、Pinecone(云原生)、Milvus(高性能)以及FAISS(Facebook 开源的库)。
提示:在实践中,嵌入模型的选择有时比底层大模型(如GPT-4)更能影响最终效果,尤其是在处理专业领域文档时。需要对特定领域的嵌入模型进行微调或选择。
四、动手实践:一个简单的 RAG 实现
下面我们用 Python 代码演示一个最简单的 RAG 流程。我们将使用 LangChain 库来简化编排,并用 ChromaDB 作为向量存储。
首先,安装必要的库:pip install langchain chromadb openai
import os
from langchain.document_loaders import TextLoader
from langchain.text_splitter import CharacterText_splitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.llms import OpenAI
# 1. 加载文档(示例文本)
loader = TextLoader('my_knowledge.txt', encoding='utf-8')
documents = loader.load()
# 2. 文本分块(chunk_size 是关键参数)
text_splitter = CharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = text_splitter.split_documents(documents)
# 3. 创建嵌入模型和向量数据库
embeddings = OpenAIEmbeddings() # 需要设置 OPENAI_API_KEY 环境变量
vectorstore = Chroma.from_documents(docs, embeddings)
# 4. 创建检索问答链
qa_chain = RetrievalQA.from_chain_type(
llm=OpenAI(temperature=0), # 使用一个简单的LLM
chain_type="stuff", # 将所有检索到的文档拼接起来
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}) # 检索最相关的3个文档块
)
# 5. 提问!
query = "我们公司的报销政策中,餐费的标准是多少?"
result = qa_chain.run(query)
print(result)
五、优化与进阶:让 RAG 更强大
上述基础 RAG 流程存在改进空间。以下是几个重要的优化方向:
- 优化文本分块策略:固定大小的分块可能割裂语义。可以尝试基于语义、句子或自然段落进行分割。
RecursiveCharacterTextSplitter是一个更智能的选择。 - 改进检索策略:除了基础的相似度搜索,可以引入 混合搜索,结合向量搜索与关键词搜索(如 BM25),以提高召回率。也可以使用 重排序模型,对初步检索到的结果进行二次排序,选出最相关的条目。
- 上下文工程:精心设计传递给大模型的提示词模板,明确指示它“基于提供的上下文回答,如果信息不足则说明”。这能进一步规范模型的输出。
# 示例:一个更复杂的提示词模板
from langchain.prompts import PromptTemplate
template = """
请严格根据以下上下文信息来回答最后的问题。如果上下文中没有包含答案所需的信息,请直接说“根据提供的资料,我无法回答这个问题”。
上下文:
{context}
问题:{question}
有用的回答:"""
PROMPT = PromptTemplate(template=template, input_variables=["context", "question"])
# 将此模板传入 RetrievalQA 链的 `chain_type_kwargs` 参数中
六、应用场景与局限性
典型应用场景:
- 企业知识库问答:让员工快速查询 HR 政策、产品手册、技术文档。
- 智能客服:基于产品文档和 FAQ 自动回答用户问题。
- 学术研究助手:从大量论文中检索并总结特定主题。
- 内容创作:为作者提供基于事实资料的素材和引用。
需要注意的局限性:
- 检索质量是瓶颈:如果相关的文档块没有被检索出来,那么大模型也无法生成正确的答案。“垃圾进,垃圾出”的原则在此同样适用。
- 对文档处理有要求:需要对文档进行良好的清洗、分块和元数据标注。
- 增加延迟和成本:每次查询都多了检索和可能更大的提示词输入,会增加响应时间和 API 调用费用。
提示:RAG 不是银弹。对于需要深度推理、创作或完全开放域的问题,它可能帮助有限。它最擅长的是需要精确、可溯源、基于事实的问答任务。