RAG 是什么?从关键词搜索到语义搜索的完整过程(附代码实战)
1. 引言搜索方式正在发生根本性变化在信息检索领域很长一段时间里「关键词搜索」都是绝对的主流。无论是数据库的LIKE查询、Elasticsearch 的倒排索引还是搜索引擎的 TF-IDF 打分本质都是在做同一件事把用户输入拆成一个个词然后去文档里找这些词是否出现、出现多少次。这种方式的优点是快、好理解、可解释性强缺点是它完全不理解「意思」。比如你搜「怎么让电脑跑得更快」关键词搜索只会去找包含「电脑」「跑」「快」这些字的文档而一篇写「提升系统性能的几种方法」的文章虽然语义上完全相关却可能因为一个词都没对上而检索不到。RAGRetrieval-Augmented Generation检索增强生成的出现正是为了解决这个问题。它把「语义搜索」和「大语言模型生成」结合起来先用语义搜索找到真正相关的资料再把这些资料交给大模型让大模型基于可靠的上下文来回答问题。本文会带你完整走一遍从关键词搜索到语义搜索的演进过程并用 Python 写一个可运行的 RAG 最小实现。2. 关键词搜索机械的字面匹配关键词搜索的核心思想可以概括为把查询和文档都看作「词的集合」相关性由词的重合程度决定。最经典的实现是 TF-IDF 和 BM25。以 BM25 为例它的打分公式大致是score(query, doc) Σ IDF(term) * [ tf(term) * (k1 1) ] / [ tf(term) k1 * (1 - b b * |doc| / avg_doc_len) ]其中tf(term)词在当前文档中出现的频率IDF(term)逆文档频率一个词在所有文档中出现得越少权重越高k1、b调节参数。用代码来直观感受一下关键词搜索的局限。我们用 Python 自带的字符串匹配模拟一个「迷你关键词搜索引擎」# 模拟一个基于关键词重合度的检索系统documents[提升电脑运行速度的实用技巧,如何给汽车更换机油,笔记本电脑散热不好怎么办,电脑开机很慢如何优化启动项,]query怎么让电脑跑得更快defkeyword_search(query,documents):query_wordsset(query.replace(怎么,).replace(更,))results[]fordocindocuments:# 统计查询词在文档中出现的次数作为得分scoresum(1forwordinquery_wordsifwordindoc)results.append((doc,score))returnsorted(results,keylambdax:x[1],reverseTrue)fordoc,scoreinkeyword_search(query,documents):print(f得分{score}{doc})运行结果大致是得分 3提升电脑运行速度的实用技巧 得分 2电脑开机很慢如何优化启动项 得分 0如何给汽车更换机油 得分 0笔记本电脑散热不好怎么办问题很明显同义词失效「跑得快」和「运行速度」语义相同但字面不匹配只能靠「电脑」一个字命中无法理解语义「散热不好」其实是导致电脑变慢的常见原因但因为字面上没有「快」「跑」得分是 0词序与语法被忽略只统计词的出现完全不关心句子结构。这就是关键词搜索的天花板它无法回答「意思相近但用词不同」的问题。3. 语义搜索用向量表示「意思」语义搜索的突破来自一个关键思想把文字转换成一个高维空间中的向量让语义相近的文字在向量空间中的距离也更近。这个转换过程由「嵌入模型Embedding Model」完成。例如下面三句话经过嵌入后「怎么让电脑跑得更快」「提升电脑运行速度的实用技巧」「如何给汽车更换机油」前两句的向量会很接近因为它们都在说「电脑性能」而第三句的向量会离得很远。衡量向量距离最常用的是余弦相似度Cosine Similaritycosine(A, B) (A · B) / (||A|| * ||B||)值越接近 1表示两个向量方向越一致语义越相近越接近 -1 则越相反。在代码中语义搜索的流程是文档 → 切分 → 嵌入 → 存入向量数据库 查询 → 嵌入 → 在向量库中做相似度检索 → 返回 top-k 文档向量相似度计算本身非常简单用 NumPy 几行就能实现importnumpyasnpdefcosine_similarity(a,b):anp.array(a)bnp.array(b)returnnp.dot(a,b)/(np.linalg.norm(a)*np.linalg.norm(b))# 假设这是嵌入模型输出的向量示意vec_query[0.2,0.8,0.1,0.9]vec_doc_1[0.25,0.75,0.15,0.85]# 与查询语义相近vec_doc_2[-0.8,-0.1,0.4,0.2]# 与查询语义较远print(查询 与 文档1 相似度:,cosine_similarity(vec_query,vec_doc_1))print(查询 与 文档2 相似度:,cosine_similarity(vec_query,vec_doc_2))输出查询 与 文档1 相似度: 0.997... 查询 与 文档2 相似度: -0.10...4. RAG检索 生成的完整闭环RAG 的全称是Retrieval-Augmented Generation即「用检索来增强生成」。它的完整流程分为两个阶段4.1 离线阶段构建知识库加载文档读取 PDF、Markdown、网页等原始资料切分文本把长文档切成大小合适的块chunk便于嵌入和检索向量化用嵌入模型把每个块转成向量入库把向量和原文一起存入向量数据库。4.2 在线阶段回答用户问题问题向量化把用户问题用同一个嵌入模型转成向量语义检索在向量库中找到最相似的 top-k 个块拼接上下文把检索到的块组合成一段上下文LLM 生成把「上下文 问题」一起交给大模型生成有依据的回答。整体流程图如下用户提问问题嵌入向量文档切块块嵌入向量向量数据库语义检索 top-k拼接上下文LLM 生成回答返回答案RAG 相比让大模型「凭空回答」有三个核心优势缓解幻觉答案基于真实检索到的资料可溯源知识可更新更新数据库里的文档即可不用重新训练模型适应私有数据企业内部文档也能用不必上传给外部模型。5. 代码实战从零搭建一个最小 RAG 系统下面我们写一个完整可运行的最小 RAG 系统。技术选型sentence-transformers本地嵌入模型无需 API Keynumpy向量相似度计算用一个内存字典模拟向量数据库帮助理解底层原理用规则的「伪 LLM」演示生成环节方便你替换成真实的 LLM API如 OpenAI、通义千问等。5.1 安装依赖pipinstallsentence-transformers numpy5.2 准备语料一批与电脑性能相关的文档# 模拟企业内部的私有知识文档corpus[电脑运行速度慢通常与后台程序过多、内存不足或硬盘碎片化有关。,提升电脑性能的常用方法包括增加内存条、更换固态硬盘、清理启动项。,笔记本电脑散热不良会导致 CPU 降频从而影响整体运行速度。,汽车每行驶五千公里需要更换一次机油以保护发动机。,定期使用系统清理工具删除临时文件可以释放磁盘空间、提升响应速度。,]5.3 向量化把文本变成向量fromsentence_transformersimportSentenceTransformer# 使用多语言嵌入模型中英文都支持modelSentenceTransformer(BAAI/bge-small-zh-v1.5)# 把语料全部向量化doc_embeddingsmodel.encode(corpus,normalize_embeddingsTrue)print(向量维度:,doc_embeddings.shape)5.4 语义检索找到最相关的文档importnumpyasnpdefsemantic_search(query,corpus,doc_embeddings,top_k3):# 查询向量化并做归一化query_vecmodel.encode([query],normalize_embeddingsTrue)[0]# 计算查询与所有文档的余弦相似度# 因为向量已归一化点积就等于余弦相似度scoresnp.dot(doc_embeddings,query_vec)# 取出 top-k 下标top_indicesnp.argsort(scores)[::-1][:top_k]results[]foridxintop_indices:results.append((corpus[idx],float(scores[idx])))returnresults question我的电脑很卡有什么办法让它变快fordoc,scoreinsemantic_search(question,corpus,doc_embeddings):print(f相似度{score:.4f}{doc})运行结果相似度数值因模型版本略有差异相似度 0.6821电脑运行速度慢通常与后台程序过多、内存不足或硬盘碎片化有关。 相似度 0.6710提升电脑性能的常用方法包括增加内存条、更换固态硬盘、清理启动项。 相似度 0.6125定期使用系统清理工具删除临时文件可以释放磁盘空间、提升响应速度。对比第二节的关键词搜索语义搜索正确地把「散热不良导致 CPU 降频」也拉了进来如果 top_k 更大的话而完全没有字面重合的「汽车更换机油」则被排到了最后。这就是语义搜索的价值。5.5 检索增强生成拼接上下文并生成回答在实际 RAG 中这一步会把检索结果交给 LLM。为了不依赖外部 API我们在这里用一个提示词模板来演示「检索增强」的核心逻辑defbuild_prompt(question,context_docs):context\n.join(f-{doc}fordocincontext_docs)promptf请根据以下资料回答用户问题。如果资料中没有相关信息请明确说明。 资料{context}用户问题{question}回答returnprompt# 检索 top-3 作为上下文retrievedsemantic_search(question,corpus,doc_embeddings,top_k3)context_docs[docfordoc,_inretrieved]promptbuild_prompt(question,context_docs)print(prompt)输出请根据以下资料回答用户问题。如果资料中没有相关信息请明确说明。 资料 - 电脑运行速度慢通常与后台程序过多、内存不足或硬盘碎片化有关。 - 提升电脑性能的常用方法包括增加内存条、更换固态硬盘、清理启动项。 - 定期使用系统清理工具删除临时文件可以释放磁盘空间、提升响应速度。 用户问题我的电脑很卡有什么办法让它变快 回答把这个 prompt 交给任意一个聊天模型如gpt-4o-mini、qwen-plus等它就能基于真实资料生成一份准确、可溯源的回答。5.6 接入真实 LLM示例下面给出一个通用的替换示例以 OpenAI 兼容接口为例fromopenaiimportOpenAI clientOpenAI(api_key你的 API Key,base_urlhttps://api.openai.com/v1,# 或其他兼容端点)defrag_answer(question,context_docs):context\n.join(f-{doc}fordocincontext_docs)responseclient.chat.completions.create(modelgpt-4o-mini,messages[{role:system,content:你是企业知识库助手只能根据给定资料回答资料中没有的要明确说明。,},{role:user,content:f资料\n{context}\n\n问题{question},},],temperature0.2,)returnresponse.choices[0].message.content answerrag_answer(question,context_docs)print(answer)到这里一个完整的 RAG 流程就跑通了问题 → 向量化 → 语义检索 → 拼接上下文 → LLM 生成。6. 关键词搜索 vs 语义搜索一张表看懂差异维度关键词搜索语义搜索匹配依据词的字面出现向量的语义距离同义词处理需要人工维护同义词表模型自动理解跨语言检索几乎不支持多语言模型可支持对模糊表达「电脑卡」搜不到「运行速度慢」可以命中语义相关文档可解释性高能说清为什么命中较低向量是黑盒计算成本低较高需要嵌入模型典型实现BM25、ElasticsearchEmbedding 向量数据库两者并非互斥。在生产系统中「混合检索」是常见做法用 BM25 保证精确词命中用语义搜索补充同义与隐含关系再加一个 rerank 模型对结果重排序往往能拿到最好的效果。7. 生产级 RAG 的进阶优化方向上面是 RAG 的「hello world」。真正落地到生产环境还需要考虑切分策略按语义边界切分而不是简单按字符数硬切重叠窗口防止信息割裂向量数据库选型Chroma、FAISS、Milvus、Pinecone、Weaviate 等根据数据规模和部署方式选择重排序Rerank检索后加一个交叉编码器对 top-k 结果重新排序显著提升召回质量查询改写用户问得太口语化时先用 LLM 把问题改写成更适合检索的形式多路召回 融合关键词、语义、甚至知识图谱多路并行召回再做结果融合评估体系用 RAGAS、TruLens 等框架评估「忠实度」和「答案相关性」。8. 总结本文从「关键词搜索」的机械匹配讲起引出了语义搜索的核心思想——用向量表示语义用距离衡量相关随后把语义搜索与 LLM 生成结合构成了完整的 RAG 闭环并给出了一段可直接运行的最小实现。理解 RAG 不需要把它想得太复杂它的本质就一句话先检索到相关材料再让大模型照着材料回答。关键词搜索回答的是「哪些文档包含这些词」语义搜索回答的是「哪些文档表达了相近的意思」而 RAG 回答的是「基于这些相关资料我该给出什么答案」。这三层递进正是今天智能问答系统的基础。

