手写一个 RAG:从零搭建可溯源的检索增强问答系统
1. 引言为什么需要手写 RAG大语言模型虽然强大但存在知识截止、幻觉和无法引用来源等问题。检索增强生成Retrieval-Augmented GenerationRAG通过先检索再生成的方式把外部知识注入模型从而提升回答的准确性和可溯源性。本文不依赖 LangChain 等框架从零手写一个可运行的 RAG 系统覆盖文档加载、切分、向量化、检索、生成和溯源全流程。手写 RAG 的好处在于你能清楚看到每个环节的输入输出便于调试、定制和教学。下面我们逐步搭建。2. 系统架构与整体流程一个最小可用的 RAG 系统包含以下模块文档加载器读取本地文本或 Markdown 文件。文本切分器把长文档切成适合检索的块Chunk。向量化器把文本块编码为向量。向量存储与检索保存向量并支持相似度检索。提示词组装把检索结果和用户问题拼成 Prompt。生成器调用大模型生成回答。溯源模块把回答关联到原始文档块。整体流程如下flowchart TD A[加载文档] -- B[文本切分] B -- C[向量化] C -- D[向量存储] E[用户提问] -- F[问题向量化] F -- G[相似度检索] D -- G G -- H[组装 Prompt] H -- I[大模型生成] I -- J[返回回答与溯源]3. 环境准备与依赖安装本文使用 Python 3.10核心依赖如下pip install openai numpy说明openai用于调用 Embedding 模型和对话模型兼容 OpenAI 接口的服务均可。numpy用于向量相似度计算。如果你希望完全本地运行可以把 Embedding 和生成模型替换为本地模型如 BGE、Qwen本文以 OpenAI 兼容接口为例便于快速跑通。4. 文档加载与文本切分首先实现文档加载器支持读取纯文本和 Markdown 文件from pathlib import Path def load_document(file_path: str) - str: 读取本地文本或 Markdown 文件内容。 path Path(file_path) if not path.exists(): raise FileNotFoundError(f文件不存在: {file_path}) return path.read_text(encodingutf-8)接下来实现文本切分器。切分策略直接影响检索质量这里采用「按段落优先、按长度兜底」的策略import re def split_text(text: str, chunk_size: int 500, overlap: int 50) - list[str]: 把长文本切成多个块。 - chunk_size: 每个块的最大字符数 - overlap: 相邻块之间的重叠字符数用于保持上下文连贯 # 先按空行切分为段落 paragraphs [p.strip() for p in re.split(r\n\s*\n, text) if p.strip()] chunks [] current for para in paragraphs: # 如果当前块加上新段落会超长先保存当前块 if len(current) len(para) 1 gt; chunk_size and current: chunks.append(current) # 保留尾部 overlap 字符作为下一块的开头保持上下文 current current[-overlap:] if overlap gt; 0 else current (current \n para).strip() if current: chunks.append(current) return chunks切分时保留重叠区域可以避免关键信息恰好落在块边界而被截断。5. 向量化与向量存储向量化负责把文本块转换为向量。这里封装一个 Embedding 客户端from openai import OpenAI class EmbeddingClient: def init(self, api_key: str, base_url: str, model: str text-embedding-3-small): self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model model def embed(self, texts: list[str]) -gt; list[list[float]]: 批量把文本编码为向量。 resp self.client.embeddings.create(modelself.model, inputtexts) return [item.embedding for item in resp.data]向量存储使用简单的内存实现支持添加和检索import numpy as np class VectorStore: def init(self): self.vectors [] # 向量列表 self.metadata [] # 每个向量对应的文本块和来源信息 def add(self, vectors: list[list[float]], metadatas: list[dict]): 批量添加向量及其元数据。 self.vectors.extend(vectors) self.metadata.extend(metadatas) def search(self, query_vector: list[float], top_k: int 3) -gt; list[dict]: 余弦相似度检索返回最相似的 top_k 个结果。 每个结果包含 text、score 和 source 字段。 if not self.vectors: return [] q np.array(query_vector) mat np.array(self.vectors) # 余弦相似度 点积 / (模长乘积) scores (mat q) / (np.linalg.norm(mat, axis1) * np.linalg.norm(q) 1e-9) top_indices np.argsort(scores)[::-1][:top_k] results [] for idx in top_indices: results.append({ text: self.metadata[idx][text], score: float(scores[idx]), source: self.metadata[idx].get(source, ), chunk_index: self.metadata[idx].get(chunk_index, 0), }) return results这里使用余弦相似度衡量相关性分数越高表示越相关。6. 索引构建把文档写入向量库把加载、切分、向量化、存储串起来构建索引def build_index(file_path: str, embed_client: EmbeddingClient, store: VectorStore): 读取文档、切分、向量化并写入向量存储。 text load_document(file_path) chunks split_text(text) vectors embed_client.embed(chunks) metadatas [ { text: chunk, source: file_path, chunk_index: i, } for i, chunk in enumerate(chunks) ] store.add(vectors, metadatas) print(f已索引 {len(chunks)} 个文本块)这一步完成后向量库就具备了检索能力。7. 检索与提示词组装用户提问时先把问题向量化再从向量库检索相关块def retrieve(query: str, embed_client: EmbeddingClient, store: VectorStore, top_k: int 3) - list[dict]: 检索与问题最相关的文本块。 query_vector embed_client.embed([query])[0] return store.search(query_vector, top_ktop_k)把检索结果组装成带上下文的 Promptdef build_prompt(query: str, retrieved: list[dict]) - str: 把检索结果和用户问题组装成 Prompt。 context \n\n.join( f[来源 {i1}] {item[text]} for i, item in enumerate(retrieved) ) prompt f请根据以下参考资料回答用户问题。如果资料中没有相关信息请明确说明“资料中未找到相关内容”不要编造。 参考资料 {context} 用户问题{query} 请给出准确、简洁的回答并在回答末尾列出引用的来源编号。 return prompt提示词中明确要求模型引用来源编号这是实现可溯源回答的关键。8. 生成回答与溯源输出调用对话模型生成回答并把来源信息一并返回class ChatClient: def __init__(self, api_key: str, base_url: str, model: str gpt-4o-mini): self.client OpenAI(api_keyapi_key, base_urlbase_url) self.model model def generate(self, prompt: str) -gt; str: resp self.client.chat.completions.create( modelself.model, messages[{role: user, content: prompt}], temperature0.2, ) return resp.choices[0].message.content def ask(query: str, embed_client: EmbeddingClient, store: VectorStore, chat_client: ChatClient, top_k: int 3) - dict: 完整的 RAG 问答入口返回回答和溯源信息。 retrieved retrieve(query, embed_client, store, top_k) prompt build_prompt(query, retrieved) answer chat_client.generate(prompt) return { answer: answer, sources: [ { text: item[text], score: item[score], source: item[source], chunk_index: item[chunk_index], } for item in retrieved ], }返回结果中同时包含回答和来源块前端可以据此展示引用来源实现可溯源。9. 完整实战跑通一个问答示例下面把以上模块串起来用一个示例文档跑通全流程。假设有一份产品说明文档product.md# 智能音箱 X1 使用说明 基本功能 智能音箱 X1 支持语音播放音乐、设置闹钟、查询天气和控制智能家居设备。 连接方式 首次使用请下载 App并按照提示连接 Wi-Fi。设备支持 2.4G 和 5G 双频段。 常见问题 如果音箱无法唤醒请检查电源和网络连接并确认麦克风未被静音。主程序如下def main(): api_key your-api-key base_url https://api.openai.com/v1 embed_client EmbeddingClient(api_key, base_url) chat_client ChatClient(api_key, base_url) store VectorStore() 1. 构建索引 build_index(product.md, embed_client, store) 2. 提问 query 音箱无法唤醒怎么办 result ask(query, embed_client, store, chat_client, top_k2) 3. 输出回答 print(回答, result[answer]) print(\n溯源信息) for i, src in enumerate(result[sources], 1): print(f [{i}] 来源: {src[source]} 第{src[chunk_index]}块 相似度: {src[score]:.4f}) print(f 内容: {src[text][:80]}...) if name main: main()运行后系统会先索引文档再根据问题检索相关块最终生成带来源编号的回答。10. 可溯源机制的设计要点可溯源是本文 RAG 的核心目标设计上包含三个层面块级溯源每个文本块都记录来源文件和块序号检索结果天然携带来源。提示词约束在 Prompt 中要求模型引用来源编号从生成侧约束回答可追溯。结果透出问答接口同时返回回答和来源块前端可展示引用卡片。此外相似度分数可以作为溯源可信度的参考分数越高说明该块与问题越相关回答依据越可靠。11. 优化方向与进阶建议手写版本跑通后可以从以下方向继续优化切分策略引入语义切分或按标题结构切分提升块质量。混合检索结合关键词检索BM25和向量检索兼顾精确匹配和语义匹配。重排序在检索后增加重排序模型进一步提升 top_k 结果的相关性。持久化存储把向量库落盘如使用 SQLite 或 FAISS避免每次重启重建索引。多文档支持扩展加载器支持 PDF、Word、网页等格式。这些优化都可以在本文的模块化结构上逐步叠加而无需推翻重写。12. 总结本文从零手写了一个可运行的 RAG 系统覆盖文档加载、文本切分、向量化、向量存储、检索、提示词组装、生成和溯源全流程。核心收获有三点理解 RAG 的完整数据流文档到块、块到向量、向量到检索、检索到生成。掌握可溯源的实现方法块级元数据 提示词约束 结果透出。获得一个可扩展的模块化代码骨架便于后续接入更复杂的优化策略。你可以把这份代码作为基础结合自己的业务文档和模型服务快速搭建一套属于自己的检索增强问答系统。

