RAG革命:从零到一构建你的第一个检索增强生成应用
一、为什么我们需要RAG?大型语言模型的“记忆”困境
大型语言模型(LLM),如GPT-4,虽然在语言理解和生成方面表现惊艳,但它们并非无所不知。其知识受限于训练数据的截止日期,对于新近发生的事件或特定领域的私有知识一无所知。更重要的是,LLM有时会产生“幻akj觉”(Hallucination)——自信地编造出看似合理但完全错误的信息。
这两种缺陷极大地限制了LLM在企业级应用中的可靠性。我们如何才能让模型既能利用其强大的推理能力,又能基于最新、准确、可信的知识来回答问题呢?
检索增强生成(Retrieval-Augmented Generation, RAG) 应运而生,它通过一种“开卷考试”的模式,完美地解决了这个问题。
核心思想:在回答问题前,先从外部知识库(如公司文档、产品手册、数据库等)中检索出最相关的信息,然后将这些信息连同用户的问题一起“喂”给LLM,让它基于给定的材料进行回答。
二、RAG的工作流程:一场精心编排的“开卷考试”
一个完整的RAG系统主要包含两个阶段:索引构建(离线) 和 检索生成(在线)。
阶段一:索引构建 (Indexing) - 为知识库“划重点”
这个阶段的目标是将你的原始文档转换成一个可被快速检索的格式,通常是向量索引。
加载 (Load):首先,我们需要加载各种来源的文档。这些文档可以是PDF、TXT、Markdown、HTML网页,甚至是数据库中的记录。
切分 (Split):直接将整篇文档丢给模型是不现实的,因为存在Token长度限制。因此,我们需要将长文档切分成更小的、语义完整的块(Chunks)。
- Token切分:最简单粗暴的方式,按固定Token数量切分。
- 语义切分:更智能的方式,例如按段落、标题,或者使用专门的算法(如
Spacy
或NLTK
)来识别句子边界,确保每个Chunk包含相对完整的语义。 - Markdown切分:针对Markdown格式,可以按标题层级进行切分,这是一种非常高效且保留了文档结构的策略。
向量化 (Embed):计算机无法直接理解文本,我们需要将每个文本块(Chunk)通过一个嵌入模型(Embedding Model) 转换成一个高维的数字向量。这个向量可以被看作是该文本块在语义空间中的“坐标”。语义相近的文本,其向量在空间中的距离也更近。
存储 (Store):最后,我们将所有文本块的向量表示存储在一个专门的向量数据库(Vector Database) 中,如FAISS、ChromaDB或Milvus。这个数据库能够进行极其高效的向量相似度搜索。
阶段二:检索生成 (Retrieval & Generation) - 查找资料并作答
这个阶段是用户与系统交互的实时过程。
- 用户提问:用户输入一个查询(Query)。
- 查询向量化:将用户的查询同样通过嵌入模型转换成一个向量。
- 检索 (Retrieve):在向量数据库中,使用这个查询向量去搜索与之最相似的N个文本块向量。这个过程通常使用余弦相似度等算法。
- 增强 (Augment):将检索到的这N个文本块(我们称之为“上下文”或Context)与用户的原始问题组合成一个新的、更丰富的提示词(Prompt)。
- 生成 (Generate):将这个增强后的提示词发送给LLM。由于LLM现在有了明确的参考资料,它就能生成一个基于事实、更加准确和忠实的答案。
三、快速上手:用Python构建一个迷你RAG应用
下面我们使用langchain
框架来演示如何快速搭建一个RAG流程。
# 1. 安装必要的库
# pip install langchain langchain-community langchain-openai chromadb
import os
from langchain_community.document_loaders import TextLoader
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain.text_splitter import CharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain.chains import RetrievalQA
# 设置你的OpenAI API Key
# os.environ["OPENAI_API_KEY"] = "sk-..."
# -- 阶段一:索引构建 --
# 1. 加载文档
loader = TextLoader("./my_document.txt")
documents = loader.load()
# 2. 切分文档
text_splitter = CharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = text_splitter.split_documents(documents)
# 3. 向量化并存储到Chroma向量数据库
embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_documents(chunks, embeddings)
# -- 阶段二:检索生成 --
# 4. 创建一个检索器
retriever = vector_store.as_retriever()
# 5. 创建一个LLM
llm = ChatOpenAI(model_name="gpt-3.5-turbo")
# 6. 创建一个RetrievalQA链
# 这条链封装了“检索->增强->生成”的完整流程
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # "stuff"表示将所有检索到的内容"塞"进一个Prompt
retriever=retriever
)
# 7. 提问!
query = "What is the core idea of RAG?"
response = qa_chain.invoke(query)
print(response['result'])
将你的知识内容保存在my_document.txt
中,运行以上代码,你就拥有了一个具备基本功能的RAG问答机器人!
四、结语
RAG技术通过“检索”这一简单而强大的机制,为大型语言模型架起了一座通往外部实时、私有知识的桥梁。它不仅是缓解模型“幻觉”的有效手段,更是推动LLM从“通用聊天工具”走向“专业领域助手”的关键一步。虽然我们今天只实现了一个基础版本,但在下一篇文章中,我们将深入探讨如何对RAG的各个环节进行优化,并建立科学的评估体系,打造生产级别的RAG应用。