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

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

一、为什么需要 RAG?

LLM 有三大局限:

  1. 知识陈旧:训练数据有截止日期
  1. 不懂私域:不知道你的企业文档、产品手册
  1. 会胡说:对不知道的内容会编造
RAG 的解决方案:先用检索找到相关文档,再让 LLM 基于这些文档回答。

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

二、核心架构

\\\`python

最简化的 RAG 流程

def rag(query, documents): # 1. 检索:找最相关的 k 个文档 relevantdocs = retrieve(query, documents, k=3) # 2. 构造 prompt context = "\n\n".join(relevantdocs) 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, 多语言)
\\\`python 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 | 国产、生产级、性能强 | | Qdrant | Rust 写、性能好 | | pgvector | PostgreSQL 插件,运维简单 | | Pinecone | SaaS,托管方便 |

\\\`python import chromadb client = chromadb.PersistentClient(path="./chromadb") collection = client.getorcreatecollection("docs")

存文档

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

检索

results = collection.query( querytexts=["怎么读文件"], nresults=2 ) \\\`

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

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

4.1 切片策略

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

4.2 推荐实践

\\\`python from langchain.textsplitter import RecursiveCharacterTextSplitter

splitter = RecursiveCharacterTextSplitter( chunksize=500, # 每片字符数 chunkoverlap=50, # 重叠字符(避免切断) separators=["\n\n", "\n", "。", "!", "?", " ", ""] ) chunks = splitter.splittext(longdocument) \\\`

五、Prompt 工程

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

5.1 经典 RAG prompt 模板

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

规则:

  1. 只使用参考资料中的信息回答
  1. 如果参考资料中找不到答案,明确说"我不知道"
  1. 回答简洁,引用参考资料编号
  1. 不要编造信息
参考资料: [1] {doc1} [2] {doc2} [3] {doc3}

用户问题:{query} \\\`

5.2 进阶:HyDE

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

六、生产环境最佳实践

6.1 检索质量评估

\\\`python

用 GPT-4 评估检索质量

def eval
retrieval(query, retrieveddocs, groundtruth): prompt = f"""判断以下文档是否与问题相关: 问题:{query} 文档:{retrieveddocs} 相关:是 / 否""" return llm(prompt) \\\`

6.2 缓存

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

6.3 监控

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

七、完整代码示例

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

\\\`python from langchain.documentloaders import DirectoryLoader from langchain.textsplitter import RecursiveCharacterTextSplitter from langchain.embeddings import OpenAIEmbeddings from langchain.vectorstores import Chroma from langchain.chains import RetrievalQA from langchain.chatmodels import ChatOpenAI

1. 加载文档

loader = DirectoryLoader("./docs", glob="/.md") docs = loader.load()

2. 切片

splitter = RecursiveCharacterTextSplitter(chunksize=500, chunkoverlap=50) chunks = splitter.splitdocuments(docs)

3. 存向量

vectordb = Chroma.from
documents(chunks, OpenAIEmbeddings(), persistdirectory="./chroma")

4. 问答

qa = RetrievalQA.from
chaintype( llm=ChatOpenAI(model="gpt-4o"), retriever=vectordb.asretriever(k=3) ) print(qa.run("什么是 RAG?")) \\\`

八、进阶方向

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