【Spring AI】Spring生态AI应用开发框架

文章目录

Spring AI技术详细解析:Spring生态AI应用开发框架与生产级实践

前言

若对您有帮助的话,请点赞收藏加关注哦,您的关注是我持续创作的动力!有问题请私信或联系邮箱:funian.gm@gmail.com

随着大语言模型(LLM)、向量数据库、AI工具链的快速发展,企业级AI应用开发面临「多模型兼容、生态集成复杂、企业级特性缺失(安全/监控/可扩展性)」等痛点。Spring AI 作为 Spring 生态官方推出的AI应用开发框架,以「标准化API、Spring生态无缝集成、企业级特性、低代码门槛」为核心设计理念,让Java开发者无需关注底层AI模型细节,即可快速构建稳定、可扩展的AI应用(如智能问答、RAG检索增强、AI工具调用、多模态应用等)。

一、Spring AI 核心优势

Spring AI 并非从零构建,而是基于 Spring 生态的核心能力(依赖注入、自动配置、AOP、事务管理等),封装了AI领域的通用能力,核心优势如下:

核心优势 具体说明
Spring 生态无缝集成 原生支持 Spring Boot、Spring Cloud、Spring Security 等组件,无需额外适配
标准化 API 设计 统一 LLM 调用、向量库操作、工具调用接口,屏蔽不同AI服务(OpenAI/Gemini/本地化模型)的差异
多模型/多服务商支持 支持主流LLM(OpenAI、Gemini、Anthropic、Llama 3)、向量库(Milvus、Chroma、Elasticsearch)、AI工具(Function Calling、第三方API)
企业级特性内置 支持配置加密、权限控制、监控告警(Actuator)、分布式追踪(Sleuth)、事务管理等企业级需求
低代码开发体验 基于 Spring Boot 自动配置,注解驱动开发,无需手动管理AI客户端连接、生命周期
RAG 全流程支持 内置文档加载(PDF/Word/Markdown)、文本分割、嵌入生成、向量存储、检索增强全流程组件
多模态与流式响应 支持文本、图片等多模态输入,原生支持LLM流式响应(SSE),提升用户体验
可扩展性强 支持自定义 AI 客户端、向量库适配器、工具处理器,适配复杂业务场景

二、Spring AI 生产级目录结构

Spring AI 基于 Spring Boot 构建,目录结构遵循 Spring 生态规范,同时针对AI应用特点优化,以下是生产级项目的标准目录结构(适用于中大型AI应用,如企业知识库、智能客服、AI助手):

