SpringAI——如何使用java来接入大模型

文章标题:SpringAI——运用Java对接大模型的方式

文章内容:

目录

利用Spring AI与Java迅速对接大模型

一、Spring AI概述

二、环境准备

三、项目构建

四、API密钥配置

五、创建配置类(CommonConfiguration.java)

六、编写Controller接口

会话记忆功能

定义会话存储方式

InMemoryChatMemory实现原理剖析

配置会话记忆

何为“配置会话记忆Advisor”?

添加会话ID

设置conversationId的缘由

应用场景 实现多轮对话的“记忆管控”

场景阐释:

Repository:

Repository的实现类:

怎样实现记忆管理?

✳ 实现目标

历史会话与记录查询接口

MessageVO返回前端


利用Spring AI与Java快速对接大模型

伴随AI技术的飞速发展,越来越多开发者渴望将大模型的能力融入自身的Java应用中。Spring AI提供了便捷的工具与框架,使得Java开发人员能够快速且优雅地接入各类主流大模型API,例如OpenAI、DeepSeek、阿里云通义千问等。

一、Spring AI简介

Spring AI是Spring生态里的新成员,专门为AI服务接入而设计,提供统一的API接口以及自动配置功能,极大地简化了开发人员与不同大模型API集成的流程。

二、环境准备

首先,需确保以下环境具备:

  • JDK 17或更高版本

  • Maven或Gradle

  • 一个大模型服务API账号

SpringAI——如何使用java来接入大模型

三、项目构建

借助Spring Initializr创建一个Spring Boot项目,添加如下依赖:

  • Spring Web

  • Spring AI

SpringAI——如何使用java来接入大模型

例如,使用Maven配置依赖:

SpringAI——如何使用java来接入大模型

四、API密钥配置

application.yml文件中配置大模型API,比如OpenAI:

SpringAI——如何使用java来接入大模型
spring:
    ai:
    openai:
      base-url: https://ark.cn-beijing.volces.com/api
      api-key:你的API-key
      chat:
        options:
          model: deepseek-r1-250120
          temperature: 0.7
      embedding:
        options:
          model: text-embedding-v3
          dimensions: 1024
logging:
  level:
    org.springframework.ai.chat.client.advisor: debug
    com.gege.ai: DEBUG

注意:

Spring AI在请求Chat API时,会默认在指定的base-url后面自动追加/v1/chat/completions路径。这意味着base-url不应包含v3之后的路径。若包含多余路径,会导致拼接后的路径错误,进而出现404 Not Found异常。

错误示例:

base-url: https://ark.cn-beijing.volces.com/api/v3

五、创建配置类(CommonConfiguration.java)

@Configuration
public class CommonConfiguration {

    // 定义一种记忆存储方式,底层用map保存消息,可考虑持久化存储
    // ChatMemory基于conversationId存储,区分不同会话
    // 即能根据会话id查询该会话的聊天记录
    @Bean
    public ChatMemory chatMemory() {
        return new InMemoryChatMemory(); // 创建InMemoryChatMemory对象,用于保存聊天记录
    }

    @Bean
    public ChatClient chatClient(OpenAiChatModel openAiChatModel) {
        return ChatClient.builder(openAiChatModel)
                .defaultSystem("你是一个可爱的助手,你的名字叫做格格,你的任务是帮助用户解决问题")
                .defaultAdvisors(
                        new SimpleLoggerAdvisor(),
                        // 环绕增强
                        new MessageChatMemoryAdvisor(chatMemory())  // 将聊天记录保存到内存中
                )
                .build();
    }
}

此处定义了两个关键Bean:

  • ChatMemory:用于存储聊天记录,当前为内存存储。

  • ChatClient:配置了系统提示和默认增强器,自动处理对话历史。

六、编写Controller接口

创建REST接口供前端调用:

import static org.springframework.ai.chat.client.advisor.AbstractChatMemoryAdvisor.CHAT_MEMORY_CONVERSATION_ID_KEY;

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


    private  final ChatClient chatClient;
    private final ChatHistoryRepository chatHistoryRepository;
    @RequestMapping("/chat")
    public String chat( String prompt) {
        return chatClient.prompt()
                .user(prompt)
                .call()
                .content();
    }

     // 使用流式传输,需指定页面内容类型,否则会乱码
    @RequestMapping(value = "/chatFlux",produces = "text/html;charset=UTF-8")
    public Flux<String> chatFlux(String message) {
        return chatClient.prompt()
                .user(message)
                .stream()
                .content();
    }

}

会话记忆功能

定义会话存储方式

SpringAI——如何使用java来接入大模型

图中给出以下接口定义:

public interface ChatMemory {
    void add(String conversationId, List<Message> messages);
    List<Message> get(String conversationId, int lastN);
    void clear(String conversationId);
}

