当前位置: 首页 > news >正文

【0-1的agent进阶篇】RAG:从Embedding到检索增强生成的底层逻辑

🔥个人主页:代码不加冰(欢迎来访)
🎬作者简介:java后端学习者
❄️个人专栏:LeetCode刷题日记 , 苍穹外卖日记,SSM框架深入,JavaWeb,
命运的结局尽可永在,不屈的挑战却不可须臾或缺!


大家好,我是代码不加冰,好久没有发文章了,最近开始回归更新,让我们一起进步。这篇主要给大家分享一下RAG的相关内容。不是仅仅是一个词汇,而是带你从项目的源码看起,分析内部的逻辑。


RAG = 把用户的问题变成向量 → 去知识库找相关内容 → 把找到的内容塞回 Prompt → 再让大模型回答。

而 LangChain4j 做的事情,就是把这套流程拆成了一组非常清晰的抽象。

而我们今天主要侧重第一步Embedding,也就是文本向量化,文字怎么变成向量,刚听到这些词的时候我也会觉得莫名其妙,这明明是毫不相关的领域,到底是什么联系的,这篇文章就带你深入了解。

一、RAG 到底是什么

RAG = Retrieval-Augmented Generation

翻译成人话:

先检索,再增强,最后生成。

它解决的核心问题是:

LLM 的知识是死的(训练数据截止日期之后的事它不知道),
但 RAG 可以在用户提问时,从外部知识库实时检索相关信息,塞进 Prompt,让 LLM 基于这些信息回答。

所以 RAG 不是重新训练 LLM,而是给 LLM 配一个“外挂知识库”


二、RAG 整体链路

RAG 有两条链路,一上一下,缺一不可:

text RAG │ ┌──────────┴──────────┐ │ │ Ingestion Retrieval 数据入库 数据检索

链路一:Ingestion(数据入库)—— 提前准备好知识

text 公司员工手册.pdf ↓ Document ↓ DocumentSplitter(切分) ↓ TextSegment(文本片段) ↓ EmbeddingModel(向量化) ← 这里就是 Embedding ↓ Embedding(向量) ↓ EmbeddingStore(向量数据库)

最终库里存的是:

text TextSegment: "员工请假需要提前3天提交申请..." Embedding: [0.023, -0.182, 0.731, ...]

Ingestion 的本质:把人类能理解的知识,转换成机器能进行语义检索的向量。


链路二:Retrieval(检索增强)—— 用户提问时实时检索

text 用户:“请假需要提前多久?” ↓ UserMessage ↓ Query ↓ QueryTransformer(查询改写) ↓ QueryRouter(路由选择) ↓ ContentRetriever(真正检索) ← 这里会用到 Embedding 进行相似度搜索 ↓ EmbeddingStore(向量库) ↓ Top-K 相关内容 ↓ ContentAggregator(汇总排序) ↓ ContentInjector(注入 Prompt) ↓ 增强后的 UserMessage ↓ ChatModel(LLM) ↓ Answer

三、现在,我们聚焦Embedding

上面的链路中,Ingestion 的 EmbeddingModelRetrieval 的相似度搜索,都围绕同一个核心概念:

Embedding(文本向量化)


四、Embedding 到底是什么

在 LangChain4j 源码中,Embedding 就是一个float[]

java public class Embedding { private final float[] vector; }

例如一个 384 维的向量:

java float[] vector = { 0.12f, -0.52f, 0.83f, 0.19f, ... }; // 长度 = 384

Embedding ≠ 文本
Embedding 是文本经过模型编码后得到的一组数字

维度取决于模型:

模型维度
某些轻量模型384
OpenAI text-embedding-ada-0021536
Cohere 某些模型768

所以:

text Embedding = float[dimension]

五、为什么一定要 Embedding

这是最核心的问题。

用户问:

“请假需要提前多久申请?”

知识库里可能根本没有这句话,它写的是:

“员工休假须至少提前三个工作日提交审批。”

如果用传统字符串匹配(关键词搜索),基本匹配不到。

但如果用Embedding

text 问题:“请假需要提前多久申请?” ↓ Embedding ↓ [0.21, 0.83, -0.14, 0.52, ...] 知识:“员工休假须至少提前三个工作日提交审批。” ↓ Embedding ↓ [0.19, 0.79, -0.11, 0.48, ...]

这两个向量在向量空间中距离很近,所以能被检索到。

text 请假需要提前多久? ↓ Embedding ↓ [0.21, 0.83...] ↓ 向量数据库进行相似度搜索 ↓ 找到:“员工休假须至少提前三个工作日……”

这就是Semantic Search(语义搜索)

不是字面匹配,而是意思匹配。


六、CosineSimilarity:怎么判断两个向量像不像

向量数据库找到了候选结果,但怎么排序呢
相似度计算,最常用的是余弦相似度

源码中的计算逻辑:

java double dotProduct = 0.0; double normA = 0.0; double normB = 0.0; for (int i = 0; i < vectorA.length; i++) { dotProduct += vectorA[i] * vectorB[i]; normA += vectorA[i] * vectorA[i]; normB += vectorB[i] * vectorB[i]; } return dotProduct / Math.sqrt(normA) * Math.sqrt(normB);

用人话翻译:

判断两个向量的“方向”有多一致。

比如:

text A ────────────────> B ──────────────> 方向非常接近 → 相似度高 ✅ text A ────────────────> B <──────────────── 方向相反 → 相似度低 ❌

七、为什么用余弦,而不是算距离

因为 Embedding 关注的是语义方向,而不是长度

举个例子:

text

A = [1, 1] B = [10, 10]

虽然|A| ≠ |B|(长度不同),但方向完全一样。
余弦相似度:

