
这次我们来看一个面向大模型RAG检索增强生成的实战教程项目。这个项目旨在提供一套从零开始、手把手搭建企业级RAG系统的完整流程目标是帮助开发者避开常见陷阱快速构建可用的知识库问答应用。如果你正在寻找一个能落地、有代码、可复现的RAG学习路径这篇文章会直接带你走通核心环节。RAG的核心价值在于让大模型能够“引用”外部知识库来回答问题从而突破其固有知识的时间限制和幻觉问题。一个企业级的RAG系统远不止是简单的向量检索生成它涉及到文档处理、向量化、检索优化、提示工程、评估迭代等多个环节。本教程将围绕这些核心环节展开重点关注如何用主流的开源工具链如LangChain搭建一个健壮的系统。本文将带你完成以下内容首先梳理RAG项目的核心组件与选型考量然后一步步搭建开发环境准备测试数据接着实现一个基础的RAG流水线并进行效果测试之后我们会深入探讨如何优化检索质量与生成效果这是提升系统可用性的关键最后会讨论如何将原型封装为可部署的服务并给出持续迭代的建议。整个过程会以代码和实操为主概念讲解为辅。1. 核心能力速览本教程覆盖范围能力项说明技术栈以 Python 为主使用 LangChain 框架搭配向量数据库如 Chroma、嵌入模型如 BGE和开源大模型如 Qwen、ChatGLM。硬件门槛开发/测试阶段对GPU无强制要求。嵌入模型和7B以下的大模型可在CPU或消费级GPU上运行。生产级部署需根据模型尺寸和并发量配置GPU资源。核心流程文档加载 - 文本分割 - 向量化 - 存储 - 检索 - 提示构建 - 大模型生成 - 评估。关键优化点覆盖文档分块策略、检索器优化重排序、混合搜索、提示工程、上下文管理等企业级关注点。输出成果可获得一个具备RAG核心功能的本地Web应用或API服务能够基于自有文档进行问答。适合场景个人学习RAG技术、为企业构建内部知识库系统、开发智能客服原型、需要将大模型与特定领域知识结合的各类应用。2. RAG系统架构与核心组件解析在动手之前需要理解一个典型RAG系统的数据流和核心组件。这能帮助你在后续开发中明确每一步的目的。一个标准的RAG流程可以概括为“索引”和“查询”两个阶段索引阶段 (Indexing)文档加载从PDF、Word、TXT、Markdown、网页等来源读取原始文本。文本分割将长文档切割成适合检索的“块”Chunks。分块大小和重叠度是重要参数。向量化使用嵌入模型Embedding Model将文本块转换为高维向量。存储将向量及其对应的原始文本块元数据存入向量数据库。查询阶段 (Retrieval Generation)问题向量化将用户问题用同样的嵌入模型转换为向量。检索在向量数据库中搜索与问题向量最相似的文本块通常返回Top-K个。上下文构建将检索到的文本块组合成提示词的上下文部分。提示工程设计一个将“问题”和“上下文”有效组合的提示模板。生成将构建好的提示发送给大语言模型生成最终答案。本教程将使用LangChain作为编排框架它将这些组件模块化让我们可以灵活替换和组合。向量数据库我们选择轻量级的Chroma嵌入模型选用中文效果优秀的BAAI/bge-small-zh-v1.5大模型则使用开源的Qwen2.5-7B-Instruct或其他你方便的模型。3. 环境准备与依赖安装我们需要一个干净的Python环境。推荐使用Conda或venv进行环境隔离。3.1 创建并激活Python环境# 使用 conda conda create -n rag_tutorial python3.10 conda activate rag_tutorial # 或使用 venv python -m venv rag_tutorial # Windows rag_tutorial\Scripts\activate # Linux/Mac source rag_tutorial/bin/activate3.2 安装核心依赖创建一个requirements.txt文件内容如下langchain0.1.0 langchain-community0.0.10 langchain-chroma0.1.0 chromadb0.4.22 sentence-transformers2.2.2 unstructured[pdf,docx]0.10.30 pypdf3.17.0 fastapi0.104.1 uvicorn[standard]0.24.0 pydantic2.5.0然后安装pip install -r requirements.txt -i https://pypi.tuna.tsinghua.edu.cn/simple3.3 准备大模型本项目需要一个大语言模型进行文本生成。你有多种选择本地部署使用ollama运行qwen2.5:7b或使用vLLM、Transformers部署。API调用使用阿里云灵积、百度千帆、OpenAI等平台的API需付费和网络条件。为了教程的完整性和本地可复现性我们以使用ollama本地运行qwen2.5:7b为例。 首先安装并启动Ollama服务然后拉取模型# 拉取模型 (确保网络通畅模型约4.7GB) ollama pull qwen2.5:7b # 运行模型服务指定端口 ollama run qwen2.5:7b # 注意以上run命令会启动一个交互式会话。对于API调用通常需要以服务模式运行。 # 更常见的做法是使用Ollama的API它默认在11434端口提供服务。4. 搭建基础RAG流水线现在开始编写核心代码。我们将创建一个简单的Python脚本实现完整的索引和查询流程。4.1 文档加载与处理首先在项目根目录创建一个docs文件夹放入你的测试文档例如PDF、TXT。然后创建rag_core.py脚本。# rag_core.py import os from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_huggingface import HuggingFaceEmbeddings from langchain_chroma import Chroma from langchain.prompts import ChatPromptTemplate from langchain_community.llms import Ollama from langchain_core.output_parsers import StrOutputParser from langchain_core.runnables import RunnablePassthrough # 1. 加载文档 def load_documents(directory_path./docs): loader DirectoryLoader(directory_path, glob**/*.pdf, loader_clsPyPDFLoader) documents loader.load() print(f已加载 {len(documents)} 个文档) return documents # 2. 分割文本 def split_documents(documents): text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块的大小 chunk_overlap50, # 块之间的重叠 length_functionlen, separators[\n\n, \n, 。, , , , , , ] ) chunks text_splitter.split_documents(documents) print(f文档被分割成 {len(chunks)} 个文本块) return chunks # 3. 初始化嵌入模型和向量数据库 def create_vectorstore(chunks): # 使用BGE小型中文模型 embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-small-zh-v1.5, model_kwargs{device: cpu}, # 有GPU可改为cuda encode_kwargs{normalize_embeddings: True} ) # 将向量存储到Chroma持久化到本地chroma_db目录 vectorstore Chroma.from_documents( documentschunks, embeddingembeddings, persist_directory./chroma_db ) vectorstore.persist() print(向量数据库已创建并持久化) return vectorstore # 4. 构建RAG链 def create_rag_chain(vectorstore): # 定义检索器搜索最相似的3个块 retriever vectorstore.as_retriever(search_kwargs{k: 3}) # 定义提示模板 template 请根据以下上下文信息回答问题。如果上下文信息不足以回答问题请直接说“根据提供的信息无法回答该问题”不要编造答案。 上下文 {context} 问题{question} 请给出专业、准确的答案 prompt ChatPromptTemplate.from_template(template) # 初始化大模型这里连接本地Ollama服务 llm Ollama(modelqwen2.5:7b, base_urlhttp://localhost:11434) # 构建RAG链 rag_chain ( {context: retriever, question: RunnablePassthrough()} | prompt | llm | StrOutputParser() ) return rag_chain if __name__ __main__: # 主流程首次运行需要执行索引 print( 开始构建RAG系统 ) docs load_documents() if docs: chunks split_documents(docs) vs create_vectorstore(chunks) chain create_rag_chain(vs) # 测试查询 test_question 本文档主要讲了什么内容 print(f\n测试问题{test_question}) answer chain.invoke(test_question) print(f模型回答{answer}) else: print(未找到文档请在./docs目录下放置PDF文件。)4.2 首次运行与测试将你的PDF文档放入./docs目录。确保Ollama服务正在运行ollama run qwen2.5:7b或服务模式。运行脚本python rag_core.py观察输出。脚本会加载文档、分割、生成向量并存入./chroma_db目录最后问一个测试问题。如果一切顺利你会看到模型生成的答案。5. 功能测试与效果验证基础流程跑通后我们需要系统性地测试RAG系统的各个环节是否工作正常。5.1 检索质量测试检索是RAG的基石。我们需要验证向量数据库是否能返回相关的内容。# 在 rag_core.py 中添加测试函数 def test_retrieval(vectorstore, question, k3): retriever vectorstore.as_retriever(search_kwargs{k: k}) docs retriever.invoke(question) print(f\n问题{question}) print(f检索到的Top-{k}文本块) for i, doc in enumerate(docs): print(f\n--- 块 {i1} (长度{len(doc.page_content)}) ---) # 打印前200个字符预览 print(doc.page_content[:200] ...) print(f来源{doc.metadata.get(source, N/A)}) return docs # 在主流程中调用测试 if __name__ __main__: # ... 之前的加载和创建vectorstore代码 ... vs create_vectorstore(chunks) # 测试检索 test_questions [什么是机器学习, 文档中提到了哪些关键技术, 总结一下第一章的内容。] for q in test_questions: test_retrieval(vs, q)判断标准检索到的文本块应该与问题语义高度相关。如果返回不相关的内容可能需要调整分块策略chunk_size,chunk_overlap或尝试不同的嵌入模型。5.2 生成质量测试检索到相关上下文后需要测试大模型能否基于上下文生成准确、流畅的答案。def test_generation(rag_chain, question_context_pairs): 测试模型根据给定上下文生成答案的能力 for question, expected_context_keyword in question_context_pairs: print(f\n[测试] 问题{question}) print(f 期望上下文中包含的关键词{expected_context_keyword}) answer rag_chain.invoke(question) print(f 模型答案{answer[:300]}...) # 截断显示 # 人工判断答案是否基于上下文是否有幻觉常见问题与排查答案与上下文无关检查提示模板template是否将{context}正确传递给了模型。可能是上下文没有被正确格式化到提示中。答案出现幻觉编造在提示词中加强指令如“严格根据上下文回答”、“如果上下文没有提到请说不知道”。答案冗长或格式差在提示词中指定输出格式例如“请用简洁的列表形式回答”。5.3 端到端集成测试模拟真实用户场景提出一系列问题观察最终输出。def run_qa_session(rag_chain): print(\n RAG问答会话开始 ) questions [ RAG系统的主要优势是什么, 本文档的撰写者是谁, # 测试模型对未知信息的处理 请列出文档中提到的三个主要步骤。 ] for q in questions: print(f\n用户{q}) answer rag_chain.invoke(q) print(f系统{answer}) print( 会话结束 )6. 关键优化策略与实践基础版RAG往往效果不佳。以下是提升系统效果的核心优化方向。6.1 优化文本分块分块是源头直接影响检索精度。策略选择对于技术文档按章节/标题分块可能比固定长度分块更好。可以尝试MarkdownHeaderTextSplitter。大小调整chunk_size太小会丢失全局信息太大会引入噪声。通常从256、512、1024等值开始尝试。重叠设置chunk_overlap确保上下文连贯通常设为chunk_size的10%-20%。6.2 优化检索器增加重排序Rerank初步检索返回Top-K如10个结果后使用一个更精细的交叉编码器模型对它们进行重排序只取最相关的Top-N如3个送入大模型。这能显著提升精度。# 示例使用BGE重排序模型需额外安装flag-embedding from langchain.retrievers import ContextualCompressionRetriever from langchain.retrievers.document_compressors import CrossEncoderReranker from langchain_community.cross_encoders import HuggingFaceCrossEncoder cross_encoder HuggingFaceCrossEncoder(model_nameBAAI/bge-reranker-base) compressor CrossEncoderReranker(modelcross_encoder, top_n3) compression_retriever ContextualCompressionRetriever( base_compressorcompressor, base_retrievervectorstore.as_retriever(search_kwargs{k: 10}) ) # 然后将 compression_retriever 用于RAG链混合搜索结合向量搜索语义相似和关键词搜索如BM25。LangChain的EnsembleRetriever可以融合多个检索器的结果。元数据过滤检索时增加过滤条件如按文档来源、章节、日期等筛选。6.3 优化提示工程提示词是指导模型行为的“说明书”。明确指令要求模型“根据上下文”、“引用原文”、“不知道就说不知道”。提供格式示例对于列表、总结等任务在上下文中给出一个例子。角色设定让模型扮演“专业的技术助理”。迭代优化收集bad cases分析是检索问题还是生成问题针对性调整提示词。6.4 优化上下文管理上下文窗口限制大模型有上下文长度限制。需要确保检索到的所有块的总长度不超过限制并为模型指令和答案留出空间。上下文去重检索到的不同块可能包含重复信息可以在送入模型前进行去重或摘要。历史对话对于多轮问答需要将历史对话也纳入上下文管理。7. 构建Web服务与API接口一个原型最终需要封装成服务。我们使用FastAPI来构建一个简单的Web API。创建app.py文件# app.py from fastapi import FastAPI, HTTPException from pydantic import BaseModel from typing import List, Optional import uvicorn from rag_core import create_rag_chain, create_vectorstore_from_existing import os app FastAPI(title企业级RAG问答API, version1.0) # 全局变量存储已初始化的RAG链 rag_chain None class QueryRequest(BaseModel): question: str top_k: Optional[int] 3 class QueryResponse(BaseModel): question: str answer: str source_documents: Optional[List[dict]] [] app.on_event(startup) async def startup_event(): 启动服务时加载向量库和RAG链 global rag_chain print(正在加载向量数据库和RAG链...) try: # 假设向量库已构建好从本地加载 vectorstore create_vectorstore_from_existing() # 你需要实现这个函数 rag_chain create_rag_chain(vectorstore) print(RAG链加载成功) except Exception as e: print(f启动失败: {e}) raise app.post(/query, response_modelQueryResponse) async def query_rag_system(request: QueryRequest): if rag_chain is None: raise HTTPException(status_code503, detailRAG系统未就绪) try: # 这里可以扩展例如先调用检索器获取来源文档 answer rag_chain.invoke({question: request.question}) # 简化处理实际应返回检索到的文档信息 return QueryResponse(questionrequest.question, answeranswer) except Exception as e: raise HTTPException(status_code500, detailf查询处理失败: {str(e)}) app.get(/health) async def health_check(): return {status: healthy, rag_chain_ready: rag_chain is not None} if __name__ __main__: # 启动服务默认在 http://127.0.0.1:8000 uvicorn.run(app, host0.0.0.0, port8000)同时在rag_core.py中补充从已有数据库加载的函数def create_vectorstore_from_existing(persist_directory./chroma_db): embeddings HuggingFaceEmbeddings(model_nameBAAI/bge-small-zh-v1.5) vectorstore Chroma(persist_directorypersist_directory, embedding_functionembeddings) return vectorstore启动服务python app.py访问http://127.0.0.1:8000/docs可以看到自动生成的API文档并可以直接测试/query接口。8. 资源占用与性能观察在开发和部署时需要关注系统资源消耗。CPU/内存占用嵌入模型BAAI/bge-small-zh在CPU上推理时会占用一定的CPU和内存。处理大量文档索引时内存消耗会上升。大模型Qwen2.5-7B在CPU上推理非常慢且占用大量内存16GB在GPU如RTX 4060 8G上运行则速度较快显存占用约6-7GB。向量数据库Chroma在内存中维护索引文档和向量越多内存占用越大。性能瓶颈索引阶段文档解析和向量化最耗时可以考虑异步或批处理。查询阶段向量检索速度很快主要耗时在大模型生成答案。可以通过设置max_tokens控制生成长度来调节响应时间。监控建议使用htop、nvidia-smi或Python的psutil库来监控进程的资源使用情况。9. 常见问题与排查方法问题现象可能原因排查方式解决方案运行python rag_core.py报错No module named ‘langchain_...‘依赖未正确安装检查pip list确认包是否安装重新安装requirements.txt注意LangChain版本兼容性Ollama 连接失败Ollama服务未启动或端口不对在浏览器访问http://localhost:11434启动Ollama服务ollama serve(后台) 或ollama run model检索结果完全不相关1. 嵌入模型不匹配2. 分块大小不合理3. 文档语言与模型不匹配1. 检查嵌入模型名称2. 打印检索到的文本块内容3. 测试嵌入模型对简单句子的相似度1. 更换嵌入模型如BAAI/bge-large-zh2. 调整chunk_size和chunk_overlap3. 确保使用中文优化的模型处理中文文档模型回答“根据提供的信息无法回答”但上下文明显相关提示词指令不够强或模型理解有误检查构建的最终提示词prompt格式强化提示词例如“你必须使用以下上下文来回答问题上下文一定包含答案。”向量数据库加载失败提示“Collection not found”首次运行未成功创建数据库或路径错误检查./chroma_db目录是否存在及内容确保先成功运行索引流程或检查persist_directory路径API服务响应慢1. 大模型生成慢2. 检索文档过多1. 观察GPU利用率2. 检查top_k参数1. 考虑使用更小模型或API2. 减少top_k或启用重排序精选结果处理PDF时乱码或空白PDF是扫描件或特殊编码使用unstructured库的其他加载器或OCR功能尝试UnstructuredPDFLoader或先用OCR工具处理PDF10. 企业级项目进阶与最佳实践要将原型发展为可用的企业级项目还需要考虑以下方面文档预处理管道格式支持扩展支持更多格式Excel、PPT、图片OCR、音视频转文本。文档解析使用更鲁棒的解析库如unstructured处理复杂的版面。文本清洗去除页眉页脚、无关符号、标准化格式。可观测性与评估日志记录记录每一次查询的问题、检索到的文档、生成的答案、耗时。效果评估构建测试集定期评估检索命中率、答案准确率。可以使用RAGAS、TruLens等框架。反馈闭环提供“点赞/点踩”功能收集bad cases用于优化。系统架构与部署服务化将索引服务、检索服务、模型服务解耦通过消息队列如Redis通信。缓存对常见问题答案进行缓存减少模型调用。异步处理索引大量文档时使用异步任务队列如Celery。容器化使用Docker封装环境便于部署和扩展。安全与合规权限控制API接口增加认证API Key/JWT。内容审核对用户输入和模型输出进行安全过滤。数据隐私敏感文档需脱敏处理向量数据库部署在内网。版权合规确保输入文档有合法使用授权生成内容不侵犯版权。从搭建第一个可运行的RAG管道到构建一个健壮、可评估、可部署的企业级系统中间有大量的细节需要打磨。本教程提供了完整的起点和关键的优化方向。建议你先按照步骤跑通基础流程然后选择一个最影响你当前效果的环节通常是检索或提示词进行深度优化。持续迭代和基于数据的评估是构建高质量RAG系统的唯一路径。