Spring AI 初识

本文将详细介绍基于Spring Boot 3.4.2和Spring AI 1.0.0-M5构建的现代化AI应用项目。该项目展示了如何将AI能力无缝集成到Spring Boot应用中,提供了多种AI交互模式,包括简单对话、结构化数据响应和流式响应等。
官方文档链接:https://docs.spring.io/spring-ai/reference/api/chatmodel.html

技术栈分析

核心技术栈

  • Java 17:采用最新的LTS版本,提供更好的性能和安全性
  • Spring Boot 3.4.2:最新的Spring Boot版本,支持现代化开发模式
  • Spring AI 1.0.0-M5:Spring官方AI框架,提供统一的AI服务抽象

依赖版本详情

<repositories>
        <repository>
            <id>spring-milestones</id>
            <name>Spring Milestones</name>
            <url>https://repo.spring.io/milestone</url>
            <snapshots>
                <enabled>false</enabled>
            </snapshots>
        </repository>
        <repository>
            <id>spring-snapshots</id>
            <name>Spring Snapshots</name>
            <url>https://repo.spring.io/snapshot</url>
            <releases>
                <enabled>false</enabled>
            </releases>
        </repository>
    </repositories>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.0-M5</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <!-- Spring AI OpenAI Starter -->
        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        </dependency>
    </dependencies>

项目架构设计

分层架构

src/main/java/com/example/springbootv3_0demo/
├── controller/          # 控制器层
│   └── AiController.java
├── service/            # 服务层
│   └── AiService.java
├── advice/             # 异常处理
│   └── GlobalExceptionAdvice.java
└── resources/          # 配置文件
    └── application.properties

核心组件分析

1. AiService - AI服务核心

@Service
public class AiService {
    private final ChatModel chatModel;
    
    @Autowired
    public AiService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }
}

主要功能:

  • 简单对话生成
  • 系统提示模板对话
  • 结构化数据响应
  • 流式响应处理

2. AiController - REST API接口

@RestController
@RequestMapping("/api/ai")
public class AiController {
    
    @Autowired
    private AiService aiService;
    
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return aiService.generate(message);
    }
}

API接口:

  • /api/ai/chat - 简单对话
  • /api/ai/chat-with-prompt - 系统提示对话
  • /api/ai/chat-rest-response - 结构化数据响应
  • /api/ai/chatActorsFilms - 电影数据查询
  • /api/ai/chat-stream - 流式响应

核心功能实现

1. 简单对话功能

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

特点:

  • 直接调用ChatModel的call方法
  • 适用于简单的问答场景
  • 返回纯文本响应

2. 系统提示模板对话

public String generateWithSystemPrompt(String userMessage) {
    SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("""
            你是一个资深{domain}专家,回答需满足以下要求:
            1. 语言风格:{tone}
            2. 回答长度:{length}
            3. 用户问题:{userMessage}
            """);
    
    Prompt prompt = new Prompt(
            systemPromptTemplate.createMessage(Map.of(
                    "domain", "科技",
                    "tone", "幽默",
                    "length", "不超过100字",
                    "userMessage", userMessage
            ))
    );
    
    return chatModel.call(prompt).getResult().getOutput().getText();
}

特点:

  • 使用SystemPromptTemplate构建系统提示
  • 支持参数化模板
  • 可控制AI回答的风格和长度
  • 专业化场景定制

3. 结构化数据响应

public record RestResponse(String title, String poetry, String genre, String theme) {
}

public RestResponse restResponse(String genre, String theme) {
    BeanOutputConverter<RestResponse> converter = new BeanOutputConverter<>(RestResponse.class);
    
    String promptString = """
            Write me {genre} poetry about {theme}
            {format}
            """;
    
    PromptTemplate promptTemplate = new PromptTemplate(promptString);
    promptTemplate.add("genre", genre);
    promptTemplate.add("theme", theme);
    promptTemplate.add("format", converter.getFormat());
    
    Generation result = chatModel.call(promptTemplate.create()).getResult();
    return converter.convert(result.getOutput().getText());
}


