RAG 系统入门
RAG(Retrieval-Augmented Generation)让模型结合你的私有数据回答问题,是 AI 应用的关键组成。
1) 概念与适用场景
- 解决知识时效/私有数据问题:让模型“先检索,再回答”。
- 适用:企业知识库问答、文档助手、客服 FAQ、合规检索、代码库问答。
- 不适用:需要强推理但无资料支撑;实时多模态推理可需额外能力。
2) 端到端流程
用户问题 → 向量化 → 检索 → 构建上下文 → LLM 生成回答 → (可选) 返引用/信心分
核心组件:切分、Embedding、向量库、检索、重排、构建 Prompt、生成、反馈闭环。
3) 数据准备与切分
- 清洗:去除页眉脚、目录/水印、脚注;保持段落语义。
- 切分:递归字符切分(200-400 token 一块,overlap 10-20%),表格/代码可按语义块。
- 元数据:文档名、章节、页码、时间戳、来源类型,用于过滤与引用。
Python 示例(LangChain)
from langchain.text_splitter import RecursiveCharacterTextSplitter
splitter = RecursiveCharacterTextSplitter(
chunk_size=300,
chunk_overlap=60
)
chunks = splitter.split_text(text)
4) 向量化(Embedding)
- 选型:OpenAI text-embedding-3-*;兼顾成本与中文/英文效果。
- 维度:默认即可;保持同一模型以免空间不一致。
- 去重:哈希内容或 ID,避免重复写入。
Python 示例
from openai import OpenAI
client = OpenAI()
emb = client.embeddings.create(
model="text-embedding-3-small",
input=chunks
).data
5) 向量数据库选择
- 托管:Pinecone、Weaviate Cloud,省运维,适合生产。
- 自建:Chroma(轻量)、Weaviate、Milvus;注意持久化与备份。
- 选择要点:延迟、扩展性、过滤支持(metadata filter)、多租户隔离。
6) 检索策略
- 相似度检索(k=3-6)是基础。
- 过滤:按文档类型/时间/租户过滤,减少误检。
- 多路检索:BM25 + 向量混合;或 rerank 提升相关性(如 Cohere Rerank / bge-reranker)。
- 去重与重排:合并同源段落,避免冗余。
7) 构建上下文与提示
- 格式:列出检索到的段落,附带编号/来源,提示“仅依据上下文回答,未知则说未知”。
- 长度控制:上下文 tokens 控制在模型上限的 1/2-2/3,留出输出空间。
- 引用:要求回答时带上段落编号,便于追溯。
示例 Prompt 片段
你是知识库助手。仅根据“提供的段落”回答,不要编造。
提供的段落:
[1] 段落内容...
[2] 段落内容...
若未找到答案,回复“未在提供的段落中找到”。
回答请附上引用编号,如 [1][2]。
8) 生成阶段与防幻觉
- System 约束:只根据上下文,不要自创事实。
- JSON/结构化输出:便于前端或下游使用。
- 置信度:可返回检索得分/来源,低分可提示“低置信”。
- 拒答:未知时返回“未找到”,不要臆测。
9) 增量与更新
- 定期/实时同步:监听文件变更,增量更新向量库。
- 版本化:为文档打版本号,检索结果附版本,便于溯源。
- 过期策略:下线旧版本,清理无效向量。
10) 性能与成本
- 控制 chunk 大小与检索 k,减少上下文长度。
- 预摘要:对超长文档先摘要,再检索摘要。
- 缓存:热门问答缓存;对常见查询提前生成答案。
- 硬件/服务:向量库放近模型区域,降低延迟。
11) 评估与对齐
- 离线评估集:准备问答对,检查相关性、准确性、引用正确率。
- 线上反馈:thumbs up/down 收集误检,回填改进检索或切分。
- 统计:命中率、无答案率、平均引用数、P95 延迟、成本。
12) 安全与多租户
- 租户隔离:metadata 带 tenant_id,检索时强过滤。
- 访问控制:按用户/角色过滤可见文档。
- 数据敏感:敏感字段脱敏;日志不存原文。
13) 最小可行示例(简版)
# 1) 切分 + 嵌入 + 入库(示意)
chunks = splitter.split_text(text)
vectors = client.embeddings.create(
model="text-embedding-3-small",
input=chunks
).data
db.add(texts=chunks, embeddings=[v.embedding for v in vectors])
# 2) 查询
query = "退款流程是什么?"
q_emb = client.embeddings.create(model="text-embedding-3-small", input=query).data[0].embedding
docs = db.similarity_search_by_vector(q_emb, k=4)
# 3) 构建提示
context = "\\n".join([f"[{i+1}] {d.page_content}" for i, d in enumerate(docs)])
prompt = f"""你是客服助手,只根据提供的段落回答,不要编造。
提供的段落:
{context}
问题:{query}
如未找到答案,回复“未在提供的段落中找到”。回答附引用编号。"""
resp = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}]
)
print(resp.choices[0].message.content)
14) 小练习
- 用 3 篇文章构建一个本地向量库(Chroma),实现检索问答,输出带引用编号。
- 加入 rerank(如 bge-reranker 或 Cohere Rerank),对比回答质量。
- 为查询增加“无答案”判定逻辑,避免误答,并记录检索得分与延迟。
📚 相关资源
❓ 常见问题
关于本章主题最常被搜索的问题,点击展开答案
RAG 和直接问大模型有什么区别?什么时候该用 RAG?
RAG 让模型「先检索再回答」,解决知识时效和私有数据问题。适合企业知识库问答、文档助手、客服 FAQ、合规检索、代码库问答。不适合需要强推理但无资料支撑的任务。流程是:用户问题 → 向量化 → 检索 → 构建上下文 → LLM 生成 → 返引用/信心分。
chunk 应该切多大?overlap 设多少?
用 RecursiveCharacterTextSplitter,chunk_size 200-400 token、overlap 10-20%(约 60 token)是基础配置。表格/代码按语义块切,不要硬切。每个 chunk 必须带 metadata:文档名、章节、页码、时间戳、来源类型,用于后续过滤和引用。chunk 太小语义碎,太大检索精度降。
向量数据库选 Pinecone、Chroma 还是 Weaviate?
托管走 Pinecone / Weaviate Cloud(省运维,适合生产);自建轻量用 Chroma;要规模和过滤选 Weaviate / Milvus。选型四要点:延迟、扩展性、metadata filter 支持、多租户隔离。embedding 模型用 OpenAI text-embedding-3-small,整个项目锁定同一模型避免空间不一致;写入前用 hash 去重。
只用向量检索精度不够,rerank 有用吗?
有用。基础是相似度检索 k=3-6;想提升相关性叠 rerank(Cohere Rerank 或 bge-reranker),先召回 top-20-50 再 rerank 到 top-5。多路检索 BM25 + 向量混合也常见。同时按 metadata(文档类型、时间、租户)过滤减少误检,合并同源段落避免冗余。
构建 prompt 时 context 长度该控制在哪?
上下文 tokens 控制在模型上限的 1/2 - 2/3,留出输出空间。检索段落要带编号 [1][2][3] 和来源,prompt 里写「仅根据提供的段落回答,未找到回复'未在提供的段落中找到'」。回答必须附引用编号便于追溯。返检索得分作为信心分,低分提示「低置信」。