一、RAG 是什么?给你的大模型外接一个“图书馆”

检索增强生成,英文是 Retrieval-Augmented Generation (RAG),是一种将外部知识库检索大语言模型的生成能力相结合的技术架构。简单来说,它就像给一个记忆力超强但有时会“记岔”或知识过时的大脑(LLM)配了一个随叫随到的、最新的图书馆。当模型需要回答问题时,它先去图书馆(知识库)查找相关资料,然后基于找到的资料(检索结果)和自己的理解,生成最终答案。

为什么要这么麻烦?因为纯依赖大模型内部参数(即模型权重)进行生成的方式存在几个固有问题:知识滞后(模型训练数据有截止日期)、幻觉(一本正经地编造事实)、以及无法访问私有或特定领域数据(企业内部文档、最新论文等)。RAG 通过“检索”这一步,动态地为模型注入了最新、最相关、最可靠的信息,从而显著提升回答的准确性、时效性和可解释性。

核心思想:不要只依赖模型的“记忆”,而是让它在回答前先“查资料”。检索结果(Context)与原始问题(Query)一起,构成提示词(Prompt)输入给生成模型,引导其生成有据可依的回答。

二、为什么需要 RAG?它解决了什么痛点?

RAG 的价值在于它直击了纯大语言模型(LLM)的几个核心痛点。首先,它有效缓解了大语言模型幻觉问题。当模型的回答基于其检索到的真实文档片段时,它“胡说八道”的概率会大大降低,因为答案有迹可循。其次,它解决了知识时效性难题。你无需频繁地对庞大的模型进行昂贵的微调(Fine-tuning),只需更新外部知识库(比如每天新增的新闻、产品文档),模型就能立刻获取最新信息。

此外,RAG 让构建领域专家或私有知识助手变得非常实用和高效。企业可以将内部的规章制度、产品手册、技术文档等构建为知识库,让通用大模型在 RAG 的加持下,瞬间变身成精通自家业务的专家,且整个过程的数据都保留在内部,安全可控。

三、RAG 的核心工作流程解析

一个典型的 RAG 流程可以清晰地分为三个主要阶段:索引检索生成

  1. 索引:这是离线准备阶段。将你的原始文档(PDF、网页、数据库记录等)进行处理,包括清洗、切片(Chunking),然后使用一个 Embedding 模型(如 text-embedding-ada-002, sentence-transformers)将文本片段转换为高维向量(Embedding)。最后,将这些向量连同原始文本一起存入一个向量数据库(如 Chroma, FAISS, Pinecone)中,建立索引。
  2. 检索:当用户提出一个问题时,首先用同样的 Embedding 模型将问题也转换成向量。然后,在向量数据库中执行相似性搜索(如余弦相似度),找到与问题向量最相关的 Top-K 个文本片段。这些片段就是模型生成答案时需要参考的“资料”。
  3. 生成:将检索到的相关文本片段(作为上下文 Context)与用户原始问题一起,按照特定的提示词模板(Prompt Template)组装成一个完整的提示(Prompt)。最后,将这个增强后的提示输入给 大语言模型(如 GPT-3.5, Llama 2),由它基于这些证据生成最终的回答。
提示:整个流程中,检索的质量是决定最终答案好坏的关键。不相关的文档片段可能会误导模型,比没有更糟糕。

四、构建你的第一个检索器:从文档到向量

我们以 Python 为例,简要演示如何将一段文本转化为可用于检索的向量,并构建一个简单的检索器。这里我们使用轻量级的 sentence-transformers 库和内存向量库。

from sentence_transformers import SentenceTransformer
import numpy as np

# 1. 加载一个预训练的 Embedding 模型
model = SentenceTransformer('all-MiniLM-L6-v2')

# 2. 准备你的“知识库”文档(这里简化为一个列表)
documents = [
    "RAG的核心是结合检索和生成,提高回答准确性。",
    "向量数据库用于高效存储和检索文本嵌入向量。",
    "文档需要先切片成合适的大小再生成向量。",
    "提示工程在RAG中至关重要,用于组织检索结果。"
]

# 3. 将文档转换为向量
doc_embeddings = model.encode(documents)
print(f"文档向量维度: {doc_embeddings.shape}") # 输出例如 (4, 384)

# 4. 定义一个简单的检索函数(使用内积作为相似度)
def retrieve(query, top_k=2):
    query_embedding = model.encode([query])
    # 计算查询向量与所有文档向量的相似度(点积)
    similarities = np.dot(doc_embeddings, query_embedding.T).flatten()
    # 获取最相似文档的索引
    top_indices = similarities.argsort()[-top_k:][::-1]
    return [documents[i] for i in top_indices]

# 5. 测试检索
query = "RAG如何提升答案质量?"
relevant_docs = retrieve(query)
print("检索到的相关文档:", relevant_docs)

这段代码展示了最基础的检索逻辑。在实际生产环境中,你需要使用持久化的向量数据库(如 ChromaDBQdrant),并处理更大规模的文档,包括更精细的切片策略(如固定大小切片、递归字符切片)和元数据过滤。

五、集成生成器:让大模型基于证据说话

检索到了相关文档,下一步就是让大语言模型基于这些“证据”来生成答案。关键在于设计一个好的提示模板Prompt Template),清晰地指示模型它的任务和参考内容。

一个常见的提示模板结构如下:

请根据以下参考资料回答用户问题。如果参考资料中没有相关信息,请明确说明你不知道。
参考资料:
{context}
用户问题:{question}
你的回答:

在 Python 中,你可以这样集成(假设使用 OpenAI API):

import openai
# 假设已从上一步检索到 relevant_docs
context = "\n".join(relevant_docs)
prompt = f"""请根据以下参考资料回答用户问题。如果参考资料中没有相关信息,请明确说明你不知道。
参考资料:
{context}
用户问题:RAG如何提升答案质量?
你的回答:"""

response = openai.ChatCompletion.create(
    model="gpt-3.5-turbo",
    messages=[
        {"role": "system", "content": "你是一个乐于助人的AI助手。"},
        {"role": "user", "content": prompt}
    ],
    temperature=0.7 # 可调整创造性,较低值答案更稳定
)
print(response.choices[0].message.content)
提示temperature 参数在 RAG 场景中建议设置得较低(如 0.1 - 0.5),因为我们需要模型忠实于检索到的资料,而不是自由发挥。

六、进阶思考与常见陷阱

在实践中构建高质量的 RAG 系统,远不止连接“检索”和“生成”那么简单。你会遇到许多挑战,需要不断调优。

最重要的实践原则迭代优化。RAG 系统是一个 pipeline,每个环节(切片、嵌入、检索、提示、生成)都可能成为瓶颈。从一个简单的原型开始,用真实问题测试,分析 bad case,然后有针对性地优化其中一个环节,这是最有效的方法。