🎯 阅读指南

⭐ 温馨提示:

  • 新手推荐:从头到尾按顺序阅读,理解每个概念和步骤
  • 老手速通:直接跳到「快速上手」章节,5分钟搞定接入
  • 实战派:重点关注「实际应用场景」和「代码示例」

💡 为什么需要 Spring AI?

场景一:智能客服系统

想象一下,你正在开发一个电商平台的客服系统。传统方式需要:

  • 编写大量 if-else 规则
  • 维护复杂的对话流程
  • 手动处理各种用户问题

有了 Spring AI 后:

// 就这么简单!
String answer = chatClient.prompt()
    .user("用户:我想退货")
    .call()
    .content();
// AI 自动理解用户意图,生成专业回复

场景二:知识库问答系统

你的公司有大量内部文档(产品手册、技术文档、FAQ),员工每次都要翻文档找答案,效率低下。

有了 Spring AI + RAG:

  • 上传文档到知识库
  • 用户提问时,AI 自动检索相关文档片段
  • 基于文档内容生成准确回答

示例:

  • 企业内训系统:员工问"如何申请年假?",AI 自动从 HR 手册中找到答案
  • 技术支持平台:用户问"如何重置密码?",AI 从技术文档中提取步骤
  • 产品咨询助手:客户问"这个功能怎么用?",AI 基于产品文档回答

场景三:代码生成助手

开发过程中,经常需要:

  • 生成单元测试代码
  • 解释复杂代码逻辑
  • 重构代码优化建议

有了 Spring AI:

String code = chatClient.prompt()
    .system("你是一个 Java 专家")
    .user("帮我生成一个用户登录的单元测试")
    .call()
    .content();

🤔 Spring AI 是什么?

官方定义

Spring AI 是 Spring 官方推出的 AI 应用开发框架,旨在简化 Java 开发者接入大模型的过程。它提供了统一的 API,让你可以用同样的代码调用不同的 AI 模型(OpenAI、DashScope、本地模型等)。

简单理解

Spring AI = Spring 生态 + AI 能力

就像 Spring Data JPA 让你不用写 SQL 就能操作数据库一样,Spring AI 让你不用写复杂的 HTTP 请求就能调用大模型。

传统方式(原生 API 调用):

// 需要手动构建 HTTP 请求
HttpClient client = HttpClient.newHttpClient();
HttpRequest request = HttpRequest.newBuilder()
    .uri(URI.create("https://api.openai.com/v1/chat/completions"))
    .header("Authorization", "Bearer " + apiKey)
    .header("Content-Type", "application/json")
    .POST(HttpRequest.BodyPublishers.ofString(jsonBody))
    .build();
HttpResponse<String> response = client.send(request, 
    HttpResponse.BodyHandlers.ofString());
// 还要手动解析 JSON 响应...

Spring AI 方式:

// 一行代码搞定!
String answer = chatClient.prompt()
    .user("你好")
    .call()
    .content();

核心优势

  1. 统一接口:一套代码,支持多种模型(OpenAI、DashScope、本地模型)
  2. 开箱即用:Spring Boot 自动配置,无需手动管理连接
  3. 流式支持:原生支持流式响应,适合实时对话场景
  4. Spring 生态:完美集成 Spring Boot、Spring WebFlux 等

🚀 快速上手:5分钟接入大模型

在这里插入图片描述

第一步:添加依赖

pom.xml 中添加 Spring AI 依赖:

<dependencies>
    <!-- Spring Boot Starter -->
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-webflux</artifactId>
    </dependency>
    
    <!-- Spring AI OpenAI(支持 OpenAI 兼容接口) -->
    <dependency>
        <groupId>org.springframework.ai</groupId>
        <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
        <version>1.0.0-M4</version>
    </dependency>
</dependencies>

💡 小贴士:

  • spring-ai-openai-spring-boot-starter 不仅支持 OpenAI,还支持所有 OpenAI 兼容的接口(如 DashScope、DeepSeek 等)
  • 使用 webflux 而非 web,因为 Spring AI 基于响应式编程,支持流式处理

第二步:配置 API Key 和模型

application.yml 中配置:

spring:
  ai:
    openai:
      # 你的 API Key(这里以 DashScope 为例)
      api-key: sk-your-api-key-here
      # API 地址(DashScope 使用兼容模式)
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      chat:
        options:
          # 模型名称(DashScope 使用 qwen1.5-110b-chat)
          model: qwen1.5-110b-chat
          # 温度参数(0.0-2.0,越高越随机)
          temperature: 0.7

