Embedding 模型
本教程共 45 篇 · 第 19 篇 · 更新于 2026-08-16 · 约 9 分钟阅读
本节目标:理解 Embedding 的核心概念,掌握
EmbeddingModel接口的用法。学会接入主流嵌入模型,能计算文本相似度,并知道怎么按维度、语言和成本选型。
19.1 什么是 Embedding
Embedding 是把文本、图片、视频变成一串浮点数的过程。这串浮点数叫向量,向量的长度叫维度。
向量有个神奇的性质:语义相近的文本,向量在空间里也离得近。“苹果好吃”和”苹果很甜”的向量距离小,“苹果好吃”和”汽车没油了”的向量距离大。
有了这个性质,就能用数学算相似度,而不是靠关键词匹配。搜索”怎么修自行车”,能找出”轮胎漏气怎么办”这样的结果,尽管两个句子没有一个词相同。这就是语义搜索,也是 RAG 的地基。
嵌入模型分两种。一种是密集向量,每个维度都承载语义信息,主流厂商都用这种。一种是稀疏向量,大多数字段是 0,类似传统词袋,现在用得少。
19.2 EmbeddingModel 接口
Spring AI 把嵌入能力抽象成 EmbeddingModel 接口。核心方法只有几个:
public interface EmbeddingModel extends Model<EmbeddingRequest, EmbeddingResponse> {
EmbeddingResponse call(EmbeddingRequest request);
float[] embed(Document document);
float[] embed(String text);
List<float[]> embed(List<String> texts);
EmbeddingResponse embedForResponse(List<String> texts);
int dimensions();
}
embed(String) 最常用,传一句话返回一个向量。embed(List) 支持批量,一次请求处理多条文本,省时省钱。dimensions() 返回向量维度,选数据库字段长度时要用。
embed(Document) 接收文档对象。默认只取正文,支持 MetadataMode 的实现(如 OpenAI、Mistral)可以把文档元数据也拼进去。RAG 场景里,文档的标题、来源这类元数据参与嵌入,检索时能带上更多上下文。
批量嵌入要留意厂商的限额。一次传几百条没问题,传几万条会被限流。稳妥的做法是分片提交,每片几百条,配合重试机制。
底层的 call 方法接收 EmbeddingRequest,返回 EmbeddingResponse。每个 Embedding 对象包含一个结果向量和元数据。想拿完整响应(比如带 token 用量),用 embedForResponse。
19.3 第一个嵌入示例
以 OpenAI 为例。依赖还是 OpenAI starter,配置里指定嵌入模型:
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-openai</artifactId>
</dependency>
spring.ai.openai.api-key=${OPENAI_API_KEY}
spring.ai.openai.embedding.model=text-embedding-3-small
注入 EmbeddingModel 直接调用:
@RestController
public class EmbeddingController {
private final EmbeddingModel embeddingModel;
public EmbeddingController(EmbeddingModel embeddingModel) {
this.embeddingModel = embeddingModel;
}
@GetMapping("/ai/embedding")
public Map<String, Object> embed(@RequestParam(defaultValue = "Spring AI 很棒") String message) {
EmbeddingResponse response = embeddingModel.embedForResponse(List.of(message));
return Map.of("embedding", response);
}
}
embedForResponse 返回的 EmbeddingResponse 里,getResult().getOutput() 就是 float 数组。
19.4 计算相似度
拿到向量后,最常用的相似度算法是余弦相似度。两个向量夹角的余弦值越接近 1,语义越相近。
public double cosineSimilarity(float[] a, float[] b) {
double dot = 0, normA = 0, normB = 0;
for (int i = 0; i < a.length; i++) {
dot += a[i] * b[i];
normA += a[i] * a[i];
normB += b[i] * b[i];
}
return dot / (Math.sqrt(normA) * Math.sqrt(normB));
}
// 用法
float[] v1 = embeddingModel.embed("Java 开发");
float[] v2 = embeddingModel.embed("Java 程序员");
float[] v3 = embeddingModel.embed("红烧肉做法");
System.out.println(cosineSimilarity(v1, v2)); // 接近 1
System.out.println(cosineSimilarity(v1, v3)); // 接近 0
真实项目里不用自己写循环。向量数据库(PGvector、Milvus 等)内置相似度计算,把向量存进去,查询时数据库直接返回最相近的结果。这部分在后面 RAG 章节展开。
19.5 各厂商接入一览
| 厂商 | starter | 配置前缀 | 默认模型 | 维度 |
|---|---|---|---|---|
| OpenAI | spring-ai-starter-model-openai | spring.ai.openai.embedding | text-embedding-ada-002 | 1536 |
| Google GenAI | spring-ai-starter-model-google-genai-embedding | spring.ai.google.genai.embedding | text-embedding-004 | 768 |
| Mistral | spring-ai-starter-model-mistral-ai | spring.ai.mistralai.embedding | mistral-embed | 1024 |
| Ollama | spring-ai-starter-model-ollama | spring.ai.ollama.embedding | 本地模型 | 按模型 |
| ONNX Transformers | spring-ai-starter-model-transformers | spring.ai.embedding.transformer | all-MiniLM-L6-v2 | 384 |
| PostgresML | spring-ai-starter-model-postgresml-embedding | spring.ai.postgresml.embedding | distilbert-base-uncased | 768 |
| Bedrock | spring-ai-starter-model-bedrock | spring.ai.bedrock.* | Titan / Cohere | 按模型 |
| Azure OpenAI | 走 OpenAI | spring.ai.openai.embedding | 同 OpenAI | 按模型 |
OpenAI
text-embedding-3 系列支持降维。默认 1536 维的模型,可以只取前 256 维,质量损失很小,存储省一大截:
spring.ai.openai.embedding.model=text-embedding-3-small
spring.ai.openai.embedding.dimensions=256
Google GenAI
Google 的文本嵌入模块是独立 starter,认证方式和聊天模块共用:
spring.ai.google.genai.embedding.api-key=YOUR_API_KEY
spring.ai.google.genai.embedding.text.model=text-embedding-004
它有个特色参数 task-type,告诉模型这份向量拿去干什么,检索、分类、聚类效果更准。RETRIEVAL_QUERY 用于搜索问题,RETRIEVAL_DOCUMENT 用于文档入库。模型版本 004 起支持降维:
spring.ai.google.genai.embedding.text.task-type=RETRIEVAL_DOCUMENT
spring.ai.google.genai.embedding.text.dimensions=256
Mistral
Mistral 有两个嵌入模型。mistral-embed 是 1024 维的通用模型。codestral-embed 是 1536 维的代码专用模型,做代码检索、代码 RAG 时效果更好。中文场景下它的表现一般,英文和代码场景优先考虑。
本地方案
不想把数据发给第三方,用本地嵌入。Ollama 最简单,装好客户端后拉模型:
ollama pull nomic-embed-text
spring.ai.ollama.base-url=http://localhost:11434
spring.ai.ollama.embedding.model=nomic-embed-text
缺模型时,ollama run 会自动拉取;Spring AI 侧默认不会自动下载,需要显式配置 spring.ai.ollama.init.pull-model-strategy 才会在启动时拉取(默认 never,见第 14 章)。想手动控制,就提前执行 ollama pull。
ONNX Transformers 更进一步,模型直接打进应用进程,零网络依赖。先要把 HuggingFace 的句子模型转成 ONNX 格式,再配两个 URI:
spring.ai.embedding.transformer.onnx.model-uri=https://huggingface.co/intfloat/e5-small-v2/resolve/main/model.onnx
spring.ai.embedding.transformer.tokenizer.uri=https://huggingface.co/intfloat/e5-small-v2/raw/main/tokenizer.json
转换命令长这样:
optimum-cli export onnx --model sentence-transformers/all-MiniLM-L6-v2 onnx-output-folder
生成的 model.onnx 和 tokenizer.json 就是配置里要填的两个文件。模型文件会被本地缓存,第二次启动不用重新下载。
PostgresML
PostgresML 把嵌入能力装进 PostgreSQL。它用 HuggingFace 的 transformer 在数据库里算向量,默认模型是 distilbert-base-uncased,输出 768 维:
spring.ai.postgresml.embedding.transformer=distilbert-base-uncased
spring.ai.postgresml.embedding.vector-type=PG_VECTOR
vector-type 有两个选项:PG_ARRAY 用普通数组存向量,PG_VECTOR 用 pgvector 扩展,后者支持索引和相似度查询,RAG 场景必选。
Bedrock
Bedrock 的嵌入走 InvokeModel API,而不是 Converse API(Converse 不支持嵌入)。用统一的 spring-ai-starter-model-bedrock,按需启用具体模型:
spring.ai.bedrock.aws.region=us-east-1
spring.ai.bedrock.aws.access-key=${AWS_ACCESS_KEY_ID}
spring.ai.bedrock.aws.secret-key=${AWS_SECRET_ACCESS_KEY}
spring.ai.model.embedding=bedrock-titan
spring.ai.bedrock.titan.embedding.model=amazon.titan-embed-text-v2:0
Cohere 模型同理,把 spring.ai.model.embedding 改成 bedrock-cohere 即可。
Azure OpenAI
2.0 起 Azure 上的嵌入模型直接用 OpenAI 客户端访问,配置方式和第 16 章一样,把 base-url 指向 Azure 端点:
spring.ai.openai.base-url=https://your-resource.openai.azure.com/openai/v1
spring.ai.openai.api-key=${AZURE_OPENAI_API_KEY}
spring.ai.openai.embedding.model=text-embedding-3-small
19.6 多模态嵌入
文本嵌入之外,还有多模态嵌入。multimodalembedding@001 是 Vertex AI 的多模态嵌入模型,由 google-genai 模块接入,能把图片、文本、视频映射到同一个 1408 维向量空间。图片的向量和文字的向量可以直接比,实现”用文字搜图片”。
spring.ai.google.genai.embedding.project-id=YOUR_PROJECT_ID
spring.ai.google.genai.embedding.location=us-central1
spring.ai.google.genai.embedding.multimodal.model=multimodalembedding@001
两点提醒。这个模块目前标记为 EXPERIMENTAL,还不能直接配合 VectorStore 使用。纯文本场景别用它,官方建议用文本嵌入模型,效果更好也更便宜。
19.7 维度与选型
选嵌入模型看四个因素。
语言。中文场景优先 Google text-embedding-004、OpenAI text-embedding-3 这类多语言模型;英文和代码场景 Mistral 也值得试。
维度与成本。维度越高,单条文本的存储越大,向量检索越慢。text-embedding-3 系列和 Google 004 都支持降维,先全量跑一遍,再砍到 256 或 512 维看效果。
数据是否出境。敏感数据用 Ollama 或 ONNX 本地嵌入。
和向量库的匹配。数据库字段长度必须 ≥ 模型维度。先定模型,再建表,顺序别反。
Tip所有
EmbeddingModel实现都遵守同一接口。选型阶段多试两家,业务代码一行不用改。
19.8 小结
Embedding 把文本变成向量,让相似度可以计算。EmbeddingModel 接口统一了各家实现,embed 和 embedForResponse 覆盖日常需求。选型时先看语言,再定维度,最后考虑数据是否出境。下一节看图像、音频和审核模型,三类接口同样统一。