RAG 管道从零搭建:文档问答系统实战
用 LlamaIndex + ChromaDB + GPT-4o 搭一个生产级 RAG 系统——文档切分策略、Embedding 选型、检索优化、重排序、回答生成、评估指标,含完整代码和踩坑记录。
发布 2026-06-21更新 2026-09-30适用场景
- 公司内部知识库问答(HR 政策、技术文档、FAQ)
- 法律/医疗文档检索 + 问答
- 产品手册智能客服
- 个人笔记/文献智能检索
RAG 架构总览
文档 → 切分 → Embedding → 存入向量数据库
↓
用户提问 → Embedding → 检索 Top-K → 重排序 → LLM 生成回答
技术选型
| 组件 | 选型 | 理由 |
|---|---|---|
| 框架 | LlamaIndex | 比 LangChain 更专注 RAG |
| Embedding | BGE-large-zh-v1.5 | 中文效果最佳,开源免费 |
| 向量数据库 | ChromaDB | 轻量,原型够用 |
| 重排序 | bge-reranker-large | 显著提升准确率 |
| LLM | GPT-4o | 性价比好,支持国内中转 |
| 文档解析 | Unstructured | 支持 PDF/Word/HTML |
第一步:环境准备
pip install llama-index chromadb sentence-transformers unstructured
第二步:文档加载与切分
切分是 RAG 效果的决定性因素。
from llama_index.core import SimpleDirectoryReader
from llama_index.core.node_parser import SentenceSplitter
# 1. 加载文档
documents = SimpleDirectoryReader("./docs").load_data()
# 2. 切分
splitter = SentenceSplitter(
chunk_size=512, # 每块 512 token
chunk_overlap=50, # 重叠 50 token,保证上下文连贯
)
nodes = splitter.get_nodes_from_documents(documents)
切分策略选择
| 策略 | chunk_size | 适用场景 |
|---|---|---|
| 小块 | 256 | FAQ、短问答 |
| 中块 | 512 | 通用文档(推荐起步值) |
| 大块 | 1024 | 长文档、技术手册 |
| 按段落 | 不固定 | 保持语义完整 |
关键:chunk_overlap 设 chunk_size 的 10%,避免切断语义。
第三步:Embedding 与入库
from llama_index.embeddings.huggingface import HuggingFaceEmbedding
from llama_index.vector_stores.chroma import ChromaVectorStore
import chromadb
# 1. 用 BGE 中文模型
embed_model = HuggingFaceEmbedding(
model_name="BAAI/bge-large-zh-v1.5",
max_length=512,
)
# 2. 创建向量数据库
db = chromadb.PersistentClient(path="./chroma_db")
chroma_collection = db.get_or_create_collection("docs")
vector_store = ChromaVectorStore(chroma_collection=chroma_collection)
# 3. 构建索引
from llama_index.core import StorageContext, VectorStoreIndex
storage_context = StorageContext.from_defaults(vector_store=vector_store)
index = VectorStoreIndex(nodes, embed_model=embed_model, storage_context=storage_context)
第四步:检索 + 重排序
from llama_index.core.postprocessor import SentenceTransformerRerank
# 重排序模型——显著提升检索质量
reranker = SentenceTransformerRerank(
model="BAAI/bge-reranker-large",
top_n=3, # 重排序后取前 3
)
# 检索器
retriever = index.as_retriever(similarity_top_k=10) # 先粗检索 10 条
# 检索 + 重排序
nodes = retriever.retrieve("年假怎么请?")
reranked = reranker.postprocess_nodes(nodes, query_str="年假怎么请?")
为什么要重排序
Embedding 检索快但不够精准。先粗检索 10 条,再用重排序模型精选 3 条,准确率提升 20-30%。
第五步:生成回答
from llama_index.llms.openai import OpenAI
llm = OpenAI(model="gpt-4o", temperature=0)
# 构建 query engine
query_engine = index.as_query_engine(
llm=llm,
similarity_top_k=10,
node_postprocessors=[reranker],
response_mode="compact", # 紧凑模式,省 token
)
# 提问
response = query_engine.query("年假怎么请?需要提前多久申请?")
print(response.response)
Prompt 优化
默认 prompt 是英文的,中文场景建议自定义:
from llama_index.core import PromptTemplate
qa_prompt = PromptTemplate("""
你是一个文档问答助手。请根据以下检索到的文档片段回答问题。
如果文档中没有相关信息,明确说"文档中未找到相关信息",不要编造。
文档片段:
{context_str}
问题:{query_str}
回答:
""")
query_engine.update_prompts({"response_synthesis_prompt": qa_prompt})
第六步:评估
检索质量评估
# 准备测试集:问题 + 正确答案所在文档
test_cases = [
{"question": "年假怎么请?", "relevant_doc_id": "hr_policy_003"},
{"question": "报销流程是什么?", "relevant_doc_id": "finance_007"},
]
# 计算 Recall@K
def eval_retrieval(query_engine, test_cases, k=5):
hits = 0
for tc in test_cases:
nodes = query_engine.retrieve(tc["question"])
retrieved_ids = [n.node.metadata["doc_id"] for n in nodes[:k]]
if tc["relevant_doc_id"] in retrieved_ids:
hits += 1
return hits / len(test_cases)
recall = eval_retrieval(query_engine, test_cases, k=5)
print(f"Recall@5: {recall:.1%}")
回答质量评估
用 LLM 自动评估回答质量:
eval_prompt = f"""
请评估以下回答的质量,打分 1-5:
问题:{question}
检索到的文档:{retrieved_docs}
回答:{answer}
评分标准:
5 — 完全正确,基于文档
3 — 部分正确,有遗漏
1 — 错误或编造
"""
常见问题与优化
问题 1:检索不到相关文档
原因:切分太碎,语义丢失。
解决:
- 增大 chunk_size(512 → 1024)
- 加 chunk_overlap
- 用 parent-child 切分(检索小块,返回大块)
问题 2:回答不基于文档(幻觉)
解决:
- prompt 强制要求"只基于文档回答"
- temperature 设 0
- 检查检索结果是否相关(不相关就回答"未找到")
问题 3:中文检索效果差
解决:
- 用 BGE 系列中文 Embedding 模型
- 不要用 OpenAI 的 Embedding(中文效果一般)
- 加重排序模型
问题 4:速度慢
解决:
- Embedding 用 GPU
- 向量数据库加 HNSW 索引
- 减少 similarity_top_k(10 → 5)
- LLM 用流式输出
2026-09-30 更新:模型选型这一段已经过时
本文写成于 2026 年年中,正文里的 GPT-4o 作为生成端模型已经不再是性价比选项。本站不改正文代码里的模型名(保留原始教程的可复现性),但选型这一段必须按当前口径读:
| 环节 | 2026-09 的选型原则 |
|---|---|
| 生成端 LLM | 别再用 GPT-4o 这一代。按「每任务成本」选:高频问答走轻量档(如 GPT-6 Luna $0.10/$0.50 一类),需要多步推理的走中阶档。缓存命中率是成本第一变量——prompt 前缀必须稳定 |
| Embedding | 优先选与你语料语言匹配的模型;中文语料用通用多语言 embedding 的效果通常优于英强中弱的旧款。别只看 MTEB 总分,看中文子集 |
| 重排序(rerank) | 成本敏感时先做「是否真的需要 rerank」的 A/B——很多场景里调好切分比加 rerank 收益更大 |
| 本地部署 | 完全离线场景走 Ollama / LM Studio,但注意本地模型的长上下文与多步推理能力弱于前沿闭源模型,RAG 的「上下文已经喂足」特性恰好能补一部分短板 |
常见失效模式与排查顺序
RAG 线上出问题时,90% 的锅不在生成端。按下面顺序排查,能省掉大量无效调 prompt 的时间:
| 现象 | 最可能的原因 | 先做什么 |
|---|---|---|
| 答案「看起来对但细节错」 | 检索没命中的上下文被模型用先验补全 | 打印本次召回的 chunk,确认里面到底有没有答案 |
| 问什么都能答上来(包括不存在的) | 防幻觉 prompt 太弱,或 similarity_top_k 过大引入噪声 | 收紧 prompt 的「只依据上下文」约束 + 降 top_k |
| 明明文档里有,就是搜不到 | 切分把答案切断了,或 embedding 与查询语义不匹配 | 换切分策略(按标题层级 / 加 overlap)后重试 |
| 表格、公式类内容答不准 | 解析器把结构丢了 | 换支持表格结构的解析器,或表格单独走结构化检索 |
| 换一批文档效果骤降 | 新文档格式 / 语言分布与调优时不同 | 重新跑评估集,别沿用旧参数 |
| 延迟高 | 瓶颈常在向量库 ANN 参数与 rerank,不在 LLM | 先量三段耗时再优化 |
排查铁律:先确认召回,再动 prompt。看不到召回内容就调 prompt,等于闭眼修车。
评估:怎么做得不骗自己
- 先建一个小而真的评估集(30~100 条即可),题目来自真实用户提问,不是作者编的。
- 分别评估两段:检索段用 Recall@K(答案所在 chunk 是否被召回),生成段用人工抽检或 LLM-as-judge。只测端到端准确率,你会分不清是检索错还是生成错。
- 留一组「文档里没有答案」的题,专门测模型会不会瞎编。RAG 最容易翻车的不是答不出,而是答得太自信。
- 每次改切分 / embedding / top_k,都重跑一次评估集,把数字记进表格。凭感觉调参是 RAG 项目最常见的失败原因。
- 上线后记录「问题 + 召回 + 回答」三元组,它们既是排查依据,也是下一轮评估集的素材。
小结
RAG 的工程重心不在模型,在数据管道:切分策略决定召回上限,embedding 决定语义匹配质量,rerank 与 top_k 决定噪声水平,生成端只负责把已经找到的内容说清楚。把顺序搞反——先换更强的模型再抱怨效果差——是这类项目最典型的浪费。
生产部署清单
- 文档解析支持 PDF/Word/HTML/Markdown
- 切分策略测试过(chunk_size 调优)
- Embedding 模型选定并部署
- 向量数据库持久化
- 重排序模型集成
- LLM prompt 优化(中文 + 防幻觉)
- 检索质量评估(Recall@K > 80%)
- 回答质量评估(人工抽检)
- 流式输出(用户体验)
- 缓存层(常见问题缓存回答)
- 日志记录(问题 + 检索结果 + 回答)
- 限流 + 鉴权
相关工具
Coze vs Dify:AI Agent 平台怎么选?零代码 vs 开源全控对比
Coze vs Dify 2026 选型对比:字节零代码 Bot 平台 vs 开源 LLMOps 全控平台,从平台定位、开发体验、工作流编排、RAG 精度、私有部署、价格模型和适合人群 7 个维度判断,帮你选对 AI Agent 平台。
Coze vs FastGPT:AI 知识库平台怎么选?Bot 工厂 vs RAG 专精对比
Coze vs FastGPT 2026 选型对比:字节零代码 Bot 平台 vs labring 开源 RAG 专精平台,从平台定位、开发体验、RAG 精度、工作流编排、模型生态、私有部署、价格模型和适合人群 8 个维度判断,帮你选对 AI 知识库平台。
FastGPT vs Dify:国内企业级 RAG 与 Agent 平台怎么选
FastGPT vs Dify 2026 选型对比:从数据合规、私有部署、RAG 精度、工作流复杂度、插件生态、Bot 多平台发布和适合人群判断,帮企业和团队决定是走 FastGPT 的知识库路线,还是 Dify 的 LLMOps 路线。
Dify vs Flowise:LLM 应用开发平台怎么选(2026)
Dify 与 Flowise 都是可视化 LLM 应用开发平台。一句话结论 + 决策树 + 成本对比:要开箱即用的完整产品与中文生态选 Dify,要节点级自由编排与自部署选 Flowise。
Dify vs Langflow:开源 LLMOps 平台怎么选?业务全控 vs LangChain 工程对比
Dify vs Langflow 2026 选型对比:Apache 2.0 LLMOps 全平台 vs MIT 可视化 LangChain 画布,从平台定位、开发体验、工作流编排、RAG 能力、模型生态、私有部署、价格模型和适合人群 8 个维度判断,帮你选对开源 LLMOps 平台。
Dify vs Manus:Agent 平台 vs 通用 Agent 怎么选?2026 对比
Dify vs Manus 2026 选型对比:开源 LLMOps Agent 平台 vs Butterfly Effect 通用 AI Agent。从定位、核心能力、可定制性、开源、价格和适用场景 6 个维度帮你选对 Agent 工具。
2026 年 AI Agent 平台全景图:14 款实测对比
2026 年 AI Agent 平台全景图:14 款平台按低代码 / 通用 Agent / 桌面 Agent / MCP 协议 / 框架分桶,6 大决策维度(开源 / 私有部署 / 模型 / 工作流 / 知识库 / 发布),选型决策树,Top 14 速查卡与 2026 趋势(MCP 标准化、Skills 市场、A2A 协议)。
Coze 深度评测:字节 AI Bot 平台真能零代码搭出能用 Agent 吗
Coze 扣子深度评测:字节跳动出品的低代码 AI Agent 平台,国内版接豆包/飞书生态、国际版接 GPT/Claude。本文写它真正解决的零代码门槛问题、工作流编排能力边界、200+ 插件生态、多端发布、按调用计费的真实成本,以及 5 类不推荐用 Coze 的场景。AI 之家 编辑部基于官方文档与多份第三方评测整理。
Coze vs Dify:国内 Agent 平台双雄实测
字节 Coze 与开源 Dify 横向对比实测:搭建效率、可定制性、私有化部署、知识库与工作流能力、计费模式、踩坑记录与选型决策树。B 端深度需求选 Dify,C 端轻量场景选 Coze。2026 最新数据。