RAG 管道从零搭建:文档问答系统实战
适用场景
- 公司内部知识库问答(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 用流式输出
生产部署清单
- 文档解析支持 PDF/Word/HTML/Markdown
- 切分策略测试过(chunk_size 调优)
- Embedding 模型选定并部署
- 向量数据库持久化
- 重排序模型集成
- LLM prompt 优化(中文 + 防幻觉)
- 检索质量评估(Recall@K > 80%)
- 回答质量评估(人工抽检)
- 流式输出(用户体验)
- 缓存层(常见问题缓存回答)
- 日志记录(问题 + 检索结果 + 回答)
- 限流 + 鉴权