text

cos θ = 1

所以它认为A 和 B 非常相似,这非常适合语义向量——
“很小的事”和“很大的事”在语义上可能是同一类。


八、为什么 LangChain4j 又搞了一个 RelevanceScore

余弦相似度的范围是[-1, 1],但 RAG 开发中更希望看到[0, 1]

所以 LangChain4j 做了转换:

java return (cosineSimilarity + 1) / 2;

映射关系:

余弦相似度RelevanceScore
-10
-0.50.25
00.5
0.50.75
11

然后就可以做阈值过滤:

java if (match.score() >= 0.7) { // 认为比较相关,采纳 }

九、不同 Embedding 模型为什么不能混用

这是一个非常容易踩的坑。

  • 模型 A:维度 = 1536

  • 模型 B:维度 = 384

如果你的向量数据库 Collection 定义的是dimension = 1536,却塞入 384 维向量:

text ❌ 报错

可以这样理解:

text EmbeddingModel.dimension() = 数据库表结构的 Schema

就像age INT不能塞字符串一样,向量维度必须严格匹配


十、Embedding 在 RAG 中的两个位置

理解了 Embedding,你就能看懂它在 RAG 中的两次出现:

text RAG │ ┌──────────┴──────────┐ │ │ Ingestion Retrieval │ │ ▼ ▼ 文档 → EmbeddingModel 问题 → EmbeddingModel │ │ ▼ ▼ Embedding Embedding │ │ ▼ ▼ EmbeddingStore 相似度搜索(Cosine) │ ▼ 找到相关内容
阶段Embedding 的作用
Ingestion把文档变成向量,存入数据库
Retrieval把用户问题变成向量,去数据库里找最相似的

十一、整体回顾

text ┌─────────────────────────────────────────────────────────────┐ │ RAG 完整链路 │ ├──────────────────────────┬──────────────────────────────────┤ │ Ingestion │ Retrieval │ ├──────────────────────────┼──────────────────────────────────┤ │ │ │ │ PDF → Document │ 用户问题 → Query │ │ ↓ │ ↓ │ │ DocumentSplitter │ QueryTransformer │ │ ↓ │ ↓ │ │ TextSegment │ QueryRouter │ │ ↓ │ ↓ │ │ ★ EmbeddingModel ★ │ ContentRetriever │ │ ↓ │ ↓ │ │ ★ Embedding ★ │ ★ 相似度搜索(Cosine)★ │ │ ↓ │ ↓ │ │ EmbeddingStore │ ContentAggregator │ │ │ ↓ │ │ │ ContentInjector │ │ │ ↓ │ │ │ ChatModel → Answer │ └──────────────────────────┴──────────────────────────────────┘

下一篇预告

这一篇我们重点攻克了Embedding 是什么、为什么、怎么算相似度

下一篇继续深入:向量数据库的存储与索引

http://www.jsqmd.com/news/1402325/

相关文章:

  • OpenClaw AI智能体框架部署指南:从环境配置到实战应用
  • DeekSeek harness安装插件后报Failed to load plugins
  • 2026 年洛阳诚信的商家 GEO 优化运营品牌推荐,为啥你的门店明明在商圈,却没人找上门?这招靠它就能精准拉到附近的精准客。 - 行业推荐官【认证】
  • Kali Linux渗透测试实战指南:从零基础到靶场攻防
  • 用 HackRF + GNU Radio 制作 FM 发射机:从 WAV 到 103 MHz 空中信号
  • 辣知·化智25 上古夷夏文明的东西格局
  • Windows系统删除“...”畸形文件夹的三种有效方法
  • Python脚本执行全解析:从基础运行到工程化部署的完整指南
  • Windows远程桌面连接Ubuntu:xrdp+Xorg保姆级配置与排错指南
  • 降AIGC终极攻略!AI率92%暴降至5%!实测10款降AIGC软件!免费降AIGC额度薅到爽!
  • 鸿蒙开发环境搭建:DevEco Studio Windows Tool 3.0.0.800 安装与配置全指南
  • 基于OpenClaw与本地大模型构建梅花易数AI智能体实践
  • 金钟壶红丝垂落,像把窑火留在月白上
  • 用过才知道,涿州会计供应商真的能满足财务需求吗?
  • 一次请求的前世今生(进阶):底层原理与生产环境那些坑
  • 并查集实战:扩展域思想解决“团伙”问题与C++实现
  • Python list()与map()深度解析:从基础到高阶的性能优化与实战应用
  • 炼油厂核心装置沙盘模型控制系统设计:基于STM32与Modbus RTU的常减压蒸馏-催化裂化-乙烯裂解全流程联动方案
  • 2026 年山东口碑好的美麟鸡柳棒品牌哪家可靠,楼下炸串摊的这玩意儿,比别家嫩三倍还无骨,老板说没人猜得出它的真实身份 - 行业严选官
  • 光纤线缆标识解读:从编码原理到运维实战的完整指南
  • Windows 10应用安装失败深度解析:从UWP机制到Microsoft To-Do安装全攻略
  • 基于OpenClaw构建自复盘AI智能体协作系统:从架构到实践
  • 数据脱敏实战:从原理到实现,构建隐私保护工具链
  • C# Dictionary字典
  • 找回昨天的自己:ArkTS 时间分组与关键词搜索的查询实现
  • VSCode我推荐的23个不得不装的插件
  • Hand Visibility Detector 论文解读
  • LeetCode AI助手:苏格拉底式引导与面试模拟提升算法思维
  • 算法与数据结构实战指南:从核心原理到工程优化
  • OpenClaw与Deepgram构建自动化语音转录工作流实战