复制代码
spring-ai-project/
├── .env                # 环境变量(API密钥、数据库地址等敏感信息,不提交Git)
├── .env.example        # 环境变量示例(提交Git,指导部署)
├── .gitignore          # Git忽略文件
├── pom.xml             # Maven依赖配置
├── src/
│   ├── main/
│   │   ├── java/
│   │   │   └── com/
│   │   │       └── example/
│   │   │           ├── SpringAiApplication.java  # 应用入口
│   │   │           ├── config/                  # 配置类(AI客户端、向量库、安全配置)
│   │   │           │   ├── AiClientConfig.java  # AI客户端配置(OpenAI/Gemini)
│   │   │           │   ├── VectorStoreConfig.java # 向量库配置(Milvus/Chroma)
│   │   │           │   └── SecurityConfig.java  # 安全配置(API密钥校验、权限控制)
│   │   │           ├── controller/              # 接口层(REST API,对外提供服务)
│   │   │           │   ├── ChatController.java  # 对话接口(问答、流式响应)
│   │   │           │   ├── RagController.java   # RAG检索增强接口(知识库问答)
│   │   │           │   └── ToolController.java  # 工具调用接口(Function Calling)
│   │   │           ├── service/                 # 业务逻辑层(封装AI核心逻辑)
│   │   │           │   ├── ChatService.java     # 对话业务(LLM调用、Prompt优化)
│   │   │           │   ├── RagService.java      # RAG业务(检索+生成)
│   │   │           │   └── ToolService.java     # 工具业务(工具注册、调用逻辑)
│   │   │           ├── repository/              # 数据访问层(向量库、数据库操作)
│   │   │           │   ├── VectorRepository.java # 向量库操作封装
│   │   │           │   └── DocumentRepository.java # 文档元数据数据库操作
│   │   │           ├── model/                   # 数据模型(请求/响应DTO、实体类)
│   │   │           │   ├── req/                 # 请求模型
│   │   │           │   │   ├── ChatReq.java     # 对话请求
│   │   │           │   │   └── RagQueryReq.java # RAG查询请求
│   │   │           │   ├── resp/                # 响应模型
│   │   │           │   │   ├── ChatResp.java    # 对话响应
│   │   │           │   │   └── StreamChatResp.java # 流式对话响应
│   │   │           │   └── entity/              # 数据库实体(文档元数据、用户会话)
│   │   │           │       └── DocumentEntity.java # 文档元数据实体
│   │   │           ├── ai/                      # AI核心组件(Prompt模板、工具定义、自定义适配器)
│   │   │           │   ├── prompt/              # Prompt模板(Mustache格式)
│   │   │           │   │   ├── ChatPrompt.java  # 对话Prompt模板
│   │   │           │   │   └── RagPrompt.java   # RAG检索Prompt模板
│   │   │           │   ├── tool/                # 工具定义(Function Calling)
│   │   │           │   │   ├── WeatherTool.java # 天气查询工具
│   │   │           │   │   └── CalculatorTool.java # 计算工具
│   │   │           │   └── adapter/             # 自定义适配器(适配特殊AI模型/向量库)
│   │   │           │       └── CustomVectorStoreAdapter.java
│   │   │           └── util/                    # 工具类(文档处理、格式转换、异常处理)
│   │   │               ├── DocumentUtils.java   # 文档加载、分割工具
│   │   │               ├── PromptUtils.java     # Prompt格式化工具
│   │   │               └── ExceptionHandler.java # 全局异常处理
│   │   └── resources/
│   │       ├── application.yml                  # 通用配置文件
│   │       ├── application-dev.yml              # 开发环境配置
│   │       ├── application-prod.yml             # 生产环境配置
│   │       └── prompts/                         # Prompt模板文件(外部化管理)
│   │           ├── chat-template.mustache       # 对话Prompt模板文件
│   │           └── rag-template.mustache        # RAG Prompt模板文件
│   └── test/                                    # 单元测试与集成测试
│       └── java/
│           └── com/
│               └── example/
│                   ├── ChatServiceTest.java     # 对话服务测试
│                   └── RagServiceTest.java      # RAG服务测试
└── docs/                                        # 项目文档(架构图、API文档)
    └── api-docs.md                              # API接口文档

核心目录说明

目录/文件 核心作用 关键注意点
config/ AI客户端、向量库、安全等配置 基于Spring Boot自动配置,通过@Configuration声明
controller/ 对外提供REST API接口 区分对话、RAG、工具调用等场景,使用@RestController
service/ 封装AI核心业务逻辑 隔离控制器与AI组件,便于测试和维护
repository/ 数据访问层(向量库+数据库) 向量库操作通过Spring AI标准化API封装,屏蔽底层差异
ai/prompt/ Prompt模板管理 推荐使用Mustache外部化模板,避免硬编码
ai/tool/ 工具定义(Function Calling) 工具类需实现Function接口,声明参数和描述
resources/prompts/ 外部化Prompt模板文件 支持动态参数替换,便于多环境适配和修改
util/DocumentUtils 文档处理工具(加载、分割、嵌入) 复用Spring AI内置的DocumentReaderTextSplitter

目录设计原则

  1. 分层解耦:遵循「接口层→业务层→数据访问层→AI组件层」分层,职责清晰;
  2. 配置与代码分离:敏感信息(API密钥)通过环境变量注入,配置文件按环境拆分;
  3. 标准化与定制化平衡:优先使用Spring AI标准化API,特殊场景通过自定义适配器扩展;
  4. 可测试性:业务逻辑封装在Service层,便于单元测试(可Mock AI客户端);
  5. 外部化管理:Prompt模板、配置参数等外部化,避免硬编码导致的频繁部署。

三、Spring AI 核心技术解析

1. 快速入门:Spring Boot 集成 OpenAI 实现对话

Spring AI 提供自动配置 starters,无需复杂配置即可集成主流LLM,以下是最小化对话示例:

步骤1:添加依赖(pom.xml)
xml 复制代码
<!-- Spring Boot 父依赖 -->
<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>3.2.5</version>
    <relativePath/>
</parent>

