SpirngAI

Spring AI 集成应用实战:从零搭建本地 AI 对话与 RAG 知识库

📌 前置说明 :本文基于 Spring AI 1.0.0 官方文档编写,覆盖完整的本地部署操作步骤,无需付费 API Key,适合想快速在本地体验 AI 能力的 Java 开发者。


一、Spring AI 是什么?

Spring AI 是 Spring 官方推出的 AI 工程化框架,其核心目标是:将企业数据与 API 通过 AI 模型连接起来

🔥 核心特性

特性 说明
多模型支持 OpenAI、Azure OpenAI、Anthropic、Ollama(本地模型)、Amazon Bedrock 等
多模态 Chat(对话)、Embedding(向量化)、Text-to-Image(生图)、TTS/ASR(语音)
向量数据库 支持 14 种向量库:Chroma、Milvus、Pinecone、Weaviate、Redis、PostgreSQL/PGVector 等
RAG 支持 内置 ETL 数据工程框架,支持文档加载、切分、向量化、检索全流程
工具调用 AI 模型可调用本地 @Tool 注解方法或 POJO 函数
Structured Output AI 输出直接映射为 Java POJO,无需手动解析

🎯 支持的 Spring Boot 版本

  • Spring Boot 3.4.x
  • Spring Boot 3.5.x
  • Java 17+(推荐 Java 21)

二、项目创建(Spring Initializr 方式)

2.1 访问 Spring Initializr

浏览器打开:https://start.spring.io/

配置如下:

配置项
Project Gradle (Kotlin)
Language Java
Spring Boot 3.4.x
Group com.example
Artifact spring-ai-demo
Java Version 21

2.2 添加依赖(Dependencies)

在页面下方点击 "Add Dependencies",添加以下依赖:

  • Spring Web(用于 REST 接口)
  • AI Models → 选择你要用的模型驱动(见下方说明)
  • Vector Stores → 选择你要用的向量数据库

2.3 可选模型依赖说明

复制代码
# OpenAI(需要 API Key)
spring-ai-starter-model-openai

# Ollama(本地模型,无需 API Key)
spring-ai-starter-model-ollama

# Azure OpenAI
spring-ai-starter-model-azure-openai

# HuggingFace
spring-ai-starter-model-huggingface

💡 新手推荐先使用 Ollama,完全本地运行,零成本,可随意折腾。


三、依赖配置详解

3.1 build.gradle 完整配置

kotlin 复制代码
plugins {
    id("java")
    id("org.springframework.boot") version "3.4.4"
    id("io.spring.dependency-management") version "1.1.7"
}

java { sourceCompatibility = JavaVersion.VERSION_21 }

repositories {
    mavenCentral()
}

dependencies {
    // ===== Spring AI BOM(版本管理,必须) =====
    implementation(platform("org.springframework.ai:spring-ai-bom:1.0.0"))
    
    // ===== Spring Boot Web =====
    implementation("org.springframework.boot:spring-boot-starter-web")
    
    // ===== AI 模型(选择你需要的) =====
    // OpenAI(需要 OPENAI_API_KEY 环境变量)
    implementation("org.springframework.ai:spring-ai-openai")
    
    // Ollama 本地模型(无需 API Key)
    implementation("org.springframework.ai:spring-ai-ollama")
    
    // ===== 向量数据库(选择你需要的) =====
    // Chroma(轻量级,适合本地开发)
    implementation("org.springframework.ai:spring-ai-starter-vectorstore-chroma")
    
    // Redis(生产推荐)
    implementation("org.springframework.ai:spring-ai-starter-vectorstore-redis")
    
    // PostgreSQL + PGVector
    implementation("org.springframework.ai:spring-ai-starter-vectorstore-pgvector")
    
    // ===== 测试 =====
    testImplementation("org.springframework.boot:spring-boot-starter-test")
}

3.2 Maven 配置(备选)

xml 复制代码
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-bom</artifactId>
            <version>1.0.0</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<dependencies>
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai</artifactId>
    </dependency>
    <!-- 添加其他依赖同上 -->
</dependencies>

四、Ollama 本地模型部署(无需 API Key)

这是最推荐的本地 AI 体验方式,完全免费,无需注册账号

4.1 安装 Ollama

Windows(PowerShell):

powershell 复制代码
winget install Ollama.Ollama

macOS:

bash 复制代码
brew install ollama

Linux:

bash 复制代码
curl -fsSL https://ollama.com/install.sh | sh

4.2 启动 Ollama 服务

