检索增强生成(RAG)是构建基于真实数据的 LLM 应用的主流架构。RAG 不依赖模型训练时的记忆,而是在查询时从知识库检索相关文档并作为上下文传递给 LLM。本指南涵盖完整的 RAG 流水线 — 从文档加载、向量化、向量检索到高级策略、RAGAS 评估和生产部署,包含 LangChain + OpenAI 的 Python 代码示例。
要点概括:RAG = 检索相关文档 + 增强提示词 + 生成有依据的答案。消除幻觉、保持实时性、避免昂贵的微调。流程:加载文档、分块、用 text-embedding-3-small 嵌入、存入向量数据库(Pinecone、Qdrant、pgvector),查询时检索 top-k 结果传给 LLM。高级技术包括 HyDE、父子分块、混合搜索和重排序。用 RAGAS 评估(忠实度、相关性、上下文召回率)。
核心要点
- RAG 将 LLM 回答锚定在真实数据上,与纯提示相比可减少 80-95% 的幻觉。
- 根据内容选择分块大小:Q&A 用 512 tokens,摘要用 1024 tokens,保持 10-20% 重叠。
- text-embedding-3-small 以 $0.02/百万 tokens 的价格提供最优性价比。
- 混合搜索(稠密向量 + 稀疏 BM25)始终优于纯向量相似度搜索。
- 上线前必须用 RAGAS 评估 — 测量忠实度、答案相关性和上下文召回率。
- 初始检索后用交叉编码器重排序可显著提升答案质量,延迟成本极小。
什么是 RAG,为什么重要
大语言模型在静态数据集上训练,有知识截止日期。它们无法访问您的内部文档、最新 API 文档或实时数据。当被问到训练数据之外的信息时,它们要么拒绝回答,要么更糟 — 产生幻觉,给出听起来自信但实际错误的答案。
RAG 通过在生成之前引入检索步骤来解决这个问题。在查询时,系统在知识库中搜索相关文档,将其注入提示作为上下文,并指示 LLM 基于该上下文回答。LLM 成为数据上的推理引擎,而非记忆引擎。
2020 年 Meta 的 Lewis 等人首次提出 RAG,目前已成为企业 AI 应用、客服机器人、文档助手等需要事实性可验证答案的系统的标准架构。
RAG vs 微调 vs 提示工程
在构建 RAG 系统之前,了解何时使用哪种方法:
| 标准 | 提示工程 | RAG | 微调 |
|---|---|---|---|
| 知识更新 | 手动修改提示 | 更新文档,重新嵌入 | 重新训练模型 |
| 成本 | 低 | 中等(向量库+嵌入) | 高(GPU 训练) |
| 延迟 | 最低 | +100-300ms 检索 | 与基础模型相同 |
| 领域数据准确性 | 低 | 高 | 高 |
| 幻觉控制 | 差 | 优秀(可引用) | 中等 |
| 数据时效性 | 静态 | 可实时 | 训练后过时 |
| 适用场景 | 简单任务、格式化 | 知识密集型应用 | 风格/行为调整 |
实际上,大多数生产系统三者结合:提示工程控制格式,RAG 提供知识,微调调整专业行为。但如果你在构建知识密集型应用,从 RAG 开始 — 它以最小投入带来最大收益。
RAG 架构:完整流水线
RAG 系统有两个主要阶段:离线索引流水线处理文档,在线查询流水线处理用户问题。以下是完整架构:
RAG Architecture Overview
========================
INDEXING PIPELINE (Offline)
--------------------------
[Documents] [Text Splitter] [Embedding] [Vector Store]
PDF, HTML --> Chunk into --> Convert to --> Store in
Markdown 256-512 tokens dense vectors Pinecone/
Database with overlap (1536-dim) Qdrant/PG
QUERY PIPELINE (Online)
-----------------------
[User Query] [Embed Query] [Retrieve] [Re-rank] [Generate]
"How do I --> Convert to --> Top-k --> Cross- --> LLM with
deploy?" query vector similar encoder context
chunks scoring + prompt
| | |
v v v
Same embedding Filter & Grounded
model as indexing compress answer with
context citations索引流水线运行一次(或定时运行)处理文档。查询流水线对每个用户问题运行,端到端通常在 1-3 秒内完成。
提示: 索引流水线是一次性或定期批处理任务,可以在后台运行。查询流水线是延迟敏感的关键路径 — 这里每毫秒都重要。优化重点应放在查询流水线上。
第一步:文档加载
第一步是将源文档加载为流水线可处理的格式。LangChain 提供了几乎所有格式的加载器:
from langchain_community.document_loaders import (
PyPDFLoader,
UnstructuredHTMLLoader,
UnstructuredMarkdownLoader,
TextLoader,
CSVLoader,
DirectoryLoader,
WebBaseLoader,
)
# Load a single PDF
pdf_docs = PyPDFLoader("report.pdf").load()
# Load all markdown files from a directory
md_docs = DirectoryLoader(
"./docs/", glob="**/*.md",
loader_cls=UnstructuredMarkdownLoader
).load()
# Load a web page
web_docs = WebBaseLoader("https://docs.example.com/api").load()
# Load CSV data
csv_docs = CSVLoader("products.csv").load()
print(f"Loaded {len(pdf_docs)} pages from PDF")
print(f"First doc metadata: {pdf_docs[0].metadata}")每个加载器返回 Document 对象列表,包含 page_content(文本)和 metadata(来源、页码等)。元数据对引用至关重要 — 务必保留。
第二步:文本分割(分块)
原始文档太大,无法直接嵌入或放入提示。你需要将它们分割成块。分块大小直接影响检索质量 — 这是最需要调优的参数。
常用分割策略:
- RecursiveCharacterTextSplitter — 默认选择。按段落、句子、单词依次分割,保持语义连贯。
- 语义分块 — 用嵌入检测主题边界,生成尊重内容结构的可变大小块。
- Markdown/HTML 分割器 — 按标题(h1、h2、h3)分割,适合文档和结构化内容。
- 基于 Token 的分割 — 按 token 数而非字符数分割,更准确地管理 LLM 上下文窗口。
from langchain.text_splitter import (
RecursiveCharacterTextSplitter,
MarkdownHeaderTextSplitter,
)
# Default: RecursiveCharacterTextSplitter
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=512,
chunk_overlap=50,
length_function=len,
separators=["\n\n", "\n", ". ", " ", ""]
)
chunks = text_splitter.split_documents(pdf_docs)
print(f"Split into {len(chunks)} chunks")
# Markdown-aware splitting
md_splitter = MarkdownHeaderTextSplitter(
headers_to_split_on=[
("#", "h1"),
("##", "h2"),
("###", "h3"),
]
)
md_chunks = md_splitter.split_text(md_content)分块大小指南:Q&A 系统用 256-512 tokens,50-100 token 重叠。摘要用 1024-2048 tokens。代码按函数/类边界分割。务必测试多种大小并用 RAGAS 衡量检索准确率。
第三步:嵌入模型
嵌入将文本块转换为捕获语义含义的稠密数值向量。语义相似的文本产生在向量空间中接近的向量,从而实现语义搜索。
| 模型 | 维度 | 最大 Tokens | 费用(百万tokens) | 适用场景 |
|---|---|---|---|---|
| OpenAI text-embedding-3-small | 1536 | 8191 | $0.02 | 通用,最优性价比 |
| OpenAI text-embedding-3-large | 3072 | 8191 | $0.13 | 高精度需求 |
| Cohere embed-v3 | 1024 | 512 | $0.10 | 多语言,搜索优化 |
| sentence-transformers | 384 | 512 | 免费(自部署) | 隐私敏感,离线 |
| Ollama nomic-embed-text | 768 | 8192 | 免费(本地) | 本地开发,无 API 费用 |
| Voyage AI voyage-3 | 1024 | 16000 | $0.06 | 代码搜索,长上下文 |
from langchain_openai import OpenAIEmbeddings
# OpenAI embeddings (recommended for most cases)
embeddings = OpenAIEmbeddings(
model="text-embedding-3-small",
# dimensions=512 # optional: reduce dimensions for cost savings
)
# Embed a single query
query_vector = embeddings.embed_query("How to deploy a RAG app?")
print(f"Vector dimensions: {len(query_vector)}") # 1536
# Embed multiple documents (batched automatically)
doc_vectors = embeddings.embed_documents(
[chunk.page_content for chunk in chunks]
)建议:原型阶段从 text-embedding-3-small 开始,它以最低成本覆盖大多数场景。只有在评估指标显示需要时才切换到专用模型。
第四步:向量数据库
向量数据库为嵌入建立索引以支持快速相似度搜索。选择取决于规模、基础设施和查询模式。
| 向量数据库 | 类型 | 最大向量数 | 混合搜索 | 定价 | 适用场景 |
|---|---|---|---|---|---|
| Pinecone | 托管云 | 数十亿 | 是 | 免费层+按量付费 | 生产 SaaS,零运维 |
| Weaviate | 自部署/云 | 数十亿 | 是(BM25) | 开源+云 | 混合搜索,多模态 |
| Qdrant | 自部署/云 | 数十亿 | 是(稀疏) | 开源+云 | 过滤,载荷搜索 |
| ChromaDB | 嵌入式/自部署 | 百万级 | 否 | 开源 | 原型,本地开发 |
| pgvector | PostgreSQL 扩展 | 百万级 | 配合 pg_trgm | 免费 | 已有 Postgres 基础设施 |
| FAISS | 内存库 | 数十亿 | 否 | 免费(Meta) | 研究,批处理 |
from langchain_chroma import Chroma
from langchain_openai import OpenAIEmbeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
# Create and persist a vector store
vectorstore = Chroma.from_documents(
documents=chunks,
embedding=embeddings,
persist_directory="./chroma_db",
collection_name="my_docs"
)
# Load an existing vector store
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings,
collection_name="my_docs"
)
# Basic similarity search
results = vectorstore.similarity_search("deployment guide", k=4)
for doc in results:
print(doc.page_content[:100])推荐:原型阶段用 ChromaDB 或 FAISS。生产环境 Pinecone 运维负担最低。如果已用 PostgreSQL,pgvector 避免引入新服务。混合搜索需求选 Weaviate 或 Qdrant。
第五步:检索策略
检索质量决定 RAG 质量。完美的 LLM 也无法从不相关的上下文生成正确答案。以下是关键检索策略:
- 相似度搜索(k-NN) — 基本余弦相似度。快速简单,返回 k 个最相似的块。
- 最大边际相关性(MMR) — 平衡相关性和多样性,防止返回 k 个近乎重复的块。
- 混合搜索 — 结合稠密向量搜索和稀疏关键词搜索(BM25),同时捕获语义匹配和精确关键词匹配。
- 重排序 — 用交叉编码器模型对初始结果重新评分,以极小的延迟成本(+50-100ms)显著提升精确度。
- 上下文压缩 — 仅从检索块中提取相关句子,减少 LLM 上下文中的噪音。
对大多数生产系统,最优组合是:混合搜索(稠密+BM25)+ 交叉编码器重排序。在各种基准测试中,这始终优于纯向量相似度搜索。
第六步:基于上下文生成
最后一步将检索到的上下文与精心设计的提示一起传递给 LLM。提示模板至关重要 — 它告诉 LLM 仅使用提供的上下文并引用来源。
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = ChatPromptTemplate.from_template(
"""Answer the question based ONLY on the following context.
If the context does not contain the answer, say "I don't have
enough information to answer this question."
Context:
{context}
Question: {question}
Answer (cite sources):"""
)
# Build the RAG chain
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
def format_docs(docs):
return "\n\n".join(
f"[Source: {d.metadata.get('source', 'unknown')}]\n"
+ d.page_content for d in docs
)
rag_chain = (
{"context": retriever | format_docs,
"question": RunnablePassthrough()}
| prompt
| llm
| StrOutputParser()
)
answer = rag_chain.invoke("How do I deploy to production?")
print(answer)RAG 提示工程要点:(1) 明确指示模型仅从上下文回答。(2) 包含来源元数据以供引用。(3) 告诉模型在上下文不足时回答"我不知道"。(4) 使用系统消息保持一致行为。
高级 RAG 技术
基础 RAG 流水线运行后,以下技术可显著提升质量:
HyDE(假设文档嵌入):不直接嵌入用户查询,而是先让 LLM 生成假设答案,然后嵌入该答案进行检索。这弥合了问题式查询和文档式内容之间的差距,在许多基准上提升 10-25% 的召回率。
父子分块:索引小块(256 tokens)以实现精确检索,但返回父块(1024-2048 tokens)作为上下文。在保持检索精确度的同时给 LLM 更多上下文。
多查询检索:用 LLM 生成 3-5 个查询改写,对每个运行检索并合并结果。捕获单一查询表述可能遗漏的相关文档。
上下文压缩:检索后用小型 LLM 从每个块中仅提取相关句子。减少噪音,在上下文窗口中放入更多有用信息。
查询路由:分类查询类型(事实查找、比较、操作指南等),路由到不同的检索策略或知识库。简单的分类器可大幅提升结果。
完整 RAG 实现(Python)
以下是使用 LangChain、OpenAI 和 ChromaDB 的完整 RAG 流水线代码,涵盖文档加载、分块、嵌入、索引和查询:
# Complete RAG pipeline: pip install langchain langchain-openai
# pip install langchain-chroma unstructured pypdf
import os
os.environ["OPENAI_API_KEY"] = "sk-..."
from langchain_community.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain_openai import OpenAIEmbeddings, ChatOpenAI
from langchain_chroma import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
# 1. Load documents
docs = PyPDFLoader("knowledge_base.pdf").load()
# 2. Split into chunks
splitter = RecursiveCharacterTextSplitter(
chunk_size=512, chunk_overlap=50
)
chunks = splitter.split_documents(docs)
# 3. Create vector store with embeddings
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = Chroma.from_documents(
chunks, embeddings, persist_directory="./db"
)
# 4. Build RAG chain
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
llm = ChatOpenAI(model="gpt-4o", temperature=0)
prompt = ChatPromptTemplate.from_template(
"Answer based ONLY on this context:\n{context}\n\n"
"Question: {question}\nAnswer:"
)
rag_chain = (
{"context": retriever | (lambda docs: "\n".join(
d.page_content for d in docs)),
"question": RunnablePassthrough()}
| prompt | llm | StrOutputParser()
)
# 5. Query
print(rag_chain.invoke("What is the deployment process?"))以上代码用不到 50 行创建了完整的 RAG 流水线。生产环境需添加错误处理、流式传输、缓存,并将 ChromaDB 换为托管向量存储。
进阶:混合搜索与重排序
本示例添加 BM25 混合搜索和交叉编码器重排序,显著提升检索质量:
# pip install rank-bm25 sentence-transformers
from rank_bm25 import BM25Okapi
from sentence_transformers import CrossEncoder
import numpy as np
# BM25 sparse retriever
corpus = [doc.page_content for doc in chunks]
tokenized = [doc.split() for doc in corpus]
bm25 = BM25Okapi(tokenized)
def hybrid_search(query, k=10):
# Dense search via vector store
dense_results = vectorstore.similarity_search(query, k=k)
# Sparse search via BM25
bm25_scores = bm25.get_scores(query.split())
top_bm25_idx = np.argsort(bm25_scores)[-k:][::-1]
sparse_results = [chunks[i] for i in top_bm25_idx]
# Merge and deduplicate
seen = set()
merged = []
for doc in dense_results + sparse_results:
if doc.page_content not in seen:
seen.add(doc.page_content)
merged.append(doc)
return merged[:k]
# Cross-encoder re-ranking
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
def rerank(query, docs, top_k=4):
pairs = [[query, d.page_content] for d in docs]
scores = reranker.predict(pairs)
ranked = sorted(zip(scores, docs), reverse=True)
return [doc for _, doc in ranked[:top_k]]
# Usage: hybrid search + re-rank
candidates = hybrid_search("How to handle auth?", k=10)
final_docs = rerank("How to handle auth?", candidates, top_k=4)用 RAGAS 评估 RAG 质量
不能衡量就无法改进。RAGAS(检索增强生成评估)是评估 RAG 流水线的标准框架,测量四个关键指标:
- 忠实度 — 生成的断言是否有检索上下文支持?(减少幻觉)
- 答案相关性 — 答案是否真正与所提问题相关?
- 上下文精确度 — 检索的块是否相关?不相关的块是否被排除?
- 上下文召回率 — 检索器是否找到了回答所需的所有相关信息?
# pip install ragas
from ragas import evaluate
from ragas.metrics import (
faithfulness,
answer_relevancy,
context_precision,
context_recall,
)
from datasets import Dataset
# Prepare evaluation dataset
eval_data = {
"question": [
"How do I deploy to production?",
"What authentication methods are supported?",
],
"answer": [ # generated by your RAG pipeline
rag_chain.invoke("How do I deploy to production?"),
rag_chain.invoke("What auth methods are supported?"),
],
"contexts": [ # retrieved chunks for each question
[d.page_content for d in retriever.invoke(
"How do I deploy to production?")],
[d.page_content for d in retriever.invoke(
"What auth methods are supported?")],
],
"ground_truth": [ # human-written correct answers
"Deploy using Docker with the provided Dockerfile...",
"OAuth 2.0, API keys, and JWT are supported...",
],
}
dataset = Dataset.from_dict(eval_data)
results = evaluate(
dataset,
metrics=[faithfulness, answer_relevancy,
context_precision, context_recall],
)
print(results)
# {faithfulness: 0.92, answer_relevancy: 0.89,
# context_precision: 0.85, context_recall: 0.78}在 50-100 个有标准答案的问题测试集上运行 RAGAS。忠实度低于 0.8 说明 RAG 系统在产生幻觉。上下文召回率低于 0.7 说明检索器遗漏了相关文档。
生产环境注意事项
从原型到生产需要解决以下问题:
缓存:缓存相同查询的嵌入结果。缓存相同查询+上下文组合的 LLM 响应。使用 Redis 或简单内存 LRU 缓存。典型工作负载可减少 40-60% 成本。
流式传输:逐 token 流式传输 LLM 响应。用户看到部分响应时会感觉系统更快。LangChain 和 OpenAI 都原生支持流式传输。
成本优化:除非评估需要,否则用 text-embedding-3-small。批量嵌入请求。积极缓存。简单查询用小模型(GPT-4o-mini),复杂查询路由到 GPT-4o。
监控:记录每个查询、检索块和生成答案。跟踪检索延迟、生成延迟和用户反馈。设置忠实度下降告警。工具:LangSmith、Phoenix、W&B。
安全:对文档实施访问控制 — 用户只能检索有权查看的文档。清洗用户查询以防止提示注入。不要在 UI 中未经过滤地暴露原始检索块。
常见陷阱及避免方法
以下是大多数 RAG 实现中常犯的错误:
- 分块太大 — 检索到不相关内容稀释答案。从 512 tokens 开始逐步调小。
- 块之间无重叠 — 块边界处的句子被截断。始终使用 10-20% 重叠。
- 忽略元数据 — 没有来源跟踪就无法提供引用或按文档类型/日期过滤。
- 嵌入模型选择不当 — 对专业内容(代码、法律、医疗)使用通用模型。评估领域专用模型。
- 检索块过多 — 塞入 20 个块增加噪音。用 3-5 个块加重排序效果更好。
- 无评估 — 没有 RAGAS 等指标就是盲飞。改动前后必须测量。
- 上下文窗口溢出 — 未计算分块大小 x 块数 x 提示模板 tokens。计算总 tokens 并控制在限制内。
- 嵌入过时 — 更新文档但不重新嵌入。建立增量索引流水线。
RAG 框架对比
多个框架简化了 RAG 开发,以下是主要选项对比:
| 框架 | 语言 | 优势 | 劣势 | 适用场景 |
|---|---|---|---|---|
| LangChain | Python, JS | 最大生态,最多集成,优秀文档 | 抽象层开销,频繁破坏性更新 | 通用 RAG,快速原型 |
| LlamaIndex | Python | 专为 RAG 设计,高级索引策略 | 学习曲线陡,社区较小 | 复杂文档索引 |
| Haystack | Python | 流水线优先设计,面向生产 | 自定义流程灵活性较差 | 企业级流水线 |
| Semantic Kernel | C#, Python | 微软生态,Azure 集成 | 较新,示例较少 | .NET / Azure 团队 |
| Vercel AI SDK | TypeScript | 流式优先,React 集成 | RAG 工具不够成熟 | Next.js Web 应用 |
推荐:从 LangChain 开始,凭借其生态广度和文档质量。如果你的场景以文档为主且需要复杂索引,评估 LlamaIndex。TypeScript Web 应用推荐 Vercel AI SDK。
RAG 真实应用场景
RAG 不是理论概念 — 它支撑着数千个生产应用:
- 内部知识库问答:员工查询公司政策、HR 文档、工程手册。RAG 从 Confluence、Notion 或飞书检索并带引用回答。
- 客服聊天机器人:AI 客服搜索产品文档、历史工单和 FAQ 解决客户问题,减少 40-60% 人工客服工作量。
- 法律文档分析:律师查询数千份合同、法规和判例。RAG 找到相关条款并总结先例。
- 代码助手:开发者查询代码库。RAG 搜索索引的源码、README 和架构文档提供上下文感知的答案。
- 医学研究:研究人员查询论文、临床试验数据和药物相互作用数据库。RAG 呈现相关研究并提供引用。
成本估算指南
了解 RAG 成本有助于预算规划。以下是典型知识库(10,000 篇文档,平均 2,000 tokens)的成本分解:
| 组件 | 计算 | 月成本 |
|---|---|---|
| 初始嵌入(一次性) | 2000万 tokens x $0.02/百万 | $0.40 |
| 向量存储(Pinecone) | ~10万向量,免费层 | $0 |
| 查询嵌入(1K查询/天) | 3万查询 x 平均100 tokens | $0.06 |
| LLM 生成(GPT-4o-mini) | 3万查询 x 平均2K tokens | $9.00 |
| LLM 生成(GPT-4o) | 3万查询 x 平均2K tokens | $75.00 |
| 重排序(可选,自部署) | GPU 实例 | $50-150 |
关键洞察:嵌入成本可忽略不计。LLM 生成模型选择主导你的账单。80% 查询用 GPT-4o-mini,仅将复杂查询路由到 GPT-4o。这种"模型路由"策略可减少 60-70% 成本。
监控与可观测性
没有监控的 RAG 系统是等待静默失败的系统。你需要对流水线每个阶段的可见性:
- 检索延迟:测量从查询到检索块的时间。超过 500ms 需告警。慢检索通常意味着索引需要优化或向量存储容量不足。
- 检索相关性:抽样 1% 查询让标注员评估检索块是否相关。相关性下降通常意味着知识库已变化但嵌入过时。
- 生成质量:跟踪用户反馈(点赞/踩、修正)。每周在固定测试集上运行 RAGAS 评估。忠实度下降表明提示或检索出现回归。
- Token 用量和成本:跟踪每次查询消耗的 tokens(嵌入+生成)。设置预算告警。识别可用小模型处理的高成本查询。
- 错误率:监控嵌入 API 错误、向量存储超时和 LLM 失败。设置回退:主检索失败时返回缓存响应或优雅降级。
LangSmith、Phoenix(Arize)和 Weights & Biases Traces 是 LLM 应用的领先可观测性工具。LangSmith 与 LangChain 无缝集成,开箱即提供追踪可视化、评估和成本跟踪。
生产部署:pgvector + PostgreSQL
如果团队已经使用 PostgreSQL,pgvector 是最简单的生产路径。无需新基础设施、无需新供应商 — 只是现有数据库上的一个扩展:
-- Enable the extension
CREATE EXTENSION IF NOT EXISTS vector;
-- Create a table for document chunks
CREATE TABLE documents (
id SERIAL PRIMARY KEY,
content TEXT NOT NULL,
metadata JSONB DEFAULT '{}',
embedding vector(1536), -- matches text-embedding-3-small
created_at TIMESTAMP DEFAULT NOW()
);
-- Create an HNSW index for fast similarity search
CREATE INDEX ON documents
USING hnsw (embedding vector_cosine_ops)
WITH (m = 16, ef_construction = 64);
-- Similarity search query
SELECT id, content, metadata,
1 - (embedding <=> $1::vector) AS similarity
FROM documents
WHERE 1 - (embedding <=> $1::vector) > 0.7
ORDER BY embedding <=> $1::vector
LIMIT 5;# Python: LangChain + pgvector
# pip install langchain-postgres psycopg2-binary
from langchain_postgres import PGVector
from langchain_openai import OpenAIEmbeddings
CONNECTION = "postgresql://user:pass@localhost:5432/mydb"
embeddings = OpenAIEmbeddings(model="text-embedding-3-small")
vectorstore = PGVector.from_documents(
documents=chunks,
embedding=embeddings,
connection=CONNECTION,
collection_name="my_docs",
)
# Query with metadata filtering
results = vectorstore.similarity_search(
"deployment guide",
k=4,
filter={"source": "docs/deploy.md"}
)pgvector 支持 IVFFlat 和 HNSW 索引进行快速近似最近邻搜索。100 万向量以内无需分片即可良好运行。超过此规模,考虑专用向量存储。
RAG vs 智能体 RAG
传统 RAG 遵循固定流水线:先检索后生成。智能体 RAG 让 LLM 控制检索过程本身 — 它可以决定何时搜索、搜索什么、是否再次搜索以及何时有足够上下文回答。
在智能体 RAG 系统中,LLM 作为推理智能体可以调用检索作为工具。如果首次检索未返回相关结果,智能体会重新构造查询并重试。它还可以组合多次搜索的信息、跨来源验证事实,并在无法回答时优雅退出。
智能体 RAG 对需要多步推理的复杂问题特别强大:"比较产品 X、Y 和 Z 的定价"需要三次独立检索,固定流水线无法优雅处理。LangGraph 和 CrewAI 等框架让构建智能体 RAG 系统变得简单。
多模态 RAG:超越文本
现代 RAG 不限于文本。多模态 RAG 可以索引和检索图像、表格和结构化数据:
- 表格提取:用 Unstructured 或 Docling 从 PDF 提取表格,转换为 markdown 或 JSON 后再分块。表格包含纯文本提取会遗漏的密集信息。
- 图像理解:用 GPT-4o 或 Claude 生成图表、截图的文本描述。将这些描述与原始图像引用一起索引。
- 结构化数据:为数据库和 API 生成 schema 和样本数据的自然语言摘要。需要实时数据库访问时用 text-to-SQL。
- 代码仓库:分别索引函数、类和文档字符串。用代码专用嵌入模型(Voyage AI voyage-code-3)获得更好的语义匹配。
多模态 RAG 增加了复杂性但显著提升了覆盖率。从纯文本 RAG 开始,然后根据用户问了但找不到答案的内容添加模态。
文档预处理技巧
输入文档的质量直接决定 RAG 质量。垃圾进,垃圾出。以下是预处理最佳实践:
- 清理 HTML 残留:索引前剥离导航、页脚、广告和模板。用 readability 库或 Unstructured 提取正文。
- 规范化格式:将所有文档转换为统一 markdown。跨来源统一标题、列表和代码块。
- 去重:索引前移除重复或近似重复的文档。重复浪费存储并导致冗余检索结果。
- 添加元数据:为每个文档添加标题、作者、日期、分类和来源 URL。元数据支持过滤并提升引用质量。
- 处理 OCR 内容:扫描 PDF 需要 OCR。用 Tesseract 或云 OCR 服务。后处理 OCR 输出修复常见错误。
- 验证编码:确保所有文本为 UTF-8。混合编码导致嵌入和检索中的静默损坏。
快速入门清单
按此清单从零构建你的第一个 RAG 流水线:
- 确定知识来源 — 识别包含用户所需答案的文档、数据库或 API。从 50-100 篇精选文档开始。
- 选择技术栈 — 推荐:Python + LangChain + OpenAI + ChromaDB。一小时内从零到可用原型。
- 设置文档加载 — 为文档类型选择合适的加载器。保留元数据(来源 URL、页码、章节标题)用于引用。
- 调优分块 — 从 RecursiveCharacterTextSplitter 开始,512 tokens,50 token 重叠。根据内容类型和检索准确率调整。
- 生成和存储嵌入 — 使用 text-embedding-3-small。本地存 ChromaDB 或云端用 Pinecone。
- 构建检索链 — 从简单相似度搜索(k=4)开始。如果看到重复结果,添加 MMR。
- 编写提示模板 — 指示 LLM 仅从上下文回答并引用来源。用 10 个代表性问题测试。
- 用 RAGAS 评估 — 创建 50+ 问题测试集并附标准答案。测量忠实度、相关性和召回率。
- 迭代改进 — 调整分块大小,尝试混合搜索,添加重排序。每次改动应提升 RAGAS 分数。
- 部署 — 添加流式传输、缓存、错误处理和监控。使用 LangSmith 等可观测性工具。
流式 RAG 响应
用户期望实时响应。流式传输在 token 生成时即刻交付,而非等待完整响应。以下是 LangChain 实现流式传输的方法:
import asyncio
from langchain_openai import ChatOpenAI
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
llm = ChatOpenAI(model="gpt-4o", temperature=0, streaming=True)
prompt = ChatPromptTemplate.from_template(
"Answer based ONLY on this context:\n{context}\n\n"
"Question: {question}\nAnswer:"
)
retriever = vectorstore.as_retriever(search_kwargs={"k": 4})
rag_chain = (
{"context": retriever | (lambda docs: "\n".join(
d.page_content for d in docs)),
"question": RunnablePassthrough()}
| prompt | llm | StrOutputParser()
)
# Synchronous streaming
for chunk in rag_chain.stream("How do I set up auth?"):
print(chunk, end="", flush=True)
# Async streaming (for web frameworks)
async def stream_response(question: str):
async for chunk in rag_chain.astream(question):
yield chunk # Send to client via SSE or WebSocket
# FastAPI example
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
@app.get("/ask")
async def ask(q: str):
return StreamingResponse(
stream_response(q),
media_type="text/event-stream"
)流式传输将感知延迟从 3-5 秒降低到首 token 500ms 以内。面向用户的应用必须实现流式传输 — 对用户体验的提升是巨大的。
常见问题
AI 中的 RAG 是什么,如何工作?
RAG(检索增强生成)是一种在查询时从知识库检索相关文档并注入提示的架构。LLM 基于检索到的上下文生成答案,而非仅依赖训练数据,从而减少幻觉并获取最新信息。
何时用 RAG vs 微调?
需要 LLM 基于特定文档、数据库或频繁更新的内容回答时用 RAG。需要改变模型行为、语气或教授专业推理模式时用微调。RAG 实施更快、维护更便宜、更擅长提供引用。大多数生产系统从 RAG 开始,仅在需要时添加微调。
RAG 最佳分块大小是多少?
没有通用最佳分块大小。Q&A 系统用 256-512 tokens 加 50-100 token 重叠。摘要用 1024-2048 tokens。代码按函数或类边界分割。关键是测量:创建测试集,尝试不同大小,选择 RAGAS 指标最高的。
RAG 该用哪个向量数据库?
原型用 ChromaDB 或 FAISS。生产 SaaS 选 Pinecone。已用 PostgreSQL 选 pgvector。高级混合搜索选 Weaviate 或 Qdrant。最好的向量数据库是适配你现有基础设施的那个。
如何减少 RAG 中的幻觉?
五个策略:(1) 用混合搜索和重排序提升检索质量。(2) 明确提示指令仅从上下文回答。(3) 添加引用 — 要求模型引用来源文档。(4) 上下文压缩去除不相关内容。(5) 用 RAGAS 衡量忠实度并迭代。
2026 年 RAG 最佳嵌入模型是什么?
OpenAI text-embedding-3-small 是最佳通用选择。多语言用 Cohere embed-v3。私有/离线部署用 sentence-transformers 或 Ollama。代码搜索用 Voyage AI voyage-3。选择前务必在你的数据上做基准测试。
如何评估 RAG 流水线质量?
用 RAGAS 框架测量四个指标:忠实度、答案相关性、上下文精确度和上下文召回率。创建 50-100 个有标准答案的测试集,每次流水线改动后运行 RAGAS 评估。
RAG 中的混合搜索是什么,为什么更好?
混合搜索结合稠密向量相似度搜索和稀疏关键词搜索(BM25)。稠密搜索捕获语义(同义词、改述),稀疏搜索捕获精确关键词(产品名、错误码)。两者结合优于单一方法。大多数现代向量存储原生支持混合搜索。
RAG 是构建需要有依据、准确、最新答案的 LLM 应用最实用、最有影响力的架构。从简单流水线开始(LangChain + OpenAI + ChromaDB),用 RAGAS 测量,然后迭代。需要更好检索时添加混合搜索和重排序。生态已成熟,工具已就绪 — 关键差异在于你如何针对自己的数据和用例调优分块、检索和提示。