<dependencies>
    <!-- Spring Web 依赖 -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
    <!-- Spring AI OpenAI 依赖(自动配置) -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>1.0.0.RELEASE</version>
    </dependency>
    <!-- Lombok 简化代码(可选) -->
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>
步骤2:配置 OpenAI 密钥(application-dev.yml)
yaml 复制代码
spring:
  ai:
    openai:
      api-key: ${OPENAI_API_KEY:sk-your-openai-key}  # 从环境变量读取,默认值兜底
      base-url: https://api.openai.com/v1  # 国内用户可配置代理地址
      chat:
        model: gpt-3.5-turbo  # 默认模型
        temperature: 0.7      # 随机性:0-1,值越高越随机
步骤3:实现对话 Service(ChatService.java)
java 复制代码
package com.example.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.ChatResponse;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.stereotype.Service;

import java.util.HashMap;
import java.util.Map;

@Service
@RequiredArgsConstructor
public class ChatService {

    // Spring AI 自动注入 OpenAI ChatClient
    private final ChatClient chatClient;

    /**
     * 简单对话(直接调用LLM)
     */
    public String simpleChat(String userMessage) {
        // 构建Prompt并调用LLM
        ChatResponse response = chatClient.generate(userMessage);
        // 返回LLM生成的内容
        return response.getResult().getOutput().getContent();
    }

    /**
     * 基于Prompt模板的对话(优化Prompt结构)
     */
    public String templateChat(String userMessage, String systemPrompt) {
        // Prompt模板(使用Mustache语法)
        String template = """
                {{systemPrompt}}
                用户问题:{{userMessage}}
                """;
        // 模板参数
        Map<String, Object> params = new HashMap<>();
        params.put("systemPrompt", systemPrompt);
        params.put("userMessage", userMessage);
        // 构建Prompt
        Prompt prompt = new PromptTemplate(template, params).create();
        // 调用LLM
        return chatClient.generate(prompt).getResult().getOutput().getContent();
    }
}
步骤4:实现对话接口(ChatController.java)
java 复制代码
package com.example.controller;

import com.example.model.req.ChatReq;
import com.example.model.resp.ChatResp;
import com.example.service.ChatService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/chat")
@RequiredArgsConstructor
public class ChatController {

    private final ChatService chatService;

    /**
     * 简单对话接口
     */
    @PostMapping("/simple")
    public ChatResp simpleChat(@RequestBody ChatReq req) {
        String response = chatService.simpleChat(req.getUserMessage());
        return new ChatResp(response);
    }

    /**
     * 模板对话接口
     */
    @PostMapping("/template")
    public ChatResp templateChat(@RequestBody ChatReq req) {
        // 系统提示词(定义AI角色)
        String systemPrompt = "你是一个专业的技术顾问,回答简洁明了,基于技术事实";
        String response = chatService.templateChat(req.getUserMessage(), systemPrompt);
        return new ChatResp(response);
    }
}
步骤5:请求模型(ChatReq.java)与响应模型(ChatResp.java)
java 复制代码
// ChatReq.java
package com.example.model.req;

import lombok.Data;
import javax.validation.constraints.NotBlank;

@Data
public class ChatReq {
    @NotBlank(message = "用户消息不能为空")
    private String userMessage;
}

// ChatResp.java
package com.example.model.resp;

import lombok.AllArgsConstructor;
import lombok.Data;

@Data
@AllArgsConstructor
public class ChatResp {
    private String content;
}
运行与测试
  1. 启动应用(需配置OPENAI_API_KEY环境变量);
  2. 发送POST请求到http://localhost:8080/api/chat/template
json 复制代码
{
  "userMessage": "什么是Spring AI?"
}
  1. 响应结果:
json 复制代码
{
  "content": "Spring AI是Spring生态下的AI应用开发框架,提供标准化API集成LLM、向量库、AI工具,简化Java开发者构建企业级AI应用的流程..."
}

2. 核心特性:流式响应(SSE)

AI对话场景中,流式响应能提升用户体验(无需等待完整结果),Spring AI 原生支持 SSE 流式输出:

代码示例:流式对话接口
java 复制代码
// ChatService.java 新增流式方法
public Flux<String> streamChat(String userMessage, String systemPrompt) {
    String template = """
            {{systemPrompt}}
            用户问题:{{userMessage}}
            """;
    Map<String, Object> params = new HashMap<>();
    params.put("systemPrompt", systemPrompt);
    params.put("userMessage", userMessage);
    Prompt prompt = new PromptTemplate(template, params).create();
    
    // 流式生成(返回Flux<String>)
    return chatClient.stream(prompt)
            .map(chatResponse -> chatResponse.getResult().getOutput().getContent());
}

