Java后端拥抱AI开发之个人学习路线 - - Spring AI【第三期】(向量数据库 + RAG检索增强生成)

向量化和向量数据库

一、文本向量化

嵌入模型(Embedding Model)

  • 嵌入(Embedding)的工作原理是将文本、图像和视频转换为称为向量(Vectors)的浮点数数组。
  • 这些向量旨在捕捉文本、图像和视频的含义。嵌入数组的长度称为向量的维度(Dimensionality)。
  • 如何确定是相似的?
    • 每个向量都有一个长度和方向。例如,在这个图中,p和a指向相同的方向,但长度不同。p和b正好指向相反的方向,但有相同的长度。然后还有c,长度比p短一点,方向不完全相同,但很接近。因此要么只考虑一个维度,要么同时考虑多个维度

二、向量数据库

  • 向量存储(VectorStore)是一种用于存储和检索高维向量数据的数据库或存储解决方案 ,它特别适用于处理那些经过嵌入模型转化后的数据。在VectorStore中,查询与传统关系数据库不同。**它们执行相似性搜索,而不是精确匹配。**当给定一个向量作为查询时,VectorStore返回与查询向量"相似"的向量。
    • 其核心功能是通过高效的索引结构和相似性计算算法,支持大规模向量数据的快速查询与分析,向量数据库维度越高,查询精准度也越高,查询效果也越好。
  • VectorStore用于将您的数据与AI模型集成。 在使用它们时的第一步是将您的数据加载到矢量数据库中。然后,当要将用户查询发送到AI模型时,首先检索一组相似文档。然后,这些文档作为用户问题的上下文,并与用户的查询一起发送到AI模型。这种技术被称为检索增强生成 (Retrieval Augmented Generation, RAG)
  • 常见的向量数据库有:PGVectorQdrantMilvusPostgreSQL等。

三、编码案例(用RedisStack来实现)

复制代码
// 1. 引入pom依赖
//<!--spring-ai-alibaba dashscope-->
<dependency>
    <groupId>com.alibaba.cloud.ai</groupId>
    <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
</dependency>
//<!-- 添加 Redis 向量数据库依赖 -->
<dependency>
    <groupId>org.springframeworkframework.ai</groupId>
    <artifactId>spring-ai-starter-vector-store-redis</artifactId>
</dependency>

// 2. 配置yml文件
//===SpringAI Alibaba Config===
spring.ai.dashscope.api-key=${aliqwen-api}
spring.ai.dashscope.chat.options.model=qwen-plus
spring.ai.dashscope.embedding.options.model=text-embedding-v3

// =====Redis Stack=====
spring.data.redis.host=localhost
spring.data.redis.port=6379
spring.data.redis.username=
spring.data.redis.password=
spring.ai.vectorstore.redis.initialize-schema=true
spring.ai.vectorstore.redis.index-name=custom-index
spring.ai.vectorstore.redis.prefix=custom-prefix
  1. Redis Stack是什么?
    • Redis Stack 是Redis Labs 推出的一个"增强版 Redis",不是Redis 的替代品,而是在原生 Redis 基础上的功能扩展包,专为构建现代实时应用而设计。
  1. Redis Stack相比原生Redis的优势?

