【SpringAIAlibaba新手村系列】(11)Embedding 向量化与向量数据库

简介: 本文围绕 Embedding 与向量数据库展开,讲解了文本向量化、相似度检索和 VectorStore 的基本用法,并结合 SimpleVectorStore 示例说明了 Spring 中自动装配与手动注册 Bean 的区别,为后续学习 RAG 打下基础。

第十一章 Embedding 向量化与向量数据库

版本标注

  • Spring AI: 1.1.2
  • Spring AI Alibaba: 1.1.2.0

章节定位

  • EmbeddingModelVectorStore 是 RAG 的核心基础设施。
  • 学习本章时,重点应放在统一抽象:EmbeddingModel 负责向量化,VectorStore 负责检索;至于底层是 Redis 还是其他向量库,属于实现选型问题。

s01 > s02 > s03 > s04 > s05 > s06 > s07 > s08 > s09 > s10 > [ s11 ] s12 > s13 > s14 > s15 > s16 > s17 > s18

"Embedding 不负责回答问题, 它负责让机器看懂相似度" -- 向量化是检索、推荐和 RAG 的基础设施。


一、为什么需要 Embedding?

1.1 计算机 vs 人类

作为人类,我们天然理解文字的含义:

"猫" ──→ 毛茸茸的、会喵喵叫、养来抓老鼠...
"狗" ──→ 忠诚的、会看家、是人类的好朋友...

但对计算机来说,"猫"和"狗"只是两个字符串,它不知道它们有什么关系。

1.2 Embedding 的思想

Embedding(向量化) 的核心思想是:把文字转换成一串数字(向量)

           Embedding 思想
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

"猫" ──→ [0.12, -0.34, 0.56, 0.78, ...]  (1536维向量)
"狗" ──→ [0.15, -0.31, 0.53, 0.80, ...]  (1536维向量)
"汽车" ──→[0.89, 0.12, -0.34, 0.21, ...]  (1536维向量)

              ↓ 向量距离很近(相似)
              ↓ 向量距离很远(不相似)

如果两个向量的距离很近,说明对应的文本在语义上也相似。

1.3 Embedding 的应用场景

应用场景 说明
语义搜索 搜"手机"也能找到"智能手机"相关内容
相似推荐 找到和当前内容最相似的其他内容
分类聚类 把相似的内容自动归类
去重判断 判断两段文字是否重复

二、向量数据库

2.1 传统数据库的局限

传统数据库存储的是精确值:

SELECT * FROM products WHERE name = '手机';  -- 精确匹配

但如果你搜"手机",就搜不到"智能手机"。

2.2 向量数据库的优势

向量数据库可以存储向量,并支持相似度检索:

// 向量数据库查询
SearchRequest request = SearchRequest.builder()
    .query("手机")                    // 搜索关键词
    .topK(5)                          // 返回最相似的5个结果
    .build();

vectorStore.similaritySearch(request);  // 返回语义相似的结果

即使搜索词不完全一样,也能找到语义上相关的内容!

2.3 Redis Stack

本章示例使用的向量数据库是 Redis Stack

  • 本身就是内存数据库,性能很快
  • 支持向量存储和检索
  • 安装使用简单

三、项目代码详解

3.1 VectorStore 配置方式

这一章有一个很容易忽略、但实际运行时一定会踩到的点:VectorStore 只是一个接口,Spring 容器不会无条件帮你变出一个可注入的实现对象。

也就是说,如果你在控制器里直接写:

@Resource
private VectorStore vectorStore;

但项目里既没有自动装配出某个向量库实现,也没有自己手动声明 Bean,启动时就会报:

A component required a bean of type 'org.springframework.ai.vectorstore.VectorStore' that could not be found.

在学习阶段,最简单的做法之一,是先手动提供一个 SimpleVectorStore Bean:

package com.atguigu.study.config;

import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.vectorstore.SimpleVectorStore;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class VectorStoreConfig {
   

    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel) {
   
        return SimpleVectorStore.builder(embeddingModel).build();
    }
}

