Spring AI 学习篇(五)| 嵌入模型与向量表示的本质
- 一、本章核心学习目标
- 二、前置知识准备
- 三、为什么我们需要嵌入模型?
- 嵌入的本质:把文本变成机器可理解的数字向量
- 四、向量相似度计算原理
- 1. 余弦相似度(最常用)
- 2. 点积相似度
- 3. 欧氏距离
- 五、Spring AI EmbeddingClient统一API详解
- 1. 基础依赖配置
- (1) 对接DeepSeek商业嵌入API
- (2) 对接本地Ollama嵌入模型
- 2. 基础配置
- (1) DeepSeek商业API配置
- (2) Ollama本地模型配置
- 3. 基础调用示例
- 4. 计算两个文本的相似度
- 六、2026年主流嵌入模型对比与选型
- 1. 嵌入模型核心参数说明
- 2. 主流嵌入模型横向对比
- 3. 嵌入模型选型决策树
- 4. 本地Ollama部署BGE-M4
- 七、嵌入模型的常见误区
- 1. ❌ 误区一:用聊天模型做嵌入
- 2. ❌ 误区二:混用不同嵌入模型的向量
- 3. ❌ 误区三:向量维度越高越好
- 4. ❌ 误区四:嵌入模型越新越好
- 八、企业级性能优化技巧
- 1. 批量生成嵌入向量
- 2. 缓存嵌入向量
- 3. 限制文本长度
- 4. 选择合适的批量大小
- 九、常见坑与解决方案
- 1. ❌ 向量维度不匹配
- 2. ❌ 中文效果差
- 3. ❌ 批量调用报错
- 4. ❌ 本地模型速度慢
- 十、本章总结与下章预告
- 本章总结
- 下章预告
- 十一、课后练习
一、本章核心学习目标
学完本章,你将能够:
- 深刻理解嵌入(Embedding)的本质和核心原理
- 掌握向量相似度计算的三种常用方法
- 熟练使用Spring AI
EmbeddingClient统一API - 独立完成主流嵌入模型的对比与选型
- 实现本地Ollama嵌入模型与商业API的无缝切换
- 避开嵌入模型使用中的常见误区
- 掌握嵌入模型的企业级性能优化技巧
二、前置知识准备
- 已经完成前4篇的学习,熟练掌握ChatClient和提示词工程
- 了解大模型的知识截止和幻觉问题
- 熟悉Spring Boot的配置和依赖注入
三、为什么我们需要嵌入模型?
在前面的学习中,我们已经能够让大模型回答问题,但大模型有三个无法通过提示词工程解决的原生缺陷:
- 知识截止限制:所有大模型都有训练数据截止日期,无法回答训练之后发生的事情
- 幻觉问题:大模型会编造不存在的事实和数据
- 私有数据不可用:企业内部文档、客户数据等不能上传给大模型训练
嵌入模型就是解决这三个问题的核心钥匙,它是RAG(检索增强生成)技术的基础。
嵌入的本质:把文本变成机器可理解的数字向量
通俗地说,嵌入就是把人类语言的文本转换成一串固定长度的数字向量。它的神奇之处在于:语义相似的文本,生成的向量在空间中距离也会很近。
举个直观的例子:
- “我喜欢吃苹果” 和 “我爱吃苹果” → 向量距离非常近
- “我喜欢吃苹果” 和 “我喜欢吃香蕉” → 向量距离较近(都是水果)
- “我喜欢吃苹果” 和 “今天天气很好” → 向量距离非常远
这样一来,机器就可以通过计算向量之间的距离,来判断两段文本的语义相似度。
预告式提及:下一章我们会学习向量数据库,它就是专门用来存储这些向量并快速计算相似度的数据库。
四、向量相似度计算原理
向量相似度计算是检索的核心,常用的有三种方法:
1. 余弦相似度(最常用)
计算两个向量之间的夹角余弦值,取值范围是[-1, 1]。值越接近1,相似度越高;越接近-1,相似度越低;0表示完全无关。
特点:不受向量长度的影响,只关心向量的方向,是文本相似度计算的首选方法。
2. 点积相似度
计算两个向量对应位置元素的乘积之和。取值范围没有限制,值越大相似度越高。
特点:同时考虑向量的方向和长度,适合需要考虑文本重要性的场景。
3. 欧氏距离
计算两个向量在空间中的直线距离。取值范围是[0, +∞),值越小相似度越高。
特点:适合比较向量的绝对差异,在文本相似度计算中使用较少。
企业级最佳实践:中文RAG场景首选余弦相似度。
五、Spring AI EmbeddingClient统一API详解
和ChatClient一样,Spring AI为所有嵌入模型提供了统一的EmbeddingClient接口。无论你使用的是商业API还是本地模型,代码完全相同。
1. 基础依赖配置
(1) 对接DeepSeek商业嵌入API
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-deepseek</artifactId></dependency>(2) 对接本地Ollama嵌入模型
<dependency><groupId>org.springframework.ai</groupId><artifactId>spring-ai-starter-model-ollama</artifactId></dependency>2. 基础配置
(1) DeepSeek商业API配置
spring:ai:deepseek:api-key:你的DeepSeek API Keyembedding:options:model:deepseek-embedding(2) Ollama本地模型配置
spring:ai:ollama:base-url:http://localhost:11434embedding:options:model:bge-m4重要说明:只需要添加对应的依赖和配置,Spring AI会自动为你创建EmbeddingClient实例,不需要编写任何额外代码。
3. 基础调用示例
importorg.springframework.ai.embedding.EmbeddingClient;importorg.springframework.web.bind.annotation.GetMapping;importorg.springframework.web.bind.annotation.RequestParam;importorg.springframework.web.bind.annotation.RestController;importjava.util.List;@RestControllerpublicclassEmbeddingController{privatefinalEmbeddingClientembeddingClient;publicEmbeddingController(EmbeddingClientembeddingClient){this.embeddingClient=embeddingClient;}// 生成单个文本的嵌入向量(返回 float[])@GetMapping("/embed")publicfloat[]embed(@RequestParamStringtext){returnembeddingClient.embed(text);}// 批量生成嵌入向量@GetMapping("/embed/batch")publicList<float[]>embedBatch(@RequestParamList<String>texts){returnembeddingClient.embed(texts);}// 获取嵌入向量的维度@GetMapping("/embed/dimension")publicintgetDimension(){returnembeddingClient.dimensions();}}4. 计算两个文本的相似度
Spring AI 不提供内置的向量计算工具类,需要手动实现余弦相似度:
@GetMapping("/similarity")publicdoublecalculateSimilarity(@RequestParamStringtext1,@RequestParamStringtext2){float[]vector1=embeddingClient.embed(text1);float[]vector2=embeddingClient.embed(text2);returncosineSimilarity(vector1,vector2);}/** * 手动实现余弦相似度:cos(θ) = (A·B) / (|A| × |B|) */privatedoublecosineSimilarity(float[]a,float[]b){doubledotProduct=0,normA=0,normB=0;for(inti=0;i<a.length;i++){dotProduct+=a[i]*b[i];normA+=a[i]*a[i];normB+=b[i]*b[i];}returndotProduct/(Math.sqrt(normA)*Math.sqrt(normB));}测试示例:
- 输入:text1=“我喜欢吃苹果”,text2=“我爱吃苹果” → 相似度≈0.95
- 输入:text1=“我喜欢吃苹果”,text2=“今天天气很好” → 相似度≈0.1
六、2026年主流嵌入模型对比与选型
这是本章最核心的内容,也是你在实际开发中必须做出的决策。
1. 嵌入模型核心参数说明
| 参数 | 含义 | 影响 |
|---|---|---|
| 向量维度 | 向量包含的数字个数 | 维度越高,语义表达能力越强,但存储和计算成本也越高 |
| 最大序列长度 | 模型一次能处理的最大token数 | 超过这个长度的文本会被截断,导致语义丢失 |
| 中文性能 | 模型对中文语义的理解能力 | 直接决定RAG系统的检索准确率 |
| 商用许可证 | 是否可以免费用于商业用途 | 关系到企业的法律风险 |
2. 主流嵌入模型横向对比
| 模型名称 | 厂商/机构 | 类型 | 向量维度 | 最大长度 | 100万tokens成本 | 中文评分 | 商用许可 |
|---|---|---|---|---|---|---|---|
| BGE-M4 | 智源研究院 | 开源本地 | 1024 | 8192 | 0元 | 95 | Apache 2.0(免费商用) |
| M3E-v2 | 阿里达摩院 | 开源本地 | 1024 | 512 | 0元 | 90 | Apache 2.0 |
| Jina Embeddings v3 | Jina AI | 开源本地 | 1024 | 8192 | 0元 | 85 | Apache 2.0 |
| DeepSeek-embedding-v2 | 深度求索 | 商业API | 1536 | 4096 | 1元 | 85 | 商业 |
| 智谱embedding-3 | 智谱AI | 商业API | 1024 | 4096 | 5元 | 88 | 商业 |
| OpenAI text-embedding-3-small | OpenAI | 商业API | 1536 | 8191 | 0.14元 | 80 | 商业 |
3. 嵌入模型选型决策树
嵌入模型选型决策树: ├── 数据敏感,绝对不能上传到第三方服务器 → 必须选择开源本地模型 │ ├── 有GPU(4GB以上显存) → BGE-M4(首选,中文性能天花板) │ └── 只有CPU → M3E-v2(推理速度快,CPU友好) └── 数据不敏感,可以上传 → 商业API ├── 预算有限 → DeepSeek-embedding-v2(性价比之王) ├── 已经在使用智谱生态 → 智谱embedding-3 └── 多语言需求 → OpenAI text-embedding-3-small重要纠正:之前有说法"DeepSeek没有向量模型",这是完全错误的。DeepSeek不仅有官方的嵌入API,而且价格极低,是商业API的首选。
2026年最新进展:BGE-M4已经内置了稀疏向量支持,不需要再单独部署Splade等稀疏向量模型,可以直接实现混合检索。我们会在第8篇详细讲解混合检索技术。
4. 本地Ollama部署BGE-M4
如果你选择本地部署BGE-M4,只需要一行命令:
ollama pull bge-m4然后修改application.yml配置为Ollama模式,不需要修改任何Java代码。
七、嵌入模型的常见误区
1. ❌ 误区一:用聊天模型做嵌入
很多新手会误以为"同一个模型既可以聊天也可以做嵌入",这是完全错误的。
聊天模型和嵌入模型的训练目标完全不同:
- 聊天模型的训练目标是生成下一个token
- 嵌入模型的训练目标是让语义相似的文本生成相似的向量
用聊天模型生成的向量,相似度计算结果会非常不准确。
2. ❌ 误区二:混用不同嵌入模型的向量
每个嵌入模型生成的向量空间是完全不同的。你不能把BGE-M4生成的向量和DeepSeek生成的向量放在同一个向量数据库中进行比较,结果会完全没有意义。
企业级最佳实践:一个RAG系统只能使用一个嵌入模型,所有向量都必须由同一个模型生成。
3. ❌ 误区三:向量维度越高越好
虽然维度越高语义表达能力越强,但1024维已经足够应对绝大多数中文场景。更高的维度(如1536维)只会增加存储和计算成本,带来的性能提升非常有限。
4. ❌ 误区四:嵌入模型越新越好
不要盲目追求最新的嵌入模型。BGE-M4已经经过了大量企业的验证,稳定性和效果都有保障。新模型可能存在各种问题,不建议直接用于生产环境。
八、企业级性能优化技巧
1. 批量生成嵌入向量
不要每次只生成一个文本的向量,尽量批量生成,这样可以大幅提升效率。
importjava.util.Arrays;importjava.util.List;// 推荐:批量生成List<String>texts=Arrays.asList("文本1","文本2","文本3");List<float[]>embeddings=embeddingClient.embed(texts);// 不推荐:逐个生成for(Stringtext:texts){float[]embedding=embeddingClient.embed(text);}2. 缓存嵌入向量
对于不会变化的文本(如知识库文档),生成一次向量后就缓存起来,不要重复生成。可以使用Redis作为缓存。
3. 限制文本长度
不要把过长的文本直接传给嵌入模型,应该先进行切分。我们会在第7篇详细讲解文本切分策略。
4. 选择合适的批量大小
商业API一般都有批量大小限制,比如DeepSeek的最大批量大小是2048。超过这个限制会导致调用失败。
九、常见坑与解决方案
1. ❌ 向量维度不匹配
问题:嵌入模型生成的向量维度和向量数据库要求的维度不一致
解决方案:确保嵌入模型和向量数据库使用相同的维度。BGE-M4是1024维,DeepSeek是1536维。
2. ❌ 中文效果差
问题:使用OpenAI等国外模型,中文检索准确率低
解决方案:中文场景优先使用BGE-M4、M3E-v2等国产模型,它们的中文性能远超国外模型。
3. ❌ 批量调用报错
问题:批量调用时提示"请求过大"
解决方案:减小批量大小,一般每次处理10-100个文本比较合适。
4. ❌ 本地模型速度慢
问题:使用CPU运行BGE-M4,生成速度很慢
解决方案:如果有NVIDIA显卡,Ollama会自动启用GPU加速,推理速度大幅提升。
十、本章总结与下章预告
本章总结
- 嵌入模型的本质是将文本转换为数字向量,语义相似的文本向量距离更近
- 余弦相似度是文本相似度计算的首选方法
- Spring AI提供了统一的
EmbeddingClient接口,支持商业API和本地模型 - 中文RAG场景首选BGE-M4开源本地模型,商业API首选DeepSeek-embedding-v2
- 不要用聊天模型做嵌入,不要混用不同模型的向量
- 批量生成和缓存可以大幅提升嵌入模型的性能
预告式提及:我们现在已经学会了如何生成向量,但如何高效地存储和检索这些向量呢?下一章我们将学习向量数据库,它是RAG系统的核心存储组件。
下章预告
下一章我们将深入学习向量数据库的选择与Spring AI集成。你将学会:
- 什么是向量数据库?为什么不用MySQL存向量?
- 主流向量数据库对比:Milvus vs Qdrant vs Chroma
- Spring AI向量数据库统一API详解
- 本地向量数据库Chroma的快速上手
- 向量的增删改查与批量导入导出
十一、课后练习
- 分别使用DeepSeek商业API和本地Ollama BGE-M4生成"Java是最好的编程语言"的向量,观察它们的维度差异
- 计算三对文本的相似度:“Java开发"和"Java编程”、“Java开发"和"Python开发”、“Java开发"和"打篮球”
- 实现一个批量生成嵌入向量的接口,支持一次处理最多50个文本
- 尝试修改Ollama配置,使用M3E-v2模型,对比它和BGE-M4的生成速度和相似度计算结果
- 思考一下:为什么嵌入模型能够捕捉到文本的语义信息?