Skip to main content

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 diagrid-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​

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 diagrid-spring-ai-memory — the Catalyst-managed kvstore store (application.properties):

diagrid.spring-ai.memory.enabled=true
diagrid.spring-ai.memory.statestore=kvstore

4. Configure API Key​

export OPENAI_API_KEY="your-key-here"

5. Install Dependencies​

mvn package -DskipTests

6. Run with Catalyst Cloud​

Create the Catalyst project with managed workflow and a managed KV store enabled (and set it as the default for this session), register the agent, then run:

diagrid project create spring-ai-durable-memory --enable-managed-workflow --deploy-managed-kv --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 before it can answer, so this request reports a connection reset (a PowerShell error on Windows) rather than a status code — that is expected. 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

Check memory again:

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.

tip

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 diagrid-spring-ai-memory
  • Saw that MessageChatMemoryAdvisor persists 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​


Spring is a trademark of Broadcom Inc. and/or its subsidiaries.