相关新闻

3分钟掌握无损歌词下载:163MusicLyrics歌词获取终极指南

3分钟掌握无损歌词下载:163MusicLyrics歌词获取终极指南

3分钟掌握无损歌词下载:163MusicLyrics歌词获取终极指南 【免费下载链接】163MusicLyrics 云音乐歌词获取处理工具【网易云、QQ音乐】 项目地址: https://gitcode.com/GitHub_Trending/16/163MusicLyrics 还在为喜欢的歌曲找不到高质量歌词而烦恼吗&#xff…

2026/8/2 19:20:50阅读更多 →
Unity C#变量定义与访问权限:从public/private到封装设计

Unity C#变量定义与访问权限:从public/private到封装设计

1. 项目概述:从“Hello World”到掌控数据如果你刚开始接触Unity和C#,可能还沉浸在拖拽物体、调整Transform的兴奋中。但很快你会发现,想让一个方块动起来,或者让角色血量减少,光靠Inspector面板拖拽是远远不够的。这时…

2026/8/2 19:18:50阅读更多 →
终极网页时光机:5分钟学会如何永久保存任何网页内容

终极网页时光机:5分钟学会如何永久保存任何网页内容

终极网页时光机:5分钟学会如何永久保存任何网页内容 【免费下载链接】wayback-machine-webextension A web browser extension for Chrome, Firefox, Edge, and Safari 14. 项目地址: https://gitcode.com/gh_mirrors/wa/wayback-machine-webextension 你是否…

