返回首页

RAG 系统详解:Python 指南

本文解释检索增强生成 (RAG) 原理,将其与微调对比,并提供使用 LangChain、Chroma 和 Wikipedia 数据的动手最小 Python 实现。它涵盖分块、索引、检索和生成阶段,帮助开发者通过外部知识源减少 LLM 幻觉。

掌握 RAG:立即构建你的第一个 Python 管道
Advertisement 728x90

RAG 系统原理与最小 Python 实现

语言模型受训练数据限制:知识静态,无法覆盖时事,且易产生幻觉——编造事实。上下文窗口限制进一步恶化问题,输入超限时会被截断。检索增强生成(RAG) 通过在生成过程中从外部源拉取相关上下文,显著降低错误并提供来源引用。

RAG 将文档、数据库或代码仓库连接起来,使响应可验证且实时更新。对于模型未训练过的细分领域尤为关键。

文档分块

分块是将文本拆分成片段(chunks)以便索引。LLM 只能处理有限数量的 chunks,因此分块质量直接决定事实检索效果。

Google AdInline article slot

大块增加噪声、提高成本并降低搜索精度。小块则破坏上下文,将相关概念割裂。常见策略:

  • 固定字符或 token 长度。
  • 语义边界(段落、标题)。
  • 重叠以保持连续性。

需根据语料库和查询经验调优参数。

RAG 工作流程阶段

RAG 涵盖预处理、检索和生成三个阶段:

Google AdInline article slot
  • 索引:将 chunks 转为向量并存入向量数据库。
  • 检索:查询向量化,提取 top 相关 chunks。
  • 增强:结果排序,去重并清理噪声。
  • 生成:将上下文注入查询,供 LLM 生成。

基础朴素 RAG 保持简洁。高级 RAG 增加查询重写和重排序。模块化 RAG 使用路由和结果融合等模块。

RAG 与微调对比

RAG 无需修改模型即可扩展知识访问,与需要数据和大量计算的微调不同。提示工程仅优化查询,无法引入外部数据。

| 方法 | 优势 | 劣势 |

Google AdInline article slot

|------|------|------|

| RAG | 数据新鲜、低成本、可引用来源 | 依赖检索质量 |

| 微调 | 深度定制 | 资源消耗大、易过时 |

RAG 适用于动态数据;微调适合静态领域。

最小 RAG 流水线:动手实践

我们用 Python 构建朴素 RAG,基于维基百科(500 篇文章)和 Qwen2.5-3B-Instruct。借助 LangChain、Chroma 和 sentence-transformers。

步骤 1:加载与分块

import torch
from datasets import load_dataset
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_core.documents import Document

device = "cuda" if torch.cuda.is_available() else "cpu"


dataset = load_dataset(
    "wikimedia/wikipedia",
    "20231101.ru",
    split="train[:500]"
)

documents = [
    Document(
        page_content=row["text"],
        metadata={"title": row["title"]}
    )
    for row in dataset
]

splitter = RecursiveCharacterTextSplitter(
    chunk_size=800,
    chunk_overlap=100
)

chunks = splitter.split_documents(documents)

print("Chunks:", len(chunks))

生成约 17,892 个 800 字符 chunks,重叠 100 字符。

步骤 2:向量索引

from langchain_community.vectorstores import Chroma
from langchain_community.embeddings import HuggingFaceEmbeddings

embedding_model = HuggingFaceEmbeddings(
    model_name="sentence-transformers/paraphrase-multilingual-MiniLM-L12-v2",
    model_kwargs={"device": device}
)

vectorstore = Chroma.from_documents(
    documents=chunks,
    embedding=embedding_model
)

使用多语言 MiniLM 嵌入,Chroma 存储。

步骤 3:检索

import textwrap

def search(query, k=3, width=80):
    results = vectorstore.similarity_search(query, k=k)

    for i, doc in enumerate(results, 1):
        print(f"\n--- Result {i} ---")
        print("Title:", doc.metadata["title"])
        print(textwrap.fill(doc.page_content[:1000], width))

search("Who is Pushkin?")

从普希金相关文章中提取片段。

步骤 4:生成

添加 LLM 从检索 chunks 生成(代码省略,完整版见源码)。

关键要点

  • RAG 通过外部上下文减少幻觉,无需调整模型。
  • 成功关键在于分块和嵌入:针对领域定制。
  • 从朴素 RAG 开始,生产环境扩展到高级/模块化。
  • LangChain 简化原型开发。
  • 用真实数据集测试优化。

— Editorial Team

Advertisement 728x90

继续阅读