一、大模型的不足,RAG 的破局点

大模型(LLMs)虽能力强劲,但仍存在明显短板:

  • 幻觉问题:因生成文本基于概率的 token by token 形式,易一本正经地 “胡说八道”。
  • 时效性问题:大模型训练成本高、周期长,难以及时纳入新数据,无法直接回答 “推荐几部热映电影” 这类时效性问题。
  • 数据安全问题:通用大模型缺乏企业内部数据,若要安全使用,企业需将数据本地部署,大模型仅能做归纳,限制了其在企业场景的深度应用。

在此背景下,检索增强生成(RAG)技术应运而生。

二、RAG 是什么?

  RAG(Retrieval Augmented Generation,检索增强生成),指大模型在回答问题或生成文本时,先从大量文档中检索相关信息,再基于这些信息生成内容,以此提升预测质量。其核心流程包含 ** 检索(Retrieval)与生成(Generation)** 两大模块,通过外部数据存储(如向量数据库、特征存储等),为问答系统(Q/A System)提供检索到的上下文数据,再由大模型(LLM)结合这些数据生成回答。

三、RAG 的核心模块

(一)检索器模块(R)

检索是 RAG 的关键环节,需从海量知识库中检索最相关的前 k 个文档,构建高质量检索器颇具挑战,涉及多个关键问题。

1. 语义表示的准确性

  语义空间是查询和文档被映射的多维空间,构建准确语义空间有多种方法。处理外部文档时,先进行分块以获取更细致特征,再将这些文档块嵌入语义空间。分块策略需考虑被索引内容特点、嵌入模型及最适块大小、用户查询预期长度与复杂度等。实际上,准确的查询结果是通过灵活应用多种分块策略来实现的,无最佳策略,只有最适合的策略。确定块大小后,借助 UAE、Voyage、BGE 等出色的嵌入模型,将文档块和查询嵌入语义空间,这些模型在大规模语料库上预训练过,能较好捕捉语义信息。

2. 查询与文档语义空间的协调

   在 RAG 应用中,检索器有用同一嵌入模型或两个不同模型处理查询和文档的情况。且用户原始查询可能表达不清或语义信息不足,协调两者语义空间至关重要。可通过查询重写优化,利用大语言模型生成指导性伪文档,结合原始查询形成新查询;或通过文本标识符建立查询向量,生成 “假想文档” 捕捉相关模式。还可采用多查询检索方法,让大语言模型同时产生多个搜索查询并行执行,聚合结果,适用于复杂问题。此外,嵌入变换技术也有帮助,如 LlamaIndex 在查询编码器后加特殊适配器并微调,优化查询嵌入表示;SANTA 方法利用结构化与非结构化数据的自然对应关系对比学习,或采用围绕实体的掩码策略,让语言模型预测和填补被掩盖的实体信息,助力检索系统理解结构化信息。

3. 检索模型与大语言模型的对齐

    即便检索模型命中率高,若检索到的文档不符合大语言模型需求,仍无法改善 RAG 最终效果。可通过大语言模型监督训练实现对齐,如 REPLUG 使用检索模型和大语言模型计算检索文档的概率分布,通过 KL 散度监督训练,利用大语言模型作为监督信号提升检索模型性能;也可在检索模型外部附加适配器实现对齐,应对微调嵌入模型可能面临的 API 限制或计算资源不足等挑战;还可通过指令微调将知识注入白盒模型,直接替换检索模块,根据查询输出相关文档。

(二)生成器模块(G)

    生成是 RAG 的核心部分之一,负责将检索到的信息转化为自然流畅的文本。其输入不仅有传统上下文信息,还有检索到的相关文本片段,这让生成组件能更深入理解问题背景,生成更丰富准确的回答,且能指导内容生成,确保与检索信息一致。为优化生成,需进行后检索处理,在检索到相关信息后,对其进行压缩、重新排序等操作,提升检索结果质量,更好满足用户需求或为后续任务准备。同时,生成器的优化要确保生成文本既流畅又能有效利用检索文档,更好回应用户查询。RAG 中生成器的微调方式与大语言模型的普通微调方法大体相同,包括通用优化过程和运用对比学习等。

