Spring AI

本文最后更新于 2025年10月26日

持续更新中

1.Spring AI概述

Spring AI是一款Spring官方推出的一款Java调用大模型的工具,用于开发基于Java语言的大模型应用。作为Spring家族的产品,Spring AI充分利用了Spring Boot的一些特性,大大的简化了开发。

本文以DeepSeek大模型为例,介绍Spring AI在开发大模型应用中的用法。

类似的框架还有LangChain4j,截至成文日期,Spring AI对比LangChain4j有以下区别:

Spring AI LangChain4j
Chat 支持 支持
Function 支持 支持
RAG 支持 支持
对话模型 15+ 15+
向量模型 10+ 15+
向量数据库 15+ 20+
多模态模型(图像,音频) 5+ 1
JDK 17 8

2.ChatClient聊天机器人

通过Spring AI基于DeepSeek大模型,创建一个智能聊天机器人,并进行对话。

1.基于jdk-21创建spring-boot项目,引入spring-boot依赖3.5.7,spring-ai依赖1.0.3

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>org.example</groupId>
    <artifactId>spring-ai</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>3.5.7</version>
    </parent>

    <properties>
        <maven.compiler.source>21</maven.compiler.source>
        <maven.compiler.target>21</maven.compiler.target>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    </properties>
    
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>1.0.3</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

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

        <dependency>
            <groupId>org.springframework.ai</groupId>
            <artifactId>spring-ai-starter-model-deepseek</artifactId>
        </dependency>
    </dependencies>
    
</project>

