RAG 入门到精通:构建你的第一个智能问答系统

RAG(Retrieval-Augmented Generation)是当下最实用的 LLM 应用范式。它让 LLM 能够基于你的私有知识回答问题,无需重新训练模型。

一、为什么需要 RAG?

LLM 有三大局限:

  1. 知识陈旧:训练数据有截止日期
  2. 不懂私域:不知道你的企业文档、产品手册
  3. 会胡说:对不知道的内容会编造

RAG 的解决方案:先用检索找到相关文档,再让 LLM 基于这些文档回答。

用户问题 → 检索相关文档 → 拼接 prompt → LLM → 答案

二、核心架构

# 最简化的 RAG 流程
def rag(query, documents):
    # 1. 检索:找最相关的 k 个文档
    relevant_docs = retrieve(query, documents, k=3)
    
    # 2. 构造 prompt
    context = "\n\n".join(relevant_docs)
    prompt = f"""基于以下资料回答用户问题。如果资料中没有,请说"我不知道"。

资料:
{context}

问题:{query}
"""
    
    # 3. 调用 LLM
    return llm(prompt)

看起来简单,但每个环节都有坑。

三、检索的关键技术

3.1 Embedding 模型

把文本变成向量(一串数字),用向量相似度找相关文档。 常用模型:
  • text-embedding-3-small (OpenAI, 1536 维, $0.02/百万 token)
  • bge-large-zh-v1.5 (BAAI, 中文优秀, 开源)
  • text-embedding-v3 (Cohere, 多语言)
from openai import OpenAI

client = OpenAI()
response = client.embeddings.create(
    model="text-embedding-3-small",
    input="Python 怎么读取文件?"
)
vector = response.data[0].embedding  # 1536 维向量

3.2 向量数据库

存向量、做相似度搜索。
数据库特点
Chroma轻量、嵌入式、易上手
Milvus国产、生产级、性能强
QdrantRust 写、性能好
pgvectorPostgreSQL 插件,运维简单
PineconeSaaS,托管方便
import chromadb
client = chromadb.PersistentClient(path="./chroma_db")
collection = client.get_or_create_collection("docs")

# 存文档
collection.add(
    documents=["Python 用 open() 读文件...", "Java 用 FileReader..."],
    ids=["doc1", "doc2"]
)

# 检索
results = collection.query(
    query_texts=["怎么读文件"],
    n_results=2
)

四、Chunking:最容易被忽略的关键

文档不可能整篇塞给 LLM(超出 token 限制),必须切片。

4.1 切片策略

策略适用缺点
固定长度快速实验切断语义
按段落一般文档长段落仍超限
按语义高质量场景慢
按 Markdown 标题结构化文档不适用非 MD
滑动窗口长文档信息重复

4.2 推荐实践

from langchain.text_splitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter(
    chunk_size=500,        # 每片字符数
    chunk_overlap=50,      # 重叠字符(避免切断)
    separators=["\n\n", "\n", "。", "!", "?", " ", ""]
)
chunks = splitter.split_text(long_document)

五、Prompt 工程

检索出来的内容不会自动成为好答案,需要 prompt 包装。

5.1 经典 RAG prompt 模板

你是 {role},专注于回答关于 {domain} 的问题。

规则:
1. 只使用参考资料中的信息回答
2. 如果参考资料中找不到答案,明确说"我不知道"
3. 回答简洁,引用参考资料编号
4. 不要编造信息

参考资料:
[1] {doc1}
[2] {doc2}
[3] {doc3}

用户问题:{query}

5.2 进阶:HyDE

让 LLM 先生成假设性答案,再拿假设答案去检索,比直接用原始问题检索效果好。

六、生产环境最佳实践

6.1 检索质量评估

# 用 GPT-4 评估检索质量
def eval_retrieval(query, retrieved_docs, ground_truth):
    prompt = f"""判断以下文档是否与问题相关:
问题:{query}
文档:{retrieved_docs}
相关:是 / 否"""
    return llm(prompt)

6.2 缓存

相同问题直接返回缓存答案,节省 80% 调用成本。

6.3 监控

记录每次调用:
  • 检索耗时 / LLM 耗时
  • 检索命中率
  • 用户反馈(点赞 / 点踩)

七、完整代码示例

用 LangChain 30 行代码搭建一个 RAG:

from langchain.document_loaders import DirectoryLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Chroma
from langchain.chains import RetrievalQA
from langchain.chat_models import ChatOpenAI

# 1. 加载文档
loader = DirectoryLoader("./docs", glob="**/*.md")
docs = loader.load()

# 2. 切片
splitter = RecursiveCharacterTextSplitter(chunk_size=500, chunk_overlap=50)
chunks = splitter.split_documents(docs)

# 3. 存向量
vectordb = Chroma.from_documents(chunks, OpenAIEmbeddings(), persist_directory="./chroma")

# 4. 问答
qa = RetrievalQA.from_chain_type(
    llm=ChatOpenAI(model="gpt-4o"),
    retriever=vectordb.as_retriever(k=3)
)
print(qa.run("什么是 RAG?"))

八、进阶方向

  • Agentic RAG:让 LLM 自己决定何时检索、检索什么
  • 多模态 RAG:检索图片、表格、PDF
  • GraphRAG:用知识图谱增强检索
  • Self-RAG:让 LLM 评估自己答案是否需要再检索
RAG 是当下 LLM 应用最成熟的范式,值得投入时间深入学习。