// ChatController.java 新增流式接口
@PostMapping("/stream")
public Flux<ServerSentEvent<String>> streamChat(@RequestBody ChatReq req) {
    String systemPrompt = "你是一个专业的技术顾问,回答简洁明了";
    // 转换为SSE格式(前端可通过EventSource接收)
    return chatService.streamChat(req.getUserMessage(), systemPrompt)
            .map(content -> ServerSentEvent.<String>builder()
                    .data(content)
                    .build());
}
前端调用示例(JavaScript)
javascript 复制代码
// 前端通过EventSource接收流式响应
const eventSource = new EventSource('http://localhost:8080/api/chat/stream');
eventSource.onmessage = (event) => {
    console.log('流式响应:', event.data);
    // 渲染到页面
    document.getElementById('chat-content').innerText += event.data;
};
eventSource.onerror = (error) => {
    console.error('流式响应错误:', error);
    eventSource.close();
};

3. 核心特性:RAG 检索增强生成(企业知识库问答)

RAG(Retrieval-Augmented Generation)是解决LLM知识时效性、准确性的核心方案,Spring AI 内置RAG全流程组件:

步骤1:添加向量库依赖(以Milvus为例)
xml 复制代码
<!-- Spring AI Milvus 向量库依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-milvus-store</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>
<!-- Milvus Java SDK -->
<dependency>
    <groupId>io.milvus</groupId>
    <artifactId>milvus-sdk-java</artifactId>
    <version>2.4.3</version>
</dependency>
步骤2:配置向量库(application-dev.yml)
yaml 复制代码
spring:
  ai:
    vectorstore:
      milvus:
        host: localhost  # Milvus服务地址
        port: 19530      # Milvus端口
        database: default  # 数据库名
        collection-name: knowledge_base  # 向量集合名(类似数据库表)
        dimension: 1536  # 嵌入向量维度(OpenAI Embedding为1536)
    embedding:
      openai:
        api-key: ${OPENAI_API_KEY}
        model: text-embedding-3-small  # Embedding模型
步骤3:RAG核心流程实现(RagService.java)
java 复制代码
package com.example.service;

import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.document.Document;
import org.springframework.ai.embedding.EmbeddingClient;
import org.springframework.ai.search.SearchResult;
import org.springframework.ai.vectorstore.VectorStore;
import org.springframework.stereotype.Service;

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

@Service
@RequiredArgsConstructor
public class RagService {

    private final VectorStore vectorStore;  // 向量库(Milvus)
    private final EmbeddingClient embeddingClient;  // Embedding客户端(OpenAI)
    private final ChatClient chatClient;  // LLM客户端

    /**
     * 1. 文档入库(加载→分割→嵌入→存储)
     */
    public void addDocumentToVectorStore(String documentContent, String documentId) {
        // 1.1 加载文档(Spring AI支持PDF/Word/Markdown等,此处简化为纯文本)
        Document document = new Document(documentContent);
        document.getMetadata().put("documentId", documentId);

        // 1.2 文本分割(避免单文档过长,提升检索精度)
        List<Document> splitDocuments = DocumentUtils.splitDocument(document);

        // 1.3 嵌入生成+向量库存储(Spring AI自动完成)
        vectorStore.add(splitDocuments);
    }

    /**
     * 2. RAG检索增强生成(检索→构建Prompt→生成回答)
     */
    public String ragQuery(String userQuery) {
        // 2.1 检索相关文档(基于用户查询生成嵌入,向量库相似度匹配)
        List<SearchResult> searchResults = vectorStore.similaritySearch(userQuery, 3);  // 取Top3相关文档

        // 2.2 构建检索结果上下文
        String context = searchResults.stream()
                .map(result -> result.getDocument().getContent())
                .collect(Collectors.joining("\n\n"));

        // 2.3 构建RAG Prompt(结合上下文和用户查询)
        String ragTemplate = """
                基于以下上下文信息回答用户问题,仅使用上下文内容,不要编造信息:
                上下文:{{context}}
                用户问题:{{userQuery}}
                回答要求:简洁明了,直击要点
                """;
        Prompt prompt = new PromptTemplate(ragTemplate, 
                "context", context, 
                "userQuery", userQuery)
                .create();

        // 2.4 调用LLM生成回答
        return chatClient.generate(prompt).getResult().getOutput().getContent();
    }
}
步骤4:文档分割工具(DocumentUtils.java)
java 复制代码
package com.example.util;