2026/8/2 19:18:50阅读更多 →
Cocos Creator热更新实战:基于Manifest的版本控制与增量更新方案

Cocos Creator热更新实战:基于Manifest的版本控制与增量更新方案

1. 项目概述:为什么我们需要基于Manifest的版本控制? 在移动游戏开发,尤其是使用Cocos Creator的项目中,资源热更新几乎是每个项目上线后都必须面对的核心运维需求。想象一下,你的游戏上线后,发现了一个UI贴…

2026/8/2 20:35:20阅读更多 →
5分钟快速上手:ComfyUI-LTXVideo终极AI视频生成指南

5分钟快速上手:ComfyUI-LTXVideo终极AI视频生成指南

5分钟快速上手:ComfyUI-LTXVideo终极AI视频生成指南 【免费下载链接】ComfyUI-LTXVideo LTX-Video Support for ComfyUI 项目地址: https://gitcode.com/GitHub_Trending/co/ComfyUI-LTXVideo 在当今AI视频生成技术飞速发展的时代,ComfyUI-LTXVid…

2026/8/2 20:35:20阅读更多 →
如何快速配置LTX-LoRAs:新手用户的完整视频编辑指南

如何快速配置LTX-LoRAs:新手用户的完整视频编辑指南

如何快速配置LTX-LoRAs:新手用户的完整视频编辑指南 【免费下载链接】LTX-LoRAs 项目地址: https://ai.gitcode.com/hf_mirrors/Alissonerdx/LTX-LoRAs LTX-LoRAs是一系列专为LTX 2.3视频生成模型设计的轻量级适配插件,通过LoRA(低秩…