这段配置类的意思很简单:

  • 容器里已经有 EmbeddingModel
  • 现在我们基于它手动创建一个 SimpleVectorStore
  • 再把这个对象注册成 VectorStore Bean

这样后面的 Controller 才能顺利注入:

@Resource
private VectorStore vectorStore;

不过,这里还有一个很重要的补充:

如果你引入的是某个具体向量库的 Starter,并且配置也写全了,那么 VectorStore 也可能由自动配置直接提供,不需要你再手动写 @Bean

比如 Redis 向量存储场景中,如果你已经引入:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

并且在 application.yml 中配置好了:

spring:
  ai:
    vectorstore:
      redis:
        initialize-schema: true
        index-name: custom-index
        prefix: custom-prefix

  data:
    redis:
      host: localhost
      port: 6379

那么在很多情况下,Spring Boot 就会自动帮你创建 Redis 对应的 VectorStore Bean,此时就不需要再写一个手动的 VectorStoreConfig

所以这一节真正要记住的是:

  • 如果你用的是 SimpleVectorStore 这种本地实现,通常自己写 @Bean 最直接
  • 如果你用的是 Redis 这类带 Starter 的向量库实现,往往可以依赖自动装配

3.2 向量化控制器

package com.atguigu.study.controller;

import com.alibaba.cloud.ai.dashscope.embedding.DashScopeEmbeddingOptions;
import jakarta.annotation.Resource;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingModel;
import org.springframework.ai.embedding.EmbeddingRequest;
import org.springframework.ai.embedding.EmbeddingResponse;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.util.Arrays;
import java.util.List;

/**
 * 向量化控制器
 * 展示文本Embedding和向量检索的使用
 */
@RestController
@Slf4j
public class Embed2VectorController
{
   
    // 注入 Embedding 模型(用于文本→向量)
    @Resource
    private EmbeddingModel embeddingModel;

    // 注入向量存储器(用于存储和检索向量)
    @Resource
    private VectorStore vectorStore;

    /**
     * 文本转向量(Text to Embedding)
     * 
     * 接口:http://localhost:8011/text2embed?msg=射雕英雄传
     * 
     * @param msg 要转换的文本
     * @return EmbeddingResponse 包含向量结果
     */
    @GetMapping("/text2embed")
    public EmbeddingResponse text2Embed(String msg)
    {
   
        // 方法1:使用默认配置进行向量化
        // EmbeddingResponse response = embeddingModel.call(
        //     new EmbeddingRequest(List.of(msg), null));

        // 方法2:显式指定使用 text-embedding-v3 模型(推荐)
        // DashScopeEmbeddingOptions 包含阿里云特有的配置
        EmbeddingResponse embeddingResponse = embeddingModel.call(
            new EmbeddingRequest(
                List.of(msg),  // 要向量化的文本列表(可以一次批量处理多个)
                DashScopeEmbeddingOptions.builder()
                    .withModel("text-embedding-v3")  // 指定使用 embedding v3 模型
                    .build()
            )
        );

        // 打印向量结果(可以看到生成了1536维的向量数组)
        System.out.println(Arrays.toString(embeddingResponse.getResult().getOutput()));

        // 返回完整的 EmbeddingResponse(包含向量数据)
        return embeddingResponse;
    }

    /**
     * 向量入库:将文本转为向量并存储到向量数据库
     * 
     * 接口:http://localhost:8011/embed2vector/add
     * 
     * 这个方法将文本转换为向量,并存入 VectorStore
     */
    @GetMapping("/embed2vector/add")
    public void add()
    {
   
        // 1. 准备要存储的文档
        //    Document 是 Spring AI 定义的文档对象
        //    包含 content(内容)、id、metadata(额外信息)
        List<Document> documents = List.of(
            new Document("i study LLM"),           // 英语:我学习大语言模型
            new Document("i love java")            // 英语:我喜欢Java
        );

        // 2. 调用 VectorStore.add() 方法
        //    内部会自动:
        //    - 用 EmbeddingModel 把文本转为向量
        //    - 将向量和原始文本一起存入底层向量库
        vectorStore.add(documents);

        // 3. 简单测试:查看底层向量库中是否已经写入文档
    }