🔑 获取 API Key:

第三步:注入 ChatClient 并调用

创建一个 Service 类:

@Service
public class AiService {
    
    private final ChatClient chatClient;
    
    // Spring 自动注入 ChatClient
    public AiService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    // 最简单的调用方式
    public String chat(String userMessage) {
        return chatClient.prompt()
                .user(userMessage)  // 用户消息
                .call()             // 调用大模型
                .content();          // 提取回答内容
    }
}

🎉 就这么简单! 现在你可以在 Controller 中使用了:

@RestController
public class ChatController {
    
    @Autowired
    private AiService aiService;
    
    @GetMapping("/chat")
    public String chat(@RequestParam String message) {
        return aiService.chat(message);
    }
}

测试一下:

curl "http://localhost:8080/chat?message=你好"

如果返回 AI 的回答,说明接入成功!🎊


📚 进阶用法

1. 带系统提示词(System Prompt)

系统提示词用于设定 AI 的角色和行为:

public String chatWithRole(String userMessage) {
    return chatClient.prompt()
            .system("你是一个专业的 Java 开发工程师,擅长解释代码和技术问题。")
            .user(userMessage)
            .call()
            .content();
}

应用场景:

  • 代码审查助手system("你是一个代码审查专家,专注于代码质量和最佳实践")
  • 技术文档生成system("你是一个技术文档编写专家,擅长编写清晰、准确的技术文档")
  • 客服机器人system("你是一个友好的客服助手,总是耐心、专业地回答用户问题")

2. 带对话历史(上下文记忆)

让 AI 记住之前的对话内容:

public String chatWithHistory(String userMessage, String history) {
    return chatClient.prompt()
            .system("以下是对话历史:\n" + history)
            .user(userMessage)
            .call()
            .content();
}

实际应用:

// 第一次对话
String answer1 = chatClient.prompt()
    .user("我叫张三")
    .call()
    .content();

// 第二次对话(带历史)
String history = "用户:我叫张三\n助手:你好,张三!";
String answer2 = chatClient.prompt()
    .system("以下是对话历史:\n" + history)
    .user("我刚才说我叫什么?")
    .call()
    .content();
// AI 会回答:你叫张三

3. 流式响应(实时输出)

流式响应让 AI 的回答可以实时显示,就像 ChatGPT 那样一个字一个字地输出:

public Flux<String> streamChat(String userMessage) {
    return chatClient.prompt()
            .user(userMessage)
            .stream()    // 流式调用
            .content();  // 返回 Flux<String>
}

在 Controller 中使用(SSE):

@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(@RequestParam String message) {
    return aiService.streamChat(message)
            .map(chunk -> ServerSentEvent.<String>builder()
                    .data(chunk)
                    .build())
            .concatWith(Flux.just(ServerSentEvent.<String>builder()
                    .data("[DONE]")
                    .build()));
}

前端调用(EventSource):

const eventSource = new EventSource(
    `http://localhost:8080/chat/stream?message=你好`
);

eventSource.onmessage = (event) => {
    if (event.data === '[DONE]') {
        eventSource.close();
    } else {
        // 实时显示 AI 回答
        console.log(event.data);
    }
};

4. 完整示例:带历史记录的流式对话

@Service
public class AiService {
    
    private final ChatClient chatClient;
    
    public AiService(ChatClient chatClient) {
        this.chatClient = chatClient;
    }
    
    /**
     * 流式对话(带历史记录)
     */
    public Flux<String> streamChat(String userMessage, String history) {
        if (history != null && !history.isEmpty()) {
            return chatClient.prompt()
                    .system("以下是对话历史:\n" + history)
                    .user(userMessage)
                    .stream()
                    .content();
        } else {
            return chatClient.prompt()
                    .user(userMessage)
                    .stream()
                    .content();
        }
    }
    
    /**
     * 同步对话(带历史记录)
     */
    public String chat(String userMessage, String history) {
        if (history != null && !history.isEmpty()) {
            return chatClient.prompt()
                    .system("以下是对话历史:\n" + history)
                    .user(userMessage)
                    .call()
                    .content();
        } else {
            return chatClient.prompt()
                    .user(userMessage)
                    .call()
                    .content();
        }
    }
}

