
简介面向大模型应用开发者与算法工程师这套基于Python语言的RAG检索增强生成最佳实践源码聚焦知识库问答与长文本生成场景覆盖检索、重排、提示词构造到模型调用的完整链路。压缩包共22个文件大小约527KB其中XML配置负责环境与参数管理Python源码实现核心检索与生成逻辑Markdown与文本文件提供说明文档图片辅助展示流程整体目录结构清晰便于按需查阅。目前已有946人学习浏览适合具备一定Python基础、希望快速落地RAG方案的开发者。通过研读核心模块代码可以掌握检索器构建、查询处理、提示词设计及大模型接入的工程技巧配合配置文件与文档能够快速复现项目环境并根据业务需求灵活扩展是一份兼顾学习与生产参考价值的工程示例。1. 23 个文件的 RAG 源码包它不是黑匣子是一套能拆着改的检索增强工程拿到这个压缩包时我先数了文件23 个。里面既有custom_retriever.py这种直接对应检索增强核心逻辑的模块也有prompt.py这类最容易被忽略、却最能影响输出质量的模板。它没有把 RAG 讲成黑匣子而是把“从语料里检索什么”和“让大模型怎么答”拆成两件事检索、Prompt 都能单独调。如果你正在搭 rag 知识库或者已经接过大模型 API 但觉得回答不够准这套源码能当最小骨架。适合刚入门的 Python 开发者也适合已经跑通 ChatBot、想找一份能改的工程的人。建议先别急着换模型把默认流程跑通再按后文思路改。2. 拆包看工程23 个文件里找出 5 个核心 Python 模块任何 RAG 项目里真正决定上限的往往不是大模型而是文件怎么被切成片段、片段怎么被检索、检索结果怎么被组织到 Prompt 里。这套源码之所以适合当最佳实践来读是因为它把这几个环节拆到了独立文件里每个文件只解决一个问题。2.1 按文件类型清点源码、配置、文档、许可各就各位解压之后不要急着运行先花两分钟做一次类型清点。按项目描述里的口径23 个文件大致是7 个 XML 配置文件、5 个 Python 源码文件、3 个 Markdown 文档、2 个 Git 忽略文件、2 个文本文件、1 个 IntelliJ IDEA 项目文件、1 个开源许可文件以及少量图片素材。看起来杂实际只有三类参与运行的、描述工程的、约束工程的。# 用命令行看一遍文件分布先建立整体印象 cd rag-best-practices find . -type f | sort # Windows PowerShell 下可以换成 # Get-ChildItem -Recurse -File | Select-Object FullName执行完你会看到.idea/里有一堆 XML那里大多是编辑器状态运行时不会被加载。真正被 Python 解释器读到的只有源码和testdata.txt。做这一步的意义是避免被无关文件干扰。文件/目录在这套工程里的角色运行时是否必需main.py主流程入口串起检索与生成是query.py查询调试入口方便单独看召回是custom_retriever.py自定义检索器处理语料加载和召回是prompt.pyPrompt 拼接与模板管理是glmfz.py大模型 API 调用封装是requirements.txt依赖清单是testdata.txt小规模测试语料启动阶段需要.idea/下的 XMLIDE 工程配置否.gitignore、LICENSE工程约束与许可否2.2 5 个 Python 文件的分工从入口到封装层打开源码后我建议按依赖方向读而不是按文件名顺序读。常见依赖方向是main.py调用custom_retriever.py做检索调用prompt.py拼上下文最后交给glmfz.py里封装的模型客户端生成答案query.py则是对custom_retriever.py的轻量封装方便在命令行里直接看召回内容。# 模块之间的依赖方向一般是这样的 # main.py - custom_retriever.py # main.py - prompt.py # main.py - glmfz.py # query.py - custom_retriever.py # glmfz.py - requests / openai sdk这种单向依赖有实际好处检索效果不好时只需要改custom_retriever.py回答风格不对时只需要改prompt.py模型调用报错时先看glmfz.py。如果这几个逻辑混在同一个文件里后面每调一次参数都要在几百行里翻找。custom_retriever.py在最佳实践工程里通常承载三个方法加载语料、构建索引、执行检索。prompt.py则负责把检索到的多个文本片段拼成一段有结构的上下文同时把“原文没有的内容不要编造”这类约束写进去。glmfz.py从命名看接近 GLM 封装我拆过的类似项目里这个文件往往集中管理 API Key、模型名、超时时间、重试次数。2.3testdata.txt和.idea别把测试数据当装饰testdata.txt在 RAG 工程里通常是小规模语料或问题集。它的作用不是“演示一下”而是让你在跑通链路时有一份可预期的输入。你可以手动改里面的内容验证检索结果是否跟着变化。比如把某个领域的几条业务知识放进去再问一个对应的问题看召回是否命中。.idea下的 7 个 XML 是 IntelliJ IDEA 的工程描述其中vcs.xml记录版本控制配置jupyter-settings.xml是 Jupyter 插件设置dataSources.xml是 IDE 的数据库连接记录。这些东西不影响程序运行但它说明了作者的工作方式用 IDEA 打开、用 Git 管理、可能用 Jupyter 做过调试。拿到源码后我一般会保留.idea里的公共配置但会把本机路径相关的部分交给 IDEA 重新生成。3. 把检索增强链路跑起来main.py、query.py、glmfz.py 三个入口的职责边界RAG 链路最低限度是文档切块、向量化、检索 top_k、拼 Prompt、调用大模型。main.py是总装车间query.py是中途下车查看召回结果的调试窗口glmfz.py是对外模型请求的最后一道关卡。3.1 main.py索引、检索、生成的一次串行RAG 不是把整个文件塞给模型而是先找出与问题最相关的几个片段再让模型基于片段回答。main.py里最常见的骨架是这样# main.py —— RAG 主流程的典型骨架 from custom_retriever import CustomRetriever from prompt import build_prompt from glmfz import GLMClient def run(question: str) - str: retriever CustomRetriever() llm GLMClient() # 1. 读取测试语料并构建索引 retriever.load_data(testdata.txt) retriever.build_index() # 2. 检索阶段只拿 top_k 个片段后续 Prompt 才放得下 docs retriever.retrieve(question, top_k3) # 3. 生成阶段把检索结果和问题拼成 Prompt prompt build_prompt(question, docs) return llm.generate(prompt)这段代码里值得关注的参数是top_k3。知识库问答场景中top_k 在 2 到 5 之间比较常见。片段本身很长时取 3 个就够强行拉到 8 个以上会把无关内容塞进上下文模型容易答非所问token 成本也更高。如果你的语料每段只有一两句话可以把 top_k 调到 5否则召回的信息量不够。build_index()这一步在最小工程里通常只对testdata.txt做切分和向量化。语料规模变大后这里需要换成持久化索引但先跑通这份测试数据更重要因为链路没通之前换什么存储都无从验证。3.2 query.py只看检索召回先不看大模型很多刚接触 RAG 的人只盯着最终答案出了问题也分不清是检索错了还是模型答错了。query.py一般就是那个“中途下车”的入口它把召回结果暴露出来让你先确认有没有找到该找的内容。# query.py —— 检索与生成分离的命令行入口 from custom_retriever import CustomRetriever retriever CustomRetriever() retriever.load_data(testdata.txt) retriever.build_index() while True: q input(请输入问题输入 q 退出) if q.lower() q: break hits retriever.retrieve(q, top_k3, score_threshold0.35) for i, hit in enumerate(hits, 1): print(f[{i}] score{hit.score:.4f}) print(hit.text[:120]) print(- * 40)score_threshold0.35是一个经验值不是公式。用不同 embedding 模型时分数分布差异很大所以第一次跑建议先不设阈值把所有召回的 score 打印出来观察相关片段和不相关片段的分界线。如果分数普遍在 0.3 以下说明向量模型没有理解这批语料问题多半出在 embedding 选择上而不是阈值本身。如果你的版本里retrieve()不接受score_threshold参数可以在custom_retriever.py的检索方法里加一个过滤条件或者先在调用处打印分数再人工判断。重点是养成先看召回、再看答案的习惯。3.3 glmfz.py大模型调用的封装层大模型 API 调用如果散落在每个文件里换模型时就要改所有调用位置。所以这种源码包通常会把请求逻辑收进一个文件glmfz.py大概率就是干这个的。常见做法是封装成一个类统一管理模型名、API Key、超时和重试。# glmfz.py —— 大模型 API 客户端的一层极简封装 import os import time import requests API_URL https://open.bigmodel.cn/api/paas/v4/chat/completions MODEL glm-4-flash class GLMClient: def __init__(self, api_key: str None, timeout: int 30): self.api_key api_key or os.getenv(ZHIPU_API_KEY, ) self.timeout timeout def generate(self, prompt: str, temperature: float 0.3) - str: payload { model: MODEL, messages: [{role: user, content: prompt}], temperature: temperature, } headers { Authorization: fBearer {self.api_key}, Content-Type: application/json, } # 简单重试网络抖动时不用整个流程重跑 for attempt in range(3): try: resp requests.post( API_URL, headersheaders, jsonpayload, timeoutself.timeout ) resp.raise_for_status() return resp.json()[choices][0][message][content] except Exception as exc: print(f请求失败第 {attempt 1} 次重试{exc}) time.sleep(2 * attempt 1) raise RuntimeError(LLM API 调用失败)这里有几个参数可以单独拿出来讲。temperature0.3适合知识库问答因为答案需要贴近资料原文随机性越低越好。如果做头脑风暴或文案润色可以调到 0.7 以上但 RAG 场景下我不建议超过 0.5否则模型容易在事实性回答里自由发挥。重试次数设成 3 就够time.sleep(2 * attempt 1)会让重试间隔从 1 秒递增到 3 秒、5 秒避免短时间内连续请求同一个地址。超时时间 30 秒是折中值如果经常超时先看网络环境再考虑调到 60 秒不要一上来就把超时改成几百秒否则错误会拖很久才暴露。4. 从 Python 环境到 IntelliJ IDEA这套源码的正确打开方式源码本身是死的环境才是活的。这套仓库带着.idea配置作者大概率是在 IntelliJ IDEA 里开发的所以最省事的打开方式也是 IDEA而不是用记事本改完再到处找问题。4.1 先装 Python 3.10 再用 venv 隔离依赖如果你是第一次整 Python 环境找一份 Python 安装教程把 3.10 或更高版本装好。安装界面里如果出现“Add python.exe to PATH”建议勾上后面在命令行里少折腾一步。但我更建议无论装没装过都单独给这个项目建一个虚拟环境避免依赖污染全局环境。cd rag-best-practices python -m venv .venv # Windows 激活方式 .venv\Scripts\activate # macOS / Linux 激活方式 # source .venv/bin/activate python -m pip install --upgrade pip pip install -r requirements.txtpython -m venv .venv会在当前目录创建一个隔离环境后续安装的依赖都落在.venv里不会和系统里其他 Python 项目打架。requirements.txt就是一套依赖清单里面的版本号尽量不要随便删。如果你在安装时遇到超时可以把 pip 源指向国内 PyPI 镜像常见做法是在命令里加-i参数指定镜像地址。安装完成后用下面这条命令验证关键依赖是否真的装到了虚拟环境里.venv\Scripts\python -c import requests; print(requests ok)如果这步就报ModuleNotFoundError说明当前python命令和.venv里的解释器不是同一个后面所有命令都要改用.venv\Scripts\python.exe开头。4.2 导入 IntelliJ IDEA 并配置项目 SDK具体操作分四步用 IDEA 打开项目、确认信任、把项目 SDK 指向虚拟环境、在终端里跑命令。先在 IDEA 里选择File - Open定位到解压后的文件夹不要打开压缩包也不要只打开某个.py文件。首次打开时会弹是否信任项目选信任否则 IDEA 会限制脚本执行。然后进入File - Project Structure - SDKs添加一个 Python SDK选择Existing environment把路径指向.venv\Scripts\python.exe。最后在Modules里把当前模块的 SDK 换成刚才配好的解释器。项目里那 7 个 XML 不需要手动改。dataSources.xml只是 IDE 数据库面板里的连接记录如果它指向一个不存在的数据库直接忽略即可jupyter-settings.xml是笔记本调试插件的配置不跑 Jupyter 就不会用到。真正会影响运行的解释器配置在Project Structure里不在 XML 里。4.3 第一次运行先跑 query.py 还是 main.py如果还没配好大模型 API Key直接跑main.py大概率会报鉴权错误。更聪明的顺序是先跑query.py它只做加载、索引、检索不调用模型既验证了文件读取又验证了向量化链路而且不消耗 token。# 先验证依赖 .venv\Scripts\python -c import requests; print(requests ok) # 再跑检索入口 .venv\Scripts\python query.py输入一个与testdata.txt内容相关的问题看到召回片段和 score 后再配置 API Key 并运行main.py。设置环境变量的方式取决于系统# Windows PowerShell 临时设置 $env:ZHIPU_API_KEY你的密钥 # macOS / Linux 临时设置 # export ZHIPU_API_KEY你的密钥 .venv\Scripts\python main.py具体环境变量名以readme.txt或glmfz.py里的读取逻辑为准。如果项目里用的是别的模型服务把变量名和模型地址换成对应的即可。5. 复现避坑我遇到的 5 个 RAG 源码运行问题这类源码包我拆过不少真正跑不起来的通常不是算法而是环境、编码、接口这几个边界问题。每一条我都按“现象、原因、解决”写清楚。5.1 坑一ModuleNotFoundError依赖没装进当前解释器现象在命令行执行python main.py报No module named requests但pip list里明明能看到 requests。原因当前终端里的python指向的是系统解释器依赖却装进了.venv。在 IntelliJ IDEA 里也常见项目 SDK 配置成了系统 PythonIDEA 控制台里当然找不到虚拟环境里的包。解决不要依赖默认python直接用.venv里的解释器重装依赖并启动.venv\Scripts\python.exe -m pip install -r requirements.txt .venv\Scripts\python.exe main.py从那以后我在 IDEA 里跑项目前都会先看右下角解释器路径是不是项目里的.venv这能省掉很多“明明装了却找不到包”的折磨。5.2 坑二IDEA 提示 No Python interpreter configured现象打开项目后运行任何 Python 文件都提示没有配置 Python 解释器找不到 SDK。原因.idea里的模块配置记录的是作者机器上的 SDK 路径换了一台电脑路径不存在IDEA 就无法自动关联。解决打开File - Project Structure - SDKs添加 Python SDK选择Existing environment指向.venv\Scripts\python.exe然后在Modules里选中该 SDK。不要手动去改.idea/misc.xmlIDEA 重新加载时经常会覆盖手改的内容。5.3 坑三testdata.txt 一读就 UnicodeDecodeError或者中文乱码现象Windows 下运行main.py报UnicodeDecodeError: gbk codec cant decode byte或者检索结果里的中文变成乱码。原因Python 在 Windows 上默认按 GBK 读取文本文件而testdata.txt是 UTF-8 编码。解决在读文件的地方显式指定编码# custom_retriever.py 里处理语料加载时 with open(testdata.txt, r, encodingutf-8) as f: content f.read()如果改完仍然乱码先确认文件真实编码用 IDEA 打开这个文件看右下角显示的编码是 UTF-8 还是 GBK。有时候问题不是代码而是文件本身已经被错误地保存成其他编码。5.4 坑四检索召回全是不相关内容现象问“项目延期怎么处理”召回结果却是“数据库连接超时”之类的无关片段而且 score 看起来还不低。原因常见有三种。第一语料切块太粗整篇文章被当成一个片段向量化语义被打散第二embedding 模型对中文支持不够第三相似度阈值设得太低把不相关内容也放行了。解决先通过query.py把 score 打印出来观察相关片段和无关片段的分数分界线。如果分数普遍很低说明 embedding 模型和语料不匹配常见做法是换一个中文向量模型比如bge-small-zh-v1.5这类模型体积可控中文语义召回效果也稳定很多。如果分数普遍很高但内容不对就要检查custom_retriever.py里是不是把文档切得太粗或者索引构建时错误地覆盖了旧数据。5.5 坑五大模型 API 报鉴权错误或一直超时现象运行main.py时报 401或者请求在大模型接口那一步卡住最后 ReadTimeout。原因API Key 没设置、模型名不匹配、超时时间太短、没有重试机制。这类问题通常在glmfz.py里一眼就能看出来。解决先把 Key 通过环境变量传入并确认没有多余空格然后单独写一条小调用验证模型名和地址是否可用from glmfz import GLMClient client GLMClient() print(client.generate(你好请回复OK))如果这条调用能通说明主流程问题出在 Prompt 或检索环节如果也报错问题就集中在模型名、地址或 Key 上。超时时间建议先给 30 秒如果经常超时再调到 60 秒同时保留重试逻辑避免一次网络抖动把整个任务拖垮。6. 往最小工程里加自己的东西三个立刻能用的增强方向跑通不是终点。这份源码包更适合当骨架来改下面这三个方向都是不换架构就能落地的。6.1 给 query.py 加缓存省掉重复检索同一个问题被反复问时每次都重新做 embedding 和检索成本没必要。给检索方法套一层内存缓存即可。from functools import lru_cache lru_cache(maxsize128) def retrieve_with_cache(query: str): return retriever.retrieve(query, top_k3)参数maxsize128表示最多缓存 128 个不同问题超过后按 LRU 清理。这里有个前提如果语料更新了缓存必须能失效否则新数据永远不生效。常见做法是在custom_retriever.py里维护一个版本号重新加载语料后调用cache_clear()强制让下一批查询重新索引。6.2 把 prompt.py 改成 system/user 双层模板很多最小工程只写一层 Prompt也就是把资料和问题直接拼接。这样做能用但想要控制回答格式时会很吃力。更易维护的结构是加一个 system 层。# prompt.py —— 双层模板示例 SYSTEM 你是企业知识库助手。只能依据给定资料回答资料里没有的内容直接说不知道不要编造。 USER 资料片段 {context} 问题{question} 要求先给结论再给出依据。 def build_prompt(question: str, context: str) - str: return f{SYSTEM}\n\n{USER.format(contextcontext, questionquestion)}如果你后续要用开源模型做本地部署这种模板结构也更利于量化模型理解任务边界。6.3 把 query 入口包成一个可复用的工具函数有人会把 RAG 和 MCP 放在一起比较我的理解是MCP 解决的是模型怎么调用外部工具RAG 解决的是模型怎么读到私有知识两者不是替代关系。这套源码里的query.py完全可以把内部逻辑抽成一个函数后续接到工具调用层时直接用。def ask(question: str) - str: docs retriever.retrieve(question, top_k3) prompt build_prompt(question, docs) return llm.generate(prompt)这一步不改任何检索逻辑只是把入口从命令行变成可被其他程序调用的函数。等你想接 Agent 或 MCP 时只需要在这个函数外面包一层协议描述不需要重新实现 RAG。从那以后我每次拿到新的 RAG 源码都会先走三件事按 readme 装干净依赖用 query 入口单独看召回再让大模型生成。这三步走完我就知道哪一层在拖后腿后面的缓存、模板、工具化都是在这个最小工程上长出来的。希望帮到你。本文还有配套的精品资源点击获取