    /**
     * 向量检索:从向量数据库中查找相似内容
     * 
     * 接口:http://localhost:8011/embed2vector/get?msg=LLM
     * 
     * @param msg 搜索关键词
     * @return 和 msg 语义最相似的文档列表
     */
    @GetMapping("/embed2vector/get")
    public List<Document> getAll(@RequestParam(name = "msg") String msg)
    {
   
        // 1. 构建搜索请求
        //    SearchRequest 是 Spring AI 的搜索请求构建器
        SearchRequest searchRequest = SearchRequest.builder()
            .query(msg)              // 搜索词(会自动转为向量)
            .topK(2)                 // 返回最相似的2个结果
            .build();

        // 2. 执行相似度搜索
        //    传入搜索请求,返回匹配度最高的文档列表
        List<Document> list = vectorStore.similaritySearch(searchRequest);

        // 3. 打印结果
        System.out.println(list);

        // 4. 返回结果
        return list;
    }
}

四、核心原理详解

4.1 为什么有时要写配置类,有时又不用?

这个问题表面上看是在问“为什么有时要写 @Configuration”,其实本质上是在问:

为什么 EmbeddingModel 往往可以直接注入,而 VectorStore 却经常需要我自己注册成 Bean?

  • EmbeddingModel 通常是 框架已经帮你自动装配好的 Bean
  • VectorStore 通常是 你还需要自己决定具体实现的 Bean

所以两者最大的区别不在于“是不是接口”,而在于:

这个对象到底是谁放进 Spring 容器里的。

1. 为什么 EmbeddingModel 经常可以直接注入?

当你引入了 Spring AI Alibaba 的 DashScope Starter,并且配置好了 API Key 之后,框架通常会自动帮你创建好一个 EmbeddingModel Bean。

所以在控制器里这样写:

@Resource
private EmbeddingModel embeddingModel;

往往就能直接成功。

这是因为:

  • Starter 已经知道你在使用 DashScope
  • 它也知道该创建哪种 Embedding 实现
  • 所以自动配置类会替你把这个对象注册进 Spring 容器

也就是说,这里的 EmbeddingModel 并不是“凭空出现的”,而是 框架提前帮你准备好的

2. 为什么 VectorStore 有时不能直接注入?

因为 VectorStore 虽然也是一个接口,但它背后的实现选择并不唯一。

比如你完全可以选择:

  • SimpleVectorStore
  • Redis VectorStore
  • Milvus
  • PGVector
  • Elasticsearch
  • 你自己写的实现

Spring 不知道你到底想用哪一个,所以在没有更具体条件时,它通常不会替你“猜一个”然后自动注册。

这时如果你直接写:

@Resource
private VectorStore vectorStore;

但项目里又没有任何一个 VectorStore Bean,Spring 启动时就会报:

A component required a bean of type 'org.springframework.ai.vectorstore.VectorStore' that could not be found.

3. 这时为什么要写配置类?

当你用的是 SimpleVectorStore 这种不依赖外部中间件、也没有专门 Starter 自动装配的实现时,你就需要明确告诉 Spring:

“这个项目里我决定使用 SimpleVectorStore 作为 VectorStore 的实现。”

最常见的写法就是:

@Configuration
public class VectorStoreConfig {
   

    @Bean
    public VectorStore vectorStore(EmbeddingModel embeddingModel) {
   
        return SimpleVectorStore.builder(embeddingModel).build();
    }
}

这段代码的意思是:

  • EmbeddingModel 已经由框架自动提供
  • 现在我们基于它手动创建一个 SimpleVectorStore
  • 再把这个对象注册成 VectorStore Bean

这样后面谁再写:

@Resource
private VectorStore vectorStore;

Spring 就能顺利注入了。

但这里要补一句非常关键的话:

并不是所有 VectorStore 都必须手动写配置类。

如果你已经引入了某个向量库的 Starter,例如 Redis 向量存储:

<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

并且相关配置也写全了,那么 VectorStore 可能就会由自动配置直接提供。这时你只需要像下面这样注入即可:

@Resource
private VectorStore vectorStore;

不需要你再额外写:

@Bean
public VectorStore vectorStore(...) {
    ... }

4. 那什么时候可以不写配置类?

如果某个对象只是你在当前方法里临时用一下,其实完全可以自己创建:

VectorStore vectorStore = SimpleVectorStore.builder(embeddingModel).build();

这种写法没有错,只是它有一个前提:

  • 这个对象只在当前方法里用
  • 你不打算让它被别的 Controller / Service 复用
  • 你也不需要 Spring 去管理它

一旦这个对象要在整个项目里复用,或者要被注入到别的类里,通常还是应该把它写成 @Bean

5. 最终怎么判断该不该写配置类?

可以用一个非常实用的判断标准:

  • 框架已经自动装配好的对象:通常直接注入就行,例如 EmbeddingModel
  • 没有 Starter 自动装配、需要你自己决定实现的对象:通常要自己注册 Bean,例如 SimpleVectorStore
  • 已经有专门 Starter 且配置完整的对象:很多时候也可以直接注入,例如 Redis VectorStore

所以一句话总结就是:

不是“有的类能直接注入,有的类必须写配置类”,而是“谁已经被框架自动放进 Spring 容器里了,谁还需要你自己决定实现并注册进去”。

4.2 Embedding 的维度

// 阿里云的 text-embedding-v3 模型生成的向量维度是 1536
// 每个文本都会被转换成 1536 个数字的数组
// 
// 就好像把文字映射到一个1536维的空间中的一点
// 语义相似的文字,在这个空间中的距离也会比较近

4.3 相似度计算

向量数据库通常使用以下几种相似度算法:

算法 说明 适用范围
余弦相似度 (Cosine) 夹角越小越相似 方向敏感场景
欧氏距离 (L2) 直线距离 数值敏感场景
内积 (IP) 点积运算 排序优化

很多向量库默认采用余弦相似度或相近策略,具体行为取决于底层实现和索引配置。


五、本章小结

5.1 核心概念

概念 说明
Embedding 把文本转为向量的技术
VectorStore 向量数据库存储接口
维度/Vector Dimension 向量的长度(如1536)
相似度检索 根据向量距离找相似内容
text-embedding-v3 阿里云的向量化模型

5.2 使用流程

文本 → EmbeddingModel → 向量 → VectorStore.add() → 存入数据库
                               ↑
搜索词 → 自动转向量 → similaritySearch() → 返回相似结果

5.3 拓展:RAG 的基础

Embedding + VectorStore = RAG(检索增强生成) 的核心技术!

后面章节我们会学习完整的 RAG 流程。

本章重点

  1. 理解 Embedding 的核心思想(文字→数字)
  2. 掌握文本向量化的方法
  3. 学会使用向量数据库进行相似度检索

下章剧透(s12):

掌握了向量化技术,下一章我们将学习 RAG(检索增强生成)——让 AI 能够"查阅"知识库来回答专业问题!


💡 TIP:不要把 VectorStore 只理解成 Redis

本章示例使用 Redis 只是因为它简单、容易上手。但在实际项目里,VectorStore 更重要的价值在于统一抽象:

抽象 作用
EmbeddingModel 把文本转成向量
VectorStore 存储向量并做相似度检索
SearchRequest 描述一次检索请求

这样你后续切换底层实现时,业务代码改动会小很多。


📝 编辑者:Flittly
📅 更新时间:2026年4月

