MessageWindowChatMemory is a Spring AI ChatMemory implementation that keeps a bounded window of conversational messages through a ChatMemoryRepository. Its default repository stores history in an in-memory Map, making conversational context easy to add but unavailable after application restart. section 5.2.1 section 5.3
Listing 5.1 Adding message-oriented chat memory as a default advisor
@Bean
ChatClient chatClient(
ChatClient.Builder chatClientBuilder,
VectorStore vectorStore,
ChatMemory chatMemory) {
return chatClientBuilder
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(chatMemory).build(), #1
QuestionAnswerAdvisor.builder(vectorStore)
.searchRequest(SearchRequest.builder().build()).build())
.build();
}
Register MessageChatMemoryAdvisor with the ChatMemory bean when building a ChatClient; the advisor supplies stored messages to subsequent prompts. section 5.2.1
Configure the memory window and conversation identity as application requirements grow:
- Use the default configuration for a simple in-memory conversation, with the most recent 20 messages sent in prompts. section 5.2.1 section 5.2.3
- Override the ChatMemory bean and call maxMessages() to change the limit; older entries drop off as new messages arrive. section 5.2.3
- Pass a conversation ID through the advisor parameters so separate conversations do not share context. section 5.3
@Bean
ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.maxMessages(50)
.build();
}
Define a ChatMemory bean with MessageWindowChatMemory.builder(), inject ChatMemoryRepository, and set maxMessages(50) for a larger window. section 5.2.3
Maintaining context across follow-up questions
A request asks about the Burger Force Field card in Burger Battle, then asks whether it protects against Burgerpocalypse. Reusing the same conversation ID lets the second request refer to the earlier card without repeating its name; a different ID starts without that context. section 5.2.1 section 5.3
Listing 5.3 Providing the conversation ID to the advisor
import static org.springframework.ai.chat.memory
.ChatMemory.CONVERSATION_ID;
// ...
@Override
public Answer askQuestion(Question question, String conversationId) {
var gameNameMatch = String.format(
"gameTitle == '%s'",
normalizeGameTitle(question.gameTitle()));
return chatClient.prompt()
.system(systemSpec -> systemSpec
.text(promptTemplate)
.param("gameTitle", question.gameTitle()))
.user(question.question())
.advisors(advisorSpec -> advisorSpec
.param(FILTER_EXPRESSION, gameNameMatch)
.param(CONVERSATION_ID, conversationId)) #1
.call()
.entity(Answer.class);
}
Supply ChatMemory.CONVERSATION_ID through the advisor specification alongside the application’s retrieval parameters. section 5.3
The default in-memory behavior suits introductory or temporary use, but it is not recommended for production when history must survive restarts, be shared across instances, or avoid indefinite application-memory growth. Replace the repository with CassandraChatMemoryRepository, JdbcChatMemoryRepository, or Neo4jChatMemoryRepository for durable storage. section 5.3 section 5.4.1
@Bean
ChatMemory chatMemory(ChatMemoryRepository chatMemoryRepository) {
return MessageWindowChatMemory.builder()
.chatMemoryRepository(chatMemoryRepository)
.build();
}
Build MessageWindowChatMemory with an injected repository so the windowing behavior remains while storage moves to a configured persistent implementation. section 5.4.1
