跳到主内容
AIHO 2026 全新改版上线

RAG 管道从零搭建:文档问答系统实战

适用场景

  • 公司内部知识库问答(HR 政策、技术文档、FAQ)
  • 法律/医疗文档检索 + 问答
  • 产品手册智能客服
  • 个人笔记/文献智能检索

RAG 架构总览

文档 → 切分 → Embedding → 存入向量数据库
                                    ↓
用户提问 → Embedding → 检索 Top-K → 重排序 → LLM 生成回答

技术选型

组件选型理由
框架LlamaIndex比 LangChain 更专注 RAG
EmbeddingBGE-large-zh-v1.5中文效果最佳,开源免费
向量数据库ChromaDB轻量,原型够用
重排序bge-reranker-large显著提升准确率
LLMGPT-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适用场景
小块256FAQ、短问答
中块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%)
  • 回答质量评估(人工抽检)
  • 流式输出(用户体验)
  • 缓存层(常见问题缓存回答)
  • 日志记录(问题 + 检索结果 + 回答)
  • 限流 + 鉴权