目录
相关文章
|
12天前
|
人工智能 运维 Java
【SpringAIAlibaba新手村系列】(12)RAG 检索增强生成技术
本文深入探讨 RAG 技术在 AiOps 场景中的应用,以基于 ops.txt 构建运维知识库为例,讲解了如何通过将文本切分、向量化并存入向量数据库,实现 AI 故障查询。内容涵盖 EmbeddingModel、VectorStore 的基本概念,以及利用 Redis 的 SETNX 机制防止知识库重复导入的工程实践。核心在于让 AI 结合外部知识库,更准确地回答运维问题。
170 0
|
20天前
|
人工智能 前端开发 Java
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
本文围绕 Spring AI 中的流式输出与响应式编程展开,重点解释了传统一次性响应与流式返回的差异,以及 Flux 在异步数据流中的核心作用。文章结合 ChatModel.stream() 与 ChatClient 的多种代码示例,说明如何实现 AI 内容的边生成边返回,并帮助读者理解流式调用在用户体验、性能和长文本场景中的实际价值。
413 4
【SpringAIAlibaba新手村系列】(4)流式输出与响应式编程
|
18天前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(5)Prompt 提示词基础与多种消息类型
本章详解Spring AI 1.1.2中Prompt核心机制:以System/User/Assistant/Tool四类消息构建结构化提示,强调“角色决定语义”;涵盖多模型配置、链式API与底层Message组装两种实践方式,并给出系统消息设计最佳实践。
292 7
|
人工智能 JavaScript Java
【SpringAIAlibaba新手村系列】(1)初识 Spring AI Alibaba 框架
本文介绍了SpringAIAlibaba框架的基本概念和使用方法。作为Spring官方AI框架的阿里云实现版本,它简化了Java开发者调用AI模型的过程。文章详细讲解了核心概念如ChatModel、ChatClient,以及阿里云百炼平台的功能。通过HelloWorld项目示例,展示了如何配置APIKey、编写控制层代码,实现普通调用和流式输出两种AI交互方式。重点阐述了SpringAI与SpringAIAlibaba的关系,以及自动配置机制的工作原理,帮助开发者快速上手这一框架。
1467 4
|
17天前
|
人工智能 JavaScript Java
【SpringAIAlibaba新手村系列】(10)Text to Voice 文本转语音技术
本文围绕 Spring AI Alibaba 1.1.2.2 的文本转语音实现展开,记录了基于 DashScopeAudioSpeechModel 与 stream() 的可运行方案。文章重点说明了模型、音色、输出格式与流式拼接音频文件的关键细节。
186 6
|
17天前
|
人工智能 自然语言处理 前端开发
【SpringAIAlibaba新手村系列】(9)Text to Image 文本生成图像技术
本文介绍 Spring AI 中的文生图能力,围绕 ImageModel、ImagePrompt 与阿里云百炼图像模型展开,演示如何根据文字描述生成图片链接,并结合 Prompt 编写技巧与参数配置,帮助开发者提升生成效果与落地能力。
208 8
|
存储 人工智能 Java
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
本章深度解析 Spring AI 中 `ChatModel`(底层接口)与 `ChatClient`(高级封装)的本质区别:前者如“手动挡”,精准控制但需写大量样板代码;后者似“智能点餐机”,链式调用、支持系统提示、模板、工具调用等,开发高效。初学者推荐优先使用 `ChatClient`。
295 0
【SpringAIAlibaba新手村系列】(3)ChatModel 与 ChatClient 的深度对比
|
18天前
|
人工智能 JSON Java
【SpringAIAlibaba新手村系列】(7)结构化输出与对象映射
本文详解 Spring AI 结构化输出功能,通过 Java Record 与 .entity() 方法,实现 AI 的 JSON 响应自动映射为 Java 对象,解决纯文本难以集成的问题。文中还对比了 Lambda 写法并提供 Prompt 设计最佳实践。
160 3
|
12天前
|
人工智能 Java API
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
本文详细解析 Spring AI 的 Tool Calling 技术,阐明其如何弥补大模型“会说不会做”的局限。通过 @Tool 注解,开发者可轻松将 Java 方法暴露为 AI 工具。文中深入讲解了 ToolCallbacks.from() 注册工具的原理,以及工具方法在当前 Spring Boot 进程内通过反射动态执行的底层逻辑,强调了模型决策与框架执行的协同过程,为理解 AI 赋能实际操作奠定基础。
352 0
|
20天前
|
存储 人工智能 Java
吃透 Spring AI Alibaba 多智能体|四大协同模式+完整代码
本文详细讲解 Spring AI Alibaba Multi-Agent 多智能体架构,包含顺序执行、并行执行、LLM 路由、监督者四大协同模式,搭配可运行代码示例与真实业务场景,从零带你上手多智能体开发。
704 3

热门文章

最新文章

下一篇
开通oss服务