# Spring AI Advisors

> A complete guide for Advisors in Spring AI, covering memory, RAG, tool calling, authentication, and custom advisor implementation.

- Skill: `mat-garcia/spring-ai-advisors` (Agent Skill)
- Install (CLI): `npx skillmds@latest add mat-garcia/spring-ai-advisors`
- Raw SKILL.md: https://api.skillmd.com/api/skills/mat-garcia/spring-ai-advisors/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- License: Complete terms in LICENSE.txt
- Author: mat-garcia (https://skillmd.com/u/mat-garcia)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/mat-garcia/spring-ai-advisors

---


# Spring AI - Advisors & Middleware Pattern

## Description

Complete guide for Advisors in Spring AI. Advisors implement the chain of responsibility pattern, providing middleware-like interceptors for request/response processing. Covers memory, RAG, tool calling, authentication, and custom advisor implementation.

## When to Use

- Adding cross-cutting concerns to chat operations
- Implementing chat memory management
- Injecting context (RAG, tools) automatically
- Request/response filtering and validation
- Security and authorization checks
- Observability and monitoring
- Response post-processing
- Multi-advisor chains

## Topics Covered

### 1. Advisor Architecture

- **Chain of Responsibility Pattern**: Sequential processing
- **Request/Response Interception**: Both directions
- **Advisor Stack**: Multiple advisors in sequence
- **Short-circuit Logic**: Early termination conditions
- **Context Propagation**: Sharing state across chain

### 2. Built-in Advisors

#### ChatMemoryAdvisor

- Automatic chat history management
- Context window optimization
- Token-aware message windowing
- Repository-based storage
- Thread-safe operations

#### RAGAdvisor (Recursive)

- Automatic document retrieval
- Query transformation on demand
- Context augmentation
- Document chunking integration
- Configurable retrieval strategy

#### ToolsAdvisor

- Automatic tool discovery and registration
- Function call handling
- Tool execution with error recovery
- OpenAI-compatible format
- Multi-tool orchestration

#### ParallelToolsAdvisor

- Execute multiple tools concurrently
- Result aggregation
- Timeout management
- Partial failure handling

#### ReasoningAdvisor

- Step-by-step reasoning support
- Thought process tracking
- Multi-hop reasoning
- Uncertainty quantification

#### SecurityAdvisor

- Input validation and sanitization
- Output filtering
- Role-based access control
- Audit logging
- Threat detection

#### MonitoringAdvisor

- Performance metrics
- Token usage tracking
- Cost analysis
- Error rate monitoring
- Latency analysis

### 3. Custom Advisor Implementation

- Extending ChatClientRequestAdvisor
- Before/after processing hooks
- Error handling in advisors
- State management
- Testing custom advisors

### 4. Advisor Configuration

- Registration order (chain sequence)
- Conditional activation
- Dynamic advisor loading
- Advisor parameters
- Runtime modification

### 5. Recursive Advisor Execution

- Two-stage process: collect, then execute
- Advisor interaction
- Nested advisor calls
- Termination conditions
- Performance optimization

## Code Patterns

### Basic Advisor Chain

```java
@Configuration
public class AdvisorConfiguration {
    @Bean
    public ChatClientRequestAdvisor chatMemoryAdvisor(
            ChatMemoryParticipant chatMemory) {
        return new ChatMemoryAdvisor(chatMemory);
    }

    @Bean
    public ChatClientRequestAdvisor ragAdvisor(
            VectorStore vectorStore) {
        return new RAGAdvisor(vectorStore);
    }

    @Bean
    public ChatClientRequestAdvisor toolsAdvisor(
            ToolRegistry toolRegistry) {
        return new ToolsAdvisor(toolRegistry);
    }
}
```

### Using Advisors with ChatClient

```java
@Service
public class ConversationService {
    @Autowired
    private ChatClient chatClient;

    @Autowired
    private List<ChatClientRequestAdvisor> advisors;

    public String chat(String userMessage, String conversationId) {
        // Advisors are automatically applied in order
        return chatClient.prompt()
            .user(userMessage)
            .advisors(advisors)  // Chain of advisors
            .call()
            .content();
    }
}
```

### ChatMemoryAdvisor

```java
@Configuration
public class ChatMemoryConfig {
    @Bean
    public ChatMemoryParticipant chatMemory(
            ChatMemoryStore store) {
        return new InMemoryChatMemoryStore()
            .withMaxMessages(50)
            .withTokenLimit(2000);
    }

    @Bean
    public ChatClientRequestAdvisor memoryAdvisor(
            ChatMemoryParticipant chatMemory) {
        return ChatMemoryAdvisor.builder()
            .chatMemory(chatMemory)
            .userIdResolver(request -> getCurrentUserId())
            .conversationIdResolver(request -> getCurrentConversationId())
            .build();
    }
}
```

### RAGAdvisor (Recursive)

```java
@Configuration
public class RAGAdvisorConfig {
    @Bean
    public ChatClientRequestAdvisor ragAdvisor(
            VectorStore vectorStore) {
        return new RAGAdvisor(
            vectorStore,
            new DefaultQueryTransformer(),
            DefaultRetrievalStrategy.TOP_K_5,
            DefaultContextAssembly.RANKED
        );
    }
}
```

### Combined Advisors (Memory + RAG + Tools)

```java
@Service
public class SmartAssistant {
    @Autowired
    private ChatClient chatClient;

    @Autowired
    private ChatClientRequestAdvisor memoryAdvisor;

    @Autowired
    private ChatClientRequestAdvisor ragAdvisor;

    @Autowired
    private ChatClientRequestAdvisor toolsAdvisor;

    public String ask(String question, String conversationId) {
        return chatClient.prompt()
            .system("You are a helpful assistant")
            .user(question)
            .advisors(List.of(
                memoryAdvisor,    // First: load chat history
                ragAdvisor,       // Second: augment with docs
                toolsAdvisor      // Third: enable tool use
            ))
            .call()
            .content();
    }
}
```

### Custom Advisor Implementation

```java
public class CustomValidationAdvisor
        extends ChatClientRequestAdvisor {

    @Override
    public ChatClientRequestAdvisor.ChatClientRequestAdvisorContext
            before(ChatClientRequestAdvisor.ChatClientRequestAdvisorContext context) {

        // Validate user input
        String userMessage = extractUserMessage(context);
        if (containsProhibitedContent(userMessage)) {
            throw new SecurityException("Invalid content detected");
        }

        return context;
    }

    @Override
    public ChatClientRequestAdvisor.ChatClientRequestAdvisorContext
            after(ChatClientRequestAdvisor.ChatClientRequestAdvisorContext context) {

        // Post-process response
        String response = context.getResponse().getResult().getOutput().getContent();
        String sanitized = sanitizeResponse(response);

        return context.withResponse(sanitized);
    }
}
```

### Conditional Advisor Activation

```java
@Configuration
public class ConditionalAdvisorConfig {
    @Bean
    @ConditionalOnProperty(name = "ai.rag.enabled", havingValue = "true")
    public ChatClientRequestAdvisor ragAdvisor(VectorStore vectorStore) {
        return new RAGAdvisor(vectorStore);
    }

    @Bean
    @ConditionalOnProperty(name = "ai.security.enabled", havingValue = "true")
    public ChatClientRequestAdvisor securityAdvisor() {
        return new SecurityAdvisor();
    }
}
```

### Advisor with Metrics

```java
public class MetricsAdvisor extends ChatClientRequestAdvisor {
    private final MeterRegistry meterRegistry;

    @Override
    public ChatClientRequestAdvisor.ChatClientRequestAdvisorContext
            before(ChatClientRequestAdvisor.ChatClientRequestAdvisorContext context) {
        context.setAttribute("startTime", System.currentTimeMillis());
        return context;
    }

    @Override
    public ChatClientRequestAdvisor.ChatClientRequestAdvisorContext
            after(ChatClientRequestAdvisor.ChatClientRequestAdvisorContext context) {

        long duration = System.currentTimeMillis() -
            (long) context.getAttribute("startTime");

        meterRegistry.timer("ai.request.duration").record(duration, TimeUnit.MILLISECONDS);
        meterRegistry.counter("ai.request.success").increment();

        return context;
    }
}
```

## Configuration

### Advisor Order

```java
@Configuration
public class AdvisorOrderConfig {
    @Bean("advisors")
    public List<ChatClientRequestAdvisor> advisors(
            ChatClientRequestAdvisor memoryAdvisor,
            ChatClientRequestAdvisor ragAdvisor,
            ChatClientRequestAdvisor toolsAdvisor,
            ChatClientRequestAdvisor securityAdvisor) {

        return List.of(
            securityAdvisor,      // 1. Validate early
            memoryAdvisor,        // 2. Load context
            ragAdvisor,           // 3. Augment with docs
            toolsAdvisor          // 4. Enable tools
            // Add custom advisors here
        );
    }
}
```

### Properties

```properties
spring.ai.advisor.memory.enabled=true
spring.ai.advisor.memory.max-messages=50
spring.ai.advisor.rag.enabled=true
spring.ai.advisor.rag.top-k=5
spring.ai.advisor.tools.enabled=true
spring.ai.advisor.recursive.enabled=true
```

## Best Practices

- Keep advisor logic focused and composable
- Order advisors thoughtfully (security early)
- Handle exceptions gracefully
- Implement proper resource cleanup
- Test advisors in isolation
- Monitor advisor performance
- Document advisor interactions
- Use type safety for advisor configuration

## Related Skills

- `chat-memory/SKILL.md` - Chat memory storage
- `rag-retrieval/SKILL.md` - RAG integration
- `tools-agents/SKILL.md` - Tool calling
- `chat-models/SKILL.md` - Chat models

## References

- API: `/pages/api/advisors.adoc`
- Recursive: `/pages/api/advisors-recursive.adoc`
- Examples: Provider-specific advisor usage

