用LangChain搭建RAG基线:解决大模型幻觉的知识库问答实战

发布时间:2026/10/17 3:46:28
用LangChain搭建RAG基线:解决大模型幻觉的知识库问答实战 做AI应用开发的人都有过这种体验模型一脸正经地胡说八道你问它内部流程它张口就来一套看似合理但完全不存在的规定。这其实就是大模型最典型的“幻觉”问题。RAG检索增强生成就是目前工业界最常用、最有效的缓解手段之一而LangChain则是把这套流程打包成顺手工具链的框架。这篇文章是我在实际项目里搭RAG基线时的完整经验记录从流程设计、组件选型到最小可用代码和后续调优思路都有适合刚接触LangChain、准备做知识库问答或者想要系统搭一套RAG基础链路的朋友参考。我先把话说在前面RAG本身不难难点全在细节。流程谁都能背下来但“为什么这么切文本”“为什么召回不准”“为什么答案还是瞎编”这些才是真正拉开差距的地方。下面我就按实际落地的顺序把整套东西从头讲一遍。1. RAG在解决什么问题先理解检索增强的底层逻辑1.1 大模型的三类硬伤你没法靠一个通用大模型解决所有业务问题这不是模型不够强而是它天生有三个短板。第一是幻觉。模型在生成时本质上是在做概率预测它没有能力知道自己“不知道什么”。只要问题的答案不在它的参数记忆里它就会用最流畅的方式编一个出来而且语气越笃定越容易骗到人。第二是知识陈旧。大模型的训练数据有截止时间之后发生的事它完全不知道。你问它最近一个月的行业政策它只能给出“抱歉我无法访问实时信息”或者更糟糕的——编造一条。第三是缺少领域知识。通用模型见过公开互联网的数据但你的公司制度、内部操作规程、产品手册、专业领域的私有文档它一概没见过。这类知识恰恰是业务场景里提问最密集的部分。RAG的思路很简单不逼模型去“记住”你的知识而是在每次提问时先到你的文档库里检索相关片段把命中内容连同问题一起喂给模型生成答案。换个生活化的说法闭卷考试改成开卷考试模型拿到问题先查资料再作答答案的“事实出处”不在模型脑子里而在你提供的资料里。1.2 RAG的标准流程拆解RAG全链路可以拆成两个阶段、六个环节。索引阶段负责把原始文档变成可检索的向量形式文档加载读取PDF、Word、Markdown、网页等各类来源。文本切分把长文档切成合适大小的文本块这是后续所有质量的基础。向量化用Embedding模型把每个文本块编码成一串浮点数向量。向量存储把向量连同原文、元数据一起写入向量数据库。检索生成阶段负责应答查询向量化用户提问时用同一个Embedding模型把问题也转成向量。相似度检索在向量库里找出与问题向量最接近的若干文本块。拼接生成把检索结果作为上下文连同原始问题一起交给大模型生成回答。这个流程里最核心的一条原则是知识库内容决定事实边界大模型只负责用流畅的语言把事实组织成答案。理解了这条原则你后面调试所有问题都有方向了。2. 方案选型别急着写代码先决定用哪套组件2.1 大模型接口怎么选搭RAG链路时生成环节的模型选择直接影响效果和成本。我自己的基线方案是“商用API为主、本地开源模型兜底”。商用API的优势是省心不用管推理服务部署模型能力和上下文长度都有保障。代价是按量计费如果知识库文档量大、检索频繁日积月累的成本不能忽略。本地部署开源模型则相反前期要搞定推理服务显存和并发需要考虑但数据不出内网隐私性更强长期跑起来成本更可控。我的建议很直接第一版基线用商用API把链路跑通确认业务价值和效果之后再评估是否有必要迁移到本地模型。不要在项目第一天就花大量时间折腾本地推理环境的性能调优那样会让你迟迟进入不了真正的业务验证环节。2.2 向量数据库选型对比向量数据库负责存储和检索文本向量选型标准主要看三个维度数据规模、运维复杂度、是否需要和现有存储打通。我在项目里实际比较过几类方案列个表给你参考方案特点适用场景踩坑提醒Chroma本地轻量零配置API简单原型验证、小规模知识库数据量大了性能下降明显FAISS纯向量索引库性能好资源占用低单机中等规模检索需要自己管理持久化Milvus分布式向量数据库功能完整百万级以上向量、多租户部署组件多运维门槛高PGVectorPostgreSQL扩展向量数据与业务数据同库管理检索性能和扩展性受限于PG对于第一次搭建RAG基础链路的场景我推荐Chroma没有之一。它装完就能用LangChain集成最成熟代码量最少。等你的文档量级明显上来了再平滑迁移到Milvus这类生产级方案。前期不要过度设计RAG链路里的业务逻辑还没验证清楚就去搭分布式集群大概率是在给自己挖坑。2.3 Embedding模型的选择逻辑不少初学者容易忽略Embedding模型觉得随便选一个就行。实际上向量质量是RAG效果的上限而生成模型只是在检索结果的基础上作答。检索不到正确内容后面所有努力都白费。选择Embedding模型时我主要看三点。第一是与文档语言的匹配度。如果你的知识库是中文为主那就优先选中文语料训练效果好的模型而不是直接用面向英文的通用模型。第二是向量维度维度越高存储成本越高检索计算越慢但通常精度更好需要在效果和成本之间权衡。第三是和服务端的一致性这一点最容易踩坑——索引和查询必须用同一个Embedding模型否则两个环节的向量不在同一语义空间里检索结果必然离谱。顺带提一句如果走本地方案像开源的中文向量模型这一类的路径在LangChain里集成很成熟加载本地模型权重的代码也不复杂把模型文件放到指定目录就行。3. 实战用LangChain搭一个最小可用的RAG基线3.1 安装与准备进入代码环节。先装依赖。需要说明的是LangChain版本迭代很快API变动频繁我的示例基于常见稳定版本你实操时务必锁定版本。pip install langchain langchain-community chromadb如果你用开源Embedding模型还要装对应的依赖库和模型权重。我建议在项目根目录建一个Python虚拟环境别全局装否则以后处理版本冲突会非常痛苦。3.2 文档加载与文本切分加载文档最常用的方式是LangChain提供的文档加载器。比如本地PDFfrom langchain_community.document_loaders import PyPDFLoader loader PyPDFLoader(知识库文档.pdf) documents loader.load()这里的documents是一个包含页面级文本和元数据的列表。加载器能帮你省去写PDF解析代码的时间但要注意它只是把PDF的文本抽取出来如果你的PDF是扫描图片那得先走OCR环节这一步容易漏掉我后面还会细说。文本切分是重头戏。我几乎都用RecursiveCharacterTextSplitterfrom langchain.text_splitter import RecursiveCharacterTextSplitter text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50, separators[\n\n, \n, 。, , , , , ] ) split_docs text_splitter.split_documents(documents)这里的chunk_size500指的是每个文本块最多约500个字符chunk_overlap50表示相邻两个文本块之间保留50个字符的重叠。为什么要重叠想象一下某句话的上半句在上一块的末尾下半句在下一块的开头如果不设重叠检索时可能因为只命中半句话而丢失语境。重叠的本质是给语义的连续性留一个缓冲带。但chunk_size也不是越大越好。块太大向量中包含太多无关信息检索精度会下降块太小单块语义不完整召回内容碎片化。500到800是我的经验区间你可以根据你的文档类型做实验调整后面我专门讲这个。3.3 向量化与存储切分完成后用Embedding模型把每个文本块向量化然后写入向量库。以Chroma为例from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings embedding_model HuggingFaceEmbeddings( model_name你的本地中文Embedding模型路径 ) vectorstore Chroma.from_documents( documentssplit_docs, embeddingembedding_model, persist_directory./chroma_store )persist_directory的效果是让向量数据落盘到本地目录下次启动时不需要重新加载文档和切分直接从磁盘读索引省掉大量重复计算环节。这一点在文档多的时候非常关键。3.4 检索器与问答链向量库建好之后先不做QA单独看检索效果retriever vectorstore.as_retriever( search_kwargs{k: 4} ) docs retriever.invoke(公司年假制度有哪些规定) print(len(docs))先跑这一步把返回的内容打印出来看确认检索到的内容确实和问题相关。这个步骤没有太高的技术含量但能帮你区分“链路问题”和“生成问题”。很多人的做法是从头到尾一条龙跑完一报错就晕头转向就是因为没拆开验证。检索没问题之后再组装完整的问答链from langchain.chains import RetrievalQA from langchain_community.chat_models import ChatOpenAI llm ChatOpenAI( model你选择的模型名称, api_key你的API密钥, temperature0 ) qa_chain RetrievalQA.from_chain_type( llmllm, retrieverretriever, chain_typestuff ) result qa_chain.invoke(公司年假制度有哪些规定) print(result[result])temperature0是我的习惯知识问答场景下一定要把生成多样性降到最低。回答要忠实于检索内容不需要模型发挥创造力。chain_typestuff代表把所有命中的文本块一次性全部塞进模型的上下文这是最简单也是效果最稳的方式但它对上下文长度有要求后面我会说什么时候得换掉它。3.5 完整流程串联最后给一个完整基线代码把上面的环节串起来from langchain_community.document_loaders import PyPDFLoader from langchain.text_splitter import RecursiveCharacterTextSplitter from langchain_community.vectorstores import Chroma from langchain_community.embeddings import HuggingFaceEmbeddings from langchain.chains import RetrievalQA from langchain_community.chat_models import ChatOpenAI # 1. 加载 loader PyPDFLoader(知识库文档.pdf) documents loader.load() # 2. 切分 text_splitter RecursiveCharacterTextSplitter( chunk_size500, chunk_overlap50 ) split_docs text_splitter.split_documents(documents) # 3. 向量化 存储 embedding_model HuggingFaceEmbeddings( model_name你的本地中文Embedding模型路径 ) vectorstore Chroma.from_documents( documentssplit_docs, embeddingembedding_model, persist_directory./chroma_store ) # 4. 检索器 retriever vectorstore.as_retriever( search_kwargs{k: 4} ) # 5. QA链 llm ChatOpenAI( model你选择的模型名称, api_key你的API密钥, temperature0 ) qa_chain RetrievalQA.from_chain_type( llmllm, retrieverretriever, chain_typestuff ) result qa_chain.invoke(公司年假制度有哪些规定) print(result[result])这套代码就是RAG的最省心形态跑通它基础链路就在你手里了。4. 把基线调好检索质量优化的关键细节4.1 切分策略直接决定召回质量我在实际项目中调试RAG时花时间最多的不是在模型选择上而是调文本切分的参数。切分粒度影响的是语义完整性而语义完整性直接影响检索命中率。有一回我把一份产品规格文档按固定字符长度硬切结果很多技术参数被切到了不同文本块里。用户问“某型号的功耗是多少”检索返回的块里只有产品名没有功耗数据模型自然答不上来。后来改成按章节标题和段落结构切分问题立刻消失。如果你用的RecursiveCharacterTextSplitter发现效果不佳可以考虑两种策略。一种是结构化切分针对Markdown或HTML文档按标题层级、表格、列表结构切分让每个文本块都拥有相对独立的语义。另一种是父子切分大块负责检索小块负责给模型生成兼顾上下文和精度的平衡。这个思路属于进阶调优先不展开。我的建议是先用默认递归切分跑通把每个分块打印出来人工检查一遍确认没有明显切断语义的地方再决定要不要上更复杂的方案。4.2 检索参数不是越大越好k值是最常见的检索参数代表返回多少个文本块给模型。初学者容易有个误区k值越大召回内容越多答案越完整。实际上k值过大会引入大量无关噪声模型分不清哪些该用来回答反而被干扰。k值过小则可能漏掉关键内容。我通常从3到5起步根据文档块大小和问题复杂度上下浮动。除了k值检索时的相似度阈值也值得关注。向量相似度分数低说明检索结果和问题相关性弱这种时候与其硬着头皮交给模型不如直接返回“知识库中未找到相关内容”。在知识问答场景中“明确告知没找到”远比“编造一个答案”更可靠。Chroma的检索器也支持MMR模式全称Maximal Marginal Relevance。和普通相似度检索的区别是MMR会在保证相关性的同时降低返回结果之间的冗余度。比如一个问题对应的多个文本块内容高度重复MMR会选择保留其中代表性的几块让答案视角更全面。我在处理FAQ类文档时常用这个模式。4.3 进阶视角Rerank、HyDE和混合检索如果你已经把基础链路调顺了但还觉得召回质量不够接下来的方向我来指个路。重排序Rerank的思路是第一轮用向量检索快速召回数量较大的候选集再用一个专门的重排序模型对候选结果进行精排让最相关的内容排在最前。这个方案在高精度场景下效果提升非常明显代价是多一层模型调用。查询改写HyDE的思路是先用大模型根据原始问题生成一个虚构答案再用这个答案去检索知识库相当于给查询加了一层语义扩充。在问题表述模糊、用词和文档不匹配的场景下效果提升很直接。混合检索的思路是把向量相似度检索和关键词匹配检索结合起来用分数统一排序解决向量检索对精确术语匹配不够敏感的问题。这些都是RAG进阶的经典方向等你在基础链路上积累了足够体感之后再尝试会发现理解起来轻松得多。5. 常见问题与排查技巧实录5.1 召回一直不相关召回结果不相关是最常见的问题。我的排查顺序是固定的。先确认索引和查询用的是同一个Embedding模型。如果索引阶段用了模型A查询阶段换成了模型B两个向量不在同一语义空间检索结果基本就是乱抓。其次检查切分块是否破坏了语义把切出来的块打印出来逐条看确认每块是否具备独立的可理解语境。再检查查询本身是否存在表述歧义比如用户问“这个怎么弄”这种问题谁来了也检索不好需要考虑做查询理解或引导澄清。5.2 回答还是幻觉用了RAG之后模型依然胡编要检查两个环节。一个环节是检索结果里是否混入了和问题无关的内容。如果命中的数据本身就不相关模型为了完成回答任务就只能强行编造。另一个环节是即使检索到了相关内容模型也可能不按检索内容作答。解决办法是把Prompt写强硬一些明确告诉模型“只能根据提供的参考内容回答参考内容不足时直接说明不知道”。如果你用的是LangChain的RetrievalQA可以在构造时传入自定义的system prompt模板或者直接用create_stuff_chain手动控制提示词。另外一个容易被忽略的问题是引用缺失。业务场景里用户需要知道答案来自哪里模型如果只是把检索结果“翻译”成了一段话没有关联到原文出处可信度就很难建立。这一步可以对文本块做映射处理让答案在生成时携带来源片段。5.3 Token超限报错chain_typestuff把所有命中的文本块一次性全部塞给模型当文本块数量多、单块大时很容易超过模型的上下文长度限制直接报错。这时有两条路。一条是把chunk_size降下来减少单块大小。另一条是换成其他处理模式比如map_reduce让模型先对每个文本块分别生成答案再汇总生成最终答案。代价是调用次数增多、耗时变长但可以处理更长内容。简单追问场景一般用不上但知识库文档块大时值得备一个。也可以在构造提示词时压缩上下文把那些明确低相关的检索结果过滤掉只留最核心的内容。5.4 环境与版本兼容问题LangChain的API变动相当频繁不同版本之间的类名、方法签名经常变化。我在不同机器上复现同一个项目时遇到过好几次因为版本不一致导致调用报错的情况。应对办法是固化环境。在项目目录下生成并提交依赖锁定文件把LangChain及相关库的版本钉死。新项目再建虚拟环境时按照锁定文件一次性还原。还有一个坑是底层依赖库之间的兼容性比如某个版本的LangChain要求Pydantic版本不能太高安装时经常因为依赖冲突导致一堆报错。我的经验是在纯净的虚拟环境里逐个安装谁报错解决谁不要图省事一次装一堆。典型问题直接原因排查思路检索结果不相关Embedding模型不一致统一索引与查询阶段模型切分截断语义chunk_size不当打印分块人工检查答案仍然幻觉检索噪声或被模型忽略强化Prompt限制过滤低相关结果Token超限stuff方式塞入过多内容换map_reduce或降低chunk_size导入报错LangChain版本变动锁定版本依赖隔离6. 最后再分享一点我的实际体会RAG基础链路本质上是一套“组装方案”每个组件都不难难度在把它们的边界理清楚。我从零跑通第一个完整RAG系统之后最大的感受是不要急着追求复杂的检索策略、重排序、混合召回这些高阶玩法先把加载-切分-向量化-检索-生成这条主线在每个环节上的表现摸清楚哪一环有问题就在哪一环解决比盲目堆技术组合要有效得多。如果这篇内容能帮你把第一版RAG基线跑起来后面再踩的坑应该会少一半。最后一个小建议每改一次切分参数或检索策略都记下当时的配置和效果数据这些记录是后续优化时最宝贵的依据。

关于本文作者

来自尧图内容编辑团队

尧图内容编辑团队 内容团队

尧图内容编辑团队

本文由尧图网络内容编辑团队执笔。团队由资深项目经理、前端工程师与设计师组成,所有内容均来自亲手交付的真实项目,先讲清问题、再给出可落地的解法。尧图深耕北京网站建设十年,服务过京华建材集团、智造科技等各行业客户,把一线经验沉淀为可复用的行业观察。

  • 十年建站经验,覆盖建材、制造、服务、文创等
  • 项目经理把关选题与事实准确性
  • 工程师与设计师联合撰写专业细节
  • 统一编辑规范,保证文风与排版一致
  • 每月复盘转化数据,迭代选题方向

延伸阅读

相关资讯与近期热门内容

深度阅读推荐

建站决策前值得细读的三篇

网站改版的5个关键决策
2024-08-12

网站改版的5个关键决策

什么时候该改版、改到什么程度、如何避免流量掉光,京华建材集团改版复盘给出答案。

获取专属建站方案

看完文章,把您的行业与预算告诉我们,免费获取一份量身定制的官网建设方案与报价。

立即免费咨询