public record ActorsFilms(String country,Integer year, List<Films> movies) {
}


public ActorsFilms restResponseSimple() {
    //    简洁的方式实现,转换成定义的数据结构
//        return ChatClient.create(chatModel).prompt()
//                .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
//                        .param("actor", "Tom Hanks"))
//                .call()
//                .entity(ActorsFilms.class);
    String promptString = """
             给出{country}{year}中最出色的五部电影及其导演。
            """;
    Map<String, Object> map = Map.of("country", "美国", "year", "2023");

    return ChatClient.create(chatModel).prompt()
            // 打印出执行过程日志
            .advisors(new SimpleLoggerAdvisor())
            .user(u -> u.text(promptString)
                    .params(map))
            .call()
            .entity(ActorsFilms.class);
}

特点:

  • 使用JDK 17 Record定义数据结构
  • 通过BeanOutputConverter实现类型转换,可实现RESTful API的响应格式
  • 支持复杂对象映射
  • 自动JSON序列化/反序列化

4. 流式响应处理

public Flux<String> streamResponse() {
    Flux<String> output = ChatClient.create(chatModel).prompt()
            .user("讲一个笑话")
            .stream()
            .content();
    return output;
}

特点:

  • 使用Reactor的Flux实现流式响应
  • 支持实时数据流处理
  • 提升用户体验
  • 减少服务器压力

配置详解

application.properties

# 服务器配置
server.port=9001

# 应用名称
spring.application.name=springboot-ai

# Spring AI调试日志
logging.level.org.springframework.ai=DEBUG
logging.level.org.springframework.ai.chat.client.advisor=DEBUG

# 硅基流动api配置 
spring.ai.openai.base-url=https://api.siliconflow.cn
spring.ai.openai.api-key=sk-elyllxj*******************iipkxmgkpelm
# 硅基流动api模型配置
spring.ai.openai.chat.options.model=moonshotai/Kimi-K2-Instruct
#spring.ai.openai.chat.options.responseFormat.type=json_object

配置说明:

  • base-url:使用SiliconFlow作为AI服务提供商
  • model:使用moonshotai/Kimi-K2-Instruct模型
  • responseFormat:支持JSON格式响应(当前已注释)
  • 日志级别:便于调试和监控

专业注意事项

1. 版本兼容性

  • Spring Boot 3.4.2 要求 Java 17+
  • Spring AI 1.0.0-M5 是里程碑版本,可能存在API变更
  • 建议在正式环境使用稳定版本

2. 性能优化

异步处理

@GetMapping("/chat-stream")
public Flux<String> chatStreamResponse() {
    return aiService.streamResponse();
}

优化策略:

  • 使用流式响应减少内存占用
  • 异步处理提升并发能力
  • 合理设置超时时间
  • 实施请求限流

3. 错误处理

// 全局异常处理
@RestControllerAdvice
public class GlobalExceptionAdvice {
    public static final String OPEN_AI_CLIENT_RAISED_EXCEPTION = "Open AI client raised exception";
    
    @ExceptionHandler({OpenAiApiClientErrorException.class, NonTransientAiException.class})
    ProblemDetail handleOpenAiHttpException(Exception ex) {
        HttpStatus status = Optional
                .ofNullable(HttpStatus.resolve(500))
                .orElse(HttpStatus.BAD_REQUEST);
        ProblemDetail problemDetail = ProblemDetail.forStatusAndDetail(status, ex.getMessage());
        problemDetail.setTitle(OPEN_AI_CLIENT_RAISED_EXCEPTION);
        return problemDetail;
    }
}

错误处理要点:

  • 处理AI服务不可用情况
  • 网络超时和连接异常
  • API配额限制
  • 数据格式错误

5. 监控和日志

# 详细的Spring AI日志
logging.level.org.springframework.ai=DEBUG
logging.level.org.springframework.ai.chat.client.advisor=DEBUG

完整的代码:

package com.example.springbootv3_0demo.service;