🎨 实际项目中的应用

场景一:智能对话系统

需求: 用户发送消息,AI 实时回复,并记住对话历史。

实现:

@RestController
@RequestMapping("/api/chat")
public class ChatController {
    
    @Autowired
    private AiService aiService;
    @Autowired
    private MessageRepository messageRepository;
    
    /**
     * 流式对话接口
     */
    @PostMapping("/conversations/{conversationId}/stream")
    public Flux<ServerSentEvent<String>> streamChat(
            @PathVariable Long conversationId,
            @RequestParam String message) {
        
        // 1. 保存用户消息
        Message userMsg = new Message();
        userMsg.setConversationId(conversationId);
        userMsg.setRole("user");
        userMsg.setContent(message);
        messageRepository.save(userMsg);
        
        // 2. 获取历史消息
        List<Message> history = messageRepository
            .findByConversationIdOrderByCreatedAtAsc(conversationId);
        String historyText = history.stream()
            .limit(10)  // 只取最近 10 条
            .map(msg -> msg.getRole() + ": " + msg.getContent())
            .collect(Collectors.joining("\n"));
        
        // 3. 流式调用 AI
        StringBuilder fullResponse = new StringBuilder();
        return aiService.streamChat(message, historyText)
                .doOnNext(chunk -> fullResponse.append(chunk))
                .map(chunk -> ServerSentEvent.<String>builder()
                        .data(chunk)
                        .build())
                .concatWith(Flux.just(ServerSentEvent.<String>builder()
                        .data("[DONE]")
                        .build()))
                .doOnComplete(() -> {
                    // 4. 保存 AI 回答
                    Message assistantMsg = new Message();
                    assistantMsg.setConversationId(conversationId);
                    assistantMsg.setRole("assistant");
                    assistantMsg.setContent(fullResponse.toString());
                    messageRepository.save(assistantMsg);
                });
    }
}

场景二:RAG 知识库问答

需求: 基于上传的文档回答问题。

实现思路:

  1. 用户上传文档,系统解析并切分
  2. 对每个文档片段生成向量(Embedding)
  3. 用户提问时,将问题转换为向量
  4. 在向量数据库中检索相似文档片段
  5. 将检索到的片段作为上下文,调用 AI 生成回答

核心代码:

@Service
public class RagService {
    
    @Autowired
    private AiService aiService;
    @Autowired
    private EmbeddingService embeddingService;
    @Autowired
    private DocumentRepository documentRepository;
    
    /**
     * RAG 问答
     */
    public String ragChat(String query, int topK) {
        // 1. 将问题转换为向量
        float[] queryEmbedding = embeddingService.embedText(query);
        String queryVector = embeddingService.toVectorString(queryEmbedding);
        
        // 2. 检索相似文档片段
        List<DocumentChunk> similarChunks = documentRepository
            .findSimilarChunks(queryVector, topK);
        
        // 3. 构建上下文
        StringBuilder context = new StringBuilder();
        context.append("以下是与问题相关的文档片段:\n\n");
        for (int i = 0; i < similarChunks.size(); i++) {
            context.append("片段 ").append(i + 1).append(":\n");
            context.append(similarChunks.get(i).getContent()).append("\n\n");
        }
        context.append("请基于以上文档内容回答问题:").append(query);
        
        // 4. 调用 AI 生成回答
        return aiService.chat(query, context.toString());
    }
}

🔧 高级配置

自定义 ChatClient

如果需要更精细的控制,可以手动配置 ChatClient

@Configuration
public class AiConfig {
    
    @Bean
    public ChatClient chatClient(ChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("你是一个专业的 AI 助手")
                .defaultFunctions("weather", "calculator")  // 函数调用
                .build();
    }
}

多模型支持

如果你的项目需要同时使用多个模型:

@Configuration
public class MultiModelConfig {
    
    // OpenAI 模型
    @Bean
    @Primary
    public ChatModel openAiModel() {
        OpenAiApi api = new OpenAiApi("https://api.openai.com", "sk-xxx");
        return new OpenAiChatModel(api, OpenAiChatOptions.builder()
                .withModel("gpt-4")
                .build());
    }
    
    // DashScope 模型
    @Bean
    public ChatModel dashScopeModel() {
        OpenAiApi api = new OpenAiApi(
            "https://dashscope.aliyuncs.com/compatible-mode", 
            "sk-xxx"
        );
        return new OpenAiChatModel(api, OpenAiChatOptions.builder()
                .withModel("qwen1.5-110b-chat")
                .build());
    }
    