接口含义:

  • add
    将指定会话ID的聊天消息列表存储到内存或持久存储中。

  • get
    根据会话ID和数量lastN,获取最近的N条聊天消息。

  • clear
    清空指定会话ID对应的消息列表。

InMemoryChatMemory实现原理剖析

上图中使用InMemoryChatMemory类,这是一种基于内存的简易聊天记忆方案。其实现原理大致如下:

public class InMemoryChatMemory implements ChatMemory {

    // 内部存储结构:用ConcurrentHashMap管理会话与消息列表映射
    private final Map<String, List<Message>> memory = new ConcurrentHashMap<>();

    @Override
    public void add(String conversationId, List<Message> messages) {
        memory.computeIfAbsent(conversationId, key -> new ArrayList<>())
              .addAll(messages);
    }

    @Override
    public List<Message> get(String conversationId, int lastN) {
        List<Message> messages = memory.getOrDefault(conversationId, Collections.emptyList());
        int size = messages.size();
        return messages.subList(Math.max(0, size - lastN), size);
    }

    @Override
    public void clear(String conversationId) {
        memory.remove(conversationId);
    }
}
  • 采用ConcurrentHashMap实现线程安全的内存存储,适用于并发场景。

ConcurrentHashMap是支持高并发访问的哈希映射表,允许多个线程同时读写,无线程安全问题。

  • 每个会话conversationId对应一个List<Message>,消息按时间顺序存储。

  • 获取消息时,返回最新的N条消息,便于后续调用大模型进行上下文处理。

它是ChatMemory接口的实现类,用于在内存中保存每个会话的聊天消息。

可将其理解为“简易版聊天历史数据库”,但存在于内存中,不持久化到磁盘或数据库

配置会话记忆

SpringAI——如何使用java来接入大模型

何为“配置会话记忆Advisor”?

在Spring AI中,Advisor是一种增强机制(类似AOP),可理解为:

对ChatClient的一次调用进行“前处理”和“后处理”,借此实现对话上下文(历史聊天)的自动管理。

@Bean
public ChatClient chatClient(OllamaChatModel model) {
    return ChatClient.builder(model)
            .defaultSystem("你是可爱的助手,名字叫小团团")   // 系统提示词
            .defaultAdvisors(
                new SimpleLoggerAdvisor(),                     // 日志输出
                new MessageChatMemoryAdvisor(chatMemory())     // ⭐ 记忆增强器
            )
            .build();
}

此段中:

  • MessageChatMemoryAdvisor(chatMemory)会注册一个增强器。

  • 它会拦截每次调用.call().stream()的前后行为:

    • 前:取出chatId对应的历史消息→添加到prompt中

    • 后:把当前响应消息写入对应会话历史中

添加会话ID

SpringAI——如何使用java来接入大模型

会话ID(conversationId)如何添加?——由Advisor机制 + 手动设置参数协同完成

1. 给当前对话上下文设置参数

传入名为CHAT_MEMORY_CONVERSATION_ID_KEY的参数,这是Spring AI内置识别的“会话ID键名”。

相当于告知Advisor:本轮对话属于chatId这个会话。

该key实际为:

public static final String CHAT_MEMORY_CONVERSATION_ID_KEY = "conversationId";

设置conversationId的缘由

conversationId是“会话编号”,作用是标记和区分不同用户或同一用户的不同对话场景,让每次请求能关联自身上下文(聊天历史),实现“多轮对话记忆”。

应用场景 实现多轮对话的“记忆管控”

场景阐释:

在对话系统中(如客服、智能助理、问答机器人),通常需:

需求 示例
记录用户与AI的每次会话ID 用户今日问两次问题,分别记录
支持“继续对话”功能 用户打开旧对话,接着提问
查看“历史会话列表” 展示聊天记录列表
多业务类型区分 比如“AI助理” vs “售后客服”

Repository:

若存在不同业务(如chat、助理等)在同一网站,需根据不同业务保存该业务下的会话ID,此时若要完成记忆存储,需实现将不同业务下的会话ID保存在内存中的方法及获取该业务下所有会话ID的方法

public interface ChatHistoryRepository {
    /**
     * 保存会话记录
     * @param type 业务类型
     * @param chatId 会话ID
     */
    void save(String type, String chatId);

    /**
     * 获取会话ID列表
     * @param type 业务类型
     * @return
     */
    List<String> getChatIds(String type);
}

Repository的实现类:

// TODO 先使用内存保存会话记录,后续可改造为数据库保存
@Component
public class InMemoryChatHistoryRepository  implements ChatHistoryRepository{

   private  final Map<String,List<String>> chatHistory = new HashMap<>();