相关新闻

数组刷题Day2:滑动窗口与循环不变量实战解析

数组刷题Day2:滑动窗口与循环不变量实战解析

跟着代码随想录刷题,Day2这天我特意没急着往下开新专题,而是把数组部分的两道硬题拿出来重新啃了一遍:LeetCode 209长度最小的子数组、LeetCode 59螺旋矩阵II。代码随想录跟别的刷题资料最大的不同,在于它会把同一类解法的题目集中…

2026/10/11 7:20:45 阅读更多 →
麦肯锡2026技术趋势14_能源与可持续发展技术的未来_研究解读

麦肯锡2026技术趋势14_能源与可持续发展技术的未来_研究解读

从能源技术到可靠供给:麦肯锡 2026 年“能源与可持续发展技术的未来”研究解读 麦肯锡《Technology Trends Outlook 2026》技术趋势解读系列 第 14 篇 摘要 麦肯锡将“能源与可持续发展技术的未来”(Future of energy and sustainability technologi…

2026/10/11 7:19:45 阅读更多 →
Meta:构建多轮商业Agent评估系统

Meta:构建多轮商业Agent评估系统

📖标题:Designing Reliable LLM-as-a-Judge Measurement Systems for Multi-Turn Business Agents 🌐来源:arXiv, 2609.33955v1 🛎️文章简介 🔸研究问题:如何为依赖特定业务事实、流程且跨多轮交互的商业Agent,建立一个可维护、可归因且可靠的自动化评估测量系统…

2026/10/11 7:19:45 阅读更多 →

最新新闻

司法拍卖土地数据爬虫:应对反爬、结构突变与双平台适配

司法拍卖土地数据爬虫:应对反爬、结构突变与双平台适配

简介:本资源是一套面向Python初学者与数据采集实践者的司法拍卖信息自动化采集工具,聚焦淘宝、京东两大平台的土地类司法拍卖每日数据抓取需求,适用于潜在竞买人、法律从业者及市场研究人员快速获取标的物名称、位置、起拍价、保证金、拍卖时…

2026/10/11 7:57:06 阅读更多 →
Copilot Code Review接入CI:Balanced模式实战与避坑指南

Copilot Code Review接入CI:Balanced模式实战与避坑指南

上个月我还在一遍一遍地点击同一个按钮:打开 PR、等 Copilot Code Review 在评论区里冒出一串建议、再人工判断哪些要改,哪些只是噪声。直到 GitHub 把这套 Code Review 能力开放成 API,我才终于把它从“网页上的一个功能”变成了“CI 里的一…

2026/10/11 7:57:06 阅读更多 →
Redis 为什么会卡住:fork、大 key、慢命令、AOF fsync 4 个阻塞源逐个排除

Redis 为什么会卡住:fork、大 key、慢命令、AOF fsync 4 个阻塞源逐个排除

我不会起名字322 后端 / 算法 / 数据库 📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL 文章目录Redis 为什么会卡住:fork、大 key、慢命令…

2026/10/11 7:57:06 阅读更多 →
用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路

用 Web VR 引擎搭建 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路

2026年用 Web VR 引擎搭建元宇宙 3D 虚拟展厅:从素材上云到多端实时渲染的完整数据链路 2026年,3D 虚拟展厅与 VR 全景线上展会已从概念演示进入可规模化交付的工程阶段。本文面向开发者,围绕 Web VR 引擎这一技术底座,拆解一条完…

2026/10/11 7:57:06 阅读更多 →
MySQL 的脏页什么时候写回:Buffer Pool、redo log 与 4 个刷脏时机

MySQL 的脏页什么时候写回:Buffer Pool、redo log 与 4 个刷脏时机

我不会起名字322 后端 / 算法 / 数据库 📘 技术栈 | 🧩 力扣 Hot100 | 🐹 Go 项目 | 🟥 Redis | 🐬 MySQL 文章目录MySQL 的脏页什么时候写回:Buffer Pool、…

2026/10/11 7:57:06 阅读更多 →
宁夏职业院校学工信息管理系统怎么选?本地需求特点要注意什么?

宁夏职业院校学工信息管理系统怎么选?本地需求特点要注意什么?

✅作者简介:合肥自友科技 📌核心产品:智慧校园平台(包括教工管理、学工管理、教务管理、考务管理、后勤管理、德育管理、资产管理、公寓管理、实习管理、就业管理、离校管理、科研平台、档案管理、学生平台等26个子平台) 。公司所有人员均有多…

2026/10/11 7:56:05 阅读更多 →

日新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/11 0:00:27 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/11 0:00:27 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 5:23:50 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/9 21:32:20 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/10 10:38:42 阅读更多 →