|------|---------------|-------------------------------|
| 功能维度 | 原生 Redis | Redis Stack 增强功能 |
| 数据结构 | 字符串、列表、集合、哈希等 | 增加 JSON、图、时间序列、概率结构等高级类型 |
| 查询能力 | 仅限键值查询 | 支持全文搜索、向量搜索、图查询、JSON 查询 |
| 使用场景 | 缓存、消息队列、计数器等 | 实时推荐、时序分析、知识图谱、文档数据库、AI 向量检索 |
| 开发体验 | 命令行操作,需手动拼装逻辑 | 提供 RedisInsight 和对象映射库,开发效率更高 |

  1. Redis Stack核心组件
    • RediSearch:提供全文搜索能力,支持复杂的文本搜索、聚合和过滤,以及向量数据的存储和检索
    • RedisJSON:原生支持JSON数据的存储、索引和查询,可高效存储和操作嵌套的JSON文档
    • RedisGraph:支持图数据模型,使用Cypher查询语言进行图遍历查询
    • RedisBloom:支持 Bloom、Cuckoo、Count-Min Sketch 等概率数据结构
  1. 总结一句话:RedisStack=原生Redis+搜索+图+时间序列+JSON+概率结构+可视化工具+开发框架支持

    // 3. 如何使用Redis Stack
    // 可以使用docker 安装
    docker run -d --name redis-stack-server -p 6379:6379 redis/redis-stack-server

    // 4. 案例代码
    /**

    • 文本向量化 + Redis Stack 向量数据库 控制器

    • 实现文本向量化、向量存储、向量检索三大核心AI能力
      */
      @RestController
      @Slf4j
      public class Embed2VectorController {

      /**

      • 注入文本向量化模型(Spring AI 自动配置的通义千问 Embedding 实例)
      • 负责将文本转换为向量(Embedding),用于语义表征
        */
        @Resource
        private EmbeddingModel embeddingModel;

      /**

      • 注入 Redis Stack 向量存储实例(Spring AI 自动配置)
      • 负责向量的持久化存储、相似度检索
        */
        @Resource
        private VectorStore vectorStore;

      /**

      • 文本向量化接口
      • 调用通义千问 text-embedding-v3 模型,将输入文本转换为向量
      • 示例请求:http://localhost:8011/text2embed?msg=射雕英雄传
      • @param msg 待向量化的输入文本
      • @return 向量化结果(包含向量数组、模型元信息等)
        */
        @GetMapping("/text2embed")
        public EmbeddingResponse text2Embed(String msg) {
        // 构造向量化请求:传入文本 + 指定通义千问 text-embedding-v3 模型
        EmbeddingResponse embeddingResponse = embeddingModel.call(
        new EmbeddingRequest(
        List.of(msg),
        DashScopeEmbeddingOptions.builder()
        .withModel("text-embedding-v3")
        .build()
        )
        );
        // 打印生成的向量到控制台,用于调试验证
        System.out.println(Arrays.toString(embeddingResponse.getResult().getOutput()));
        return embeddingResponse;
        }

      /**

      • 文本向量化后存入 Redis Stack 向量数据库接口
      • 向向量库中添加测试文档,Spring AI 自动完成「文本→向量→存储」全流程
        */
        @GetMapping("/embed2vector/add")
        public void add() {
        // 构造待存储的文档列表(模拟业务中的文本数据)
        List<Document> documents = List.of(
        new Document("i study LLM"), // 文档1:学习大模型
        new Document("i love java") // 文档2:热爱Java
        );
        // 将文档写入向量库:自动调用 embedding 模型向量化,再持久化到 Redis Stack
        vectorStore.add(documents);
        }

      /**

      • 从 Redis Stack 向量数据库进行相似度检索接口
      • 根据输入文本,检索向量库中语义最相似的 topK 条文档
      • 示例请求:http://localhost:8011/embed2vector/get?msg=LLM
      • @param msg 检索用的输入文本(自动向量化后,与库中向量做相似度匹配)
      • @return 相似度最高的文档列表
        */
        @GetMapping("/embed2vector/get")
        public List<Document> getAll(@RequestParam(name = "msg") String msg) {
        // 构造相似度检索请求:指定检索文本、返回前2条最相似结果
        SearchRequest searchRequest = SearchRequest.builder()
        .query(msg)
        .topK(2)
        .build();
        // 调用向量库的相似度检索方法,执行语义检索
        List<Document> list = vectorStore.similaritySearch(searchRequest);
        // 打印检索结果到控制台,用于调试验证
        System.out.println(list);
        return list;
        }
        }

RAG(Retrieval Augmented Generation) 检索增强生成

一、背景 / 概要

LLM的缺陷:

  • LLM的知识不是实时的,不具备知识更新。
  • LLM可能不知道你私有的领域业务知识。
  • LLM有时会在回答中生成看似合理但实际上是错误的信息。
  • 如果想让一个LLM了解特定领域的知识或专有数据,可以考虑使用RAG

什么是RAG:

  • 简单来说,**RAG(检索增强生成)****是一种从你的数据中查找相关信息,并在将提示发送给LLM之前将其注入到提示中的方法。**这样一来,LLM就能获得(希望是)相关的信息,并基于这些信息进行回答,从而降低产生幻觉的概率。
    • 幻觉 --> 已读乱回、已读不会、似是而非
  • RAG技术就像给AI大模型装上了「实时百科大脑」,为了让大模型获取足够的上下文,以便获得更加广泛的信息源,通过先查资料后回答的机制,让AI摆脱传统模型的"知识遗忘和幻觉回复"困境。(类似于在你考试前给你塞了个小抄,让你在考场上知道你盲区的知识)