    @Override
    public void save(String type, String chatId) {
        // 先判断是否存在,存在则更新,不存在则新增
        /*if(!chatHistory.containsKey(type)){
            chatHistory.put(type,new ArrayList<>());
        }
        List<String> chatIds = chatHistory.get(type); // 取到对应的chatIds
         */
        List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());
        if(chatIds.contains(chatId)){ // 若已存在,不处理
            return;
        }
        chatIds.add(chatId);

    }

    @Override
    public List<String> getChatIds(String type) {
//        return chatHistory.get(type) == null ? List.of() : chatHistory.get(type);
        return chatHistory.getOrDefault(type,List.of());
    }
}

computeIfAbsent

List<String> chatIds = chatHistory.computeIfAbsent(type, k -> new ArrayList<>());

含义:

chatHistory不存在keytype,则为其创建新的ArrayList并返回;
否则直接返回已有List<String>

  • 想往某个type下添加一个chatId

  • 若该类型无记录,自动创建新列表

getOrDefault

return chatHistory.getOrDefault(type, List.of());

含义:

chatHistory中存在keytype,返回对应值;
否则返回默认空List(不可变)。

怎样实现记忆管理?

基于Spring AI,可借助ChatMemory + Advisor + ChatHistoryRepository实现完整记忆管理能力。

✳ 实现目标

  • 每轮对话可带会话ID(conversationId

  • 自动记录该chatId

  • 自动查询旧对话列表(展示历史记录)

  • 绑定业务类型(如:客服、AI问答)

    @RequestMapping("/chat")
    public Flux<String> chat( String prompt,String chatId) {
        // 保存会话
        chatHistoryRepository.save("chat",chatId);// chat是一种类型
        return chatClient.prompt()
                .user(prompt)
                .advisors(a->a.param(CHAT_MEMORY_CONVERSATION_ID_KEY,chatId))
                .stream()
                .content();
    }

.advisors(a -> a.param("conversationId", chatId))告知Spring AI:当前对话属于哪个会话(chatId)

相当于告知Spring AI:“此次对话属于chatId=xxx的上下文,请读取之前消息,并记录此次对话。”

只要设置.advisors(a -> a.param("conversationId", chatId)),Spring AI会:

  1. 自动读取该chatId之前的UserMessage + AssistantMessage

  2. 自动将当前用户输入和模型生成的AssistantMessage保存到该chatId的上下文中

  3. 下次提问时,能“记住之前内容”

用户发送请求 => /chat?prompt=你好&chatId=user123

1️⃣ 保存会话chatId到ChatHistoryRepository
    chatHistoryRepository.save("chat", "user123")

2️⃣ 构造prompt请求 + 设置advisors
    ChatClient.prompt().user("你好").advisors(...)

3️⃣ MessageChatMemoryAdvisor做两件事:
    - doBefore:从chatMemory取出user123的历史消息,注入prompt
    - doAfter:将生成的新回复追加到user123的上下文记录中

4️⃣ ChatClient.stream().content()启动流式大模型响应

5️⃣ 返回Flux<String>流结果给前端,逐字展示

历史会话与记录查询接口

import static java.lang.Integer.MAX_VALUE;

@RequestMapping("/ai/history")
@RequiredArgsConstructor
@RestController
public class ChatHistoryController {
    private final ChatHistoryRepository chatHistoryRepository;
    private final ChatMemory chatMemory;

    // 查询某业务类型下所有会话ID
    @GetMapping("/{type}")
    public List<String> getChatIds(@PathVariable String type) {
        return chatHistoryRepository.getChatIds(type);
    }


    // 根据会话ID查询对应信息
    @GetMapping("/{type}/{chatId}")
    public List<MessageVO> getChatHistory(@PathVariable String type, @PathVariable String chatId) {
        List<Message> messages = chatMemory.get(chatId, MAX_VALUE);
        if (messages == null){
            return List.of();
        }

        return  messages.stream().map(MessageVO::new).toList();
    }

}
方法 功能 底层依赖
getChatIds(String type) 获取某类型下所有历史会话ID ChatHistoryRepository
getChatHistory(String type, String chatId) 获取某次对话完整聊天记录
ChatMemory(记忆上下文)

MessageVO返回前端

为何返回role?

因前端需根据role区分是谁回答,进行不同渲染

@NoArgsConstructor
@Data
public class MessageVO {
    private  String role;
    private  String content;

    public MessageVO(Message message) {
        switch (message.getMessageType()) {
            case USER:
                this.role = "user";
                break;
            case ASSISTANT:
                this.role = "assistant";
                break;
            default:
                role=",";
                break;

        }
        this.content = message.getText();
    }
}
SpringAI——如何使用java来接入大模型

MessageType有四种,当前场景只需获取其中两种。

实现多轮对话记忆管理的核心是:**使用conversationId作为对话唯一标识 + 自动记忆上下文(ChatMemory

版权声明:程序员胖胖胖虎阿 发表于 2025年7月7日 上午7:33。
转载请注明:SpringAI——如何使用java来接入大模型 | 胖虎的工具箱-编程导航

相关文章

暂无评论

暂无评论...