尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

从零搭建RAG系统:实战指南与核心优化策略

从零搭建RAG系统:实战指南与核心优化策略 1. 先搞清楚RAG到底解决什么问题以及为什么现在值得看如果你正在接触大模型应用开发或者想把手头的文档、知识库接入AI那“检索增强生成”是你绕不开的一个坎。它不是什么新概念但这两年因为大模型的能力爆发RAG从一个学术概念变成了企业落地AI最实在的路径之一。简单说RAG的核心就一句话让大模型在回答问题时能先“翻书”找资料而不是全凭记忆瞎编。这解决了大模型应用里最头疼的两个问题幻觉和知识陈旧。模型自己记不住你公司最新的产品手册也背不全你私有的技术文档但它能学会从你提供的资料库里检索相关内容然后基于这些“证据”来生成答案。这样出来的回答准确性、时效性和针对性都强得多。所以当看到“2026年最值得看的RAG教程”这个标题时先别管年份关键要看它是不是抓住了当前RAG落地的真痛点。一个真正有用的教程不应该只讲“RAG是什么”的概念而应该带你走通从零搭建一个能跑、能用、能迭代的检索增强系统的全过程。这包括了文档怎么处理、向量数据库怎么选、检索策略怎么定、以及最后怎么把整个流程串起来并部署上线。下面我就以一个实际构建者的视角拆解搭建RAG系统的关键环节和避坑点。无论你是想学习原理还是打算动手做一个自己的知识库问答应用都可以按这个思路来。2. 环境与工具准备别在第一步就卡住动手之前先把环境和工具理清楚。RAG不是一个单一工具而是一个技术栈你需要为每个环节选择合适的组件。别一上来就追求最全最新的组合先从能跑通的最小闭环开始。2.1 核心组件选型一个基础的RAG系统通常包含以下几个部分你可以根据你的技术栈和资源来搭配文档加载与解析器负责读取你的原始文件PDF、Word、TXT、网页等并转换成纯文本。常用工具有LangChain / LlamaIndex提供了丰富的文档加载器UnstructuredLoader,PyPDFLoader等是当前的主流选择。LangChain生态更广LlamaIndex在检索增强领域更专注。Apache Tika老牌的文档内容提取工具适合处理复杂格式。简单需求如果只是处理txt或markdown用Python标准库open就够。文本分割器大模型有上下文长度限制不能把整本书塞进去。需要把长文本切成有意义的“块”。这里的关键是分割策略固定长度分割简单但可能把一句话或一个概念拦腰截断。按分隔符分割如按段落、标题。更符合语义但块大小不均。高级策略重叠分割相邻块保留部分重叠内容防止信息丢失、基于语义的分割用模型判断分割点。新手建议从按段落/标题分割开始再逐步优化。嵌入模型把文本块转换成计算机能理解的“向量”一组数字。这个模型的选择直接决定了检索质量。本地模型如BGE、text2vec系列。部署在本地数据隐私有保障但需要GPU或较强的CPU。API模型如OpenAI的text-embedding-ada-002智谱、百度等国内厂商也提供。省事但会产生调用费用和数据出境风险需注意合规。选择建议优先测试开源的BGE模型它在中文社区评测中表现不错且有多种尺寸如BGE-small适合轻量级测试。向量数据库存储和快速检索向量。这是RAG的“记忆仓库”。轻量级/学习用Chroma、FAISS。纯内存或本地文件存储上手极快。生产级/分布式Milvus、Qdrant、Weaviate、Pinecone云服务。支持持久化、分布式、高级过滤和混合检索。选型关键考虑数据量百万级以下FAISS/Chroma够用以上考虑Milvus、是否需要持久化和高可用、团队技术栈如熟悉Spring生态可考虑Spring AI集成。大语言模型负责最终的答案生成。它接收“用户问题”和“检索到的相关文本块”合成最终答案。闭源APIGPT-4、Claude、文心一言、通义千问等。效果稳定但成本可控性和数据隐私是考量点。开源模型Llama 3、Qwen、ChatGLM等。可私有化部署成本固定但对硬件有要求。起步建议先用一个你最容易获取的API模型如GPT-3.5-Turbo或国内主流厂商的入门模型跑通流程验证整体可行性再考虑优化或替换为开源模型。应用框架用来编排以上所有组件。LangChain和LlamaIndex是两大主流它们抽象了RAG的通用流程提供了大量现成的模块和链能极大减少你的胶水代码。LangChain更像“AI应用的乐高”组件通用性强生态庞大。LlamaIndex更专注于“数据连接和检索增强”在数据加载、索引构建、高级检索策略上更深入。如何选如果你构建的应用不仅仅是RAG还涉及Agent、复杂工作流LangChain更合适。如果你核心就是做高性能、定制化的RAGLlamaIndex可能更顺手。新手可以从LangChain开始资料更多。2.2 本地开发环境搭建假设我们选择一条比较通用的技术路径LangChainBGE嵌入模型Chroma向量库OpenAI API。环境准备如下# 1. 创建并进入项目目录 mkdir my-rag-project cd my-rag-project python -m venv venv # 创建虚拟环境 # 2. 激活虚拟环境 # Windows: venv\Scripts\activate # macOS/Linux: source venv/bin/activate # 3. 安装核心依赖 pip install langchain langchain-community langchain-chroma # LangChain核心及Chroma集成 pip install sentence-transformers # 用于运行BGE等开源嵌入模型 pip install chromadb # 向量数据库 pip install pypdf # 用于读取PDF pip install tiktoken # 用于文本分割计数 # 4. 安装文档加载器按需 pip install unstructured # 通用文档解析 pip install pdf2image # 如果unstructured处理PDF需要 # 注意unstructured可能依赖系统库如poppler、tesseract请根据官方文档安装 # 5. 可选如果你打算用OpenAI API pip install openai # 并设置环境变量 # export OPENAI_API_KEYyour-api-key # 或在代码中设置关键避坑点虚拟环境务必使用虚拟环境隔离依赖避免包冲突。Unstructured依赖这个库功能强大但系统依赖多如果安装报错可以先用简单的PyPDFLoader处理PDF或者直接从纯文本开始测试。嵌入模型下载sentence-transformers会在第一次运行时下载模型确保网络通畅或者提前下载好模型文件到本地指定路径。3. 从0到1搭建一个最小可行RAG系统现在我们抛开所有复杂概念用最直接的代码构建一个能回答你文档内容的小系统。这个过程分为四步喂文档、存向量、搜资料、生成答案。3.1 第一步文档加载与文本分割假设你有一个knowledge_base文件夹里面放了几份PDF和TXT文档。from langchain_community.document_loaders import DirectoryLoader, PyPDFLoader, TextLoader from langchain.text_splitter import RecursiveCharacterTextSplitter # 1. 加载文档 loader DirectoryLoader( path./knowledge_base, glob**/*.pdf, # 加载所有PDF loader_clsPyPDFLoader, # 指定使用PDF加载器 # 如果要混合加载多种格式可以写多个loader或者使用UnstructuredLoader ) documents loader.load() print(f加载了 {len(documents)} 个文档片段) # 如果你的文档是txt # loader DirectoryLoader(./knowledge_base, glob*.txt, loader_clsTextLoader) # 2. 分割文本 text_splitter RecursiveCharacterTextSplitter( chunk_size500, # 每个块的最大字符数 chunk_overlap50, # 块之间的重叠字符数防止上下文断裂 length_functionlen, separators[\n\n, \n, 。, , , , , , ] # 中文优先的分隔符 ) split_docs text_splitter.split_documents(documents) print(f分割后得到 {len(split_docs)} 个文本块) print(第一个文本块预览, split_docs[0].page_content[:200])参数解释与避坑chunk_size这是最重要的参数之一。太小信息可能不完整太大可能超出模型上下文且检索精度下降。对于GPT-3.5/4500-1000是个不错的起点。务必根据你的文档内容是技术文档还是小说和后续使用的模型上下文窗口来调整。chunk_overlap设置重叠可以避免一个完整的句子或概念被切到两个块边缘而导致信息丢失。一般设为chunk_size的10%-20%。separators默认按英文标点分割对中文不友好。一定要像上面那样加入中文标点作为分隔符这样切出来的块更符合语义。3.2 第二步向量化与存储接下来我们把切好的文本块变成向量存进数据库。from langchain_chroma import Chroma from langchain_huggingface import HuggingFaceEmbeddings # 1. 选择嵌入模型 # 使用开源的BGE模型模型会自动从HuggingFace下载 embeddings HuggingFaceEmbeddings( model_nameBAAI/bge-small-zh-v1.5, # 中文小模型适合测试 model_kwargs{device: cpu}, # 如果没有GPU就用cpu encode_kwargs{normalize_embeddings: True} # 标准化向量有助于提升检索效果 ) # 如果想用OpenAI的嵌入模型 # from langchain_openai import OpenAIEmbeddings # embeddings OpenAIEmbeddings(modeltext-embedding-ada-002) # 2. 创建向量数据库并存储 # persist_directory 指定持久化目录这样下次启动就不用重新生成了 vectorstore Chroma.from_documents( documentssplit_docs, embeddingembeddings, persist_directory./chroma_db # 向量库保存到本地目录 ) print(向量数据库已创建并持久化到 ./chroma_db)关键操作与验证首次运行这一步最耗时因为要计算所有文本块的向量。耐心等待。验证存储存完后可以简单查询一下确认数据已入库。# 进行一个简单相似性搜索测试 test_query 什么是机器学习 results vectorstore.similarity_search(test_query, k2) for i, doc in enumerate(results): print(f结果 {i1}: {doc.page_content[:150]}...)持久化指定了persist_directory后向量库会保存到磁盘。下次启动可以直接加载无需重新计算向量。# 第二次启动时直接加载 vectorstore Chroma(persist_directory./chroma_db, embedding_functionembeddings)3.3 第三步组装检索与生成链这是RAG的核心我们将检索器和语言模型组装成一个“流水线”。from langchain.chains import RetrievalQA from langchain_openai import ChatOpenAI # 如果你用其他模型例如智谱AI # from langchain_community.chat_models import ChatZhipuAI # 1. 定义LLM # 使用OpenAI GPT-3.5你需要设置环境变量 OPENAI_API_KEY llm ChatOpenAI(modelgpt-3.5-turbo, temperature0.1) # temperature 控制创造性0.1表示更确定、更少胡编 # 2. 将向量数据库转换为检索器 retriever vectorstore.as_retriever( search_typesimilarity, # 相似度搜索 search_kwargs{k: 4} # 每次检索返回4个最相关的文本块 ) # 3. 创建检索增强生成链 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, # 最常用的类型将所有检索到的上下文“塞”进提示词 retrieverretriever, return_source_documentsTrue, # 返回源文档方便追溯答案来源 verboseTrue # 打印详细日志调试时非常有用 )参数深度解析search_type除了similarity余弦相似度还有mmr最大边际相关性后者在保证相关性的同时兼顾多样性避免返回内容过于同质。k检索返回的文本块数量。不是越多越好太多会挤占生成模型的上下文窗口也可能引入噪声。一般从3-5开始测试。chain_typestuff最简单直接把所有检索到的上下文拼接起来一起发给LLM。适合上下文总长度不长的情况。map_reduce先对每个检索到的文档单独生成答案再汇总。适合处理非常多的检索结果但速度慢、成本高。refine迭代式生成用第一个文档生成答案再用后续文档去优化。质量可能更高但更慢。新手无脑选stuff绝大多数场景够用。return_source_documents务必设为True。这是RAG可解释性的关键让你知道答案是从哪几个原文块来的便于验证和调试。3.4 第四步提问与验证现在你的RAG系统已经可以工作了。# 提出问题 question 根据文档RAG系统的主要优势是什么 result qa_chain.invoke({query: question}) print(问题, question) print(\n--- 生成的答案 ---) print(result[result]) print(\n--- 参考来源前2个---) for i, doc in enumerate(result[source_documents][:2]): print(f\n来源 {i1} (长度{len(doc.page_content)}):) print(doc.page_content[:300]) # 预览前300字符运行这段代码你应该能看到模型基于你的文档生成的答案以及它参考了哪些原文片段。恭喜一个最基础的RAG系统已经搭建完成4. 从“能跑”到“好用”核心环节的深度优化基础流程跑通只是第一步。要让RAG真正产生价值必须在以下几个环节下功夫。这也是区分“玩具”和“工具”的关键。4.1 检索质量优化找到真正相关的资料检索是RAG的基石检索不准后面生成再好也白搭。1. 优化文本分割策略尝试不同的分割器除了RecursiveCharacterTextSplitter还有按标记Token分割的TokenTextSplitter更精确控制LLM上下文占用以及基于语义的SemanticChunker需要嵌入模型计算量大但块质量高。针对文档类型定制技术文档可以按章节/标题分割对话记录可以按说话人分割代码可以按函数/类分割。没有银弹必须根据你的数据特点来调整。2. 升级检索策略混合检索结合稠密向量检索语义相似和稀疏向量检索关键词匹配如BM25。向量检索擅长语义但可能漏掉精确术语关键词检索擅长精确匹配。LangChain和LlamaIndex都支持将两者结果融合如加权平均、重新排序能显著提升召回率。# 伪代码示例使用LangChain的EnsembleRetriever from langchain.retrievers import BM25Retriever, EnsembleRetriever # 创建基于文本的BM25检索器 bm25_retriever BM25Retriever.from_documents(split_docs) # 创建向量检索器 vector_retriever vectorstore.as_retriever(search_kwargs{k: 5}) # 组合 ensemble_retriever EnsembleRetriever( retrievers[bm25_retriever, vector_retriever], weights[0.4, 0.6] # 调整权重 )重排序初步检索可能返回几十个相关文档用一个更小、更精的模型称为“重排器”对这些结果进行二次排序把最相关的排到最前面。Cohere、BGE都有重排模型LangChain也集成了相关接口。元数据过滤在存储向量时可以为每个文本块附加元数据如文档标题、章节、作者、日期。检索时可以同时进行向量相似度搜索和元数据过滤如“只检索2023年以后的文档”。这在企业知识库中极其有用。3. 优化查询本身查询转换用户的原始问题可能不适合直接检索。可以对查询进行改写、扩展或生成假设性答案。查询扩展用LLM生成多个与原问题相关的查询分别检索后合并结果。HyDE让LLM根据问题生成一个假设性答案然后用这个答案的向量去检索。有时比用原始问题效果更好。4.2 提示工程优化让LLM更好地利用上下文检索到的上下文塞给LLM怎么“喂”也是一门学问。1. 优化提示词模板默认的stuff链提示词可能不够强。可以自定义一个更清晰的模板from langchain.prompts import PromptTemplate prompt_template 你是一个专业的助手请严格根据以下提供的上下文信息来回答问题。 如果上下文信息不足以回答问题请直接说“根据已知信息无法回答该问题”不要编造信息。 上下文信息 {context} 问题{question} 请根据上下文提供准确、简洁的答案 PROMPT PromptTemplate( templateprompt_template, input_variables[context, question] ) # 创建链时使用自定义提示词 qa_chain RetrievalQA.from_chain_type( llmllm, chain_typestuff, retrieverretriever, chain_type_kwargs{prompt: PROMPT}, # 传入自定义提示词 return_source_documentsTrue )2. 上下文压缩与选择性利用如果检索到的上下文很长全部塞进去可能让LLM分心。可以使用ContextualCompressionRetriever它用一个LLM先快速浏览所有检索到的文档提取出与问题最相关的部分再交给生成链。这相当于让一个“小助手”先帮你划重点。4.3 生产环境考量超越单机脚本当你的RAG系统需要服务多人、处理大量数据时就要考虑工程化问题。1. 向量数据库升级从Chroma到Milvus/Qdrant当数据量超过数十万或需要高并发查询、持久化保证时就需要专业的向量数据库。Milvus集群支持水平扩展Qdrant的过滤功能非常强大。它们的LangChain集成也很成熟。索引类型选择向量数据库支持多种索引如HNSW, IVF需要在构建速度和查询速度、精度之间做权衡。生产环境通常选择HNSW它在速度和精度上比较均衡。2. 构建可维护的流水线增量更新知识库文档会变需要支持增量更新向量库而不是全量重建。大多数向量数据库支持upsert操作。流水线编排使用Airflow,Prefect,LangGraph等工具将文档加载、分割、向量化、入库等步骤编排成自动化流水线并加入错误处理和监控。版本管理对嵌入模型、向量索引、甚至文档切片方式做版本管理以便回滚和对比效果。3. 服务化与API暴露使用FastAPI等框架将你的RAG链包装成REST API提供/query端点。加入认证和限流保护你的服务。日志与监控记录每一次查询的问题、检索到的文档、生成的答案、耗时和Token消耗便于分析和优化。5. 常见问题排查与进阶方向即使按照步骤操作你也可能会遇到各种问题。下面是一个快速排查清单和几个值得关注的进阶方向。5.1 “为什么我的RAG回答还是胡编乱造”这是最常见的问题。按以下顺序排查检索结果对吗这是第一步也是最重要的一步。打开verboseTrue或单独调用检索器检查对于你的问题系统返回的文本块是否真的相关。如果不相关问题出在前端文本分割不合理块太大或太小或者切碎了语义。调整chunk_size和separators。嵌入模型不合适换一个更适合你领域如中文、医学、法律的嵌入模型试试。检索策略太简单尝试混合检索或重排序。上下文喂对了吗检索结果相关但答案还是错。检查提示词模板确保{context}和{question}的位置正确并且指令清晰强调“严格根据上下文”。LLM本身的问题如果上下文完全正确提示词也没问题但LLM还是忽略上下文自己编可以尝试降低temperature到0。在提示词中更严厉地强调“不要编造”。换一个更“听话”的模型试试。5.2 “速度太慢怎么办”嵌入模型使用更小的嵌入模型如BGE-small或使用GPU进行推理。向量索引在向量数据库中创建合适的索引如HNSW并在精度和速度之间找到平衡调整ef_construction和M参数。检索数量减少k值检索返回的文档数。LLM调用使用更快的LLM API或对答案长度进行限制。5.3 值得关注的进阶方向当你掌握了基础RAG后可以探索这些更前沿或更实用的方向多模态RAG不仅处理文本还能处理图片、表格、音频中的信息。核心挑战是如何将非文本信息与文本联合索引和检索。Agentic RAG让RAG系统不再是被动问答而是能主动思考、规划、使用工具。例如系统可以判断一个问题是否需要检索需要检索哪些资料甚至分解成多个子问题依次检索解决。这是当前非常活跃的研究方向。图增强RAG在构建知识库时不仅存储文本片段还提取实体和关系构建知识图谱。检索时既做向量相似度搜索也做图谱推理能更好地回答涉及复杂关系、多跳推理的问题。评估与持续改进如何量化你的RAG系统好坏需要设计评估体系包括检索相关性、答案忠实度是否基于上下文、答案有用性等。基于评估结果持续迭代你的分割策略、检索模型和提示词。搭建RAG系统从原理到跑通Demo可能只需要一天但从Demo到一个稳定、可靠、好用的生产系统需要持续的迭代和打磨。我的建议是先用一个最小的、最熟悉的工具栈比如LangChain Chroma 一个你熟悉的LLM API把完整流程跑起来获得正反馈。然后再针对你遇到的具体问题——是检索不准、还是生成不好、还是速度太慢——去深入优化对应的模块。记住没有完美的通用方案最好的RAG系统永远是为你自己的数据和需求量身定做的那一个。
返回列表