Quickstart: Durable Memory
Build a durable Spring AI chat agent whose conversation is persisted with a MessageChatMemoryAdvisor,
and see exactly what durability does and does not cover: the Dapr Workflow (model + tools) survives
a crash, but the memory advisor — like every Spring AI response advisor — records the assistant's reply
only after a successful call.
You will learn how to:
- Back Spring AI chat memory with a Dapr state store using
dapr-spring-ai-memory - Recognize that Spring AI runs advisors synchronously — a response advisor's phase runs on the caller thread after the call returns
- See that a crash keeps the durable workflow but skips the response phase, so the answer isn't saved until you re-attach
- Complete the advisor chain by re-issuing the call under the same instance id
Prerequisites
- Diagrid Catalyst account
- Diagrid CLI
- JDK 21 or later, and Maven 3.9+
- An OpenAI API key
1. Log in to Catalyst
diagrid login
diagrid whoami
2. Clone and Navigate
git clone https://github.com/diagridio/catalyst-quickstarts.git
cd catalyst-quickstarts/agents/spring-ai/durable-memory
3. Explore the Code
The agent is ordinary Spring AI. The MessageChatMemoryAdvisor is a response advisor: its before
phase saves the user message, its after phase saves the assistant reply — and after runs only if
the call returns successfully.
public MemoryChatController(ChatClient.Builder builder, ChatMemory chatMemory) {
this.chatMemory = chatMemory;
this.chat = builder
.defaultSystem(SYSTEM)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
}
@PostMapping("/chat")
public ResponseEntity<String> chat(@RequestParam String id,
@RequestParam String conversationId,
@RequestParam String message) {
String answer = chat.prompt()
.user(message)
.advisors(a -> a
.param(DurableAdvisor.INSTANCE_ID_KEY, id) // durability handle (re-attach)
.param(ChatMemory.CONVERSATION_ID, conversationId)) // memory grouping key
.call()
.content();
return ResponseEntity.ok(answer);
}
Chat memory is backed by a Dapr state store via dapr-spring-ai-memory — the Catalyst-managed
agent-memory store (application.properties):
dapr.spring-ai.memory.enabled=true
dapr.spring-ai.memory.statestore=agent-memory
4. Configure API Key
- macOS/Linux
- Windows
export OPENAI_API_KEY="your-openai-api-key"
$env:OPENAI_API_KEY="your-openai-api-key"
5. Install Dependencies
mvn package -DskipTests
6. Run with Catalyst Cloud
Create the Catalyst project with agent infrastructure enabled (and set it as the default for this session), register the agent, then run:
diagrid project create spring-ai-durable-memory --enable-agent-infrastructure --wait --use
diagrid agent create spring-ai-durable-memory --wait
diagrid dev run -f dev-spring-ai-durable-memory.yaml --approve
7. See "response advisors run after a success call"
7.1 Start a durable turn (it blocks on a slow tool)
From a new terminal — the booking runs under an instance id you own (trip-1) and a
conversationId (alice), and blocks ~30s while the tool "commits":
curl -X POST "http://localhost:8080/chat?id=trip-1&conversationId=alice&message=Book%20a%20flight%20to%20Oslo,%20reference%20OSLO-1"
7.2 Inspect memory
In a third terminal, while the call is still blocked:
curl "http://localhost:8080/history?conversationId=alice"
Only the user question is there — before() saved it, but after() (which saves the answer)
hasn't run yet:
["USER: Book a flight to Oslo, reference OSLO-1"]
7.3 Crash the app mid-call
curl -X POST "http://localhost:8080/crash/kill"
The process dies. The workflow is safe in Catalyst — but the memory advisor's after phase never ran.
7.4 Restart and re-check memory
diagrid dev run -f dev-spring-ai-durable-memory.yaml --approve
curl "http://localhost:8080/history?conversationId=alice"
# ["USER: Book a flight to Oslo, reference OSLO-1"] — still no answer
7.5 Re-attach — now the response advisor runs
Re-issue the same call with the same instance id. It attaches to the resumed workflow, returns
successfully, and only now does after() record the answer:
curl -X POST "http://localhost:8080/chat?id=trip-1&conversationId=alice&message=Book%20a%20flight%20to%20Oslo,%20reference%20OSLO-1"
curl "http://localhost:8080/history?conversationId=alice"
[
"USER: Book a flight to Oslo, reference OSLO-1",
"USER: Book a flight to Oslo, reference OSLO-1",
"ASSISTANT: Booking OSLO-1 confirmed. Confirmation code: BK-..."
]
The assistant answer appears only after the successful call. The question appears twice because
before() runs on every attempt (the crashed one and the re-attach) — a reminder that advisor phases
run per call, while the workflow is what's durable.
Put anything that must survive a crash inside the workflow — as a @Tool activity, whose result is
checkpointed. Advisor response-phase effects (memory, logging, post-processing) are best-effort on top
of a successful call; make retries safe with a caller-owned instance id.
8. View in the Catalyst Web Console
Open the Catalyst Cloud web console, go to Workflows, and inspect the instance to see the model and tool activities that were checkpointed and replayed on recovery.
9. Clean Up
Stop the app with Ctrl+C, then delete the project:
diagrid project delete spring-ai-durable-memory
Summary
In this quickstart, you:
- Backed Spring AI chat memory with a Dapr state store via
dapr-spring-ai-memory - Saw that
MessageChatMemoryAdvisorpersists the assistant reply in its response phase — only after a successful call - Confirmed the durability boundary: the workflow survives a crash, but the caller-side advisor chain runs only on success
- Used a caller-owned instance id to re-attach and complete the advisor chain
Next Steps
- Try the durable workflows quickstart for the drop-in crash-recovery basics
- Learn more about Spring AI + Dapr
- Learn how to deploy AI agents to Kubernetes
Spring is a trademark of Broadcom Inc. and/or its subsidiaries.