Spring AI Advisor Source Code Analysis
A framework architect’s deep dive into the middleware pipeline that makes enterprise AI modular, composable, and maintainable — and why it’s the most important abstraction in Spring AI.
Introduction
Calling a Large Language Model from a line of business application is rarely as simple as sending a prompt and getting a response. Real enterprise AI features need to inject conversation history, retrieve relevant documents, apply security filters, log usage, enforce governance, and route requests between multiple models. If these concerns are handled ad hoc inside every service, the result is a tangled mess of cross-cutting logic that is difficult to test, impossible to reuse, and tightly coupled to both the AI provider and the application’s flow.
This is not a new problem. In middleware systems, cross-cutting concerns are typically extracted into interceptors, filters, and middleware pipelines. Spring AI adopts exactly this pattern through its Advisor mechanism — a chain of pluggable components that process prompts before they reach the model and can intercept the response on its way back.
Spring AI Advisors are inspired by the tried-and-tested architectures of Servlet Filters, Spring Interceptors, and the Spring Security Filter Chain. They bring the same modularity and composability to the AI domain. By isolating concerns like chat memory, retrieval augmentation, and observability into dedicated advisor beans, Spring AI gives architects a clean, extensible model for building complex AI pipelines.
In this analysis, we will examine the Advisor mechanism from the inside out. We’ll look at the interfaces, the chain construction, the built-in advisors, the context propagation, and the design patterns that make it all work. We’ll also discuss the tradeoffs and extract lessons that apply to any framework designer building middleware for AI.
The Enterprise Problem Advisors Solve
Before diving into the code, let’s examine the real-world requirements that drove the advisor abstraction.
Chat Memory
A stateless model call has no memory of previous turns. For a conversation to feel natural, the prompt must include the full message history. Naively embedding this logic into a business service leads to duplicated retrieval, inconsistent trimming, and a maintenance headache.
Retrieval Augmented Generation (RAG)
In enterprise search and Q&A systems, relevant documents must be fetched from a vector database and injected into the prompt as context. This retrieval logic — embedding query, similarity search, result ranking — is not part of the core business logic and should not clutter the service layer.
Security Filtering
Prompt injection attacks, personally identifiable information (PII) redaction, and compliance with corporate policies all require inspecting or modifying the prompt before it leaves the controlled environment. This is a classic cross-cutting concern.
Prompt Enrichment
Many applications need to add a system message, a disclaimer, or user metadata to every request. Doing this consistently across dozens of endpoints is impossible with copy-paste.
Observability
Token usage, latency, model selection, and error rates must be logged and metered. Again, this is not part of the business logic and should be transparent to the developer.
Tool Execution
When the LLM returns a tool call, the response must be intercepted, the tool executed, and a new prompt constructed — all in a loop. Orchestrating this manually is complex and error-prone.
Compliance
Regulated industries may need to archive every prompt and response, or block certain topics entirely. Embedding such checks in every service invites omissions.
When all these concerns live inside the same method that performs the core business call, you end up with a monolithic function that is hundreds of lines long, untestable, and impossible to change without fear. Spring AI’s Advisors solve this by giving each concern its own modular, reusable, and independently testable component.
Where Advisors Fit in Spring AI Architecture
To understand the Advisor, we must place it within the overall request pipeline.
- ChatClient is the developer-facing entry point. It builds the initial
Promptand delegates to an advisor chain. - Advisor Chain is an ordered list of
Advisorbeans that can modify the request and/or wrap the model call to post-process the response. - ChatModel is the portable interface to the LLM. It receives the final
Prompt(after all advisors have processed it) and returns aChatResponse. - Advisor components sit between the client and the model, acting as a transparent middleware layer.
This design means that the application code never calls ChatModel directly unless it has no need for advisors. The ChatClient abstraction, together with the advisor chain, becomes the recommended path for all but the simplest use cases.
Advisor Architecture Deep Dive
At the heart of the mechanism lies the Advisor interface and its more powerful sibling, CallAroundAdvisor.
The Advisor Interface
public interface Advisor {
/**
* Name that uniquely identifies this advisor.
*/
String getName();
/**
* Pre-process the advised request before it is sent to the model.
* This method is called for every advisor in order.
*/
AdvisedRequest advise(AdvisedRequest request, Map<String, Object> advisorContext);
/**
* Control the order of advisor execution. Lower values run first.
*/
default int getOrder() {
return Ordered.LOWEST_PRECEDENCE;
}
}
Architectural Analysis:
advise()receives anAdvisedRequest(a wrapper around thePromptandChatOptions) and returns a possibly modifiedAdvisedRequest. This allows an advisor to inject messages, alter options, or even replace the entire prompt.advisorContextis a shared mutable map that flows through the entire chain and the model call. It enables advisors to pass state to each other (e.g., chat memory storing the message list, or a RAG advisor saving retrieved documents for later use).getName()allows identification for logging and debugging.getOrder()controls the position in the chain. Together, these support a declarative, Spring-managed advisor pipeline.
CallAroundAdvisor – Full Around Interception
For cases where an advisor needs to intercept the response (e.g., logging, retry, response filtering), Spring AI provides CallAroundAdvisor:
public interface CallAroundAdvisor extends Advisor {
/**
* Wrap the execution of the rest of the chain and the model call.
* Invoked after all {@link #advise} methods have run.
*/
ChatResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}
The CallAroundAdvisorChain is a functional interface that represents the remaining advisors and the final model call. An around advisor can decide whether to call the chain, modify the response on its way back, or even short-circuit the entire call (e.g., for caching or rate limiting).
Design Intent:
By separating request modification (via advise) from full around interception, Spring AI achieves a clean dual-phase pipeline. Simple advisors that only inject messages do not need to implement around logic. Complex advisors like retry or caching can wrap the entire model interaction without affecting the simpler ones.
Execution Flow
The ChatClient orchestrates the advisors as follows:
- Build the initial
AdvisedRequestfrom the user-provided prompt and options. - Iterate through all advisors in order, calling
advise()on each. Each advisor returns a (possibly new)AdvisedRequest. The context map is shared and mutable. - If any advisor implements
CallAroundAdvisor, build a chain of these around advisors (in the same order). The end of the chain delegates to the actualChatModel.call()using the finalAdvisedRequest. - The first around advisor's
aroundCallis invoked; it may call the next, which eventually reaches the model. TheChatResponsebubbles back through each around advisor, which can modify or replace it. - The final
ChatResponse(after all around processing) is returned to the caller.
This design ensures that simple Advisor implementations see the effect of all previous advisors' advise calls, while around advisors can wrap the entire flow.
Why Spring AI Uses Advisors
The advisor mechanism is a deliberate choice to address separation of concerns, extensibility, and reusability. Let’s contrast it with the alternative: embedding all AI logic directly into service methods.
| Approach | Advisors | Embedded Service Logic |
|---|---|---|
| Separation of Concerns | Each concern in its own bean, managed by Spring | Everything mixed in one class |
| Reusability | Advisor beans can be reused across different prompts and services | Logic must be duplicated or extracted into utility classes |
| Testing | Each advisor can be unit-tested with mock AdvisedRequest and ChatResponse | Hard to isolate; requires mocking entire model |
| Ordering | Declarative via @Order or getOrder() | Implicit, fragile ordering in code |
| Composability | Combine advisors to build complex pipelines without changing code | Adding a new concern requires modifying the service method |
| Provider Independence | Advisors work with any ChatModel implementation | May inadvertently couple to a specific provider’s features |
| Enterprise Governance | Centralized points for logging, security, compliance | Scattered, incomplete enforcement |
The advisor pattern applies the Open/Closed Principle at the framework level: the core AI call is open for extension (via new advisors) but closed for modification (the ChatClient and ChatModel interfaces remain stable). This is the same principle that made Servlet Filters and Spring Interceptors so successful.
Advisor Chain Architecture
The advisor chain is not a monolithic list but a carefully constructed pipeline that respects both order and the distinction between request and response phases.
During the request phase (solid lines), the advise method of each advisor is called in order. The request flows sequentially from SecurityFilter (which may strip PII) to ChatMemory (which injects history) to VectorStore (which adds retrieved context). The Observability around advisor also participates in this phase to record the final prompt.
During the call phase (around), the Observability advisor wraps the model call, measuring latency and token usage, and possibly adding trace IDs to the response. Other around advisors could implement retry, caching, or response validation.
Similarity to Established Patterns
Spring AI’s advisor chain draws directly from the Chain of Responsibility pattern as implemented in Servlet Filters and Spring’s HandlerInterceptor. In both, a request passes through an ordered chain, each element can modify the request, pass it on, and optionally post-process the response. The use of a shared context map is reminiscent of the FilterChain’s mutable request/response wrappers. This familiarity reduces the learning curve for experienced Spring developers.
Request Advisor vs Response Advisor
Spring AI’s two advisor types map to the two halves of the pipeline.
Request Advisors (Advisor.advise()) are responsible for augmenting the prompt before it reaches the model. They cannot see or modify the response. Examples:
- Chat memory injection
- System message addition
- RAG context retrieval and insertion
- Security/content filtering on the outgoing prompt
Response Advisors (via CallAroundAdvisor.aroundCall()) can inspect and modify the response. They can:
- Log response content and metadata
- Apply safety filters on the generated text
- Execute tool calls and feed results back
- Cache or short-circuit the call
This separation allows a clean mental model: request advisors add things; around advisors observe or react. Complex logic like tool calling, which involves multiple back-and-forth interactions, can be encapsulated entirely within an around advisor, hiding the iterative loop from the rest of the pipeline.
ChatMemoryAdvisor Source Code Analysis
The ChatMemoryAdvisor is the simplest, most illustrative built-in advisor. It injects conversation history into the prompt.
Responsibilities
- Retrieve past messages from a
ChatMemoryimplementation (in-memory, Redis, JDBC). - Prepend them to the current message list in the
AdvisedRequest. - After the model call, store the new exchange (user message and assistant response) back into the memory store.
Source Code Flow
public class ChatMemoryAdvisor implements Advisor, CallAroundAdvisor {
private final ChatMemory chatMemory;
public ChatMemoryAdvisor(ChatMemory chatMemory) {
this.chatMemory = chatMemory;
}
@Override
public AdvisedRequest advise(AdvisedRequest request, Map<String, Object> context) {
String conversationId = (String) context.get("conversationId");
List<Message> history = chatMemory.get(conversationId);
if (!history.isEmpty()) {
request = request.withPrompt(request.getPrompt().withMessages(
Stream.concat(history.stream(), request.getPrompt().getMessages().stream())
.toList()
));
}
return request;
}
@Override
public ChatResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
ChatResponse response = chain.nextCall(request);
String conversationId = (String) request.getAdvisorContext().get("conversationId");
// Extract the last user message and the assistant response
chatMemory.add(conversationId, request.getPrompt().getMessages());
// In real code, also add the assistant response
return response;
}
}
Architectural Analysis:
- The advisor uses the
advisorContextto find aconversationId. This is a common pattern: context keys are agreed upon between the advisor and the caller.ChatClientcan set initial context entries, e.g.,client.prompt().advisors(a -> a.param("conversationId", id)). - During the request phase, history messages are prepended. By using
request.withPrompt(), the advisor creates a newAdvisedRequestwith an updatedPromptobject. This preserves immutability. - The response is handled in
aroundCallbecause the advisor needs to store the assistant’s reply. It wraps the chain call, records the history after the model returns, and passes the response through unmodified.
This advisor demonstrates the elegance of the dual-phase design: request preparation (adding history) and response post-processing (saving history) are cleanly separated yet co-located in one cohesive component.
QuestionAnswerAdvisor Source Code Analysis
The QuestionAnswerAdvisor implements a classic Retrieval-Augmented Generation (RAG) pattern.
RAG Workflow
- Receive the user’s question from the
AdvisedRequest. - Embed the question and query a
VectorStorefor relevant documents. - Inject the retrieved documents as additional context (often as a
SystemMessage) into the prompt. - Call the model with the enriched prompt.
Code Snippet
public class QuestionAnswerAdvisor implements Advisor {
private final VectorStore vectorStore;
@Override
public AdvisedRequest advise(AdvisedRequest request, Map<String, Object> context) {
String userQuery = extractUserQuestion(request);
List<Document> docs = vectorStore.similaritySearch(userQuery);
String contextText = docs.stream()
.map(Document::getContent)
.collect(Collectors.joining("\n\n"));
Message contextMessage = new SystemMessage(
"Use the following documents to answer the question:\n" + contextText
);
return request.withPrompt(
request.getPrompt().withMessages(
List.of(contextMessage, /* user message */)
)
);
}
}
Architectural Analysis:
- The retrieval logic is completely isolated from the business call. The service that invokes
ChatClientonly needs to declare the advisor; it never knows about vector stores or document formats. - If the retrieval strategy changes (e.g., hybrid search, re-ranking), only this advisor is modified. The model call remains unchanged.
- The injected context is a
SystemMessage, which is role-appropriate and provider-independent. Advisors manipulate the abstractMessagehierarchy, never provider-specific tokens.
This is the advisor pattern’s value proposition in a nutshell: transparent augmentation of AI requests without coupling.
VectorStoreAdvisor Analysis
The VectorStoreAdvisor is a generalized form of the RAG advisor. While QuestionAnswerAdvisor is purpose-built for Q&A, VectorStoreAdvisor provides a configurable retrieval pipeline that can be reused for different use cases (chat over documents, entity extraction, etc.).
It typically:
- Accepts a query extraction strategy (e.g., take the last user message, or use a specific context key).
- Performs vector search with configurable similarity threshold and top-K.
- Allows specification of how the documents are injected: as system message, as user message, with metadata headers, or using a template.
This configurability is achieved through a builder pattern, and the advisor itself is stateless (other than the VectorStore reference). It is the epitome of a reusable pipeline component.
The design lesson here is that advisors can be parameterized, not just via Spring configuration but through context entries that the ChatClient passes at runtime. This balances declarative wiring with dynamic behavior.
Advisor Context Design
The shared Map<String, Object> advisorContext is the nervous system of the advisor pipeline.
Purpose
- Transport data between advisors (e.g., conversation ID, retrieved documents, security flags).
- Allow the caller to pass parameters that influence advisor behavior without reconfiguring beans.
- Store intermediate results that may be needed by post-processing or downstream advisors.
Propagation
The context map is created by ChatClient and passed to every advisor’s advise and aroundCall methods. Advisors can read from it, write to it, or even remove entries. Because it is a mutable Map, changes made by an early advisor are visible to later ones.
However, uncontrolled mutation can lead to ordering dependencies and hard-to-debug issues. Spring AI mitigates this by convention: context keys should be well-documented, and advisors should treat the map as a communication bus, not a global state dump.
Future Extensibility
The context mechanism is crucial for advanced workflows. In a tool-calling loop, an advisor can store the tool execution results in the context. A subsequent advisor (or the same one on the next iteration) can read those results and decide whether to continue. This enables stateful agent patterns without global variables.
Advisor Lifecycle
Let’s follow a complete lifecycle, from user request to final output.
Step-by-step:
- Application calls
chatClient.call()with a user message. ChatClientcreates an initialAdvisedRequestand a fresh context map.SecurityAdvisor.advise()strips any PII from the user message, returning a clean request.MemoryAdvisor.advise()fetches conversation history and prepends it.RAGAdvisor.advise()retrieves relevant documents and injects a system message.- The final
AdvisedRequestis passed to the around chain.ObserveAroundAdvisor.aroundCall()starts a timer, calls the next in chain, and the model is invoked. ChatModelreturns aChatResponse.ObserveAroundAdvisorlogs the response and token usage, then returns the response.MemoryAdvisor(as aCallAroundAdvisor) stores the new turn in memory.- The
ChatResponseis returned to the application.
This pipeline is completely transparent to the application, which only sees the final response.
Design Patterns Used
The advisor mechanism is a layered application of several classic patterns.
Chain of Responsibility Pattern
Core execution model. Each advisor (both request and around) forms a link in a chain. In the request phase, every advisor’s advise() is called in sequence. In the around phase, each around advisor’s aroundCall() may process the request, call the next link, and post-process the response.
Benefits: Loose coupling, dynamic composition, ordered processing.
Tradeoffs: The chain must be carefully ordered; an advisor that modifies the prompt in an unexpected way can break downstream logic.
Decorator Pattern (conceptual)
Around advisors wrap the model call, enhancing its behavior without changing the interface. The aroundCall method can add logging, retry, or caching — exactly like a decorator.
Benefits: Transparent addition of cross-cutting behavior.
Tradeoffs: The decorator must not break the contract; if an around advisor short-circuits without returning a valid ChatResponse, the chain fails.
Strategy Pattern
Each advisor implementation is a strategy for a specific concern (memory, RAG, security). The ChatClient depends only on the Advisor interface.
Benefits: Different strategies can be swapped at configuration time.
Tradeoffs: All strategies must adhere to the same narrow interface; complex strategies may need to implement CallAroundAdvisor and manage internal state.
Pipeline Pattern
The overall execution — request enrichment, model call, response post-processing — is a classic pipeline. Spring AI formalizes this with separate phases.
Benefits: Clear separation of phases, easier to reason about data flow.
Tradeoffs: Requires discipline to avoid mixing concerns between phases.
Dependency Injection
All advisors are Spring beans, automatically discovered and injected into ChatClient by auto-configuration. The order is controlled via @Order or the Ordered interface.
Benefits: Loose coupling, easy configuration, full Spring lifecycle support.
Tradeoffs: Relies on the Spring container; not portable to non-Spring environments without emulation.
Source Code Walkthrough
Let’s examine key implementation details from the perspective of the framework’s internal wiring.
Advisor Interface and Base Classes
The core interface defines advise() and getOrder(). CallAroundAdvisor adds aroundCall(). The ChatClient holds a List<Advisor> and sorts it by order.
Chain Construction inside ChatClient
// Simplified from ChatClient source
public ChatResponse call(Prompt prompt) {
AdvisedRequest request = new AdvisedRequest(prompt, ...);
Map<String, Object> context = new HashMap<>();
// Request phase: apply all advisors
for (Advisor advisor : advisors) {
request = advisor.advise(request, context);
}
// Build around chain
CallAroundAdvisorChain chain = buildChain(request, context);
return chain.nextCall(request);
}
private CallAroundAdvisorChain buildChain(AdvisedRequest request, Map<String, Object> context) {
List<CallAroundAdvisor> arounds = advisors.stream()
.filter(a -> a instanceof CallAroundAdvisor)
.map(a -> (CallAroundAdvisor) a)
.collect(Collectors.toList());
CallAroundAdvisorChain modelCall = req -> chatModel.call(req.getPrompt());
// Create chain in reverse order so first around advisor is outermost
for (int i = arounds.size() - 1; i >= 0; i--) {
CallAroundAdvisor current = arounds.get(i);
CallAroundAdvisorChain next = modelCall;
modelCall = req -> current.aroundCall(req, next);
}
return modelCall;
}
Architectural Insight:
- The request phase is a simple loop; each advisor transforms the request. Immutability of
AdvisedRequestensures that each step is safe. - The around chain is built by nesting lambdas, creating a functional chain. The order of around advisors is preserved (the first in the list becomes the outermost wrapper), which matches the intuitive mental model.
- The terminal link is the actual
ChatModel.call(). This clean separation between the model and the advisors means the model is completely unaware of the pipeline.
Built-in Advisors
Apart from ChatMemoryAdvisor and RAG advisors, Spring AI includes SimpleLoggerAdvisor (logging prompts and responses), RetryAdvisor (retry on transient errors), and ContentFilterAdvisor. Each follows the same pattern: implement Advisor and optionally CallAroundAdvisor, read from the context, modify the request/response.
This uniformity makes the framework predictable and easy to extend. A team that learns one advisor can implement any custom advisor.
Enterprise Benefits
The Advisor abstraction directly addresses enterprise-scale AI challenges.
Modular AI Architecture
Advisors allow splitting complex AI features into independent, testable modules. A single ChatClient invocation can compose memory, retrieval, security, and observability without any coupling between those concerns.
Better Maintainability
When the RAG retrieval strategy changes from vector search to hybrid search, only the VectorStoreAdvisor is updated. The business service, other advisors, and the model call remain untouched.
Reusable Components
The same ChatMemoryAdvisor can be used for a customer service bot and an internal knowledge assistant. Configuration differences are handled through context parameters, not code duplication.
Easier Testing
Advisors can be unit-tested by providing a mock AdvisedRequest and verifying the returned request or response. There’s no need to spin up a real model or wire an entire Spring context.
Enterprise Governance
Centralized SecurityFilterAdvisor can enforce PII redaction and content policies across every AI interaction in the company. ObservabilityAdvisor can ensure that every call is logged to the corporate monitoring system, with provider-independent metrics.
Cross-Cutting AI Concerns
The advisor chain is the single place where infrastructure concerns like rate limiting, failover, and cost tracking live. This dramatically simplifies the application layer and reduces the risk of missing a critical policy in one of a hundred endpoints.
Design Tradeoffs
No architecture is without its costs.
Added Complexity
The advisor chain introduces indirection. A developer who just wants to call a model may be confused by the extra layer. However, Spring AI provides sensible defaults (no advisors by default) and simple configuration for the most common patterns.
Execution Overhead
Each advisor adds a small runtime cost — method calls, object allocations. In the grand scheme of LLM latency (seconds), this is negligible, but for extremely high-throughput systems, it’s worth measuring.
Ordering Challenges
The order of advisors matters. If SecurityAdvisor runs after MemoryAdvisor, sensitive history may slip through. Spring AI’s getOrder() mechanism requires careful orchestration, and misconfiguration can lead to subtle bugs. Good documentation and sensible defaults are essential.
Debugging Complexity
When a prompt is modified by multiple advisors, it can be hard to trace the final content back to its source. A centralized DebugAdvisor or built-in logging can help, but this remains an area where framework support is still maturing.
Context Management Costs
The shared Map<String, Object> context is powerful but lacks type safety and can become a dumping ground. Strong conventions and possibly typed context wrappers would improve this.
Overall, the strengths massively outweigh the weaknesses for any non-trivial AI integration. The advisor mechanism is a force multiplier for team productivity and system resilience.
Comparison with Other Frameworks
| Framework / Approach | Middleware Model | Composability | Request/Response Interception | Context Propagation | Ecosystem |
|---|---|---|---|---|---|
| Spring AI Advisors | Ordered chain of Advisor beans, with around interception | High; any advisor can be combined | Yes, via advise() and aroundCall() | Shared mutable Map; can be typed via context keys | Spring Boot auto-configuration, Micrometer, AOP |
| LangChain4j | AiServices with ChatMemory and Retriever as explicit parameters | Moderate; built-in concepts but less generic pipeline | ChatMemory and ContentRetriever are separate concerns; no unified around advisor | Conversation ID and metadata | Great for prototyping, less emphasis on strict separation |
| Direct OpenAI SDK | None | None | None; cross-cutting logic is embedded in service code | None | Lowest complexity, highest coupling |
| Custom Middleware Layers | Homegrown interceptor patterns | Varies; often tightly coupled to a single provider | Usually limited to pre-processing | Custom, often brittle | High maintenance cost |
Spring AI’s advisor system stands out for its generality and deep Spring integration. While LangChain4j also offers memory and RAG, its model is less of a generic middleware pipeline and more of a collection of purpose-built features. Spring AI’s approach, by contrast, is an extensible framework that makes no assumptions about what concerns will emerge — it provides the wiring, and you define the components.
Lessons for Framework Designers
For architects building the next AI integration layer, the Spring AI advisor design offers several universal principles.
- Build Pipelines, Not Monoliths. The AI call should never be a single monolithic method. Isolate the core model invocation and surround it with a composable pipeline of interceptors.
- Isolate Cross-Cutting Concerns. Chat memory, RAG, security, and logging are not business logic. They belong in dedicated, reusable middleware components.
- Design Extensible Middleware. The
Advisorinterface with both request and around phases allows a wide variety of behaviors to be plugged in without changing the core API. - Favor Composition over Inheritance. Advisors compose via a list, not a class hierarchy. This gives maximum flexibility and avoids the fragile base class problem.
- Enable Reusability. Parameterize advisors via context maps rather than hard-coding behavior. This allows the same advisor class to serve many different flows.
- Leverage Familiar Patterns. By mirroring Servlet Filters and Spring Interceptors, Spring AI reduces the learning curve. Framework designers should build on existing mental models wherever possible.
Future Evolution
The advisor mechanism is poised to become the backbone for advanced AI workflows.
Tool Calling Advisors
An advisor can detect finishReason == "tool_calls", execute the requested function, and automatically feed the result back into a new prompt — all within a single aroundCall loop. This will make tool calling transparent to the application.
Agent Advisors
Multi-step agents that plan, act, and observe can be implemented as a specialized around advisor that iterates until a terminal condition is met, using the context to maintain state across steps.
MCP Advisors
With the Model Context Protocol, an advisor could connect to an MCP server to retrieve context, tools, or resources, injecting them into the prompt and handling the response accordingly.
Multi-Agent Workflows
As systems involving multiple specialized models emerge, advisors could route prompts to different models based on intent classification, all while keeping the application interface unchanged.
Observability Advisors
Dedicated advisors for OpenTelemetry tracing, cost allocation, and anomaly detection will integrate directly with enterprise monitoring stacks.
The advisor mechanism is inherently forward-compatible because it doesn’t prescribe what advisors do — only how they connect. This is the hallmark of a well-designed framework extension point.
FAQ
1. Why doesn’t Spring AI place chat memory inside the ChatModel?
Memory management is not the model’s concern. The model’s single responsibility is to call the LLM. Injecting history is a cross-cutting concern best handled by an advisor. This keeps the model interface clean and testable.
2. Why are advisors better than service-layer enrichment?
Service-layer enrichment scatters AI logic across every service, leading to duplication and inconsistency. Advisors centralize these concerns, making them reusable and easier to maintain.
3. How is execution order managed among advisors?
By implementing Ordered or using the @Order annotation. Lower order values execute first. The framework sorts all advisors by their order before building the chain.
4. Can advisors modify responses?
Yes, but only CallAroundAdvisor can intercept the response. The aroundCall method receives the response from the chain and can modify or replace it before returning it up the chain.
5. How does context propagate through the chain?
A mutable Map<String, Object> is created at the start of the call and passed to every advisor. Advisors can read and write to it. This map lives for the duration of the call and is not thread-safe for concurrent modifications.
6. Can an advisor short-circuit the model call?
Yes. A CallAroundAdvisor can decide not to call the chain and instead return a synthetic ChatResponse directly. This is useful for caching (return cached response) or rate limiting (return a “rate limit” message).
7. How do I pass dynamic parameters to an advisor?
Use the context map. The ChatClient builder allows setting initial context entries, and custom code before the call can populate them.
8. Can I have multiple advisors of the same type?
Yes, as long as they are distinct beans with different names. You might use two VectorStoreAdvisor instances that query different indexes, differentiated by context keys.
9. Are advisors thread-safe?
Advisors should be stateless or contain only thread-safe dependencies (like a VectorStore client). The mutable context is per-call, so no shared state conflict across threads.
10. How do advisors relate to Spring AOP?
Advisors are a higher-level abstraction specific to AI requests, while AOP is a general-purpose proxy mechanism. Advisors could be implemented using AOP, but Spring AI’s explicit chain is more transparent and easier to configure for this domain.
11. Can I use advisors without ChatClient?
Technically, the advisor chain is orchestrated by ChatClient. However, you could manually replicate the chain logic if you directly use ChatModel. Using ChatClient is recommended for all non-trivial use cases.
12. What is the performance impact of many advisors?
Negligible compared to model latency. A few extra object allocations and method calls add microseconds, while an LLM call takes seconds. Only in extremely high-throughput, low-latency scenarios would you need to profile.
Conclusion
The Advisor mechanism is not merely a feature of Spring AI — it is the architectural backbone that transforms a simple model call into a robust, enterprise-grade AI platform. By adopting a middleware pipeline with ordered, composable interceptors, Spring AI separates the concerns of prompt engineering, memory, retrieval, security, and observability into clean, reusable components.
This design draws from decades of proven patterns in the Spring ecosystem (Servlet Filters, Interceptors, Security Filter Chains) and applies them to the new frontier of AI. The result is an extensible framework where new capabilities — from tool calling to multi-agent orchestration — can be added as advisors without ever touching the core model interface.
For framework designers, the lesson is clear: in the age of LLMs, the most valuable abstraction is not the prompt or the model, but the middleware that orchestrates everything in between. Spring AI’s advisor chain is a masterclass in that principle, and it will only grow more important as AI systems become more complex.