What is the difference between MessageChatMemoryAdvisor and PromptChatMemoryAdvisor?
answer
- both read/write memory around the call
- Message advisor = separate role-tagged messages (default)
- Prompt advisor = flatten history into system prompt
- prompt-based blurs roles + can collide with system msg
- prefer MessageChatMemoryAdvisor unless model needs flattening
basics
~20 sBoth replay conversation history, but differently. MessageChatMemoryAdvisor injects the history as a list of separate role-tagged messages (USER/ASSISTANT). PromptChatMemoryAdvisor flattens the history into text inside the system prompt. Message-based preserves structure; prompt-based is a single blended system message.
solid answer
~40 sBoth are advisors that read/write ChatMemory around a model call; the difference is *how* they put the history into the prompt. MessageChatMemoryAdvisor adds the remembered turns back as distinct Message objects with their original roles (USER, ASSISTANT, ...), so the model receives a proper structured conversation — this is the default and preferred choice, and it works cleanly with chat models that natively understand multi-message dialogues. PromptChatMemoryAdvisor instead renders the history as plain text and merges it into the system prompt, producing one system message that contains the transcript. Prompt-based can help with models/APIs that handle a single instruction block better, but it blurs role boundaries and can collide with your own system message. Rule of thumb: use MessageChatMemoryAdvisor unless you have a specific reason to flatten into the system prompt.
code
java · 11 lines// Default choice: preserve role structure
var messageBased = ChatClient.builder(chatModel)
.defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
.build();
// Alternative: fold history into the system prompt as text
var promptBased = ChatClient.builder(chatModel)
.defaultAdvisors(PromptChatMemoryAdvisor.builder(chatMemory).build())
.build();
// Both honor the same conversationId param and the same ChatMemory instance.go deeper
Enough to know both replay history; the distinction is a stretch goal.
Should state the core difference: separate messages vs. flattened into the system prompt.
Should articulate role preservation, system-prompt collision risk, and pick MessageChatMemoryAdvisor as default with justification.
Weighs prompt-injection surface, provider-specific behavior, and the separation between injection (advisor) and retention (ChatMemory).
## Shared job, different injection strategy An **advisor** in Spring AI is an interceptor around a `ChatClient` call. Both `MessageChatMemoryAdvisor` and `PromptChatMemoryAdvisor` do the same two-phase work: **before** the call they load history from `ChatMemory.get(conversationId)`; **after** the call they persist the new user message and assistant reply with `ChatMemory.add(...)`. What differs is *how the loaded history is inserted into the outgoing prompt*. ## MessageChatMemoryAdvisor — history as structured messages This advisor re-inserts each remembered turn as its own `Message` with its original **role** preserved: ``` [ SYSTEM ] You are a helpful assistant. [ USER ] What's the capital of France? <- from memory [ ASSISTANT] Paris. <- from memory [ USER ] How many people live there? <- current turn ``` Modern chat APIs are natively designed around a list of role-tagged messages, so the model sees a faithful, well-structured dialogue. This is the **default** and the one you should reach for. It keeps SYSTEM/USER/ASSISTANT boundaries crisp, which improves the model's ability to follow the conversation and reduces prompt-injection blur. ```java ChatClient.builder(chatModel) .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build()) .build(); ``` ## PromptChatMemoryAdvisor — history flattened into the system prompt This advisor **renders the history as text** and folds it into the **system prompt**. Instead of many role-tagged messages, the model gets essentially one system message whose body includes a rendered transcript, followed by the current user message: ``` [ SYSTEM ] You are a helpful assistant. Use the conversation memory below: USER: What's the capital of France? ASSISTANT: Paris. [ USER ] How many people live there? ``` ### When it helps - Models or endpoints that behave better with a single consolidated instruction block, or that don't handle long multi-message histories well. - When you want the memory presented as reference context rather than as authoritative prior turns. ### Gotchas - **Role blurring**: history loses its structural USER/ASSISTANT roles once flattened to text, which can confuse instruction-tuned models. - **System-prompt collisions**: it augments the system prompt, so it can interact awkwardly with your own carefully written system message. You need to make sure both coexist. - **Injection surface**: mixing user-authored history into the system prompt (the most trusted part) is a larger prompt-injection surface than keeping it as user-role messages. ## Choosing | Aspect | MessageChatMemoryAdvisor | PromptChatMemoryAdvisor | |---|---|---| | Injection | Separate role-tagged messages | Text merged into system prompt | | Roles preserved | Yes | No (flattened) | | Default / typical | Yes | Special cases | | Risk | Low | System-prompt collision, role blur | **Default to MessageChatMemoryAdvisor.** Reach for PromptChatMemoryAdvisor only when a model/provider specifically benefits from a single flattened context block. ## Common to both - Both need a `ChatMemory` (usually `MessageWindowChatMemory`) and honor the `ChatMemory.CONVERSATION_ID` advisor param. - Both are built via a `.builder(chatMemory)` fluent API in Spring AI 1.0. - Neither decides *what* to keep — the windowing/trimming policy lives in the `ChatMemory` implementation, not the advisor.
- Which advisor is the sensible default and why?MessageChatMemoryAdvisor — it re-inserts history as role-tagged messages, matching how modern chat APIs are designed. It preserves USER/ASSISTANT boundaries, avoids collisions with your own system prompt, and reduces role-blurring/injection risk.
- Which layer decides how many messages get replayed — the advisor or the ChatMemory?The ChatMemory implementation (e.g. MessageWindowChatMemory) owns the trimming/windowing policy. The advisor just injects whatever get() returns; it doesn't decide retention.
saying these in an interview costs you the question
- Claiming both advisors inject history identically
- Saying PromptChatMemoryAdvisor keeps message roles intact
- Not knowing MessageChatMemoryAdvisor is the default/preferred choice
- Thinking the advisor, not the ChatMemory, controls how many messages are kept