2.application.yml配置中进行配置,并填写DeepSeek的API_KEY,我是从DeepSeek官方(https://platform.deepseek.com/)购买获得,充值后,可以从https://platform.deepseek.com/api_keys页面获得API_KEY

⚠ 为防止误提交代码到公开仓库,spring文档建议将API_KEY写进本机环境变量,yml中设置为api-key: ${DEEPSEEK_API_KEY}

更多配置项,可见官方文档:https://docs.spring.io/spring-ai/reference/api/chat/deepseek-chat.html

spring:
  ai:
    deepseek:
      base-url: https://api.deepseek.com
      api-key: sk-02**********************d8666

3.编写一个配置类,声明一个对话客户端,并且注入配置好的DeepSeek模型,通过defaultSystem()来指定大模型的默认角色和任务背景

package org.example;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.deepseek.DeepSeekChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ModelConfig {

    @Bean
    public ChatClient chatClient(DeepSeekChatModel model) {
        return ChatClient.builder(model)
                .defaultSystem("你是聪明的智能助手,名字叫小羊")
                .build();
    }
}

4.编写一个测试类,和创建的智能助手“小羊”打个招呼,通过user()指定用户输入的指令,此处也可以设置system()系统指定,如果不设置会使用defaultSystem

package org.example.test;

import jakarta.annotation.Resource;
import org.example.Main;
import org.junit.jupiter.api.Test;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.boot.test.context.SpringBootTest;

@SpringBootTest(classes = Main.class)
public class ModelTest {

    @Resource
    private ChatClient chatClient;

    @Test
    public void testHello() {
        String content = chatClient.prompt()
                .user("hi,你是谁?")
                .call()
                .content();

        System.out.println(content);
    }
}

得到聊天机器人的回复了,正常打印输出,一个基于DeepSeek大模型的简单的聊天机器人就实现了

通过call()是阻塞的调用,如果在http请求中使用会出现无限等待的情况,如果要实现不断输出的效果,需要web环境下使用stream()流式调用返回Flux,并设置返回格式为text/html;charset=utf-8,否则输出的中文是乱码

package org.example.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("ai")
public class ChatController {

    @Resource
    private ChatClient chatClient;
    
    @GetMapping(value = "chat-stream", produces = "text/html;charset=utf-8")
    public Flux<String> stream(String msg) {

        return chatClient.prompt()
                .user(msg)
                .stream()
                .content();

    }

}

通过使用stream()流式调用返回Flux,可以得到以下效果的输出

3.Advisors拦截增强

Spring AI通过Advisor(https://docs.spring.io/spring-ai/reference/api/advisors.html)接口提供了会话的增强功能,可以利用其开发更加高级的会话功能

Advisor接口主要用到以下实现类:

  • org.springframework.ai.chat.client.advisor.SimpleLoggerAdvisor 简单的日志打印功能
  • org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor 可以实现会话记忆
  • org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor 与RAG知识库功能有关

在使用QuestionAnswerAdvisor时,需要额外添加依赖:

<dependency>
   <groupId>org.springframework.ai</groupId>
   <artifactId>spring-ai-advisors-vector-store</artifactId>
</dependency>

3.1 记录日志

可以在创建ChatClient的时候就指定默认的Advisor为SimpleLoggerAdvisor实现输出日志功能

@Bean
public ChatClient chatClient(DeepSeekChatModel model) {
    return ChatClient.builder(model)
            .defaultAdvisors(new SimpleLoggerAdvisor())
            .defaultSystem("你是聪明的智能助手,名字叫小羊")
            .build();
}

SimpleLoggerAdvisor日志级别默认为DEBUG,如果要使用SimpleLoggerAdvisor打印日志到控制台,需要修改yml配置文件中的日志级别:

logging:
  level:
    org.springframework.ai: debug

3.2 会话记忆

大模型不具备记忆能力,要想让大模型记住之前的聊天内容,唯一的办法是把之前的聊天内容和新的提示词一并发送给大模型,此时就需要用到MessageChatMemoryAdvisor

使用MessageChatMemoryAdvisor,需要先定义一个ChatMemory接口的实现,来自定义管理会话数据的逻辑(添加,获取,删除),比如可以自己选择维护会话数据到mysql,redis,或者Map中

org.springframework.ai.chat.memory.ChatMemory

public interface ChatMemory {
    String DEFAULT_CONVERSATION_ID = "default";
    String CONVERSATION_ID = "chat_memory_conversation_id";

    default void add(String conversationId, Message message) {
        Assert.hasText(conversationId, "conversationId cannot be null or empty");
        Assert.notNull(message, "message cannot be null");
        this.add(conversationId, List.of(message));
    }

    void add(String conversationId, List<Message> messages);

    List<Message> get(String conversationId);

    void clear(String conversationId);
}

Spring AI为我们默认实现了一个实现类InMemoryChatMemoryRepository,可将会话保存到本地内存中用于测试,如果我们没有自定义ChatMemory实现类注入,默认的InMemoryChatMemoryRepository将会注入

此处,为了测试功能,就以默认的InMemoryChatMemoryRepository为例

@Bean
public ChatClient chatClient(DeepSeekChatModel model, ChatMemory chatMemory) {
    return ChatClient.builder(model)
            .defaultAdvisors(
                        SimpleLoggerAdvisor.builder().build(),
                        MessageChatMemoryAdvisor.builder(chatMemory).build()
            )
            .defaultSystem("你是聪明的智能助手,名字叫小羊")
            .build();
}

Controller的代码需要用户发起聊天时,调用接口传入会话的ID:chatId,并通过.advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId))传递给chatClient

@GetMapping(value = "chat-stream", produces = "text/html;charset=utf-8")
public Flux<String> stream(String msg, String chatId) {

    return chatClient.prompt()
            .user(msg)
            .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, chatId))
            .stream()
            .content();

}

然后测试,先指定会话ID为001,先后两次分别提问“40除以2等于几”和“那除以5呢”,会发现第二次提问没有带上40也得到了正确答案8,再将ID改为002继续问“那乘以3呢”,大模型随即忘记了数字40,失去了记忆,这说明大模型此时通过MessageChatMemoryAdvisor增强,已经有了记忆,并且能够根据不同的会话进行区分!



这样,MessageChatMemoryAdvisor大模型会话记忆就实现了,不过当前使用的InMemoryChatMemoryRepository将会话保存在内存,进程结束即销毁,如果正式的项目需要换成其他的实现来真正的持久化,而且会话的ID应该后台生成并和当前登录用户绑定,而不是由前端随便的传进去。


"如果文章对您有帮助,可以请作者喝杯咖啡吗?"

微信二维码

微信支付

支付宝二维码

支付宝


Spring AI
https://blog.liuzijian.com/post/spring/2025/02/15/spring-ai.html
作者
Liu Zijian
发布于
2025年2月15日
更新于
2025年10月26日
许可协议