Persisting Spring AI Alibaba Memory to Redis: A Hands‑On Guide

This guide walks through configuring Spring AI Alibaba 1.1.2.0 to persist an agent's short‑term memory in Redis, covering environment setup, core concepts like Checkpointer and StateSerializer, step‑by‑step Maven project creation, code snippets, common pitfalls, and verification of multi‑turn conversation state across sessions.

The Dominant Programmer
The Dominant Programmer
The Dominant Programmer
Persisting Spring AI Alibaba Memory to Redis: A Hands‑On Guide

Introduction

When building an intelligent conversational agent, memory is a core capability. Spring AI Alibaba provides a flexible short‑term memory mechanism that can checkpoint an agent’s state—including dialogue history—to external storage. Using Redis as the storage medium enables memory sharing across multiple service instances and prevents data loss on application restart.

Environment Requirements

JDK 17+

Spring Boot 3.2.5

Spring AI Alibaba 1.1.2.0

Redis 6.x/7.x (Windows: Memurai or WSL2)

Maven 3.6+

DashScope API Key (for model calls)

Core Concepts

Short‑term Memory

Short‑term memory lets an agent remember previous interactions within the same session identified by threadId. In Spring AI Alibaba, the memory is stored as an OverAllState object in the graph execution context.

Checkpointer

The Checkpointer persists the agent state to external storage. Two implementations are provided: MemorySaver: in‑memory storage for development and debugging. RedisSaver: Redis storage suitable for production.

StateSerializer

The state serializer converts Java objects to byte arrays for Redis. The framework defaults to SpringAIJacksonStateSerializer, which uses Jackson for JSON serialization.

Message Trimming

To keep the conversation within the LLM’s context window, MessagesModelHook can trim the message list (e.g., keep the first and the most recent N messages) or apply summarization.

Implementation Steps

1. Create Maven Project

Define pom.xml and manage the Jackson version to avoid NoSuchMethodError caused by version mismatches.

<?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.2.5</version>
    <relativePath/>
  </parent>
  <groupId>com.example.ai</groupId>
  <artifactId>spring-ai-redis-memory-demo</artifactId>
  <version>1.0.0</version>
  <properties>
    <java.version>17</java.version>
    <spring-ai-alibaba.version>1.1.2.0</spring-ai-alibaba.version>
    <jackson.version>2.17.2</jackson.version>
  </properties>
  <dependencyManagement>
    <dependencies>
      <dependency>
        <groupId>com.fasterxml.jackson</groupId>
        <artifactId>jackson-bom</artifactId>
        <version>${spring-ai-alibaba.version}</version>
      </dependency>
      <!-- DashScope starter for model access -->
      <dependency>
        <groupId>com.alibaba.cloud.ai</groupId>
        <artifactId>spring-ai-alibaba-starter-dashscope</artifactId>
        <version>${spring-ai-alibaba.version}</version>
      </dependency>
      <!-- Jackson components (managed by BOM) -->
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-core</artifactId>
      </dependency>
      <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-annotations</artifactId>
      </dependency>
      <!-- Redisson client -->
      <dependency>
        <groupId>org.redisson</groupId>
        <artifactId>redisson</artifactId>
        <version>3.52.0</version>
      </dependency>
    </dependencies>
  </dependencyManagement>
  <build>
    <plugins>
      <plugin>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-maven-plugin</artifactId>
      </plugin>
    </plugins>
  </build>
</project>

2. Configuration File

server:
  port: 885
spring:
  ai:
    dashscope:
      api-key: ${DASHSCOPE_API_KEY}
redis:
  host: localhost
  port: 6379
  timeout: 5000

3. Agent Configuration (using RedisSaver)

package com.badao.ai.config;

import com.alibaba.cloud.ai.graph.OverAllState;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import com.alibaba.cloud.ai.graph.checkpoint.savers.redis.RedisSaver;
import com.alibaba.cloud.ai.graph.serializer.StateSerializer;
import com.alibaba.cloud.ai.graph.serializer.plain_text.jackson.SpringAIJacksonStateSerializer;
import com.alibaba.cloud.ai.graph.state.AgentStateFactory;
import com.alibaba.cloud.ai.graph.state.strategy.ReplaceStrategy;
import com.badao.ai.hook.MessageTrimmingHook;
import org.redisson.api.RedissonClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class AgentConfig {
    @Bean
    public ReactAgent reactAgent(ChatModel chatModel, RedissonClient redissonClient) {
        // 1. Create state factory
        AgentStateFactory<OverAllState> stateFactory = (inputs) -> {
            OverAllState state = new OverAllState();
            state.registerKeyAndStrategy("messages", new ReplaceStrategy());
            state.input(inputs);
            return state;
        };
        // 2. Create serializer
        StateSerializer stateSerializer = new SpringAIJacksonStateSerializer(stateFactory);
        // 3. Build RedisSaver (method name is redisson, not redissonClient)
        RedisSaver redisSaver = RedisSaver.builder()
                .redisson(redissonClient)
                .stateSerializer(stateSerializer)
                .build();
        // 4. Build ReactAgent
        return ReactAgent.builder()
                .name("redis_memory_agent")
                .model(chatModel)
                .saver(redisSaver)
                .hooks(new MessageTrimmingHook()) // optional trimming
                .build();
    }
}

