beginner~2h

Advisors

Memory, RAG, logging, guardrails — in Spring AI, all four are the same mechanism wearing different clothes. This module is that mechanism.

Learning objectives

  • Beginner: SimpleLoggerAdvisor alone, to see exactly what prompt is being sent while learning the framework.
  • Intermediate: MessageChatMemoryAdvisor + SafeGuardAdvisor together for a customer-facing chatbot needing both continuity and basic content screening.
  • Advanced: A custom advisor chain combining memory, RAG (RetrievalAugmentationAdvisor), and a custom rate-limiting advisor, with getOrder() carefully sequenced so retrieval sees the real user question and rate limiting runs before any expensive retrieval work happens at all.

◆ The problem

Chat memory needs to inject prior messages before the call and capture the new exchange after. RAG needs to inject retrieved context before the call. Logging needs to observe both the request and response without touching either. Doing this by hand inside every controller method, for every feature, means the same "before/after the model call" plumbing copy-pasted everywhere.

An Advisor is Spring AI's answer: an interceptor that sits around the actual model call, able to modify the outgoing request before it's sent and/or the incoming response before it's returned — registered once on a ChatClient and applied automatically to every call made through it.

Advisors form a chain around the model call — each can modify the outgoing request on the way in, and the response on the way back out, before handing off to the next advisor.

AdvisorWhat it doesCovered in depth
MessageChatMemoryAdvisorInjects prior conversation history before the call, appends the new exchange afterModule 07
RetrievalAugmentationAdvisorRetrieves relevant documents from a VectorStore and injects them as context — the core of RAG (replaces the older, now-deprecated QuestionAnswerAdvisor)Module 09
SafeGuardAdvisorScreens requests/responses against a list of sensitive words/topics, blocking or rewriting matchesHere — see below
SimpleLoggerAdvisorLogs the outgoing request and incoming response — a debugging/observability aidHere — see below
ChatClient chatClient = builder .defaultAdvisors( new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory).build(), SafeGuardAdvisor.builder() .sensitiveWords(List.of("password", "ssn")) .build() ) .build();

💻 Code example

ChatClient chatClient = builder .defaultAdvisors( new SimpleLoggerAdvisor(), MessageChatMemoryAdvisor.builder(chatMemory).build(), SafeGuardAdvisor.builder() .sensitiveWords(List.of("password", "ssn")) .build() ) .build();
public class RequestIdAdvisor implements CallAdvisor { @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { String requestId = UUID.randomUUID().toString(); ChatClientRequest withId = request.mutate() .context("requestId", requestId) // shared context, see §06.4 .build(); long start = System.currentTimeMillis(); ChatClientResponse response = chain.nextCall(withId); // hand off to the next advisor / the model log.info("[{}] completed in {}ms", requestId, System.currentTimeMillis() - start); return response; } @Override public String getName() { return "RequestIdAdvisor"; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // see §06.4 }

💻 Code example

public class RequestIdAdvisor implements CallAdvisor { @Override public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) { String requestId = UUID.randomUUID().toString(); ChatClientRequest withId = request.mutate() .context("requestId", requestId) // shared context, see §06.4 .build(); long start = System.currentTimeMillis(); ChatClientResponse response = chain.nextCall(withId); // hand off to the next advisor / the model log.info("[{}] completed in {}ms", requestId, System.currentTimeMillis() - start); return response; } @Override public String getName() { return "RequestIdAdvisor"; } @Override public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // see §06.4 }

Advisors run in a defined order (lowest getOrder() value first, wrapping outward-in), and each can read/write a shared key-value context map that travels with the request through the whole chain — this is how, for example, a memory advisor can stash the resolved conversation ID for a later advisor (or your own code) to read, without threading an extra parameter through every method signature.

◆ Under the hood — why order matters

Consider MessageChatMemoryAdvisor and RetrievalAugmentationAdvisor registered together: memory should typically run "outer" (added to the request first) so retrieved RAG context doesn't get mistaken for a stored conversation turn, while RAG retrieval should run close to the model call so it retrieves based on the fully-formed user question, not an intermediate state. Getting the order wrong doesn't throw an exception — it silently produces a subtly worse prompt, which is exactly the kind of bug that's hard to notice without explicitly logging the final assembled prompt (via SimpleLoggerAdvisor) during development.

▲ Pitfall

Advisors that both write to the same context key can silently overwrite each other's data depending on order — treat context keys as a shared namespace across every advisor on the client, and pick specific, collision-resistant key names.

✓ Quick recap

What single mechanism underlies chat memory, RAG, and logging in Spring AI? Advisors — interceptors around the model call that can modify the request and/or response. What determines the order advisors run in? Their getOrder() value, lowest first, wrapping outward-in around the model call. What's the risk of getting advisor order wrong? No exception — just a silently worse-assembled prompt, which is why logging the final prompt during development matters.

Want a visual for this concept?

Generate a diagram tailored to “Advisors” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Chat Memory & Conversation Management← Back to all Spring AI chapters