一、为什么我们需要 RAG?
你肯定遇到过这种情况:向 ChatGPT 询问一个近期发生的事件,或者一个非常小众的专业问题,它可能会一本正经地“胡说八道”,生成看似合理实则错误的信息。这就是大语言模型(LLM)固有的两个痛点:知识陈旧和幻觉。它们的内部知识“凝固”在训练数据截止的那一刻,且无法自行验证信息的真伪。
检索增强生成(Retrieval-Augmented Generation,简称 RAG)正是为了缓解这些问题而生。它的核心思想非常朴素,就像我们人类“开卷考试”一样:在回答一个复杂问题前,先去查阅相关的资料(检索),然后基于查找到的可靠信息来组织答案(生成)。这相当于给 LLM 外挂了一个实时、可更新的知识库,让它的回答有据可依,从而显著提升答案的准确性、时效性和可信度。
提示:RAG 并非要取代大模型本身的推理能力,而是将其强大的语言理解与生成能力,与外部知识的精确性与时效性结合起来。这是当前企业级 AI 应用落地最主流、最有效的架构之一。
二、RAG 的基本原理:检索与生成的协同
RAG 的工作流程可以清晰地拆解为两个主要阶段:离线索引和在线检索与生成。
在离线阶段,我们需要将准备好的知识源(如公司的内部文档、产品手册、最新新闻等)进行处理。这些原始文档会被切分成合适大小的段落(Chunk),然后通过一个嵌入模型(Embedding Model)转换成高维的数学向量(即“文本嵌入”)。这些向量随后被存入一个专门的向量数据库(Vector Database)中,并建立索引,以便快速查找。
当用户提出一个问题时,在线阶段开始。首先,同一个嵌入模型会将用户的问题也转换成一个向量。然后,在向量数据库中执行相似性搜索(如余弦相似度),找出与问题向量最接近的几个文档片段。这些最相关的片段,连同用户的原始问题,一起被“打包”成一个提示词(Prompt),发送给大语言模型。LLM 最终基于这些提供的上下文,生成最终答案。
简单来说,流程就是:用户提问 -> 向量化问题 -> 检索相关文档 -> 构造增强提示 -> LLM生成答案。
三、核心组件拆解
一个完整的 RAG 系统主要由以下几个关键部分构成:
- 知识库与文档加载器:数据是基础。知识库可以是 PDF、Word、网页、数据库记录等任何结构化或非结构化数据。文档加载器负责将不同格式的文档统一读取成纯文本。
- 文本分割器:由于 LLM 的上下文窗口有限,且过长的文档不利于精确检索,需要将长文档切分成具有语义完整性的段落。分割策略(如按固定长度、按标题、按段落)对最终效果影响很大。
- 嵌入模型:这是 RAG 的“翻译官”,负责将文本转换为机器可以理解的向量。模型的质量直接决定了检索的准确性。目前常用
OpenAI Embeddings,BGE,Jina等模型。 - 向量数据库:存储和快速检索海量向量的专用数据库。它提供了高效的近似最近邻搜索(ANN)能力。主流选择有
FAISS,Chroma,Pinecone,Milvus等。 - 大语言模型:系统的“大脑”,负责最终的推理和生成。它接收检索到的上下文和原始问题,生成自然、连贯的回复。
四、一个简单的实现示例
下面我们用 Python 和 LangChain 框架来演示一个最基础的 RAG 流程。假设我们有一段关于公司产品“AiWriter”的介绍文档。
首先,确保安装必要的库:pip install langchain openai chromadb tiktoken。
from langchain_community.document_loaders import TextLoader
from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import OpenAIEmbeddings
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.llms import OpenAI
from langchain.chains import RetrievalQA
# 1. 加载并切分文档
loader = TextLoader(‘aiwriter_intro.txt’)
documents = loader.load()
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
docs = text_splitter.split_documents(documents)
# 2. 创建向量存储
embeddings = OpenAIEmbeddings() # 使用OpenAI的嵌入模型
vectorstore = Chroma.from_documents(docs, embeddings) # Chroma作为向量数据库
# 3. 创建检索问答链
llm = OpenAI(temperature=0) # 设置temperature为0以获得更确定的答案
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type=“stuff”, # 表示将所有检索到的文档塞进一个提示中
retriever=vectorstore.as_retriever()
)
# 4. 提问并获取回答
query = “AiWriter的定价策略是什么?”
result = qa_chain.invoke({“query”: query})
print(result[“result”])
这段代码清晰地展示了 RAG 的核心步骤。RetrievalQA 链在内部自动完成了“检索相关文档片段 -> 构造提示 -> 调用 LLM 生成回答”的整个过程。
五、实践中的挑战与优化方向
将上述基础示例投入生产环境会面临诸多挑战,这也是我们优化和进阶的方向。
检索质量是生命线。如果检索回来的文档片段不相关,LLM 再强大也会“巧妇难为无米之炊”。优化点包括:改进文本分割策略,使用更强大的嵌入模型,实现混合检索(结合关键词检索和语义检索),以及对检索结果进行重排序。
提示工程的艺术。如何将检索到的多个片段和原始问题组织成一个有效的提示,直接影响 LLM 的理解。需要精心设计提示模板,明确指示 LLM “仅基于提供的上下文回答”、“如果上下文未提及则说不知道”,以进一步控制幻觉。
提示:在生产级应用中,你需要特别关注成本(向量化、检索和LLM调用都可能产生费用)、延迟(整个流程的链式响应时间)以及可观测性(如何监控和评估检索与生成的质量)。
RAG 是一个快速发展的领域,后续的 Self-RAG(自我检索评估)、CRAG(纠正性 RAG)等进阶模式正在不断涌现,旨在让系统更智能、更可靠。