4. Message‑Trimming Hook (optional)

package com.badao.ai.hook;

import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.hook.HookPosition;
import com.alibaba.cloud.ai.graph.agent.hook.HookPositions;
import com.alibaba.cloud.ai.graph.agent.hook.messages.AgentCommand;
import com.alibaba.cloud.ai.graph.agent.hook.messages.MessagesModelHook;
import com.alibaba.cloud.ai.graph.agent.hook.messages.UpdatePolicy;
import org.springframework.ai.chat.messages.Message;
import java.util.ArrayList;
import java.util.List;

@HookPositions({HookPosition.BEFORE_MODEL})
public class MessageTrimmingHook extends MessagesModelHook {
    private static final int MAX_MESSAGES = 5;
    @Override
    public String getName() {
        return "message_trimming";
    }
    @Override
    public AgentCommand beforeModel(List<Message> previousMessages, RunnableConfig config) {
        if (previousMessages.size() <= MAX_MESSAGES) {
            return new AgentCommand(previousMessages);
        }
        // Keep the first message and the last three
        Message firstMsg = previousMessages.get(0);
        List<Message> recent = previousMessages.subList(previousMessages.size() - 3, previousMessages.size());
        List<Message> trimmed = new ArrayList<>();
        trimmed.add(firstMsg);
        trimmed.addAll(recent);
        return new AgentCommand(trimmed, UpdatePolicy.REPLACE);
    }
}

5. Service and Controller

package com.badao.ai.service;

import com.alibaba.cloud.ai.graph.RunnableConfig;
import com.alibaba.cloud.ai.graph.agent.ReactAgent;
import org.springframework.ai.chat.messages.AssistantMessage;
import org.springframework.stereotype.Service;

@Service
public class AgentService {
    private final ReactAgent reactAgent;
    public AgentService(ReactAgent reactAgent) { this.reactAgent = reactAgent; }
    public String chat(String userMessage, String sessionId) {
        RunnableConfig config = RunnableConfig.builder()
                .threadId(sessionId) // session isolation
                .build();
        AssistantMessage response = reactAgent.call(userMessage, config);
        return response.getText();
    }
}
package com.badao.ai.controller;

import com.badao.ai.service.AgentService;
import org.springframework.web.bind.annotation.*;
import java.util.Map;

@RestController
@RequestMapping("/api/agent")
public class AgentController {
    private final AgentService agentService;
    public AgentController(AgentService agentService) { this.agentService = agentService; }
    @PostMapping("/chat/session")
    public Map<String, Object> chatWithSession(@RequestParam String message, @RequestParam String sessionId) {
        String response = agentService.chat(message, sessionId);
        return Map.of("success", true, "response", response, "sessionId", sessionId);
    }
}

Common Issues and Solutions

RedisSaver constructor access

Attempting to instantiate new RedisSaver(...) fails because the constructor is protected. Use the builder pattern:

RedisSaver.builder().redisson(...).stateSerializer(...).build()

.

Method name mismatch

The builder method is redisson, not redissonClient. Replace calls accordingly.

Jackson version conflict

Inconsistent Jackson versions cause NoSuchMethodError. Resolve by unifying the version via dependencyManagement to 2.17.2 (or the version matching Spring Boot).

Timeout parsing error

Specifying timeout: 5000ms in application.yml leads to NumberFormatException because the field expects an int. Remove the ms suffix and use timeout: 5000.

Memory not taking effect

Possible causes: missing saver registration, inconsistent threadId, or unregistered StateSerializer. Ensure the agent builder includes .saver(redisSaver), use a consistent sessionId (mapped to threadId), and verify that state data appears in Redis (e.g., redis-cli KEYS '*').

Verification Tests

Start Redis and Spring Boot

cd C:\Redis
redis-server.exe redis.windows.conf
export DASHSCOPE_API_KEY="your_api_key"
mvn spring-boot:run

Multi‑turn conversation

# First turn: introduce name
curl -X POST "http://localhost:885/api/agent/chat/session?message=你好,我叫张三&sessionId=test-001"
# Second turn: ask name (should be remembered)
curl -X POST "http://localhost:885/api/agent/chat/session?message=我叫什么名字?&sessionId=test-001"
# Third turn: new session, memory isolated
curl -X POST "http://localhost:885/api/agent/chat/session?message=我叫什么名字?&sessionId=test-002"

Inspect Redis state

redis-cli
127.0.0.1:6379> KEYS *
# e.g., agent:test-001:state
127.0.0.1:6379> GET "agent:test-001:state"

Conclusion

The guide demonstrates how to use RedisSaver to persist an agent’s short‑term memory, ensuring context continuity across multi‑turn dialogues and supporting production‑grade deployments with shared memory and resilience to restarts. Key takeaways include building the Checkpointer with the correct builder method ( redisson), unifying Jackson versions to avoid serialization errors, isolating sessions via threadId, and optionally applying MessagesModelHook for context trimming. Future extensions could incorporate summarization or long‑term memory backed by vector databases.

References

Spring AI Alibaba official documentation

RedisSaver source examples

DashScope model integration guide

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaRedisAgentSpring BootSpring AIShort-term Memory
The Dominant Programmer
Written by

The Dominant Programmer

Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.