安装后,Ollama 自动作为系统服务运行,监听 http://localhost:11434

4.3 下载并运行模型

bash 复制代码
# 下载并运行对话模型(推荐 qwen2.5 或 llama3)
ollama run qwen2.5:7b

# 如果内存充足,可以用更大的模型
ollama run llama3:8b

# 下载 Embedding 模型(用于 RAG)
ollama pull nomic-embed-text

💡 首次运行会自动下载模型,首次下载需要等待。

模型文件存放在 ~/.ollama/models/

4.4 验证 Ollama 是否正常运行

bash 复制代码
curl http://localhost:11434

返回如下 JSON 表示正常:

json 复制代码
{"version":"0.5.0","msgs":["Ollama is running"]}

五、Spring AI + Ollama 集成配置

5.1 application.yml 配置

yaml 复制代码
spring:
  application:
    name: spring-ai-ollama-demo
  
  # ===== Ollama 配置(本地模型) =====
  ai:
    ollama:
      base-url: http://localhost:11434
      chat:
        options:
          model: qwen2.5:7b          # 使用的模型名称
          temperature: 0.7           # 生成随机性(0~1)
          num-gpu: 1                 # 使用 GPU 加速

# ===== 服务器端口 =====
server:
  port: 8080

5.2 基础对话实现(最简方式)

Spring AI 提供两种调用方式,这里先介绍最简洁的 ChatModel

java 复制代码
package com.example.springai;

import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.model.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.prompt.UserPromptTemplate;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.annotation.Bean;
import org.springframework.stereotype.Service;

import java.util.Map;

@SpringBootApplication
public class SpringAiDemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringAiDemoApplication.class, args);
    }

    // ===== 第1种:极简调用(直接传字符串) =====
    @Service
    public static class SimpleChatService {
        private final ChatModel chatModel;

        public SimpleChatService(ChatModel chatModel) {
            this.chatModel = chatModel;
        }

        public String ask(String question) {
            // 最简单的调用方式
            return chatModel.call(question);
        }
    }
}

5.3 带系统提示词的对话

java 复制代码
@Service
public static class ChatWithSystemPromptService {
    private final ChatModel chatModel;

    public ChatWithSystemPromptService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    public String askWithRole(String userQuestion) {
        // 创建系统提示词
        SystemPromptTemplate systemPrompt = 
            new SystemPromptTemplate("你是一位专业的 Java 后端工程师,用简洁专业的语言回答问题。");

        // 创建用户提示词
        UserPromptTemplate userPrompt = 
            new UserPromptTemplate(userQuestion);

        Prompt prompt = new Prompt(
            systemPrompt.createMessage(Map.of()),
            userPrompt.createMessage(Map.of())
        );

        ChatResponse response = chatModel.call(prompt);
        return response.getResult().getOutput().getText();
    }
}

5.4 使用 OllamaEmbedding(向量嵌入)

java 复制代码
@Service
public class EmbeddingService {
    private final OllamaEmbeddingModel embeddingModel;

    public EmbeddingService(OllamaEmbeddingModel embeddingModel) {
        this.embeddingModel = embeddingModel;
    }

    public float[] embed(String text) {
        EmbeddingResponse response = embeddingModel.embed(
            new EmbeddingRequest(
                List.of(new TextSegment(text)),
                OllamaOptions.builder()
                    .model("nomic-embed-text")  // 需要提前用 ollama pull nomic-embed-text
                    .build()
            )
        );
        return response.getResult().getEmbedding();
    }
}

六、Spring AI + OpenAI 集成配置

如果你有 OpenAI API Key,可以使用更强大的 GPT 模型。

6.1 获取 API Key

  1. 访问 https://platform.openai.com/api-keys
  2. 登录账号(需要国际信用卡验证)
  3. 创建 API Key,妥善保存

6.2 application.yml 配置

yaml 复制代码
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY}           # 推荐放在环境变量中
      base-url: https://api.openai.com/v1  # 默认值,可不写
      chat:
        options:
          model: gpt-4o                      # 模型名称
          temperature: 0.7
          max-tokens: 1000

6.3 设置环境变量(安全方式)

bash 复制代码
# Windows PowerShell
$env:OPENAI_API_KEY = "sk-xxxxxxxxxxxxxxxxxxxxxxxx"

# macOS / Linux
export OPENAI_API_KEY="sk-xxxxxxxxxxxxxxxxxxxxxxxx"

6.4 Java 代码调用