import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor;
import org.springframework.ai.chat.model.Generation;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.ai.chat.prompt.PromptTemplate;
import org.springframework.ai.chat.prompt.SystemPromptTemplate;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.converter.BeanOutputConverter;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;

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

/**
 * @author admin
 */
@Service
public class AiService {

    private final ChatModel chatModel;

    @Autowired
    public AiService(ChatModel chatModel) {
        this.chatModel = chatModel;
    }

    // 简单的直接调用
    public String generate(String message) {
        return chatModel.call(message);
    }

    // 使用系统提示模板
    public String generateWithSystemPrompt(String userMessage) {

        SystemPromptTemplate systemPromptTemplate = new SystemPromptTemplate("""
                你是一个资深{domain}专家,回答需满足以下要求:
                1. 语言风格:{tone}
                2. 回答长度:{length}
                3. 用户问题:{userMessage}
                """);

        Prompt prompt = new Prompt(
                systemPromptTemplate.createMessage(Map.of(
                        "domain", "科技",
                        "tone", "幽默",
                        "length", "不超过100字",
                        "userMessage", userMessage
                ))
        );

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

    public record RestResponse(String title, String poetry, String genre, String theme) {
    }


    public RestResponse restResponse(String genre, String theme) {
        String systemPrompt = """
                Write me {genre} poetry about {theme}
                """;
//        PromptTemplate promptTemplate = new PromptTemplate();

//        BeanOutputParser<RestResponse> poetryDtoBeanOutputParser = new BeanOutputParser<>(RestResponse.class);
        BeanOutputConverter<RestResponse> converter = new BeanOutputConverter<>(RestResponse.class);

        String promptString = """
                Write me {genre} poetry about {theme}
                {format}
                """;

        PromptTemplate promptTemplate = new PromptTemplate(promptString);
        promptTemplate.add("genre", genre);
        promptTemplate.add("theme", theme);
        promptTemplate.add("format", converter.getFormat());
        Generation result = chatModel.call(promptTemplate.create()).getResult();
        return converter.convert(result.getOutput().getText());
    }

    public record Films(String movie, String director) {

    }

    public record ActorsFilms(String country,Integer year, List<Films> movies) {
    }


    public ActorsFilms restResponseSimple() {
//        return ChatClient.create(chatModel).prompt()
//                .user(u -> u.text("Generate the filmography of 5 movies for {actor}.")
//                        .param("actor", "Tom Hanks"))
//                .call()
//                .entity(ActorsFilms.class);
        String promptString = """
                 给出{country}{year}中最出色的五部电影及其导演。
                """;
        Map<String, Object> map = Map.of("country", "美国", "year", "2023");

        return ChatClient.create(chatModel).prompt()
                .advisors(new SimpleLoggerAdvisor())
                .user(u -> u.text(promptString)
                        .params(map))
                .call()
                .entity(ActorsFilms.class);
    }

    public Flux<String>  streamResponse(){
        Flux<String> output = ChatClient.create(chatModel).prompt()
                .user("讲一个笑话")
                .stream()
                .content();
        return output;
    }


}

控制层

package com.example.springbootv3_0demo.controller;


import com.example.springbootv3_0demo.service.AiService;
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;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/api/ai")

public class AiController {

    @Autowired
    private AiService aiService;

    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return aiService.generate(message);
    }

    @GetMapping("/chat-with-prompt")
    public String chatWithPrompt(@RequestParam String message) {
        return aiService.generateWithSystemPrompt(message);
    }

    @GetMapping("/chat-rest-response")
    public AiService.RestResponse chatRestResponse(@RequestParam String genre, @RequestParam String theme){
       return aiService.restResponse(genre, theme);
    }

    @GetMapping("/chatActorsFilms")
    public AiService.ActorsFilms chatActorsFilms(){
        return aiService.restResponseSimple();
    }

    @GetMapping("/chat-stream")
    public Flux<String> chatStreamResponse(){
        return aiService.streamResponse();
    }
}

posted @ 2025-09-22 18:38  lyu6  阅读(23)  评论(0)    收藏  举报