概述


记忆可以让Agent更好的记住之前的会话内容。对于AI Agent,记忆至关重要,因为它们能够记住先前的交互、从反馈中学习并适应用户的偏好。

短期记忆让你的应用程序能够在单个线程或会话中记住先前的交互。

注意:会话可以隔离同一个 Agent 实例中的多个不同交互,类似于电子邮件在单个对话中分组消息的方式。

目标

  • 通过spring-ai-alibaba完成对会话的记忆功能

  • 支持多种记忆(redis,MySQL)

工程

  1. 依赖

<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-starter-memory</artifactId>
			<version>${spring-ai.version}</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-starter-memory-jdbc</artifactId>
			<version>${spring-ai.version}</version>
		</dependency>

		<dependency>
			<groupId>com.alibaba.cloud.ai</groupId>
			<artifactId>spring-ai-alibaba-starter-memory-redis</artifactId>
			<version>${spring-ai.version}</version>
		</dependency>

		<dependency>
			<groupId>org.xerial</groupId>
			<artifactId>sqlite-jdbc</artifactId>
			<version>3.49.1.0</version>
		</dependency>

		<dependency>
			<groupId>redis.clients</groupId>
			<artifactId>jedis</artifactId>
			<version>5.2.0</version>
		</dependency>

		<dependency>
			<groupId>mysql</groupId>
			<artifactId>mysql-connector-java</artifactId>
			<version>8.0.32</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.ai</groupId>
			<artifactId>spring-ai-advisors-vector-store</artifactId>
			<version>1.1.0</version>
		</dependency>
  1. application.yml

spring:
  application:
    name: spring-ai-alibaba-exmple
  ai:
    dashscope:
      api-key: ${DASH_SCOPE_API_KEY}
    memory:
      redis:
        host: localhost
        port: 6379
        password: root
        timeout: 5000
    chat:
      memory:
        repository:
          jdbc:
            mysql:
              jdbc-url: jdbc:mysql://localhost:3306/saa_data_agent?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&tinyInt1isBit=false&allowLoadLocalInfile=true&allowLocalInfile=true&allowUrl
              username: root
              password: root
              driver-class-name: com.mysql.cj.jdbc.Driver
              enabled: true
server:
  port: 8080
  1. 代码和工程结构


1.MemoryConfig.java

package com.xzgzs.ai.memory.config;

import com.alibaba.cloud.ai.memory.jdbc.MysqlChatMemoryRepository;
import com.alibaba.cloud.ai.memory.redis.RedissonRedisChatMemoryRepository;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.datasource.DriverManagerDataSource;

/**
 * @author wangyf
 * @date 2026/1/8
 */
@Configuration
public class MemoryConfig {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MemoryConfig.class);
    @Value("${spring.ai.memory.redis.host}")
    private String redisHost;
    @Value("${spring.ai.memory.redis.port}")
    private int redisPort;
    @Value("${spring.ai.memory.redis.password}")
    private String redisPassword;
    @Value("${spring.ai.memory.redis.timeout}")
    private int redisTimeout;

    @Value("${spring.ai.chat.memory.repository.jdbc.mysql.jdbc-url}")
    private String mysqlJdbcUrl;
    @Value("${spring.ai.chat.memory.repository.jdbc.mysql.username}")
    private String mysqlUsername;
    @Value("${spring.ai.chat.memory.repository.jdbc.mysql.password}")
    private String mysqlPassword;
    @Value("${spring.ai.chat.memory.repository.jdbc.mysql.driver-class-name}")
    private String mysqlDriverClassName;

    @Bean
    public MysqlChatMemoryRepository mysqlChatMemoryRepository() {
        DriverManagerDataSource dataSource = new DriverManagerDataSource();
        dataSource.setUrl(mysqlJdbcUrl);
        dataSource.setUsername(mysqlUsername);
        dataSource.setPassword(mysqlPassword);

        dataSource.setDriverClassName(mysqlDriverClassName);
        JdbcTemplate jdbcTemplate = new JdbcTemplate(dataSource);
        return MysqlChatMemoryRepository.mysqlBuilder()
                .jdbcTemplate(jdbcTemplate).build();
    }
    @Bean
    public RedissonRedisChatMemoryRepository redissonRedisChatMemoryRepository() {
        log.info("redisHost:{},redisPort:{},redisPassword:{},redisTimeout:{}", redisHost, redisPort, redisPassword, redisTimeout);
        return RedissonRedisChatMemoryRepository.builder()
                .host(redisHost)
                .port(redisPort)
                .password(redisPassword)
                .timeout(redisTimeout)
                .build();
    }
}
InMemoryController.java
package com.xzgzs.ai.memory;

import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.InMemoryChatMemoryRepository;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.messages.Message;
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 java.util.List;