java 复制代码
@Service
public class OpenAiChatService {
    private final OpenAiChatModel chatModel;

    public OpenAiChatService(OpenAiChatModel chatModel) {
        this.chatModel = chatModel;
    }

    public String chat(String message) {
        return chatModel.call(message);
    }

    // 带选项参数的调用
    public String chatWithOptions(String message) {
        return chatModel.call(message, 
            OpenAiChatOptions.builder()
                .model("gpt-4o")
                .temperature(0.3f)
                .maxTokens(500)
                .build()
        );
    }
}

七、REST API 对外暴露(Controller 层)

7.1 简单对话接口

java 复制代码
@RestController
@RequestMapping("/api/ai")
public class ChatController {

    private final SimpleChatService chatService;

    public ChatController(SimpleChatService chatService) {
        this.chatService = chatService;
    }

    @GetMapping("/chat")
    public Map<String, String> chat(@RequestParam String question) {
        String answer = chatService.ask(question);
        return Map.of(
            "question", question,
            "answer", answer
        );
    }

    @PostMapping("/chat")
    public Map<String, String> chatPost(@RequestBody Map<String, String> request) {
        String question = request.get("question");
        String answer = chatService.ask(question);
        return Map.of(
            "question", question,
            "answer", answer
        );
    }
}

7.2 启动并测试

bash 复制代码
# 启动应用
./gradlew bootRun

# 测试(浏览器或 Postman)
curl "http://localhost:8080/api/ai/chat?question=Spring%20AI%20是什么"

八、RAG(检索增强生成)实战 --- 构建私有知识库

RAG 是当前最实用的企业 AI 应用模式:将本地文档向量化存储 → 用户提问时检索相关文档 → 将文档内容注入 Prompt → AI 基于真实数据回答

8.1 核心流程图

复制代码
┌──────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  企业文档     │───▶│  DocumentLoader  │───▶│  DocumentSplitter│
│  (PDF/TXT/   │    │  加载文档         │    │  切分文本        │
│   MD/HTML)   │    └──────────────────┘    └────────┬────────┘
└──────────────┘                                      │
                                                      ▼
┌──────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  AI 模型     │◀───│  Prompt + 上下文  │◀───│ VectorStore     │
│  生成回答     │    │  注入检索结果     │    │  相似度检索      │
└──────────────┘    └──────────────────┘    └─────────────────┘

8.2 添加 RAG 相关依赖

kotlin 复制代码
// build.gradle 中添加
dependencies {
    // Ollama Embedding(用于将文本转为向量)
    implementation("org.springframework.ai:spring-ai-ollama")
    
    // Chroma 向量数据库(轻量,适合本地)
    implementation("org.springframework.ai:spring-ai-starter-vectorstore-chroma")
    
    // 或使用 Redis(生产环境推荐)
    implementation("org.springframework.ai:spring-ai-starter-vectorstore-redis")
    
    // PDF 文档解析
    implementation("org.apache.pdfbox:pdfbox:3.0.0")
    
    // Word 文档解析
    implementation("org.apache.poi:poi-ooxml:5.2.5")
}

8.3 application.yml(Chroma 配置)

yaml 复制代码
spring:
  ai:
    ollama:
      base-url: http://localhost:11434
      embedding:
        options:
          model: nomic-embed-text

    # Chroma 向量数据库配置
    vectorstore:
      chroma:
        base-url: http://localhost:8000   # Chroma 默认端口

8.4 安装并启动 Chroma

bash 复制代码
# 使用 Docker 启动 Chroma(推荐)
docker run -d --name chroma \
  -p 8000:8000 \
  ghcr.io/chroma-core/chroma:latest

# 或者用 pip 安装
pip install chromadb
python -m chromadb.run --host localhost --port 8000

8.5 文档加载与切分

java 复制代码
@Service
public class DocumentService {

    private final VectorStore vectorStore;

    public DocumentService(VectorStore vectorStore) {
        this.vectorStore = vectorStore;
    }

    /**
     * 加载文本文件并存储到向量数据库
     */
    public void loadTextFile(String filePath) {
        // 读取文本文件
        Path path = Path.of(filePath);
        String content = Files.readString(path);

        // 创建 Document 对象
        Document document = new Document(content, 
            Map.of("source", filePath, "type", "txt"));

        // 切分文档(避免单段太长影响检索)
        TextSplitter splitter = new TextSplitter(500, 100); // 每段500字,重叠100字
        List<Document> chunks = splitter.split(List.of(document));

        // 存入向量数据库
        vectorStore.add(chunks);
    }

