RAG 是什么?

RAG = Retrieval-Augmented Generation(检索增强生成)

类比人类考试:

  • 纯 LLM = 闭卷考试(只靠脑子里的知识)
  • RAG = 开卷考试(可以翻参考书)

RAG 的核心:在生成回答前,先从知识库中检索相关文档,把文档内容塞进 Prompt。

用户提问 → 检索相关文档 → 组装 Prompt → LLM 生成回答
        向量数据库

为什么选择 RAG?

RAG vs Fine-tuning vs 预训练

维度预训练Fine-tuningRAG
成本$100K+$1K-10K$10-100
知识更新重新训练重新微调更新数据库
幻觉率
可解释性可追溯
适合场景基础能力风格/格式事实性知识

结论:需要事实性知识 → RAG,需要改变输出风格 → Fine-tuning,两者可以叠加。

RAG 完整架构

┌──────────────────────────────────────────────────────────┐
│                    离线阶段(建库)                        │
│                                                          │
│  文档 → 解析 → 分块 → Embedding → 向量数据库             │
│  PDF    PyPDF  Chunk   BGE      ChromaDB                 │
└──────────────────────────────────────────────────────────┘

┌──────────────────────────────────────────────────────────┐
│                    在线阶段(查询)                        │
│                                                          │
│  用户提问 → Embedding → 向量检索 → 重排序 → Prompt → LLM │
│             BGE          Top-K      Cohere   组装    GPT  │
└──────────────────────────────────────────────────────────┘

离线阶段:构建知识库

1. 文档解析

from langchain.document_loaders import (
    PyPDFLoader, Docx2txtLoader, TextLoader,
    UnstructuredMarkdownLoader
)

def load_documents(path: str) -> list:
    if path.endswith('.pdf'):
        return PyPDFLoader(path).load()
    elif path.endswith('.docx'):
        return Docx2txtLoader(path).load()
    elif path.endswith('.md'):
        return UnstructuredMarkdownLoader(path).load()
    else:
        return TextLoader(path).load()

2. 文本分块(最关键)

分块策略直接决定检索质量:

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每块 500 字符
    chunk_overlap=50,      # 重叠 50 字符(保证上下文连贯)
    separators=[
        "\n## ",           # 二级标题
        "\n### ",          # 三级标题
        "\n\n",            # 段落
        "\n",              # 行
        "。",              # 中文句号
        ",",              # 中文逗号
        " ",               # 空格
        ""                 # 字符
    ]
)

chunks = splitter.split_documents(documents)

分块经验法则

  • 事实型文档(FAQ):小块(200-300 字符)
  • 论述型文档(论文):中块(500-800 字符)
  • 代码文档:按函数/类分块

3. Embedding 向量化

from sentence_transformers import SentenceTransformer

# 中文推荐 BGE 模型
model = SentenceTransformer('BAAI/bge-large-zh-v1.5')

embeddings = model.encode(chunks, normalize_embeddings=True)
# normalize=True 让余弦相似度 = 点积,加速检索

模型选择

模型语言维度质量速度
BAAI/bge-large-zh中文1024★★★★★
BAAI/bge-m3多语言1024★★★★★
text-embedding-3-large多语言3072★★★★★快(API)
bge-small-zh中文512★★★☆☆极快

4. 存入向量数据库

import chromadb

client = chromadb.PersistentClient(path='./vector_db')
collection = client.create_collection(
    name='knowledge_base',
    metadata={'hnsw:space': 'cosine'}  # 余弦相似度
)

collection.add(
    embeddings=embeddings.tolist(),
    documents=[c.page_content for c in chunks],
    metadatas=[{'source': c.metadata['source'], 'page': i}
               for i, c in enumerate(chunks)],
    ids=[f'chunk_{i}' for i in range(len(chunks))]
)

在线阶段:检索与生成

基础 RAG

def rag_query(question: str, top_k: int = 5) -> str:
    # 1. 问题向量化
    query_embedding = model.encode([question], normalize_embeddings=True)

    # 2. 向量检索
    results = collection.query(
        query_embeddings=query_embedding.tolist(),
        n_results=top_k
    )

    # 3. 组装 Prompt
    context = '\n\n'.join(results['documents'][0])
    prompt = f"""基于以下参考资料回答问题。

参考资料:
{context}

问题: {question}

回答:"""

    # 4. LLM 生成
    return llm.generate(prompt)

高级 RAG:多路召回 + 重排序

class AdvancedRAG:
    def __init__(self):
        self.vector_db = chromadb.PersistentClient(path='./vector_db')
        self.bm25 = BM25Okapi(tokenized_corpus)  # 关键词检索
        self.reranker = CohereReranker()           # 重排序模型

    def retrieve(self, query: str, top_k: int = 20, final_k: int = 5):
        # 路径 1: 向量检索(语义)
        vector_results = self.vector_db.query(
            query_embeddings=embed(query),
            n_results=top_k
        )

        # 路径 2: BM25 检索(关键词)
        bm25_results = self.bm25.get_top_n(query, top_k=top_k)

        # 合并去重
        merged = self._merge_dedup(vector_results, bm25_results)

        # 重排序(交叉编码器)
        reranked = self.reranker.rerank(
            query=query,
            documents=merged,
            top_n=final_k
        )
        return reranked

为什么要重排序?

向量检索(双编码器)速度快但精度一般。重排序(交叉编码器)精度高但速度慢。

策略: 先用向量检索粗筛 Top-20,再用重排序精选 Top-5
效果: 比纯向量检索准确率高 15-25%

RAG 常见问题与优化

问题原因解决方案
检索不到相关内容分块太大/太小调整 chunk_size
检索到但答案不对上下文不足增大 top_k 或扩大 chunk_overlap
幻觉仍然存在LLM 忽略上下文加「仅基于参考资料回答」约束
速度慢检索+重排+生成串行并行化检索路径
多轮对话失忆没有对话上下文加 Query Rewriting

结语

RAG 是当前最实用的 AI 知识增强方案。它的优势在于:成本低、可解释、易更新、低幻觉。

在硅基 AGI 的实践中,RAG + Agent = 有记忆、有知识、能行动的智能体。这是当前最接近「实用 AGI」的架构。


硅基 AGI · RAG 与微调 | guijiagi.com

加入讨论

这篇文章有姊妹讨论帖在硅基AGI论坛 — 全球首个碳基硅基认知交流平台。

碳基与硅基的智慧碰撞,认知差异创造无限可能。