RAG了解与实践
RAG(Retrieval-Augmented Generation) 是一种授予生成式人工智能模型信息检索功能的技术。它修改与大型语言模型的交互 (LLM),以便模型响应用户对一组指定文档的查询,使用此信息来增强从其自身庞大的静态训练数据中提取的信息。这允许LLMs使用特定于域和/或更新的信息。
可以简单理解为"检索+生成"的人工智能技术。想象你正在做一个复杂的研究项目,除了大脑里已有的知识,你还需要查阅额外的资料来提高回答的准确性和深度。RAG就是AI模型的这个"查阅资料"过程。
RAG主要分为三个关键步骤
-
索引
将要引用的数据转换为LLM embedding,即大向量空间形式的数字表示。RAG 可用于非结构化(通常是文本)、半结构化或结构化数据,然后,这些嵌入内容将存储在矢量数据库中,以便进行文档检索。
就像把所有相关的资料数据全部翻译为中文,聚合为一本书
-
检索
当收到一个问题时,AI首先在大规模知识库中快速搜索相关信息
就像图书馆里用关键词检索最相关的书籍和资料
-
增强
将检索到的相关信息作为"额外知识"注入到原始问题中
相当于给AI喂些额外上下文
-
生成
AI基于原始问题和检索到的额外信息,生成更加准确、丰富的回答
就像写论文,不仅依赖了本身知识,还参考了大量文献
代码demo
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_community.vectorstores import Chroma
from langchain.text_splitter import CharacterTextSplitter
from langchain.chains import RetrievalQA
from langchain_community.document_loaders import TextLoader
import os
# 设置OpenAI API密钥
os.environ["OPENAI_API_KEY"] = "your_openai_api_key"
# 加载文档
loader = TextLoader("./罪与罚(陀思妥耶夫斯基著).txt",encoding="utf-8")
documents = loader.load()
# 文本分割
text_splitter = CharacterTextSplitter(
chunk_size=500,
chunk_overlap=50,
separator="\n"
)
texts = text_splitter.split_documents(documents)
# 限制处理的文本数量
max_texts = 50 # 设置最大处理文档数
texts = texts[:max_texts]
# 创建向量存储
embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002"
)
vectorstore = Chroma.from_documents(texts, embeddings)
# 创建检索式问答链
qa = RetrievalQA.from_chain_type(
llm=ChatOpenAI(
model_name="gpt-3.5-turbo",
temperature=0.7,
max_tokens=1000
),
chain_type="stuff",
retriever=vectorstore.as_retriever(
search_kwargs={
"k": 10
}
)
)
# 示例查询
query = "请分析主人公拉斯柯尔尼科夫的心理变化"
result = qa.invoke({"query": query})
print(f"问题: {query}")
print(f"回答: {result}")
索引
Loader
在langchain中有以下加载器
加载器 | 数据源 | 示例 |
---|---|---|
TextLoader | 纯文本(.txt) | TextLoader(“document.txt”) |
CSVLoader | 逗号分隔的 CSV 文件 | CSVLoader(“data.csv”) |
UnstructuredFileLoader | 自动解析 PDF、Word、HTML | UnstructuredFileLoader(“doc.pdf”) |
PDFMinerLoader | 解析 PDF 纯文本 | PDFMinerLoader(“report.pdf”) |
PyMuPDFLoader | 解析 PDF(更快) | PyMuPDFLoader(“doc.pdf”) |
Docx2txtLoader | Microsoft Word(.docx) | Docx2txtLoader(“file.docx”) |
JSONLoader | JSON 文件 | JSONLoader(“data.json”) |
切分策略
- 固定大小切分
- 语义切分
- 递归切分
- 基于文档结构的切分
- 基于LLM的切分
在langchain中提供的分割器如下
CharacterTextSplitter
按字符分割
📌 介绍:
- 按 固定字符长度 分割文本,并可以 重叠 一定数量的字符,防止信息丢失。
- 默认按照 \n\n(两次换行)和 \n(单换行)进行拆分,也可以自定义分隔符。
✅ 适用于:
- 处理长文档,如 书籍、文章、代码
- 需要重叠片段来保持上下文连续性的场景
RecursiveCharacterTextSplitter
递归字符分割
📌 介绍:
- 优先使用较大的分隔符(如 \n\n),如果拆分后仍然超出 chunk_size,则继续使用更小的分隔符(如 . 句号)。
- 更智能,不会直接切断单词或句子,适用于 较大文本。
✅ 适用于:
- 处理 书籍、论文、API 文档
- 需要更自然的文本分割方式(避免断句)
TokenTextSplitter
按 Token 分割
📌 介绍:
- 按 Token(子词单元)分割文本,避免切割单词。
- 使用 tiktoken(OpenAI 的 tokenizer)或 HuggingFace tokenizer 计算 Token 数量。
✅ 适用于:
- 处理 OpenAI GPT 模型(因为有 Token 限制)
- 需要更精确的 Token 控制,防止超出模型的最大 Token 长度
MarkdownTextSplitter
按 Markdown 结构分割
📌 介绍:
- 专门用于 Markdown 文档,可按 # 一级标题、## 二级标题、代码块 等结构进行拆分。
- 保持 Markdown 的 层级关系,不会破坏格式。
✅ 适用于:
- 处理 技术文档、Wiki、API 文档
- 需要 按标题或段落 进行检索
SentenceTransformersTokenTextSplitter
按 Token + 句子分割
📌 介绍:
- 结合 SentenceTransformer 和 TokenTextSplitter,按 Token 数量切割,但尽量不破坏句子结构。
- 适用于 LLM 需要 按 Token 数量优化输入,但又不能随意拆句的情况。
✅ 适用于:
- 需要对 长文本 进行向量化,并保证语义完整性
- 适配 Hugging Face Transformers 和 BERT 相关模型
LanguageModelTextSplitter
按 LLM Token 限制分割
📌 介绍:
- 让 大模型(如 GPT-4)决定如何分割文本,并确保每段都符合模型 Token 限制。
- 适合需要适配 OpenAI API 的任务,防止超出 max_tokens 限制。
✅ 适用于:
- OpenAI、Anthropic Claude 需要 Token 限制 的情况
- 处理 超大文档(如法律、医学文本)
CodeTextSplitter
按代码结构分割
📌 介绍:
- 适用于 代码文件(Python、Java、C++ 等),按函数、类或逻辑块拆分。
- 避免在 关键语法结构(如函数、类) 之间随意切割,保持代码完整性。
✅ 适用于:
- 处理 GitHub 代码库
- 代码检索、代码对话(如 Code Llama)
embeddings
OpenAIEmbeddings 是 langchain 提供的一个 嵌入模型(Embedding Model),用于将文本转换为 向量表示,以便进行语义搜索、文本相似性计算等任务。它是 langchain 的 Embeddings 接口的实现之一,内部调用了 OpenAI 提供的 文本嵌入模型 API
模型名称 | 维度 | 说明 | 适用场景 |
---|---|---|---|
text-embedding-3-large | 3072 | 最新模型,准确性更高,适用于复杂应用 | 高精度语义搜索、大规模文本分析 |
text-embedding-3-small | 1536 | 最新模型,性价比高,适用于一般应用 | 一般语义搜索、问答系统 |
text-embedding-ada-002 | 1536 | 旧版模型,仍然可用,但不如 text-embedding-3 | 旧项目兼容 |
向量数据库Chroma
Chroma是一款轻量级、高性能的开源向量数据库,专为 LLM 应用设计,支持文档存储、元数据过滤、语义搜索,并提供本地和云端存储。
✅ 主要特点
- 🏗 简洁易用:Python 直接调用,无需复杂配置
- 🗂 支持文档存储:可存储文本 + 向量 + 元数据
- 🔄 检索增强生成(RAG)友好
📦 适用场景
- LLM 问答系统(如 OpenAI + RAG)
- 本地化知识库(文档搜索)
- AI 助手
chain_type
chain_type是指定不同的处理方式的参数,决定了如何处理检索到的文档以及如何与LLM进行交互
- stuff: 最简单的链类型,将所有文档直接传递给语言模型。适用于少量短文档。
- map_reduce: 先对每个文档单独处理,然后将结果合并。适用于大量文档
- refine: 逐步细化答案,每次处理一个文档。适用于需要高质量答案的场景
- map_rerank: 对每个文档单独处理,然后根据相关性重新排序。适用于需要最相关答案的场景
场景 | 推荐chain_type | 原因 |
---|---|---|
文档较短(可直接传递) | “stuff” | 速度快 |
文档较长(超出 Token 限制) | “map_reduce” | 可处理长文本 |
需要更高质量的回答 | “refine” | 逐步优化答案 |
需要确保最优答案 | “map_rerank” | 选择最佳答案 |
Ref
- https://en.wikipedia.org/wiki/Retrieval-augmented_generation