import org.springframework.ai.document.Document;
import org.springframework.ai.text.splitter.RecursiveCharacterTextSplitter;
import org.springframework.ai.text.splitter.TextSplitter;
import java.util.List;

public class DocumentUtils {

    /**
     * 文本分割(递归字符分割,避免破坏语义)
     */
    public static List<Document> splitDocument(Document document) {
        // 配置分割参数:chunkSize=500字符,chunkOverlap=50字符(重叠部分提升连贯性)
        TextSplitter textSplitter = new RecursiveCharacterTextSplitter(500, 50);
        return textSplitter.split(document);
    }
}
步骤5:RAG接口(RagController.java)
java 复制代码
package com.example.controller;

import com.example.model.req.RagQueryReq;
import com.example.model.req.DocumentUploadReq;
import com.example.model.resp.ChatResp;
import com.example.service.RagService;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/rag")
@RequiredArgsConstructor
public class RagController {

    private final RagService ragService;

    /**
     * 文档入库接口
     */
    @PostMapping("/document/add")
    public String addDocument(@RequestBody DocumentUploadReq req) {
        ragService.addDocumentToVectorStore(req.getContent(), req.getDocumentId());
        return "文档入库成功";
    }

    /**
     * RAG问答接口
     */
    @PostMapping("/query")
    public ChatResp ragQuery(@RequestBody RagQueryReq req) {
        String response = ragService.ragQuery(req.getUserQuery());
        return new ChatResp(response);
    }
}

4. 核心特性:工具调用(Function Calling)

Spring AI 支持LLM自动触发工具调用(如查询数据库、调用第三方API),核心是「工具注册→LLM决策→工具执行→结果返回」:

步骤1:定义工具(WeatherTool.java)
java 复制代码
package com.example.ai.tool;

import lombok.Data;
import org.springframework.ai.chat.tools.Function;
import org.springframework.stereotype.Component;

import java.util.Random;

/**
 * 天气查询工具(模拟调用天气API)
 */
@Component
public class WeatherTool {

    // 标记为工具函数(Spring AI自动注册)
    @Function(name = "queryWeather", description = "查询指定城市的天气,参数为城市名称")
    public WeatherResult queryWeather(@Function.Parameter(description = "城市名称(如北京、上海)") String city) {
        // 模拟调用天气API,返回随机天气数据
        String[] weathers = {"晴", "阴", "雨", "雪", "多云"};
        String weather = weathers[new Random().nextInt(weathers.length)];
        int temperature = new Random().nextInt(30) + 10;  // 10-40℃

        WeatherResult result = new WeatherResult();
        result.setCity(city);
        result.setWeather(weather);
        result.setTemperature(temperature + "℃");
        return result;
    }

    // 工具返回结果模型
    @Data
    public static class WeatherResult {
        private String city;
        private String weather;
        private String temperature;
    }
}
步骤2:工具调用Service(ToolService.java)
java 复制代码
package com.example.service;

import com.example.ai.tool.WeatherTool;
import lombok.RequiredArgsConstructor;
import org.springframework.ai.chat.ChatClient;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.tools.ToolCall;
import org.springframework.ai.chat.tools.ToolRegistry;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@RequiredArgsConstructor
public class ToolService {

    private final ChatClient chatClient;
    private final ToolRegistry toolRegistry;  // Spring AI工具注册中心
    private final WeatherTool weatherTool;    // 天气工具

    /**
     * 初始化工具注册(将自定义工具注册到工具中心)
     */
    public void initToolRegistry() {
        toolRegistry.register(weatherTool);
    }

