springAI+向量数据库+RAG入门案例

前言:最近在学习 Spring AI,发现网上关于 Spring AI 结合 RAG(检索增强生成)的中文资料比较少,而且很多都是理论介绍。经过几天的实践,我终于成功搭建了一个完整的 RAG 问答系统。本文将分享我的学习过程和代码实现,希望能帮助到正在学习 Spring AI 的 Java 开发者。
目录

[什么是 Spring AI?](#什么是 Spring AI?)

什么是向量数据库?

向量的本质

向量相似度示例

[什么是 RAG?](#什么是 RAG?)

环境准备:

[首先获取阿里云 API Key](#首先获取阿里云 API Key)

[创建 Spring Boot 项目](#创建 Spring Boot 项目)

[pom.xml 关键依赖](#pom.xml 关键依赖)

application.yml

接下来是核心代码实现

向量存储配置:

[RAG 服务核心](#RAG 服务核心)

[REST 控制器](#REST 控制器)

启动类

测试接口:

核心概念解析

[什么是 Prompt 工程?](#什么是 Prompt 工程?)


首先介绍一下概念

什么是 Spring AI?

Spring AI 是 Spring 官方推出的人工智能应用开发框架,旨在为 Java 开发者提供一套简洁、统一的工具,用于将 AI 能力(特别是大语言模型)集成到企业级应用中。

简单来说,它就像是 JDBC 统一了数据库访问一样,为 AI 模型访问提供了统一的抽象层

什么是向量数据库?

向量数据库 是一种专门用于存储、索引和检索向量数据 的数据库。它通过计算向量之间的相似度来实现快速、准确的语义搜索。

向量的本质

向量 是一组数字的数组,代表某个事物的特征。比如:

java 复制代码
// 文本 "Spring AI" 被转换为向量
[0.12, -0.34, 0.56, 0.78, -0.23, ...]  // 1536个数字

向量相似度示例

java 复制代码
// 文本向量化后
"Spring AI"  → [0.12, -0.34, 0.56, ...]
"Java框架"  → [0.11, -0.33, 0.55, ...]  // 相似度高
"苹果手机"  → [0.89, 0.45, -0.23, ...]  // 相似度低

什么是 RAG?

在开始之前,我们先理解一个概念:RAG(Retrieval-Augmented Generation,检索增强生成)

传统的 LLM 调用是这样的:

java 复制代码
String answer = chatClient.prompt(question).call().content();

这里介绍一下LLM概念:

LLM(Large Language Model,大语言模型) 是一种基于深度学习的人工智能模型,经过海量文本数据训练,能够理解和生成人类语言。

通俗的理解就是把 LLM 想象成一个读过全世界所有书籍的人

  • 📖 阅读了互联网上几乎所有文本(网页、书籍、论文、代码等)

  • 🧠 学会了语言的规律、知识的关联、逻辑的推理

  • 💬 能够理解你的问题,并给出合理的回答

注:也就是市面上常见的AI大模型,本文接下来使用的是阿里云的通义千问大模型
这么做的弊端就是:

AI 完全依赖训练数据中的知识,可能出现:

  • ❌ 知识过时(训练数据截止日期之前)

  • ❌ 无法访问私有数据(公司内部文档)

  • ❌ 产生幻觉(编造不存在的信息)

而RAG 的解决方案

复制代码
用户问题 → 检索相关知识 → 增强 Prompt → LLM 生成答案

简单说:先查资料,再让 AI 基于资料回答
业务上的流程图:

复制代码
┌─────────────────────────────────────────────────────────────┐
│                    Spring AI + RAG 系统                      │
├─────────────────────────────────────────────────────────────┤
│                                                             │
│  用户 → REST API → Controller → Service                     │
│                                    ↓                        │
│                           ┌────────────────┐               │
│                           │ 1. 向量检索    │               │
│                           │ VectorStore    │               │
│                           └────────────────┘               │
│                                    ↓                        │
│                           ┌────────────────┐               │
│                           │ 2. 构建上下文  │               │
│                           │ Prompt工程     │               │
│                           └────────────────┘               │
│                                    ↓                        │
│                           ┌────────────────┐               │
│                           │ 3. LLM 调用    │               │
│                           │ 通义千问       │               │
│                           └────────────────┘               │
│                                    ↓                        │
│                              返回答案                       │
└─────────────────────────────────────────────────────────────┘

本文接下来将要使用到的技术栈:

组件 技术 说明
框架 Spring Boot 3.5.13 基础框架
AI 框架 Spring AI 1.1.3 AI 应用抽象
向量存储 SimpleVectorStore 内存向量存储(开发用)
LLM 阿里云通义千问 DashScope 服务
构建工具 Maven 依赖管理
Java 版本 17 LTS 版本

环境准备:

注:这里注意使用spring-AI框架需要确保JDK版本大于17,springboot版本大于3.x

首先获取阿里云 API Key

访问 阿里云 DashScope 控制台,开通服务并获取 API Key。

创建 Spring Boot 项目

使用 Spring Initializr 创建项目,选择:

  • Spring Web

  • Lombok

pom.xml 关键依赖

XML 复制代码
<properties>
    <java.version>17</java.version>
    <spring-ai.version>1.1.3</spring-ai.version>
    <spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
</properties>

<dependencies>
    <!-- Spring Boot Web -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>

    <!-- Spring AI 核心 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-core</artifactId>
    </dependency>

    <!-- 向量存储 -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-vector-store-core</artifactId>
    </dependency>

    <!-- 简单向量存储(内存存储) -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-simple-vector-store</artifactId>
    </dependency>

    <!-- 阿里云 DashScope -->
    <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
    </dependency>

    <!-- Lombok -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>${spring-ai.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud.ai</groupId>
            <artifactId>spring-ai-alibaba-bom</artifactId>
            <version>${spring-ai-alibaba.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
    </repository>
</repositories>

application.yml

XML 复制代码
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}  # 阿里云通义千问中的appkey
      chat:
        options:
          model: qwen-turbo
          temperature: 0.7
          max-tokens: 2000
      embedding:
        enabled: true
        model: text-embedding-v2
    
    vectorstore:
      type: simple  # 使用内存向量存储

server:
  port: 8989

logging:
  level:
    com.smoky.ai: DEBUG

首先需要理解一下怎么调用AI大模型:

大家可以自己调用一下感受一下,本文就不放输出结果了,实际上就是把你的问题通过springai调用通义千问然后生成回答返回,此示例很简单,注意自己的token使用。

要点:感受流式调用和普通调用及主题调用

java 复制代码
package com.smoky.ai.springai.controller;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;



@RestController
@RequestMapping("/api/ai")
public class AiController {


    @Autowired
    private  ChatClient chatClient;


    @GetMapping("/ask")
    public String ask(@RequestParam String question) {
        return chatClient.prompt()
                .system("你是一个专业的Java助手,请用中文回答")
                .user(question)
                .call()
                .content();
    }

    /**
     * 流式输出示例
     * GET /api/ai/stream?message=讲个故事
     */
    @GetMapping(value = "/stream", produces = "text/plain;charset=UTF-8")
    public String stream(@RequestParam String message) {
        StringBuilder result = new StringBuilder();

        chatClient.prompt()
                .user(message)
                .stream()
                .content()
                .subscribe(chunk -> {
                    result.append(chunk);
                    System.out.print(chunk); // 实时输出
                });

        // 注意:这里需要等待流式输出完成,实际使用中可能需要异步处理
        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }

        return result.toString();
    }

    /**
     * 带参数的对话(使用系统提示模板)
     * GET /api/ai/role-play?role=老师&topic=数学
     */
    @GetMapping("/role-play")
    public String rolePlay(@RequestParam String role, @RequestParam String topic) {
        String systemTemplate = "你是一个{role},请用{role}的身份回答关于{topic}的问题";

        // 方法内
        return chatClient.prompt()
                .system(spec -> spec
                        .text(systemTemplate)
                        .param("role", role)
                        .param("topic", topic))
                .user("请介绍一下这个主题")
                .call()
                .content();
}
}

其次我们需要在这个过程中引入向量数据库+RAG的概念,以达到增强检索的目的

注:增强检索不是微调模型,增强检索的意义在于让我们的AI基于向量数据库已经加载的文档及数据来进行回答,从而避免了AI大模型在不知道问题的答案的时候胡乱回答,从而造成幻觉问题,也就是问一答二

内部数据流转流程图为(需要好好理解一下流程):

接下来是核心代码实现

向量存储配置:

java 复制代码
package com.smoky.ai.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();
    }
}

RAG 服务核心

java 复制代码
package com.smoky.ai.service;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.document.Document;
import org.springframework.ai.vectorstore.SearchRequest;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Slf4j
@Service
public class RagService {
    
    private final VectorStore vectorStore;
    private final ChatClient chatClient;
    
    public RagService(VectorStore vectorStore, ChatClient.Builder chatClientBuilder) {
        this.vectorStore = vectorStore;
        this.chatClient = chatClientBuilder.build();
        log.info("RagService 初始化完成");
    }
    
    /**
     * RAG 问答核心方法
     */
    public String askQuestion(String question) {
        log.info("用户问题: {}", question);
        
        // 1. 检索相关文档
        List<Document> relevantDocs = vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(question)
                .topK(3)
                .similarityThreshold(0.5)
                .build()
        );
        
        // 2. 构建上下文
        String context = relevantDocs.stream()
                .map(Document::getText)
                .collect(Collectors.joining("\n\n---\n\n"));
        
        // 3. 构建 Prompt
        String prompt = buildPrompt(question, context);
        
        // 4. 调用 LLM 生成答案
        String answer = chatClient.prompt(prompt).call().content();
        
        return answer;
    }
    
    /**
     * 添加示例文档
     */
    public void addDocuments() {
        log.info("添加示例文档到向量存储...");
        
        List<Document> documents = List.of(
            new Document("Spring AI 是一个用于构建 AI 应用的 Java 框架,它提供了统一的 API 来集成各种 AI 模型。",
                Map.of("source", "official", "topic", "spring-ai")),
            new Document("RAG(检索增强生成)是一种技术,通过从外部知识库检索相关信息来增强大模型的回答能力。",
                Map.of("source", "tutorial", "topic", "rag")),
            new Document("向量数据库用于存储和检索文档的向量表示,Chroma、Pinecone、Weaviate 等都是常用的向量数据库。",
                Map.of("source", "article", "topic", "vector-db")),
            new Document("DashScope 是阿里云提供的模型服务平台,支持通义千问等多种大语言模型和向量化模型。",
                Map.of("source", "official", "topic", "dashscope")),
            new Document("Spring AI 1.1.x 版本支持多种向量数据库,包括 Chroma、Pinecone、Qdrant 等。",
                Map.of("source", "docs", "topic", "spring-ai"))
        );
        
        vectorStore.add(documents);
        log.info("成功添加 {} 个文档", documents.size());
    }
    
    /**
     * 语义搜索
     */
    public List<Document> search(String query, int topK) {
        SearchRequest searchRequest = SearchRequest.builder()
                .query(query)
                .topK(topK)
                .similarityThreshold(0.5)
                .build();
        
        return vectorStore.similaritySearch(searchRequest);
    }
    
    /**
     * 构建 Prompt
     */
    private String buildPrompt(String question, String context) {
        return """
            你是一个专业的 AI 助手,请基于以下上下文信息回答用户的问题。
            
            要求:
            1. 如果上下文中包含相关信息,请基于上下文准确回答
            2. 如果上下文中没有相关信息,请明确说"根据现有知识库,我无法回答这个问题"
            3. 回答要简洁、准确、有条理
            
            ===== 上下文信息 =====
            %s
            =====================
            
            用户问题:%s
            
            请回答:
            """.formatted(context, question);
    }
}

REST 控制器

java 复制代码
package com.smoky.ai.controller;

import com.smoky.ai.service.RagService;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.document.Document;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

@Slf4j
@RestController
@RequestMapping("/api/rag")
@RequiredArgsConstructor
public class RagController {
    
    private final RagService ragService;
    
    @PostMapping("/ask")
    public AnswerResponse ask(@RequestBody QuestionRequest request) {
        String answer = ragService.askQuestion(request.getQuestion());
        return new AnswerResponse(answer);
    }
    
    @PostMapping("/ask-with-filter")
    public AnswerResponse askWithFilter(@RequestBody FilteredQuestionRequest request) {
        String answer = ragService.askQuestionWithFilter(
            request.getQuestion(), 
            request.getTopic()
        );
        return new AnswerResponse(answer);
    }
    
    @PostMapping("/load-documents")
    public Map<String, String> loadDocuments() {
        ragService.addDocuments();
        return Map.of("status", "success", "message", "文档已加载");
    }
    
    @GetMapping("/search")
    public List<Document> search(
            @RequestParam String q,
            @RequestParam(defaultValue = "3") int topK) {
        return ragService.search(q, topK);
    }
    
    @Data
    public static class QuestionRequest {
        private String question;
    }
    
    @Data
    public static class FilteredQuestionRequest {
        private String question;
        private String topic;
    }
    
    @Data
    @AllArgsConstructor
    public static class AnswerResponse {
        private String answer;
    }
}

启动类

java 复制代码
package com.smoky.ai;

import com.smoky.ai.service.RagService;
import lombok.extern.slf4j.Slf4j;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;

@Slf4j
@SpringBootApplication
public class SpringaiApplication {
    
    public static void main(String[] args) {
        SpringApplication.run(SpringaiApplication.class, args);
    }
    
    @Bean
    public CommandLineRunner initDocuments(RagService ragService) {
        return args -> {
            log.info("初始化知识库...");
            ragService.addDocuments();
            log.info("知识库初始化完成!");
        };
    }
}

测试接口:

bash 复制代码
# 1. 基础问答
curl -X POST http://localhost:8989/api/rag/ask \
  -H "Content-Type: application/json" \
  -d '{"question": "什么是Spring AI?"}'

# 2. 语义搜索
curl "http://localhost:8989/api/rag/search?q=向量数据库&topK=2"

# 3. 主题过滤问答
curl -X POST http://localhost:8989/api/rag/ask-with-filter \
  -H "Content-Type: application/json" \
  -d '{"question": "支持哪些数据库?", "topic": "spring-ai"}'

输出:

bash 复制代码
{
  "answer": "根据知识库信息,Spring AI 是一个用于构建 AI 应用的 Java 框架,它提供了统一的 API 来集成各种 AI 模型。Spring AI 1.1.x 版本支持多种向量数据库,包括 Chroma、Pinecone、Qdrant 等。"
}

核心概念解析

传统搜索是关键词匹配,向量检索是语义理解:

sql 复制代码
// 传统搜索(只能匹配"汽车")
SELECT * FROM docs WHERE text LIKE '%汽车%'

// 向量检索(也能匹配"轿车"、"车辆")
vectorStore.similaritySearch("汽车")

什么是 Prompt 工程?

Prompt 是给 AI 的指令,好的 Prompt 能显著提升回答质量:

java 复制代码
String prompt = """
    请基于以下上下文回答问题:
    
    上下文:%s
    问题:%s
    
    要求:
    1. 如果上下文有信息,准确回答
    2. 如果没信息,明确说不知道
    3. 回答简洁有条理
    """;

注:上述代码使用了java15的新特性文本块模式,这个可以自行了解

写在最后:

简单来说:Spring AI 提供统一接口,让 Java 应用通过向量数据库检索私有知识,增强大模型回答,实现精准的 RAG 智能问答。

类比理解:

复制代码
传统 LLM = 靠记忆回答的专家(可能记错)
Spring AI + 向量数据库 + RAG = 带图书馆的专家(先查书再回答)

Spring AI = 图书馆管理系统(统一接口)
向量数据库 = 智能书架(语义检索)
RAG = 查书→回答的工作流程
通过本文,你应该已经掌握了:

  • ✅ RAG 的核心概念

  • ✅ Spring AI 的基本使用

  • ✅ 向量检索的实现

  • ✅ Prompt 工程的方法

  • ✅ 完整的 RAG 问答系统

RAG 是解决大模型幻觉问题的有效方案,也是企业落地 AI 应用的重要技术。希望本文能帮助你快速入门 Spring AI 和 RAG!

如果有任何问题,欢迎在评论区交流讨论。

相关推荐
froginwe1120 小时前
C 运算符
开发语言
APIshop20 小时前
Java获取京东商品详情接口(item_get)实战指南
java·linux·数据库
Mr.Entropy20 小时前
springboot2.x集成Flyway
java
disgare20 小时前
关于 spring 工程中添加 traceID 实践
java·后端·spring
李白的粉20 小时前
基于springboot+vue的旅游民宿管理系统
java·spring boot·vue·毕业设计·课程设计·源代码·旅游民宿管理系统
fengfuyao98520 小时前
低数据极限下模型预测控制的非线性动力学的稀疏识别 MATLAB实现
开发语言·matlab
摇滚侠20 小时前
搭建前端开发环境 安装 nodejs 设置淘宝镜像 最简化最标准版本 不使用 NVM NVM 高版本无法安装低版本 nodejs
java·开发语言·node.js
花千树-01020 小时前
兼容 ThreadLocal 的用户上下文透传方案:WebFlux 项目改造实践
java·spring boot·servlet·jetty
t1987512820 小时前
MATLAB十字路口车辆通行情况模拟系统
开发语言·matlab
yyk的萌21 小时前
AI 应用开发工程师基础学习计划
开发语言·python·学习·ai·lua