所以RAG能做什么:

  • 通过引入外部知识源来增强LLM的输出能力,传统的LLM通常基于其训练数据生成响应,但这些数据可能过时或不够全面。RAG允许模型在生成答案之前,从特定的知识库中检索相关信息,从而提供更准确和上下文相关的回答。

RAG怎么使用:

  • RAG的流程分为两个不同的阶段:索引和检索
  • 索引(Indexing) :索引首先清理和提取各种格式的原始数据,如PDF、HTML、Word和Markdown,然后将其转换为统一的纯文本格式。为了适应语言模型的上下文限制,文本被分割成更小的、可消化的块(chunk)。然后使用嵌入模型将块编码成向量表示,并存储在向量数据库中。这一步对于在随后的检索阶段实现高效的相似性搜索至关重要。知识库分割成chunks,并将chunks向量化至向量库中
  • 检索(Retrieval): 在收到用户查询(Query)后,RAG系统**采用与索引阶段相同的编码模型将查询转换为向量表示,然后计算索引语料库中查询向量与块向量的相似性得分。**该系统优先级和检索最高k(Top-K)块,显示最大的相似性查询。

二、案例编码(AI智能运维助手demo)

需求:AI智能运维助手,通过提供的错误编码,给出异常解释辅助运维人员更好的定位问题和维护系统

技术实现:SpringAl + 阿里百炼嵌入模型text-embedding-v3 + 向量数据库RedisStack + DeepSeek

java 复制代码
// 1. 引入pom依赖(略)
// 2. 配置yml文件(略)
// 3. 提供Errorcode脚本让他存入向量数据库Redisstack,形成文档知识库ops.txt
错误码	错误描述
00000	系统 OK 正确执行后的返回
A0001	用户端错误一级宏观错误码
A0100	用户注册错误二级宏观错误码
B1111	支付接口超时
C2222	Kafka 消息解压严重

// 4. 核心代码
/**
 * 向量数据库初始化配置类(优化版:增加幂等性去重,避免重复加载向量数据)
 * 项目启动时自动将运维错误码文档(ops.txt)向量化并写入Redis Stack向量数据库
 * 优化点:通过Redis setnx命令实现幂等控制,防止重复启动项目导致向量数据重复插入
 */
@Configuration
public class InitVectorDatabaseConfig {

    /**
     * 注入Spring AI自动配置的Redis Stack向量存储实例
     * 负责向量的持久化存储、相似度检索
     */
    @Autowired
    private VectorStore vectorStore;

    /**
     * 注入类路径下的运维错误码文档资源(ops.txt)
     * 文档内容为错误码与对应故障解释,用于构建运维专属知识库
     */
    @Value("classpath:ops.txt")
    private Resource opsFile;

    /**
     * 注入RedisTemplate,用于操作Redis的setIfAbsent(setnx)命令
     * 实现向量数据加载的幂等性控制,避免重复插入
     */
    @Autowired
    private RedisTemplate<String, String> redisTemplate;

    /**
     * Bean初始化后执行的初始化方法(@PostConstruct保证在Bean完成依赖注入后执行)
     * 优化后流程:读取文档 → 幂等性去重校验 → 分词拆分 → 向量化入库(仅首次执行)
     */
    @PostConstruct
    public void init() {
        // 1. 读取本地ops.txt运维文档,使用TextReader加载文本文件
        TextReader textReader = new TextReader(opsFile);
        // 设置文件字符集为系统默认,避免中文乱码
        textReader.setCharset(Charset.defaultCharset());

        // ========== 优化:增加向量数据去重幂等性控制 ==========
        // 2. 从文档元数据中获取源文件路径,作为去重的唯一标识
        String sourceMetadata = (String) textReader.getCustomMetadata().get("source");
        // 对源文件信息做MD5加密,生成唯一哈希值,避免Redis Key过长、特殊字符问题
        String textHash = SecureUtil.md5(sourceMetadata);
        // 构造Redis去重Key,格式为「vector-xxx:{文件哈希}」,区分不同业务的向量数据
        String redisKey = "vector-xxx:" + textHash;

        // 执行Redis setIfAbsent(对应setnx命令):
        // - Key不存在:设置成功,返回true → 首次加载,允许入库
        // - Key已存在:设置失败,返回false → 已加载过,跳过操作
        Boolean retFlag = redisTemplate.opsForValue().setIfAbsent(redisKey, "1");
        System.out.println("****向量初始化幂等校验结果 retFlag : " + retFlag);

        // 3. 仅当首次加载(Key不存在)时,才执行向量化入库
        if (Boolean.TRUE.equals(retFlag)) {
            // 文件转换为向量:使用TokenTextSplitter按Token分词拆分长文本
            // 解决单段文本过长导致的向量化效果差、检索精度低问题
            List<Document> list = new TokenTextSplitter().transform(textReader.read());
            // 写入向量数据库RedisStack:Spring AI自动完成文本向量化、持久化全流程
            vectorStore.add(list);
            System.out.println("-----向量初始化数据加载完成");
        } else {
            // Key已存在,说明向量数据已完成加载,跳过重复操作
            System.out.println("-----向量初始化数据已经加载过,请不要重复操作");
            // 若需要严格控制,可取消注释抛出异常,阻止项目重复启动
            // throw new RuntimeException("向量数据已加载,禁止重复初始化");
        }
    }
}