    /**
     * 工具调用流程
     */
    public String toolCallChat(String userMessage) {
        // 1. 构建Prompt(告知LLM可使用的工具)
        String toolPrompt = """
                你可以使用以下工具解决用户问题:
                {{tools}}
                若用户问题需要工具支持,返回工具调用指令;否则直接回答。
                """;
        // 获取已注册的工具描述
        String toolsDesc = toolRegistry.getTools().stream()
                .map(tool -> tool.getName() + ":" + tool.getDescription())
                .collect(Collectors.joining("\n"));

        Prompt prompt = new PromptTemplate(toolPrompt, "tools", toolsDesc)
                .addMessage(userMessage)
                .create();

        // 2. 调用LLM,获取工具调用指令(若有)
        var response = chatClient.generate(prompt);
        List<ToolCall> toolCalls = response.getResult().getToolCalls();

        // 3. 执行工具调用(若LLM返回了工具调用指令)
        if (!toolCalls.isEmpty()) {
            ToolCall toolCall = toolCalls.get(0);
            // 执行工具并获取结果
            Object toolResult = toolRegistry.execute(toolCall);
            // 4. 将工具结果反馈给LLM,生成最终回答
            String finalPrompt = String.format(
                    "用户问题:%s\n工具调用结果:%s\n请基于工具结果生成自然语言回答",
                    userMessage, toolResult.toString()
            );
            return chatClient.generate(finalPrompt).getResult().getOutput().getContent();
        }

        // 无需工具,直接返回LLM回答
        return response.getResult().getOutput().getContent();
    }
}
步骤3:工具调用接口(ToolController.java)
java 复制代码
package com.example.controller;

import com.example.model.req.ChatReq;
import com.example.model.resp.ChatResp;
import com.example.service.ToolService;
import lombok.RequiredArgsConstructor;
import org.springframework.boot.CommandLineRunner;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import javax.annotation.PostConstruct;

@RestController
@RequestMapping("/api/tool")
@RequiredArgsConstructor
public class ToolController implements CommandLineRunner {

    private final ToolService toolService;

    // 应用启动时初始化工具注册
    @Override
    public void run(String... args) throws Exception {
        toolService.initToolRegistry();
    }

    /**
     * 工具调用对话接口
     */
    @PostMapping("/chat")
    public ChatResp toolChat(@RequestBody ChatReq req) {
        String response = toolService.toolCallChat(req.getUserMessage());
        return new ChatResp(response);
    }
}
测试工具调用

发送POST请求到http://localhost:8080/api/tool/chat

json 复制代码
{
  "userMessage": "查询北京的天气"
}

响应结果:

json 复制代码
{
  "content": "北京当前天气为晴,气温25℃"
}

四、生产级实践:关键功能集成

1. 多模型切换(OpenAI → Gemini)

Spring AI 标准化API支持多模型无缝切换,仅需修改依赖和配置:

步骤1:替换依赖(pom.xml)
xml 复制代码
<!-- 移除OpenAI依赖,添加Gemini依赖 -->
<dependency>
    <groupId>org.springframework.ai</groupId>
    <artifactId>spring-ai-google-gemini-spring-boot-starter</artifactId>
    <version>1.0.0.RELEASE</version>
</dependency>
步骤2:修改配置(application-dev.yml)
yaml 复制代码
spring:
  ai:
    google:
      gemini:
        api-key: ${GEMINI_API_KEY:your-gemini-key}
        chat:
          model: gemini-pro
          temperature: 0.7
    embedding:
      google:
        gemini:
          api-key: ${GEMINI_API_KEY}
          model: text-embedding-004  # Gemini Embedding模型
核心亮点
  • 业务代码(Service/Controller)无需修改,完全遵循「面向接口编程」;
  • 支持动态切换模型(通过配置中心修改模型参数,无需重启应用)。

2. 安全配置(API密钥校验+权限控制)

企业级应用需保障AI接口安全,结合Spring Security实现:

步骤1:添加依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-security</artifactId>
</dependency>
步骤2:安全配置(SecurityConfig.java)
java 复制代码
package com.example.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;

@Configuration
@EnableWebSecurity
public class SecurityConfig {

    // 自定义API密钥过滤器(校验请求头中的X-API-KEY)
    @Bean
    public ApiKeyFilter apiKeyFilter() {
        return new ApiKeyFilter();
    }

    @Bean
    public SecurityFilterChain securityFilterChain(HttpSecurity http) throws Exception {
        http
                .csrf(csrf -> csrf.disable())  // 前后端分离,禁用CSRF
                .sessionManagement(session -> session.sessionCreationPolicy(SessionCreationPolicy.STATELESS))  // 无状态
                .authorizeHttpRequests(auth -> auth
                        .antMatchers("/api/public/**").permitAll()  // 公开接口无需校验
                        .anyRequest().authenticated()  // 其他接口需认证
                )
                .addFilterBefore(apiKeyFilter(), UsernamePasswordAuthenticationFilter.class);  // 添加API密钥过滤器

        return http.build();
    }
}