四、使用 RAG 的好处

RAG 为大模型应用带来诸多优势。

  • 可扩展性:开发者无需为每个特定任务重新训练整个大模型,只需外挂知识库,就能为模型提供额外信息输入,提高回答准确性,还能减少模型大小和训练成本,轻松扩展知识。
  • 准确性:通过引用信息来源,用户可核实答案准确性,增强对模型输出结果的信任。
  • 可控性:允许更新或定制知识。
  • 可解释性:检索到的项目可作为模型预测来源的参考。
  • 多功能性:能针对 QA、文本摘要、对话系统等多种任务进行微调定制。
  • 时效性:利用检索技术能识别最新信息,在保持回答及时性和准确性上,比只依赖训练数据的传统语言模型更具优势。
  • 定制性:通过索引特定领域相关文本语料库,能为不同领域提供专业知识支持。
  • 安全性:通过数据库中设置的角色和安全控制,能更好控制数据使用,相比微调模型,在数据访问权限管理上更清晰。

五、RAG 与 SFT 的对比

SFT(有监督微调)是解决大模型问题的常见基础方法,但与 RAG 存在诸多差异。

对比维度 RAG SFT
数据 动态数据,不断查询外部源,确保信息保持最新,无需频繁重训模型 相对静态数据,在动态数据场景易过时,且难记住这些知识
外部知识利用 擅长利用外部资源,通过在生成响应前从知识库检索相关信息来增强能力,适合文档或非结构化 / 半结构化数据库 可微调大模型对齐预训练学到的外部知识,但对于频繁更改的数据源实用性低
模型定制化 关注信息检索与外部知识整合,难完全定制模型行为或写作风格 允许根据特定语气、写作风格或领域知识调整大模型行为
减少幻觉 因回答基于检索到的证据,不易产生幻觉 可通过特定领域训练数据减少幻觉,但面对不熟悉输入仍可能产生
透明度 将响应生成分解为不同阶段,提供数据检索匹配度,提升输出信任度 推理过程更像 “黑匣子”,透明度低
技术专业性 需要高效检索策略和大型数据库相关技术,还需维护外部数据源和数据更新 需要准备和整理高质量训练数据集、定义微调目标及相应计算资源

当然,两者并非非此即彼,合理方式是结合业务需求,发挥各自优点。

六、RAG 的典型实现方法

RAG 的实现主要包括数据索引、检索和生成三个主要步骤。

(一)数据索引构建

数据索引是离线过程,主要将私域数据向量化后构建索引并存入数据库,包含数据提取、文本分割、向量化及创建索引等环节。

1. 数据提取

   是把原始数据处理成格式化数据的过程,涵盖多格式数据(如 PDF、word、markdown 及数据库和 API 等)的加载、不同数据源获取,根据数据情况统一格式。对于 Doc 文档,直接解析文本元素(如标题、表格、段落等)并存储相关属性,为后续切分提供依据;PDF 文档解析难点在于完整恢复图片、表格、标题、段落等内容,可使用多个开源模型协同分析,如百度的 PP-StructureV2 检测多个区域并统一 OCR 和文本属性分类任务;PPT 文档则先转换成 PDF 形式,再用 PDF 解析方法处理。同时,要对源数据去重、过滤、压缩和格式化等清洗操作,还需提取文件名、时间、章节 title、图片等关键信息。

