系列目标:30 天从 LangChain 入门到企业级部署
今日任务:理解 RAG 工作流 → 掌握RetrievalQA链 → 实现“检索 + 生成 + 引用标注”三位一体问答系统!
🔗 一、RAG 是什么?为什么重要?
RAG = Retrieval-Augmented Generation(检索增强生成)
传统 LLM 的问题:
- ❌ 知识截止(如 Qwen 训练数据止于 2024)
- ❌ 幻觉(编造公司政策)
- ❌ 无法访问私有数据
RAG 的解决方案:
- 检索:从你的知识库(PDF/数据库等)找出相关片段
- 增强:将片段作为上下文注入 Prompt
- 生成:LLM 基于真实资料回答,减少幻觉
✅ 今天,我们就用 LangChain 的
RetrievalQAChain,5 行代码构建 RAG 系统!
🧱 二、RAG 核心组件回顾
表格
| 组件 | 来源 |
|---|---|
| 文档加载 | Day 16(PDF/Word/网页) |
| 向量存储 | Day 17(Chroma + nomic-embed-text) |
| LLM | Ollama + Qwen(本地运行) |
| 检索链 | 今日主角:RetrievalQA |
💡 所有组件均已就绪,只需“拼装”!
🛠️ 三、动手实践:构建端到端 RAG 问答系统
步骤 1:加载已有的 Chroma 向量库
# day18_rag_qa.py
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings, ChatOllama
# 加载 Embedding 模型
embeddings = OllamaEmbeddings(model="nomic-embed-text")
# 加载持久化的向量库(来自 Day 17)
vectorstore = Chroma(
persist_directory="./chroma_db",
embedding_function=embeddings
)
步骤 2:创建检索器(Retriever)
# 创建检索器,返回最相关的 3 个片段
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
🔍 可选
search_type="mmr"(最大边际相关性),提升多样性。
步骤 3:初始化 LLM
# 使用 Qwen 7B 生成答案
llm = ChatOllama(
model="qwen:7b",
temperature=0,
streaming=True # 支持流式输出(Day 15)
)
步骤 4:构建 RetrievalQA Chain
from langchain.chains import RetrievalQA
# 创建 RAG 链
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff", # 将所有检索结果塞入 prompt
retriever=retriever,
return_source_documents=True # 关键!返回引用来源
)
📌
chain_type选项:
"stuff":适合短文档(默认)"map_reduce"/"refine":适合长文档(Day 22 讲)
步骤 5:提问并查看带引用的回答
query = "员工年假最多可以休多少天?"
result = qa_chain.invoke({"query": query})
print("🤖 AI 回答:")
print(result["result"])
print("\n📚 引用来源:")
for i, doc in enumerate(result["source_documents"], 1):
print(f"{i}. {doc.metadata.get('source')} (页码: {doc.metadata.get('page')})")
print(f" 内容预览: {doc.page_content[:100]}...\n")
▶️ 输出示例:
🤖 AI 回答:
根据公司政策,员工每年最多可休15天带薪年假,未使用部分不可跨年累计。
📚 引用来源:
1. ./docs/company_policy.pdf (页码: 12)
内容预览: 第五章 年假管理 第三条:正式员工每年享有15天带薪年假...
2. ./docs/company_policy.pdf (页码: 13)
内容预览: 年假不可跨年度累计,未休年假将按日薪200%折算补偿...
✅ 完美实现:
- 基于真实文档回答
- 自动标注引用来源
- 避免幻觉
🎨 四、优化 Prompt:让回答更专业
默认 Prompt 可能不够精准,可自定义:
from langchain_core.prompts import PromptTemplate
prompt_template = """
你是一个公司HR助手,请基于以下上下文回答问题。
如果不知道答案,请说“根据现有资料无法回答”,不要编造。
上下文:
{context}
问题:{question}
回答:
"""
PROMPT = PromptTemplate(
template=prompt_template,
input_variables=["context", "question"]
)
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=retriever,
return_source_documents=True,
chain_type_kwargs={"prompt": PROMPT}
)
💡 自定义 Prompt 可控制语气、格式、安全边界!
⚠️ 五、注意事项 & 最佳实践
表格
| 问题 | 建议 |
|---|---|
| 检索结果不相关 | 调整 k 值;优化分块策略(Day 16) |
| LLM 忽略上下文 | 在 Prompt 中强调“必须基于上下文回答” |
| 引用太多干扰阅读 | 前端只展示“来源文件名”,点击展开详情 |
| 敏感信息泄露 | 在文档加载阶段脱敏(Day 16 + Day 14 Callback) |
| 响应慢 | 缓存高频问题;限制 k 值(通常 3~5 足够) |
💡 生产建议:
- 所有回答附带
source_documents,支持审计- 对“无法回答”的问题记录日志,持续补充知识库
📦 六、配套代码结构
langchain-30-days/
└── day18/
├── rag_qa_system.py # RAG 问答主程序
└── chroma_db/ # 向量库(来自 Day 17)
📝 七、今日小结
- ✅ 理解了 RAG 的核心价值:用私有知识增强 LLM
- ✅ 学会了用
RetrievalQA快速构建问答系统 - ✅ 实现了带引用溯源的专业回答
- ✅ 掌握了自定义 Prompt 优化回答质量
- ✅ 知道了 RAG 系统的常见陷阱与对策
🎯 明日预告:Day 19 —— RAG 进阶:多路召回、重排序(Rerank)、HyDE 查询扩展!