    /**
     * 加载 PDF 文件并存储
     */
    public void loadPdfFile(String filePath) {
        try (PDDocument pdf = PDDocument.load(new File(filePath))) {
            PDFTextStripper stripper = new PDFTextStripper();
            String text = stripper.getText(pdf);

            Document document = new Document(text, 
                Map.of("source", filePath, "type", "pdf"));
            
            TextSplitter splitter = new TextSplitter(500, 100);
            List<Document> chunks = splitter.split(List.of(document));
            
            vectorStore.add(chunks);
        } catch (IOException e) {
            throw new RuntimeException("PDF 加载失败: " + filePath, e);
        }
    }
}

8.6 检索与问答

java 复制代码
@Service
public class RagQAService {

    private final ChatModel chatModel;
    private final VectorStore vectorStore;

    public RagQAService(ChatModel chatModel, VectorStore vectorStore) {
        this.chatModel = chatModel;
        this.vectorStore = vectorStore;
    }

    /**
     * RAG 问答
     * 1. 将用户问题转为向量
     * 2. 在向量数据库中检索相似文档
     * 3. 将检索结果注入 Prompt
     * 4. 调用 AI 生成回答
     */
    public String answer(String question) {
        // Step 1: 检索相似文档
        List<Document> similarDocs = vectorStore.similaritySearch(
            SearchRequest.builder()
                .query(question)
                .topK(4)                        // 检索最相关的 4 段
                .similarityThreshold(0.7)       // 相似度阈值
                .build()
        );

        // Step 2: 拼接上下文
        StringBuilder context = new StringBuilder();
        for (Document doc : similarDocs) {
            context.append("- ").append(doc.getText()).append("\n\n");
        }

        // Step 3: 构建 RAG Prompt
        String prompt = """
            你是一个专业的问答助手。请根据以下参考材料回答用户的问题。
            
            参考材料:
            %s
            
            用户问题:%s
            
            请基于参考材料准确回答,如果材料中没有相关信息,请如实说明。
            """.formatted(context, question);

        // Step 4: 调用 AI
        return chatModel.call(prompt);
    }
}

8.7 RAG Controller

java 复制代码
@RestController
@RequestMapping("/api/rag")
public class RagController {

    private final DocumentService documentService;
    private final RagQAService ragQAService;

    public RagController(DocumentService documentService, RagQAService ragQAService) {
        this.documentService = documentService;
        this.ragQAService = ragQAService;
    }

    // 上传文档
    @PostMapping("/upload")
    public Map<String, Object> upload(@RequestParam("file") MultipartFile file) {
        try {
            // 保存到临时文件
            String tempPath = "/tmp/" + file.getOriginalFilename();
            file.transferTo(Path.of(tempPath));

            // 根据扩展名加载
            String filename = file.getOriginalFilename();
            if (filename != null && filename.endsWith(".pdf")) {
                documentService.loadPdfFile(tempPath);
            } else {
                documentService.loadTextFile(tempPath);
            }

            return Map.of("status", "success", "message", "文档已加载到知识库");
        } catch (IOException e) {
            return Map.of("status", "error", "message", e.getMessage());
        }
    }

    // 问答
    @PostMapping("/ask")
    public Map<String, String> ask(@RequestBody Map<String, String> request) {
        String question = request.get("question");
        String answer = ragQAService.answer(question);
        return Map.of("question", question, "answer", answer);
    }
}

九、AI 模型工具调用(Function Calling)

Spring AI 支持让 AI 模型主动调用本地 Java 方法,获取实时数据。

9.1 定义工具方法

java 复制代码
@Service
public class WeatherService {

    // 模拟天气数据(实际项目中调用真实天气 API)
    public record Weather(String city, String condition, int temperature) {}

    @Tool(description = "查询指定城市的当前天气")
    public Weather getWeather(@ToolParam("城市名称") String city) {
        // 实际项目中调用第三方天气 API
        return new Weather(city, "晴天", 25);
    }

    @Tool(description = "获取当前日期和时间")
    public String getCurrentTime() {
        return LocalDateTime.now().toString();
    }
}

9.2 配置 Function Calling

java 复制代码
@Configuration
public class AiConfig {

    @Bean
    public ChatModel chatModel(OllamaApi ollamaApi) {
        return new OllamaChatModel(ollamaApi, OllamaOptions.builder()
            .model("qwen2.5:7b")
            .build());
    }
}

9.3 调用工具

