首页 / Spring AI 入门教程 / Embedding 模型

Spring AI 入门教程

Embedding 模型

本教程共 45 篇 · 第 19 篇 · 更新于 2026-08-16 · 约 9 分钟阅读

Spring AIEmbedding向量语义搜索EmbeddingModelRAG余弦相似度

本节目标:理解 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配置前缀默认模型维度
OpenAIspring-ai-starter-model-openaispring.ai.openai.embeddingtext-embedding-ada-0021536
Google GenAIspring-ai-starter-model-google-genai-embeddingspring.ai.google.genai.embeddingtext-embedding-004768
Mistralspring-ai-starter-model-mistral-aispring.ai.mistralai.embeddingmistral-embed1024
Ollamaspring-ai-starter-model-ollamaspring.ai.ollama.embedding本地模型按模型
ONNX Transformersspring-ai-starter-model-transformersspring.ai.embedding.transformerall-MiniLM-L6-v2384
PostgresMLspring-ai-starter-model-postgresml-embeddingspring.ai.postgresml.embeddingdistilbert-base-uncased768
Bedrockspring-ai-starter-model-bedrockspring.ai.bedrock.*Titan / Cohere按模型
Azure OpenAI走 OpenAIspring.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.onnxtokenizer.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 接口统一了各家实现,embedembedForResponse 覆盖日常需求。选型时先看语言,再定维度,最后考虑数据是否出境。下一节看图像、音频和审核模型,三类接口同样统一。