import static org.springframework.ai.chat.memory.ChatMemory.CONVERSATION_ID;

/**
 * @author wangyf
 * @date 2026/1/7
 */
@RestController
@RequestMapping("/memory/in")
public class InMemoryController {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(InMemoryController.class);
    private final ChatClient chatClient;
    private final InMemoryChatMemoryRepository chatMemoryRepository = new InMemoryChatMemoryRepository();
    private final int MAX_MESSAGES = 100;
    private final MessageWindowChatMemory messageWindowChatMemory = MessageWindowChatMemory.builder()
            .chatMemoryRepository(chatMemoryRepository)
            .maxMessages(MAX_MESSAGES)
            .build();


    public InMemoryController(ChatClient.Builder builder) {
        this.chatClient = builder
                .defaultAdvisors(
                        MessageChatMemoryAdvisor.builder(messageWindowChatMemory)
                                .build()
                )
                .build();
    }

    @GetMapping("/call")
    public String call(@RequestParam(value = "query", defaultValue = "你好,我是北京市最帅的程序员 best_coder_cn") String query,
                       @RequestParam(value = "conversation_id", defaultValue = "handsome") String conversationId
    ) {
        return chatClient.prompt(query)
                .advisors(
                        a -> a.param(CONVERSATION_ID, conversationId)
                )
                .call().content();
    }

    @GetMapping("/messages")
    public List<Message> messages(@RequestParam(value = "conversation_id", defaultValue = "best_coder_cn") String conversationId) {
        return messageWindowChatMemory.get(conversationId);
    }


}

MysqlMemoryController.java

@RestController
@RequestMapping("/memory/mysql")
public class MysqlMemoryController {
    private static final org.slf4j.Logger log = org.slf4j.LoggerFactory.getLogger(MysqlMemoryController.class);
    private final ChatClient chatClient;
    private final int MAX_MESSAGES = 100;
    private final MessageWindowChatMemory messageWindowChatMemory;

    public MysqlMemoryController(ChatClient.Builder builder, MysqlChatMemoryRepository chatMemoryRepository) {
        this.messageWindowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(chatMemoryRepository)
                .maxMessages(MAX_MESSAGES)
                .build();
        this.chatClient = builder.defaultAdvisors(
                        MessageChatMemoryAdvisor.builder(messageWindowChatMemory)
                                .build()
                )
                .build();
    }

    @RequestMapping("/call")
    public String call(@RequestParam(value = "query", defaultValue = "你好,我是北京市最帅的程序员 best_coder_cn") String query,
                       @RequestParam(value = "conversation_id", defaultValue = "best_coder_cn") String conversationId) {
        return chatClient.prompt(query).advisors(
                        a -> a.param(CONVERSATION_ID, conversationId)
                )
                .call().content();
    }
    @RequestMapping("/messages")
    public List<Message> messages(@RequestParam(value = "conversation_id", defaultValue = "best_coder_cn") String conversationId) {
        return messageWindowChatMemory.get(conversationId);
    }
}
RedisMemoryController.java
@RestController
@RequestMapping("/memory/redis")
public class RedisMemoryController {

    private final ChatClient chatClient;
    private final MessageWindowChatMemory messageWindowChatMemory;
    private final int MAX_MESSAGES = 100;

    public RedisMemoryController(ChatClient.Builder builder, RedissonRedisChatMemoryRepository redissonRedisChatMemoryRepository) {
        this.messageWindowChatMemory = MessageWindowChatMemory.builder()
                .chatMemoryRepository(redissonRedisChatMemoryRepository)
                .maxMessages(MAX_MESSAGES)
                .build();
        this.chatClient = builder.defaultAdvisors(
                        MessageChatMemoryAdvisor.builder(messageWindowChatMemory).build()
                )
                .build();
    }

    @GetMapping("/call")
    public String call(@RequestParam(value = "query", defaultValue = "你好,我是北京市最帅的程序员 best_coder_cn") String query,
                       @RequestParam(value = "conversation_id", defaultValue = "best_coder_cn") String conversationId) {
        return chatClient.prompt(query)
                .advisors(
                        a -> a.param(CONVERSATION_ID, conversationId)
                )
                .call()
                .content();
    }
    @GetMapping("/messages")
    public List<Message> messages(@RequestParam(value = "conversation_id", defaultValue = "best_coder_cn") String conversationId) {
        return messageWindowChatMemory.get(conversationId);
    }

Logo

小龙虾开发者社区是 CSDN 旗下专注 OpenClaw 生态的官方阵地,聚焦技能开发、插件实践与部署教程,为开发者提供可直接落地的方案、工具与交流平台,助力高效构建与落地 AI 应用

更多推荐