Spring AI ChatModel 使用记录

文章目录

Spring AI ChatModel 使用记录(入门 Demo)

最近简单体验了一下 Spring AI ,基于 spring-ai 1.1.0 写了一个最小可用的 Chat Demo,主要用于熟悉 ChatModel 的基本使用方式,顺便记录一下踩坑和实现思路。

当前 Demo 实现的功能非常基础,主要包括:

  • ✅ 简单对话调用
  • ✅ 基于内存的上下文缓存(多轮对话)

本文未引入持久化、并发隔离等复杂设计。


一、依赖配置

⚠️ 注意:Spring Boot 与 Spring AI 的版本要匹配,否则很容易遇到类缺失或 Bean 无法注入的问题。

当前使用版本:

  • Spring Boot:3.3.5
  • Spring AI:1.1.0
  • JDK:17
xml 复制代码
<?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
         https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

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

    <groupId>com.ljx.ai</groupId>
    <artifactId>demo</artifactId>
    <version>1.0.0</version>

    <properties>
        <java.version>17</java.version>
        <!-- Spring AI 版本 -->
        <spring-ai.version>1.1.0</spring-ai.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>group.springframework.ai</groupId>
                <artifactId>spring-ai-bom</artifactId>
                <version>${spring-ai.version}</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>group.springframework.ai</groupId>
            <artifactId>spring-ai-openai-spring-boot-starter</artifactId>
            <version>${spring-ai.version}</version>
        </dependency>

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


    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

二、配置项说明

这里使用的是 硅基流动提供的 OpenAI 兼容接口,如果想切换到:

  • 本地模型(如 Ollama)
  • 其他 OpenAI 兼容平台

只需要调整:

  • base-url
  • model

即可。

yml 复制代码
spring:
  application:
    name: ai
  ai:
    openai:
      # OpenAI 兼容接口(硅基流动)
      base-url: https://api.siliconflow.cn
      api-key: ${SILICONFLOW_API_KEY:自己的密钥}
      chat:
        options:
          model: deepseek-ai/DeepSeek-V3.2
          temperature: 0.7
          max-tokens: 1024

三、上下文存储设计

Spring AI 的多轮对话,本质就是 Prompt 中携带历史 Message 列表

这里用到了三个 Message 类型:

  • SystemMessage:系统指令(人设、规则)
  • UserMessage:用户输入
  • AssistantMessage:模型回复

设计思路

  • 使用一个 内存 List 保存对话历史
  • 第 0 条永远保留 SystemMessage
  • 限制最大上下文轮数,避免 prompt 过长

示例实现

java 复制代码
import groovy.util.logging.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.messages.SystemMessage;
import org.springframework.ai.chat.messages.UserMessage;

import java.util.ArrayList;
import java.util.List;

@Slf4j
public class ChatStore {

    /**
     * 历史上下文
     */
    private static final List<Message> history = new ArrayList<>();

    /**
     * 最大保留轮数(不含 system)
     */
    private static final int MAX_HISTORY = 10;

    private static final Logger log = LoggerFactory.getLogger(ChatStore.class);

    /**
     * 将当前用户消息加入上下文,并返回裁剪后的历史
     */
    public static List<Message> trimHistory(String message) {
        if (ObjectUtils.isEmpty(history)) {
            history.add(new SystemMessage("你是一个金发兽耳小萝莉,请讨好你的主人"));
        }

        // system message 永远保留
        Message system = history.get(0);

        // 先加入用户消息
        history.add(new UserMessage(message));

        // 超出最大上下文长度则裁剪
        if (history.size() > MAX_HISTORY + 1) {
            List<Message> recent = history.subList(
                    history.size() - MAX_HISTORY,
                    history.size()
            );
            history.clear();
            history.add(system);
            history.addAll(recent);
        }

        return history;
    }

    /**
     * 记录模型回复
     */
    public static void addHistory(Message message) {
        history.add(message);
    }

    /**
     * 查看当前上下文内容
     */
    public static String showHistory() {
        StringBuilder sb = new StringBuilder();
        if (ObjectUtils.isNotEmpty(history)) {
            for (Message message : history) {
                if (message instanceof SystemMessage) {
                    continue;
                } else if (message instanceof UserMessage) {
                    sb.append("You say: ");
                } else if (message instanceof AssistantMessage) {
                    sb.append("She say: ");
                }
                sb.append(message.getContent()).append("\n");
            }
        }
        return sb.toString();
    }

    /**
     * 清空上下文
     */
    public static void clear() {
        history.clear();
    }
}

⚠️ 注意:

这是 全局静态上下文不支持多用户 ,真实项目应至少按 session / userId 隔离。


四、聊天调用逻辑

这里基本就是 Spring AI 的标准用法:

  1. 生成上下文 List<Message>
  2. 构造 Prompt
  3. 调用 ChatModel
  4. 保存 AssistantMessage
java 复制代码
import groovy.util.logging.Slf4j;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.ai.chat.messages.Message;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.ai.chat.prompt.Prompt;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;

@Service
@Slf4j
public class ChatServiceImpl extends ChatService {

    private static final Logger log = LoggerFactory.getLogger(ChatServiceImpl.class);

    @Autowired
    private ChatModel chatModel;

    @Override
    public String sendMessage(String message) {
        // 生成上下文
        List<Message> messages = ChatStore.trimHistory(message);

        // 构造 Prompt
        Prompt prompt = new Prompt(messages);

        // 调用模型
        String result = chatModel
                .call(prompt)
                .getResult()
                .getOutput()
                .getContent();

        // 保存回复
        ChatStore.addHistory(new AssistantMessage(result));

        return result;
    }

    @Override
    public String show() {
        return ChatStore.showHistory();
    }

    @Override
    public void clear() {
        ChatStore.clear();
    }
}

相关推荐
马***4112 小时前
适配成人英语学习痛点,打造落地性强的学习辅助方式
人工智能·学习
夜焱辰2 小时前
浏览器端 Agent 的文件版本管理:不用 Git,基于 OPFS + SQLite 自己造了一个
前端·人工智能
Ricky05532 小时前
CTRL-WORLD:一种用于机器人操控的可控生成世界模型(中美2025年联合研究)
人工智能·机器人·世界模型
刀法如飞3 小时前
AI时代:DDD领域驱动建模与Ontology语义建模的区别
java·设计模式·架构
jeffer_liu3 小时前
Spring AI 生产级实战:工具调用
java·人工智能·后端·spring·ai编程
阿乔外贸日记3 小时前
2026尼日利亚五项清关政策更新,拉高能源装备进口综合成本
大数据·人工智能·搜索引擎·智能手机·云计算·能源
比昨天多敲两行3 小时前
linux 线程概念与控制
java·开发语言·jvm
8Qi83 小时前
LeetCode 75:颜色分类(荷兰国旗问题)—— Java 题解 ✅
java·算法·leetcode·指针·排序
民乐团扒谱机3 小时前
【AI笔记】短时纯音时长对音高感知偏移效应研究综述
人工智能·笔记
zzhongcy3 小时前
@Transactional 同类内部调用失效 + 两种自代理解决方案
java