    // 为不同模型创建不同的 ChatClient
    @Bean
    public ChatClient openAiClient(@Qualifier("openAiModel") ChatModel model) {
        return ChatClient.builder(model).build();
    }
    
    @Bean
    public ChatClient dashScopeClient(@Qualifier("dashScopeModel") ChatModel model) {
        return ChatClient.builder(model).build();
    }
}

🎯 结果演示

测试 1:基础对话

请求:

curl "http://localhost:8080/chat?message=你好"

响应:

你好!很高兴为你服务。有什么我可以帮助你的吗?

测试 2:带系统提示词

代码:

String answer = chatClient.prompt()
    .system("你是一个 Java 专家")
    .user("解释一下 Spring Boot 的自动配置原理")
    .call()
    .content();

响应:

Spring Boot 的自动配置原理基于条件注解和配置类...

(AI 会生成专业的 Java 技术解释)

测试 3:流式响应

前端代码:

const eventSource = new EventSource(
    'http://localhost:8080/chat/stream?message=写一首关于春天的诗'
);

eventSource.onmessage = (event) => {
    if (event.data !== '[DONE]') {
        // 实时显示:春、天、的、阳、光...
        document.getElementById('answer').innerText += event.data;
    }
};

效果:

春
春天
春天的
春天的阳
春天的阳光
春天的阳光洒
春天的阳光洒在
...

就像 ChatGPT 那样,一个字一个字地实时显示!


🐛 常见问题

Q1:API Key 配置后还是报错?

检查清单:

  1. ✅ API Key 是否正确(注意不要有多余空格)
  2. base-url 是否正确(DashScope 使用兼容模式)
  3. ✅ 模型名称是否正确(qwen1.5-110b-chat 而非 qwen
  4. ✅ 网络是否通畅(可以先用 curl 测试 API)

Q2:流式响应不工作?

可能原因:

  1. 前端未正确处理 SSE 格式
  2. 后端未设置正确的 Content-TypeTEXT_EVENT_STREAM_VALUE
  3. 浏览器缓存问题(尝试硬刷新)

解决方案:

@GetMapping(value = "/chat/stream", 
            produces = MediaType.TEXT_EVENT_STREAM_VALUE)  // ⚠️ 必须设置
public Flux<ServerSentEvent<String>> streamChat(...) {
    // ...
}

Q3:如何切换不同的模型?

方法一:修改配置

spring:
  ai:
    openai:
      chat:
        options:
          model: gpt-4  # 切换到 GPT-4

方法二:代码中指定

// 需要在 ChatClient 构建时指定,或使用不同的 ChatModel Bean

Q4:响应速度慢怎么办?

优化建议:

  1. 使用更快的模型qwen1.5-7b-chatqwen1.5-110b-chat
  2. 降低 temperaturetemperature: 0.30.7
  3. 使用流式响应:虽然总时间相同,但用户感知更快
  4. 缓存常见问题:对常见问题缓存回答

📖 总结

核心要点

  1. Spring AI 让 AI 接入变得简单:几行配置 + 几行代码就能接入大模型
  2. 统一接口:一套代码支持多种模型(OpenAI、DashScope、DeepSeek 等)
  3. 流式支持:原生支持流式响应,适合实时对话场景
  4. Spring 生态:完美集成 Spring Boot,开箱即用

快速回顾

// 1. 配置 application.yml
spring:
  ai:
    openai:
      api-key: sk-xxx
      base-url: https://dashscope.aliyuncs.com/compatible-mode
      chat:
        options:
          model: qwen1.5-110b-chat

// 2. 注入 ChatClient
@Autowired
private ChatClient chatClient;

// 3. 调用
String answer = chatClient.prompt()
    .user("你好")
    .call()
    .content();

就这么简单! 🎉

下一步学习

  • RAG 知识库:将文档向量化,实现基于文档的问答(
  • Agent 智能体:让 AI 能够调用工具(函数调用)
  • 多模态:支持图片理解、图片生成
  • 流式优化:优化流式响应的性能和用户体验
  • (可以去看博主的上一篇文章哟)

📚 相关资源


如果这篇文章对你有帮助,欢迎点赞、收藏、关注!

如有问题或建议,欢迎在评论区讨论! 💬

更多推荐