一、今天要做什么前 6 天我们逐一学习了 RAG 的每个核心组件Day01RAG 整体架构Day02文本分块ChunkingDay03Embedding 模型Day04向量数据库Day05检索策略Day06提示词工程今天把它们全部串联起来从零搭建一个可以本地运行的 PDF 问答系统。二、系统架构设计完整代码约 150 行包含错误处理和日志输出。三、环境准备pip install langchain langchain-openai langchain-community \ chromadb pymupdf rank_bm25 python-dotenv创建.env文件OPENAI_API_KEYyour_api_key_here四、完整实现代码4.1 文档加载与预处理# rag_pdf_qa.pyimport osfrom pathlib import Pathfrom dotenv import load_dotenvload_dotenv()# 1. 文档加载 from langchain_community.document_loaders import PyMuPDFLoaderdef load_documents(pdf_path: str) - list: 加载 PDF 文档返回页面列表 loader PyMuPDFLoader(pdf_path) docs loader.load() print(f✅ 加载文档{pdf_path}共 {len(docs)} 页) return docs4.2 文本分块# 2. 文本分块 from langchain.text_splitter import RecursiveCharacterTextSplitterdef split_documents(docs: list) - list: 将文档分割为适合检索的小块 splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , ., , ] ) chunks splitter.split_documents(docs) # 为每个块添加来源元数据 for i, chunk inenumerate(chunks): chunk.metadata[chunk_id] i chunk.metadata[chunk_total] len(chunks) print(f✅ 分块完成{len(chunks)} 个文本块f平均长度 {sum(len(c.page_content) for c in chunks)//len(chunks)} 字) return chunks4.3 向量化与存储# 3 4. 向量化与向量数据库 from langchain_openai import OpenAIEmbeddingsfrom langchain_community.vectorstores import Chromadef build_vectorstore(chunks: list, persist_dir: str ./chroma_db) - Chroma: 构建向量索引如已存在则加载 embeddings OpenAIEmbeddings(modeltext-embedding-3-small) if Path(persist_dir).exists(): print(f✅ 加载已有向量库{persist_dir}) vectorstore Chroma( persist_directorypersist_dir, embedding_functionembeddings ) else: print(f⏳ 构建向量库首次运行需要调用 Embedding API...) vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directorypersist_dir ) print(f✅ 向量库构建完成已持久化到 {persist_dir}) return vectorstore4.4 混合检索器# 5. 混合检索 from langchain.retrievers import EnsembleRetrieverfrom langchain_community.retrievers import BM25Retrieverdef build_hybrid_retriever(chunks: list, vectorstore: Chroma) - EnsembleRetriever: 构建混合检索器向量 BM25 # 向量检索器 vector_retriever vectorstore.as_retriever( search_typemmr, # 使用 MMR 保证结果多样性 search_kwargs{k: 5, fetch_k: 20, lambda_mult: 0.7} ) # BM25 关键词检索器 bm25_retriever BM25Retriever.from_documents(chunks) bm25_retriever.k 5 # 混合检索器6:4 权重 ensemble EnsembleRetriever( retrievers[vector_retriever, bm25_retriever], weights[0.6, 0.4] ) print(✅ 混合检索器构建完成向量 60% BM25 40%) return ensemble4.5 提示词模板# 6. 提示词构建 from langchain_core.prompts import ChatPromptTemplateSYSTEM_PROMPT 你是一位严谨的文档问答助手。## 规则1. **只**基于下方参考资料中的内容回答2. 每个关键信息后用 [文档X 第Y页] 格式标注来源3. 若参考资料不足以回答明确说「根据现有资料无法回答此问题。」4. 禁止使用训练知识补充答案5. 使用清晰的 Markdown 格式输出USER_TEMPLATE ## 参考资料{context}---## 问题{question}prompt ChatPromptTemplate.from_messages([ (system, SYSTEM_PROMPT), (human, USER_TEMPLATE)])def format_docs(docs: list) - str:格式化检索结果为结构化上下文 # 按相关性重排首尾放最相关避免 Lost-in-the-Middle reordered [] for i, doc inenumerate(docs): if i % 2 0: reordered.append(doc) else: reordered.insert(0, doc) formatted [] for i, doc inenumerate(reordered, 1): source Path(doc.metadata.get(source, 未知)).name page doc.metadata.get(page, ?) formatted.append(f[文档{i} 第{page}页 | {source}]\n{doc.page_content} ) return\n\n---\n\n.join(formatted)4.6 LLM 生成与完整链路# 7. 生成与完整 Chain from langchain_openai import ChatOpenAIfrom langchain_core.output_parsers import StrOutputParserfrom langchain_core.runnables import RunnablePassthroughdef build_rag_chain(retriever, prompt): 构建完整 RAG 链路 llm ChatOpenAI(modelgpt-4o-mini, temperature0) chain ( {context: retriever | format_docs,question: RunnablePassthrough() } | prompt | llm | StrOutputParser() ) return chain4.7 主程序入口# 主程序 def main(): import sys # 默认使用示例 PDF也可以通过命令行参数指定 pdf_path sys.argv[1] if len(sys.argv) 1 else sample.pdf if not Path(pdf_path).exists(): print(f❌ 文件不存在{pdf_path}) print(请提供一个 PDF 文件路径例如python rag_pdf_qa.py my_doc.pdf) return print( * 50) print( RAG PDF 问答系统启动) print( * 50) # 构建索引首次运行约需 1~2 分钟 docs load_documents(pdf_path) chunks split_documents(docs) vectorstore build_vectorstore(chunks) retriever build_hybrid_retriever(chunks, vectorstore) rag_chain build_rag_chain(retriever, prompt) print(\n✅ 系统就绪输入问题开始问答输入 quit 退出\n) while True: question input(❓ 请输入问题).strip() if question.lower() in (quit, exit, q): print( 再见) break if not question: continue print(\n⏳ 检索与生成中...\n) answer rag_chain.invoke(question) print(f 回答\n{answer}\n) print(- * 50) if __name__ __main__: main()五、运行效果示例假设 PDF 是一份《RAG 技术白皮书》运行效果如下 RAG PDF 问答系统启动✅ 加载文档rag_whitepaper.pdf共 32 页✅ 分块完成186 个文本块平均长度 412 字✅ 向量库构建完成已持久化到 ./chroma_db✅ 混合检索器构建完成向量 60% BM25 40%✅ 系统就绪输入问题开始问答输入 quit 退出❓ 请输入问题RAG 和微调的区别是什么⏳ 检索与生成中... 回答RAG 和微调的主要区别体现在以下几个维度**知识更新方式**- RAG 通过更新外部知识库实现实时知识更新无需重新训练模型 [文档1 第5页]- 微调需要重新训练模型参数周期较长通常数天到数周[文档3 第12页]**成本对比**- RAG 主要成本为向量存储和检索计算远低于微调所需的 GPU 资源 [文档2 第8页]六、进阶优化方向系统跑通之后可以继续优化优化方向具体方法预期效果提升检索精度加入重排序模型Day09 会讲15~20% 精度优化分块改用父子分块Day02更完整上下文查询改写自动扩展用户问题Day0810% 召回率流式输出rag_chain.stream(question)更好的用户体验多文档支持支持文件夹批量加载扩大知识库规模对话历史加入 Chat History 支持多轮对话支持追问七、第一阶段总结至此RAG 第一阶段基础认知圆满结束回顾 7 天的学习Day01 → RAG 是什么架构全景Day02 → 文本分块Chunking 策略Day03 → Embedding 模型向量化Day04 → 向量数据库存储与检索Day05 → 检索策略BM25/向量/混合Day06 → 提示词工程防幻觉Day07 → 端到端实战本地 PDF 问答下周预告进阶优化阶段Day08查询改写——HyDE、多查询扩展Day09重排序——Cross-Encoder 精排Day10RAG 评估体系——RAGAS 框架知识点索引#知识点说明1PyMuPDF高性能 PDF 解析库支持文字、图表、表格提取2LangChainLLM 应用开发框架提供完整 RAG 工具链3LCELLangChain Expression Language链式组合语法 4RunnablePassthroughLangChain 中透传用户输入的组件5Chroma PersistChroma 向量库持久化避免重复构建索引6MMR Retrieval最大边际相关性检索保证结果多样性7StrOutputParser将 LLM 输出解析为纯字符串8StreamOutput流式输出逐 token 返回提升用户体验学AI大模型的正确顺序千万不要搞错了2026年AI风口已来各行各业的AI渗透肉眼可见超多公司要么转型做AI相关产品要么高薪挖AI技术人才机遇直接摆在眼前有往AI方向发展或者本身有后端编程基础的朋友直接冲AI大模型应用开发转岗超合适就算暂时不打算转岗了解大模型、RAG、Prompt、Agent这些热门概念能上手做简单项目也绝对是求职加分王给大家整理了超全最新的AI大模型应用开发学习清单和资料手把手帮你快速入门学习路线:✅大模型基础认知—大模型核心原理、发展历程、主流模型GPT、文心一言等特点解析✅核心技术模块—RAG检索增强生成、Prompt工程实战、Agent智能体开发逻辑✅开发基础能力—Python进阶、API接口调用、大模型开发框架LangChain等实操✅应用场景开发—智能问答系统、企业知识库、AIGC内容生成工具、行业定制化大模型应用✅项目落地流程—需求拆解、技术选型、模型调优、测试上线、运维迭代✅面试求职冲刺—岗位JD解析、简历AI项目包装、高频面试题汇总、模拟面经以上6大模块看似清晰好上手实则每个部分都有扎实的核心内容需要吃透我把大模型的学习全流程已经整理好了抓住AI时代风口轻松解锁职业新可能希望大家都能把握机遇实现薪资/职业跃迁这份完整版的大模型 AI 学习资料已经上传CSDN朋友们如果需要可以微信扫描下方CSDN官方认证二维码免费领取【保证100%免费】