/**
 * RAG(检索增强生成)运维错误码智能问答控制器
 * 基于Redis Stack向量数据库+通义千问大模型,实现运维故障码精准问答
 * 核心流程:用户提问 → 向量库检索相关故障文档 → 大模型结合文档生成专业回答
 */
@RestController
public class RagController {

    /**
     * 注入通义千问大模型客户端(指定Bean名称为qwenChatClient)
     * 负责调用通义千问大模型生成符合运维场景的回答
     */
    @Resource(name = "qwenChatClient")
    private ChatClient chatClient;

    /**
     * 注入Redis Stack向量存储实例
     * 负责根据用户提问,检索向量库中最相关的运维故障码文档
     */
    @Resource
    private VectorStore vectorStore;

    /**
     * 运维错误码RAG问答接口
     * 示例请求:http://localhost:8012/rag4aiops?msg=00000
     * @param msg 用户提问(支持错误码、运维故障描述)
     * @return 流式返回大模型回答(支持前端打字机效果)
     */
    @GetMapping("/rag4aiops")
    public Flux<String> rag(String msg) {
        // 定义系统提示词(System Prompt),严格约束大模型的角色和回答规则
        // 确保大模型仅基于给定的错误码文档回答,避免 hallucination(幻觉)
        String systemPrompt = """
                你是一个专业运维工程师,按照给出的编码/故障信息给出对应故障解释,如果查询不到相关信息,直接回复"找不到对应故障信息"。
                """;

        // 构建RAG检索增强Advisor:Spring AI封装的RAG核心组件
        // 自动完成「用户提问向量化→向量库相似度检索→检索结果注入Prompt上下文」全流程
        RetrievalAugmentationAdvisor ragAdvisor = RetrievalAugmentationAdvisor.builder()
                .documentRetriever(VectorStoreDocumentRetriever.builder()
                        .vectorStore(vectorStore)
                        .build())
                .build();

        // 调用大模型生成流式回答
        return chatClient.prompt()
                // 注入系统提示词,约束大模型行为
                .system(systemPrompt)
                // 注入用户提问
                .user(msg)
                // 应用RAG Advisor,自动检索并注入知识库上下文
                .advisors(ragAdvisor)
                // 开启流式返回,实现前端打字机效果
                .stream()
                // 提取大模型生成的回答内容
                .content();
    }
}
相关推荐
tianbaolc2 小时前
Claude Code 源码剖析 模块一 · 第一节:Claude Code 宏观架构
人工智能·ai·架构·claude code
温九味闻醉2 小时前
人工智能应用作业1:PPO强化学习算法
人工智能·算法
安科士andxe2 小时前
实践指南|安科士SFP-10/25G-LR-S-I光模块部署与运维技巧
运维·人工智能·5g
AI360labs_atyun2 小时前
我在命令行里养了只电子宠物,还顺便学会了Claude Code
人工智能·科技·学习·ai·宠物
记录无知岁月2 小时前
【学习笔记】学术英语单词总结
学习·accumulation·english word
dydm_131282 小时前
笔尖下的奇迹:当AI实时绘画“撞见”未来教育
人工智能
CanCanCanedFish2 小时前
快速解决OpenCode配置第三方API
人工智能·ai
xiaoxiaoxiaolll2 小时前
《Light》刊发北大新成果:谐振耦合架构实现超宽带孤子微梳,能效创纪录
学习
CheerWWW2 小时前
GameFramework——Download篇
笔记·学习·unity·c#