RAG
Rag介绍
什么是Rag
RAG(检索-增强-生成,Retrieval-Augmented Generation)是一种结合了信息检索与文本生成的技术框架,旨在通过动态引入外部知识提升生成模型的效果。
RAG 的历史
RAG 源自早期的基本信息检索系统。随着生成式 AI 技术的快速发展以及 GPT-2 和 BERT 等生成式语言模型的出现,对更准确、更相关的回复的需求也逐渐增加。 2020 年,RAG 体系结构被引入,标志着一项重大进步。通过使用机器学习将检索器和生成器模块相结合(将 LLM 的内部知识库与外部知识源集成),RAG 能够生成更准确、最新、连贯且上下文准确的文本。 RAG 模型以深度学习为核心,可以进行端到端训练,从而实现优化回复的输出,随着模型学会检索最可靠且上下文相关的有用信息,生成内容的质量会逐渐提升。
AI 语言模型的演变
信息检索是什么
信息检索 (IR) 指从知识源或数据集搜索并提取相关信息的过程。这一过程特别像使用搜索引擎在互联网上查找信息。您输入查询,系统会检索并为您呈现最有可能包含您正在查找的信息的文档或网页。
信息检索涉及使用相关技术来高效索引并搜索大型数据集,这让人们能够更轻松地从海量的可用数据中访问他们所需的特定信息。除了用于网络搜索引擎,IR 还经常用于数字图书馆、文档管理系统以及各种各样的信息访问应用程序。
RAG 的核心原理
-
检索(Retrieval):当输入问题或任务时,系统从外部知识库(如文档、数据库、网页等)中检索相关段落或信息。例如,使用语义搜索或关键词匹配找到最相关的文档。
-
增强(Augmentation):将检索到的信息与原始输入结合,为生成模型提供上下文支持。例如,将检索到的文本拼接在用户问题后,形成增强的输入。
-
生成(Generation):基于增强后的上下文,生成模型(如GPT、BART)输出更准确、信息丰富的回答。
RAG是如何运作的
1. 步骤1:检索(Retrieval)
-
输入处理:接收用户的问题或指令(例如“量子计算机的最新进展有哪些?”)。
-
检索外部知识:
-
知识库:系统访问预先构建的外部知识库(如维基百科、专业数据库、企业内部文档等)。
-
检索方式:
-
传统方法:使用关键词匹配(如BM25算法)查找相关文档。
-
语义检索:将问题转换为向量(通过模型如BERT、DPR),在向量数据库中(如Faiss、Pinecone)搜索语义相似的段落。
-
-
输出:返回Top-K(如5篇)最相关的文档片段。
-
2. 步骤2:增强(Augmentation)
-
上下文拼接:将检索到的文档片段与原始问题结合,形成增强的输入。例如:
问题:量子计算机的最新进展有哪些?
检索到的文档1:2023年,IBM发布了1121量子位的处理器…
检索到的文档2:谷歌宣布其量子计算机在特定任务上比经典计算机快1亿倍… -
输入格式化:将问题和文档按特定模板拼接(如分隔符标记),供生成模型理解。
3. 步骤3:生成(Generation)
-
模型处理:增强后的输入被送入生成模型(如GPT-4、T5、BART)。
-
生成输出:模型基于检索到的信息生成回答,确保内容与外部知识一致。
关键技术细节
(1)检索器的选择
-
稀疏检索(Sparse Retrieval):基于词频(如TF-IDF、BM25),适合精确关键词匹配。
-
密集检索(Dense Retrieval):
-
使用双编码器(如DPR模型)分别编码问题和文档为向量,计算余弦相似度。
-
向量数据库(如Faiss)加速大规模检索。
-
(2)生成器的优化
-
模型微调:生成器可针对特定领域微调(如医学、法律),提升专业术语处理能力。
-
注意力机制:模型通过注意力权重动态关注检索内容的关键部分。
(3)端到端训练(可选)
-
联合训练检索器与生成器:部分框架(如RAG-Token)允许同时优化检索和生成模块,提升整体协同效果。
-
知识蒸馏:用大型生成模型指导检索器选择更相关的文档。
Rag优势与挑战
优势
-
动态知识更新:无需重新训练模型,更新知识库即可获取最新信息。
-
减少幻觉(Hallucination):生成内容基于检索结果,降低编造风险。
-
可解释性:答案可关联到具体文档来源(如引用链接)。
挑战
-
检索质量依赖:不相关文档会导致生成错误。
-
长文本处理:检索到的长文档可能包含冗余信息,需摘要或截断。
-
延迟问题:大规模知识库检索可能增加响应时间。
Rag应用流程
完整的RAG应用流程主要包含数据准备阶段和应用两个阶段
数据准备阶段
-
数据收集: 需要收集与问答系统相关的各种数据,这些数据可以来自文档、网页、数据库,第三方 API 获取实时数据等多种来源。
-
数据处理: 对收集到的数据进行清洗,去除噪声、重复项和无关信息,进行压缩、格式化,提取数据中关键信息,确保数据的质量和准确性。
-
文本分割:文本分割主要考虑两个因素:
- embedding模型的Tokens限制情况。
- 语义完整性对整体的检索效果的影响。
- 固定长度分割:按字符数或词数切分(如每500字符)。
- 语义分割:用模型(如NLTK、spaCy)按句子边界或语义单元分割。
示例: # 使用 LangChain 的文本分割器 from langchain.text_splitter import RecursiveCharacterTextSplitter splitter = RecursiveCharacterTextSplitter(chunk_size=300, chunk_overlap=50) chunks = splitter.split_text(clean_text)
-
向量化(embedding):将文本转换为向量表示,用于后续相似性检索,该过程会直接影响到后续检索的效果,可以选择开源Embedding模型微调或直接训练适合自己场景的Embedding模型。
示例: # 使用Sentence-BERT生成向量 from sentence_transformers import SentenceTransformer model = SentenceTransformer("all-MiniLM-L6-v2") embeddings = model.encode(chunks)
-
数据入库:数据向量化后构建索引,将这些文本片段及其向量存入向量数据库(如FAISS、Milvus等)。
示例: # 使用Faiss存储向量 import faiss index = faiss.IndexFlatL2(embedding_dim) index.add(embeddings)
应用阶段
-
用户提问:
-
用户输入问题(如“如何预防台风停电?”)。
-
使用与知识库相同的嵌入模型将问题转换为向量。
-
-
数据检索:常见的数据检索方法包括:相似性检索、全文检索等,根据检索效果,一般可以选择多种检索方式融合,提升召回率。
-
相似性检索:即计算查询向量与所有存储向量的相似性得分,返回得分高的记录。常见的相似性计算方法包括:余弦相似性、欧氏距离、曼哈顿距离等。
-
全文检索:全文检索是一种比较经典的检索方式,在数据存入时,通过关键词构建倒排索引;在检索时,通过关键词进行全文检索,找到对应的记录。
示例: # 使用 FAISS 检索 Top-K 相似片段 query_embedding = model.encode(["如何预防台风停电?"]) distances, indices = index.search(query_embedding, k=5) retrieved_chunks = [chunks[i] for i in indices[0]]
-
-
结果排序:根据相似度得分对检索到的结果进行排序,选择最相关的片段作为后续生成的输入。
-
相似度排序:直接按向量相似度得分排序。
-
重排序(Rerank):用交叉编码器(Cross-Encoder)进一步精排。
示例: # 使用 Sentence-BERT 交叉编码器重排序 from sentence_transformers import CrossEncoder reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2") scores = reranker.predict([(query, chunk) for chunk in retrieved_chunks]) sorted_chunks = [chunk for _, chunk in sorted(zip(scores, retrieved_chunks), reverse=True)]
-
-
LLM生成答案:将增强后的Prompt输入大语言模型。
-
上下文融合设计Prompt模板 :
-
显式指令:要求模型基于检索结果生成回答。
-
结构化输入:用分隔符标记检索内容,避免模型混淆。
-
参数调节:限制最大长度、截断过长的检索内容(如保留前 500 字符)。
-
-
模型选择:使用大语言模型(如GLM)基于上述上下文信息生成回答(如GPT-4、ChatGLM)。
-
Python案例演示
1. 安装依赖库
pip install langchain langchain-community langchain-text-splitters pypdf sentence-transformers faiss-cpu
2. 完整代码
import os
from langchain_community.document_loaders import PyPDFLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import FAISS
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import RetrievalQA
from langchain_community.embeddings import HuggingFaceEmbeddings
from langchain_community.llms import HuggingFaceHub
# ------------ 1. 本地数据加载 ------------
def load_documents(file_path):
"""加载本地PDF文档"""
loader = PyPDFLoader(file_path)
pages = loader.load()
return pages
# ------------ 2. 文档分割 ------------
def split_documents(pages):
"""分割文档为chunks"""
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=50,
length_function=len
)
chunks = text_splitter.split_documents(pages)
return chunks
# ------------ 3. 向量化 & 数据入库 ------------
def create_vector_store(chunks):
"""创建向量数据库"""
embeddings = HuggingFaceEmbeddings(model_name="sentence-transformers/all-MiniLM-L6-v2")
vector_store = FAISS.from_documents(chunks, embeddings)
return vector_store
# ------------ 4. Prompt设计 ------------
def build_prompt():
"""构建增强Prompt模板"""
template = """
请根据以下上下文信息回答问题。如果无法从上下文中得到答案,请回答'我不知道'。
上下文:
{context}
问题:{question}
答案:
"""
return ChatPromptTemplate.from_template(template)
# ------------ 5. 构建Chain链 ------------
def build_qa_chain(vector_store):
"""构建RetrievalQA链"""
hf_token = os.getenv("HUGGINGFACEHUB_API_TOKEN") # 读取 API Token
if not hf_token:
raise ValueError("请在环境变量中设置 HUGGINGFACEHUB_API_TOKEN")
llm = HuggingFaceHub(
repo_id="google/flan-t5-small",
huggingfacehub_api_token=hf_token,
model_kwargs={"temperature": 0.2}
)
prompt = build_prompt()
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vector_store.as_retriever(search_kwargs={"k": 3}),
chain_type_kwargs={"prompt": prompt}
)
return qa_chain
# ------------ 主流程 ------------
if __name__ == "__main__":
file_path = "demo.pdf" # 替换为你的PDF路径
# 1. 加载文档
pages = load_documents(file_path)
# 2. 分割文档
chunks = split_documents(pages)
print(f"分割得到 {len(chunks)} 个文本块")
# 3. 向量化并存储
vector_store = create_vector_store(chunks)
# 4. 构建QA链
qa_chain = build_qa_chain(vector_store)
# 5. 提问测试
question = "文章中提到的主要观点是什么?"
result = qa_chain.invoke({"question": question}) # 修正参数名
print("答案:", result["result"])
3. 代码说明
(1) 文档加载
- 使用 PyPDFLoader 加载本地 PDF 文件,返回分页的 Document 对象。
(2) 文本分割
- 采用递归式文本分割器,确保语义连贯性(块大小 300 字符,重叠 50 字符)。
(3) 向量化与存储
-
使用 all-MiniLM-L6-v2 模型生成文本嵌入(轻量且高效)。
-
将向量存入 FAISS 本地向量数据库。
(4) Prompt 设计
-
明确要求模型基于上下文回答,避免幻觉(hallucination)。
-
上下文和问题通过占位符动态注入。
(5) Chain 链
-
使用 RetrievalQA 链整合检索与生成:
-
retriever:从 FAISS 检索 Top-3 相关文档。
-
llm:使用 flan-t5-small 生成答案(需替换为你的 HuggingFace Token)。
4. 扩展方向
增强检索
# 混合检索(关键词+语义)
from langchain.retrievers import BM25Retriever, EnsembleRetriever
bm25_retriever = BM25Retriever.from_documents(chunks)
faiss_retriever = vector_store.as_retriever()
ensemble_retriever = EnsembleRetriever(
retrievers=[bm25_retriever, faiss_retriever],
weights=[0.4, 0.6]
)
更换模型
# 使用本地模型(需提前下载)
from langchain_community.llms import HuggingFacePipeline
from transformers import AutoModelForCausalLM, AutoTokenizer, pipeline
model_name = "facebook/opt-350m"
tokenizer = AutoTokenizer.from_pretrained(model_name)
model = AutoModelForCausalLM.from_pretrained(model_name)
pipe = pipeline("text-generation", model=model, tokenizer=tokenizer)
llm = HuggingFacePipeline(pipeline=pipe)
常见的RAG 框架
Haystack
Haystack 是一个强大而灵活的框架,用于构建端到端问题解答和搜索系统,支持多种文档存储,集成流行语言,可扩展结构,文档丰富。
https://github.com/deepset-ai/haystack
RAGFlow
RAGFlow是一个注重简洁性和效率的RAG框架,支持自定义嵌入模型,与流行的矢量数据库集成。
https://github.com/infiniflow/ragflow
Txtai
Txtai 是一个多功能的人工智能数据平台,它超越了传统的 RAG 框架。它为构建语义搜索、语言模型工作流和文档处理管道提供了一套全面的工具:
- 嵌入式数据库,用于高效的相似性搜索
- 用于集成语言模型和其他人工智能服务的 API
- 用于自定义工作流的可扩展架构
- 支持多种语言和数据类型
https://github.com/neuml/txtai
STORM
STORM(斯坦福开源 RAG 模型)是斯坦福大学开发的面向研究的 RAG 框架,对于那些希望探索 RAG 技术前沿的人来说,STORM 提供了一个以严谨学术为后盾的坚实基础。
https://github.com/stanford-oval/storm
LLM-App
LLM-App 是用于构建动态 RAG 应用程序的模板和工具集合。LLM-App 的主要功能包括:
- 可快速部署的即用型 Docker 容器
- 支持动态数据源和实时更新
- 与流行的 LLM 和矢量数据库集成
- 针对各种 RAG 用例的可定制模板
https://github.com/pathwaycom/llm-app
Cognita
Cognita是专注于为构建和部署人工智能应用提供统一的平台。与流行的 ML 框架和工具集成,内置监控和可观测功能。
https://github.com/truefoundry/cognita
R2R
R2R(Retrieval-to-Retrieval,检索到检索)是一个专门的 RAG 框架,支持多步骤检索流程,与各种嵌入模型和向量存储集成,分析和可视化检索性能的工具。
https://github.com/SciPhi-AI/R2R
选型建议:
-
企业级需求:Haystack(定制性强)或 RAGFlow(开箱即用)
-
轻量级应用:txtai 或 R2R
-
学术研究:STORM
RAG更详细资料:https://arxiv.org/abs/2312.10997