java 复制代码
@Service
public class ToolCallingService {

    private final ChatModel chatModel;
    private final ToolCallingChatModel toolChatModel;

    public ToolCallingService(ChatModel chatModel, ToolCallingChatModel toolChatModel) {
        this.chatModel = chatModel;
        this.toolChatModel = toolChatModel;
    }

    public String askWithTools(String question) {
        // 启用工具调用的聊天模型
        return toolChatModel.call(question);
    }
}

十、本地部署完整步骤汇总

快速启动清单

bash 复制代码
# 1️⃣ 创建 Spring Boot 项目(使用 Spring Initializr 或 IDE)
#    添加 spring-ai-starter-model-ollama 依赖

# 2️⃣ 安装 Ollama(无需 API Key)
winget install Ollama.Ollama

# 3️⃣ 下载 AI 模型
ollama pull qwen2.5:7b          # 对话模型
ollama pull nomic-embed-text     # 向量模型

# 4️⃣ 安装 Chroma 向量数据库
docker run -d --name chroma -p 8000:8000 \
  ghcr.io/chroma-core/chroma:latest

# 5️⃣ 配置 application.yml(参考本文第五节)

# 6️⃣ 启动 Spring Boot 应用
./gradlew bootRun

# 7️⃣ 测试对话 API
curl "http://localhost:8080/api/ai/chat?question=什么是Spring+AI"

# 8️⃣ 测试 RAG(上传文档后问答)
curl -X POST http://localhost:8080/api/rag/upload \
  -F "file=@./docs/guide.txt"

curl -X POST http://localhost:8080/api/rag/ask \
  -H "Content-Type: application/json" \
  -d '{"question":"文档里讲了什么?"}'

十一、常见问题 FAQ

Q1: Ollama 模型下载太慢怎么办?

bash 复制代码
# 使用国内镜像(如果有)
# 或者用 aria2 多线程下载
ollama pull qwen2.5:7b

Q2: 内存不足怎么办?

bash 复制代码
# 使用量化模型(体积更小,精度略有下降)
ollama pull llama3:8b-instruct-q4_0    # 4bit 量化,约 5GB
ollama run llama3:8b-instruct-q4_0

Q3: Chroma 启动失败?

bash 复制代码
# 检查端口是否被占用
netstat -ano | findstr 8000

# 强制重启
docker rm -f chroma
docker run -d -p 8000:8000 ghcr.io/chroma-core/chroma:latest

Q4: Spring AI 连接 Ollama 报错?

复制代码
# 确保 Ollama 服务正在运行
ollama serve

# 验证模型是否已安装
ollama list

Q5: 如何切换到 OpenAI?

只需修改两处:

  1. build.gradle 依赖改为 spring-ai-starter-model-openai
  2. application.yml 中配置 spring.ai.openai.api-key

十二、参考资料


📝 本文由 AI 辅助编写,内容基于 Spring AI 1.0.0 官方文档实践验证。

如果有问题或建议,欢迎在评论区交流!

相关推荐
庞轩px1 小时前
第六篇:Spring用了哪些设计模式?——从单例到代理,拆解框架中的经典设计
java·spring·设计模式·bean·代理模式·aop·单例
番石榴AI1 小时前
纯 CPU 推理!0.1B 超轻量级端到端OCR模型,使用 Java 进行文档解析
java·开发语言·ocr
likerhood1 小时前
ConcurrentHashMap详细讲解(java)
java·开发语言·性能优化
源码集结号2 小时前
基于 Spring Boot + JPA + MySQL的上门家政系统代码示例
java·前端·后端
程序员老邢3 小时前
【技术底稿 32】Nginx 经典大坑复盘:本机公网域名自环代理,导致接口返回首页 / 404 实战排障
java·运维·nginx·前后端分离·技术底稿·后端部署
该昵称用户已存在3 小时前
从成本中心到价值引擎:MyEMS 开源系统激活企业能源数据资产
java·后端·struts
隐退山林3 小时前
JavaEE进阶:SpringBoot配置文件
java·spring boot·java-ee
阿维的博客日记4 小时前
求解深分页问题,last pk适合什么情况
java·mysql·深分页
极客先躯5 小时前
高级java每日一道面试题-2025年12月09日-实战篇[Docker]-如何配置 Docker 的日志驱动?有哪些日志驱动可选?
java·docker·日志驱动的作用与配置层级·日志驱动全览与对比·日志驱动配置的要点·日志标签定制·容器与宿主机时间戳