// API密钥过滤器(ApiKeyFilter.java)
package com.example.config;

import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.web.filter.OncePerRequestFilter;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class ApiKeyFilter extends OncePerRequestFilter {

    private static final String API_KEY_HEADER = "X-API-KEY";
    private static final String VALID_API_KEY = System.getenv("APP_API_KEY");  // 从环境变量读取有效API密钥

    @Override
    protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain)
            throws ServletException, IOException {
        String apiKey = request.getHeader(API_KEY_HEADER);

        // 校验API密钥
        if (VALID_API_KEY == null || !VALID_API_KEY.equals(apiKey)) {
            response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
            response.getWriter().write("无效的API密钥");
            return;
        }

        // 密钥有效,设置认证信息
        UsernamePasswordAuthenticationToken authToken = new UsernamePasswordAuthenticationToken(
                "api-user", null, null);
        SecurityContextHolder.getContext().setAuthentication(authToken);

        filterChain.doFilter(request, response);
    }
}

3. 监控与可观测性(Spring Boot Actuator + Prometheus)

步骤1:添加依赖
xml 复制代码
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
    <groupId>io.micrometer</groupId>
    <artifactId>micrometer-registry-prometheus</artifactId>
</dependency>
步骤2:配置监控(application-dev.yml)
yaml 复制代码
management:
  endpoints:
    web:
      exposure:
        include: health,info,prometheus  # 暴露的监控端点
  metrics:
    tags:
      application: spring-ai-app  # 应用标签(Prometheus查询用)
  endpoint:
    health:
      show-details: always  # 显示健康详情
步骤3:自定义AI监控指标(AiMetrics.java)
java 复制代码
package com.example.util;

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class AiMetrics {

    private final MeterRegistry meterRegistry;

    // LLM调用计数器
    private final Counter llmCallCounter = meterRegistry.counter("spring.ai.llm.call.count");
    // LLM调用失败计数器
    private final Counter llmErrorCounter = meterRegistry.counter("spring.ai.llm.error.count");
    // 向量库检索计数器
    private final Counter vectorSearchCounter = meterRegistry.counter("spring.ai.vector.search.count");

    // 记录LLM调用成功
    public void recordLlmCallSuccess() {
        llmCallCounter.increment();
    }

    // 记录LLM调用失败
    public void recordLlmCallError() {
        llmErrorCounter.increment();
    }

    // 记录向量库检索
    public void recordVectorSearch() {
        vectorSearchCounter.increment();
    }
}
步骤4:在Service中使用监控指标
java 复制代码
// ChatService.java 改造
public String simpleChat(String userMessage) {
    try {
        ChatResponse response = chatClient.generate(userMessage);
        aiMetrics.recordLlmCallSuccess();  // 记录成功
        return response.getResult().getOutput().getContent();
    } catch (Exception e) {
        aiMetrics.recordLlmCallError();   // 记录失败
        throw new RuntimeException("LLM调用失败", e);
    }
}
监控访问
  • 健康检查:http://localhost:8080/actuator/health
  • Prometheus指标:http://localhost:8080/actuator/prometheus(可导入Grafana可视化)

4. 部署方案(Docker + K8s)

步骤1:编写Dockerfile
dockerfile 复制代码
# 构建阶段
FROM maven:3.8.8-openjdk-17 AS builder
WORKDIR /app
# 复制依赖文件
COPY pom.xml .
# 下载依赖
RUN mvn dependency:go-offline
# 复制源代码
COPY src ./src
# 编译打包
RUN mvn package -DskipTests