2. 文本分割(Chunking)

   因文本可能较长或仅部分内容相关,需对文本分块切分,要考虑嵌入模型的 Tokens 限制和语义完整性对检索效果的影响。分块方式多样,句分割以 “句” 为粒度,保留句子完整语义,常见切分符有句号、感叹号等;固定大小分块根据嵌入模型的 token 长度限制,将文本分割为固定长度(如 256/512 个 tokens),会损失部分语义信息,通常在头尾增加冗余量缓解;基于意图的分块有句分割(通过句号和换行切分,常用 NLTK 和 Spacy 等工具)和递归分(通过分而治之思想递归切分到最小单元),还有用于特殊场景的特殊分割。常用工具如 langchain.text_splitter 库中的 CharacterTextSplitter,可指定分隔符、块大小、重叠和长度函数拆分文本。以下是使用 CharacterTextSplitter 进行文本分割的代码示例:

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(
    separator="\n\n",
    chunk_size=500,
    chunk_overlap=50,
    length_function=len,
)
texts = text_splitter.split_text("长文本内容...")
3. 向量化与创建索引

    向量化是将文本、图像、音频和视频等转化为向量矩阵,让计算机能理解的过程,常见嵌入模型有 ChatGPT-Embedding、ERNIE-Embedding V1、M3E、BGE 等。创建索引是将向量化后的数据构建索引并写入数据库的过程,常用工具有 FAISS、ChromaDB、ES、milvus 等,需结合业务场景、硬件、性能需求等综合选择合适数据库。以下是使用 ChromaDB 创建向量索引的代码示例:

from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma

embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_texts(
    texts=texts,
    embedding=embeddings,
    persist_directory="./chroma_db"
)
vector_store.persist()

(二)数据检索(Retrieval)

    检索是获取有效信息的关键环节。可通过元数据过滤,在文档分块多、检索效率低时,先过滤元数据提升效率和相关度;引入知识图谱(图关系检索),将实体变成 node,关系变成 relation,利用知识间关系使回答更准确,尤其适合多跳问题;检索技术主要有向量化相似度检索(计算欧氏距离、曼哈顿距离、余弦等相似度)、关键词检索(传统方式,可先做文档摘要再关键词检索提升效率)、全文检索、SQL 检索等。此外,还可进行重排序,根据相关度、匹配度等重新调整检索结果顺序;采用查询轮换策略,如子查询(在不同场景用 LlamaIndex 等框架提供的查询器,采用树查询或向量查询等)、HyDE(生成相似或更标准的 prompt 模板)等。以下是从 ChromaDB 中检索相关文档的代码示例:

from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings

embeddings = OpenAIEmbeddings()
vector_store = Chroma(
    persist_directory="./chroma_db",
    embedding_function=embeddings
)
retriever = vector_store.as_retriever()
docs = retriever.get_relevant_documents("查询内容")

(三)生成正确回复

      文本生成是将原始查询和检索到的文本结合输入模型得到结果的过程,本质是提示工程(prompt engineering)。也可利用 Langchain 和 LlamaIndex 等全流程框架,如使用 Langchain 调用 ChatOpenAI 模型,构建包含检索到的上下文(context)和问题(question)的链(rag_chain),调用大模型生成回答。以下是使用 Langchain 构建 RAG 链的代码示例:

from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate

llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0)
prompt_template = """使用以下上下文回答问题。
上下文:{context}
问题:{question}
回答:"""
PROMPT = PromptTemplate(
    template=prompt_template,
    input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
    llm=llm,
    chain_type="stuff",
    retriever=retriever,
    chain_type_kwargs={"prompt": PROMPT}
)
result = qa_chain({"query": "问题内容"})
print(result["result"])

七、RAG 的典型案例

   以 ChatPDF 及其复刻版为例,其实现流程清晰展现 RAG 的应用。首先读取 PDF 文件,转换为 txt 等可处理的文本格式;接着对提取的文本清理和标准化,如去除特殊字符、分段分句,便于后续自然语言处理;然后使用 OpenAI 的 Embeddings API 将每个分段转换为向量,编码文本语义;当用户提出问题,同样用 OpenAI 的 Embeddings API 将问题转换为向量,与每个分段向量比较,找到最相似分段;再将最相似分段与问题作为 prompt,调用 OpenAI 的 Completion API,让 ChatGPT 学习分段内容后回答问题;最后将生成的答案返回给用户,完成一次查询。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