2026/8/2 20:35:20阅读更多 →
如何轻松完成Switch和3DS游戏文件传输:Boop的终极完整指南

如何轻松完成Switch和3DS游戏文件传输:Boop的终极完整指南

如何轻松完成Switch和3DS游戏文件传输:Boop的终极完整指南 【免费下载链接】Boop GUI for network install for switch and 3ds 项目地址: https://gitcode.com/gh_mirrors/boo/Boop 还在为Switch和3DS游戏文件传输烦恼吗?复杂的命令行操作、Pyth…

2026/8/2 20:35:19阅读更多 →
VSCode+SDCC搭建STM8开发环境:从环境配置到项目实战

VSCode+SDCC搭建STM8开发环境:从环境配置到项目实战

1. 从零开始:为什么选择 VSCode SDCC 开发 STM8?如果你和我一样,厌倦了那些庞大、笨重、启动缓慢的“官方”IDE,同时又对STM8这颗性价比极高的8位MCU情有独钟,那么今天聊的这个组合,可能会让你眼前一亮。S…

2026/8/2 20:35:18阅读更多 →
终极PPT制作革命:AI如何生成原生可编辑的PowerPoint演示文稿

终极PPT制作革命:AI如何生成原生可编辑的PowerPoint演示文稿

终极PPT制作革命:AI如何生成原生可编辑的PowerPoint演示文稿 【免费下载链接】ppt-master AI turns documents or topics into real, native PowerPoint decks—with native shapes, transitions and animations, data-backed charts and tables on demand, audio n…

2026/8/2 20:33:17阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
MATLAB xcorr函数详解:从互相关原理到四大实战应用

MATLAB xcorr函数详解:从互相关原理到四大实战应用

1. 从一次信号“找茬”说起:为什么我们需要互相关几年前,我在处理一组声学传感器数据时遇到了一个棘手的问题。我有两个麦克风记录了一段相同的音频信号,理论上它们接收到的声音波形应该非常相似,只是由于麦克风位置不同&#xff…

2026/8/2 0:00:10阅读更多 →
限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

限时公开!某头部SaaS公司内部AI模板工厂架构文档(含5类行业模板源码+性能压测报告)

更多请点击: https://intelliparadigm.com 第一章:AI模板批量生成的核心价值与落地全景 AI模板批量生成正从实验性工具演进为现代软件工程的关键基础设施。它通过语义理解、上下文感知与结构化约束,将重复性高、模式明确的代码/文档/配置生成…

2026/8/2 0:00:12阅读更多 →
如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南

如何快速找回消失的网页:Web Archives浏览器扩展终极指南 【免费下载链接】web-archives Browser extension for viewing archived and cached versions of web pages, available for Chrome, Edge and Safari 项目地址: https://gitcode.com/gh_mirrors/we/web-a…

2026/8/2 0:00:13阅读更多 →
无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理

无损视频剪辑终极指南:如何实现快速高效的多媒体处理 【免费下载链接】lossless-cut The swiss army knife of lossless video/audio editing 项目地址: https://gitcode.com/gh_mirrors/lo/lossless-cut 在数字媒体创作领域,视频编辑处理的质量损…

2026/8/2 1:29:34阅读更多 →
AI辅助本科论文写作:8大工具评测与高效使用指南

AI辅助本科论文写作:8大工具评测与高效使用指南

1. 本科生论文写作的AI辅助现状本科毕业论文是每个大学生必须跨越的一道坎。记得我当年写论文时,光是文献检索就花了整整两周时间,打印的参考文献堆满了半个书桌。如今AI技术的发展为学术写作带来了革命性变化,合理使用这些工具可以节省80%以…

2026/8/2 2:32:55阅读更多 →
如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手

如何快速配置大麦自动抢票系统:从零开始搭建Python抢票助手 【免费下载链接】ticket-purchase 大麦自动抢票,支持人员、城市、日期场次、价格选择 项目地址: https://gitcode.com/GitHub_Trending/ti/ticket-purchase 还在为抢不到热门演唱会门票…

2026/8/2 2:09:20阅读更多 →