# 运行阶段
FROM openjdk:17-jdk-slim
WORKDIR /app
# 复制编译产物
COPY --from=builder /app/target/*.jar app.jar
# 暴露端口
EXPOSE 8080
# 启动命令(通过环境变量注入配置)
ENTRYPOINT ["java", "-jar", "app.jar", "--spring.profiles.active=prod"]
步骤2:K8s部署文件(deploy.yaml)
yaml 复制代码
apiVersion: apps/v1
kind: Deployment
metadata:
  name: spring-ai-app
  namespace: ai-apps
spec:
  replicas: 3
  selector:
    matchLabels:
      app: spring-ai-app
  template:
    metadata:
      labels:
        app: spring-ai-app
    spec:
      containers:
      - name: spring-ai-app
        image: spring-ai-app:latest
        ports:
        - containerPort: 8080
        env:
        - name: SPRING_PROFILES_ACTIVE
          value: "prod"
        - name: OPENAI_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-secrets
              key: openai-api-key
        - name: APP_API_KEY
          valueFrom:
            secretKeyRef:
              name: ai-secrets
              key: app-api-key
        resources:
          limits:
            cpu: "1"
            memory: "2Gi"
          requests:
            cpu: "0.5"
            memory: "1Gi"
        livenessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 30
          periodSeconds: 10
        readinessProbe:
          httpGet:
            path: /actuator/health
            port: 8080
          initialDelaySeconds: 5
          periodSeconds: 5

---
apiVersion: v1
kind: Service
metadata:
  name: spring-ai-service
  namespace: ai-apps
spec:
  selector:
    app: spring-ai-app
  ports:
  - port: 80
    targetPort: 8080
  type: ClusterIP

五、框架对比:Spring AI vs LangChain Java vs 原生LLM SDK

框架/方案 生态集成 标准化API 企业级特性 易用性 适用场景
Spring AI 极强(Spring生态无缝集成) 支持 完善(安全/监控/事务) 企业级AI应用、Java后端集成AI、微服务AI组件
LangChain Java 一般(需手动集成Spring) 支持 薄弱(缺乏企业级特性) Java轻量AI应用、快速原型开发
原生LLM SDK(OpenAI/Gemini) 无(需手动封装) 无(各厂商API差异大) 简单AI需求、无需框架依赖的场景

六、总结

Spring AI 作为 Spring 生态下的AI开发框架,以「标准化API、生态无缝集成、企业级特性」为核心竞争力,完美解决了Java开发者构建AI应用的痛点(多模型兼容、生态集成复杂、缺乏企业级保障)。其关键亮点总结如下:

  1. 目录结构:遵循Spring Boot规范,分层清晰、职责明确,支持中大型企业级AI应用扩展;
  2. 核心技术:LLM调用、流式响应、RAG检索增强、工具调用等核心能力开箱即用,API标准化,多模型无缝切换;
  3. 生产实践:原生支持安全校验、监控告警、容器化部署、多环境配置,满足企业级应用的稳定性和安全性要求;
  4. 易用性:自动配置、注解驱动、Prompt模板化,降低Java开发者的AI技术门槛;
  5. 适用场景:尤其适合企业知识库、智能客服、AI助手、微服务AI组件、Java后端AI集成等场景。

学习 Spring AI 的关键是「熟悉Spring生态+掌握AI核心概念」,建议从快速入门示例(集成OpenAI实现对话)入手,逐步掌握RAG、工具调用等核心特性,再结合Spring Security、Actuator等组件实现企业级部署。

推荐优先参考 Spring AI 官方文档 ,其文档详尽、示例丰富,是快速上手的核心资源。

相关推荐
零售ERP菜鸟1 分钟前
IT价值证明:从“成本中心”到“增长引擎”的确定性度量
大数据·人工智能·职场和发展·创业创新·学习方法·业界资讯
童话名剑1 小时前
目标检测(吴恩达深度学习笔记)
人工智能·目标检测·滑动窗口·目标定位·yolo算法·特征点检测
木卫四科技1 小时前
【木卫四 CES 2026】观察:融合智能体与联邦数据湖的安全数据运营成为趋势
人工智能·安全·汽车
珠海西格电力6 小时前
零碳园区有哪些政策支持?
大数据·数据库·人工智能·物联网·能源
启途AI7 小时前
2026免费好用的AIPPT工具榜:智能演示文稿制作新纪元
人工智能·powerpoint·ppt
TH_17 小时前
35、AI自动化技术与职业变革探讨
运维·人工智能·自动化
楚来客7 小时前
AI基础概念之八:Transformer算法通俗解析
人工智能·算法·transformer
风送雨7 小时前
FastMCP 2.0 服务端开发教学文档(下)
服务器·前端·网络·人工智能·python·ai
效率客栈老秦7 小时前
Python Trae提示词开发实战(8):数据采集与清洗一体化方案让效率提升10倍
人工智能·python·ai·提示词·trae
小和尚同志7 小时前
虽然 V0 很强大,但是ScreenshotToCode 依旧有市场
人工智能·aigc