Spring AI Advisor 深度实战:构建严谨的 AI Agent 拦截链

前言

在构建生产级 AI Agent 系统时,我们经常会遇到这样的需求:如何在请求发送给大模型之前进行统一处理?如何在多个处理步骤之间协调配合?如何优雅地实现日志记录、安全检查和提示增强?

Spring AI 的 Advisor 机制为这些问题提供了优雅的解决方案。本文将结合实际项目经验,深入探讨如何设计和实现一个严谨的 Advisor 体系。

一、Advisor 体系架构设计

在一个真实的 AI Agent 项目中,我们需要多层次的请求处理:

复制代码
@Configuration
public class AgentAdvisorConfig {
    
    @Bean
    public ChatClient agentChatClient(ChatClient.Builder builder) {
        return builder
                .defaultAdvisors(
                    new SecurityCheckAdvisor(),      // 优先级最高:安全检查
                    new ContextEnrichAdvisor(),      // 上下文增强
                    new ReReadingAdvisor(),          // Re2 推理增强
                    new TokenLimitAdvisor(),         // Token 限制
                    new LoggingAdvisor(),            // 日志记录
                    new FallbackAdvisor()            // 降级处理
                )
                .build();
    }
}

每个 Advisor 都有明确的职责和执行顺序:

复制代码
public class AdvisorOrder {
    public static final int SECURITY = -100;    // 最早:安全检查
    public static final int CONTEXT = -50;      // 上下文准备
    public static final int ENHANCE = 0;        // 提示增强
    public static final int LIMIT = 50;         // 限制处理
    public static final int LOGGING = 100;      // 日志记录
    public static final int FALLBACK = 200;     // 最后:兜底处理
}

二、核心 Advisor 实现详解

1. 安全防护 Advisor

复制代码
@Component
public class SecurityCheckAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    
    private final Set<String> blockedKeywords = Set.of("malware", "hack", "exploit");
    private final Pattern sqlInjectionPattern = Pattern.compile(
        "(?i)(\\bSELECT\\b|\\bDROP\\b|\\bDELETE\\b|\\bUPDATE\\b|\\bINSERT\\b)"
    );
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, 
                                     CallAroundAdvisorChain chain) {
        String userInput = advisedRequest.userText();
        
        // 关键词检查
        if (containsBlockedKeywords(userInput)) {
            return createBlockedResponse("输入包含不安全内容");
        }
        
        // SQL 注入检查
        if (sqlInjectionPattern.matcher(userInput).find()) {
            return createBlockedResponse("检测到潜在的不安全操作");
        }
        
        // 内容长度检查
        if (userInput.length() > 10000) {
            return createBlockedResponse("输入内容过长");
        }
        
        // 通过检查,继续执行链
        return chain.nextAroundCall(advisedRequest);
    }
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, 
                                             StreamAroundAdvisorChain chain) {
        String userInput = advisedRequest.userText();
        
        if (containsBlockedKeywords(userInput)) {
            return Flux.just(createBlockedResponse("流式响应被安全策略拦截"));
        }
        
        return chain.nextAroundStream(advisedRequest);
    }
    
    private AdvisedResponse createBlockedResponse(String message) {
        return new AdvisedResponse(
            new AssistantMessage("抱歉," + message),
            Map.of("blocked", true, "reason", "security_check")
        );
    }
    
    private boolean containsBlockedKeywords(String text) {
        return blockedKeywords.stream().anyMatch(text.toLowerCase()::contains);
    }
    
    @Override
    public int getOrder() {
        return AdvisorOrder.SECURITY;
    }
    
    @Override
    public String getName() {
        return "SecurityCheckAdvisor";
    }
}

2. Re2 推理增强 Advisor(实战版本)

复制代码
@Component
public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    
    private static final String DEFAULT_TEMPLATE = """
            请仔细分析以下问题:
            {re2_input_query}
            
            现在,请重新阅读理解问题,确保没有遗漏任何关键信息:
            {re2_input_query}
            
            请基于完整的理解给出答案。
            """;
    
    private static final String RE2_ENABLED = "RE2_ENABLED";
    private static final String RE2_ORIGINAL_QUERY = "RE2_ORIGINAL_QUERY";
    private static final String RE2_PROCESSING_TIME = "RE2_PROCESSING_TIME";
    
    private final String promptTemplate;
    private final boolean enableForStreaming;
    
    public ReReadingAdvisor() {
        this(DEFAULT_TEMPLATE, true);
    }
    
    public ReReadingAdvisor(String promptTemplate, boolean enableForStreaming) {
        this.promptTemplate = promptTemplate;
        this.enableForStreaming = enableForStreaming;
    }
    
    private AdvisedRequest prepareRequest(AdvisedRequest advisedRequest) {
        long startTime = System.currentTimeMillis();
        String userText = advisedRequest.userText();
        
        // 边界检查:空输入
        if (userText == null || userText.trim().isEmpty()) {
            return advisedRequest;
        }
        
        // 边界检查:超长输入(避免 Token 浪费)
        if (userText.length() > 8000) {
            return advisedRequest;
        }
        
        // 检查是否已被其他 Advisor 禁用重读
        Map<String, Object> existingContext = advisedRequest.adviseContext();
        if (Boolean.FALSE.equals(existingContext.get("SIMPLE_QUERY"))) {
            return advisedRequest;  // 简单查询不需要重读
        }
        
        Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
        advisedUserParams.put("re2_input_query", userText);
        
        return AdvisedRequest.from(advisedRequest)
                .userText(promptTemplate)
                .userParams(advisedUserParams)
                .adviseContext(context -> {
                    context.put(RE2_ENABLED, true);
                    context.put(RE2_ORIGINAL_QUERY, userText);
                    context.put(RE2_PROCESSING_TIME, System.currentTimeMillis() - startTime);
                    return context;
                })
                .build();
    }
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, 
                                     CallAroundAdvisorChain chain) {
        AdvisedRequest modified = prepareRequest(advisedRequest);
        AdvisedResponse response = chain.nextAroundCall(modified);
        
        // 在响应中添加处理标记
        return enrichResponse(response, modified);
    }
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, 
                                             StreamAroundAdvisorChain chain) {
        if (!enableForStreaming) {
            return chain.nextAroundStream(advisedRequest);
        }
        
        return Mono.just(advisedRequest)
                .publishOn(Schedulers.boundedElastic())
                .map(this::prepareRequest)
                .flatMapMany(chain::nextAroundStream)
                .map(this::enrichStreamResponse)
                .onErrorContinue((throwable, obj) -> {
                    // 流式处理中的错误不影响其他块
                    System.err.println("Re2 streaming error: " + throwable.getMessage());
                });
    }
    
    private AdvisedResponse enrichResponse(AdvisedResponse response, AdvisedRequest request) {
        // 在响应上下文中添加 Re2 处理信息
        Map<String, Object> context = new HashMap<>(response.adviseContext());
        context.putAll(request.adviseContext());
        
        return new AdvisedResponse(
            response.response(),
            context,
            response.responseMetadata()
        );
    }
    
    private AdvisedResponse enrichStreamResponse(AdvisedResponse response) {
        // 流式响应不需要额外处理,但可以添加标记
        return response;
    }
    
    @Override
    public int getOrder() {
        return AdvisorOrder.ENHANCE;
    }
    
    @Override
    public String getName() {
        return "ReReadingAdvisor";
    }
}

3. 上下文增强与 Advisor 协作

Advisor 链中的上下文共享是构建复杂 Agent 的关键:

复制代码
@Component
public class ContextEnrichAdvisor implements CallAroundAdvisor {
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, 
                                     CallAroundAdvisorChain chain) {
        // 读取之前的 Advisor 设置的上下文
        Map<String, Object> context = new HashMap<>(advisedRequest.adviseContext());
        
        // 检查是否有用户认证信息(假设由 SecurityCheckAdvisor 注入)
        String userId = (String) context.get("AUTH_USER_ID");
        if (userId != null) {
            // 根据用户ID加载个性化配置
            UserPreferences prefs = loadUserPreferences(userId);
            
            // 更新请求上下文,供后续 Advisor 使用
            AdvisedRequest enriched = AdvisedRequest.from(advisedRequest)
                .adviseContext(ctx -> {
                    ctx.put("USER_PREFERENCES", prefs);
                    ctx.put("USER_LEVEL", prefs.getLevel());
                    
                    // 根据用户级别决定是否启用 Re2
                    if (prefs.getLevel() < 2) {
                        ctx.put("SIMPLE_QUERY", true);  // 初级用户不需要重读
                    }
                    return ctx;
                })
                .build();
            
            return chain.nextAroundCall(enriched);
        }
        
        return chain.nextAroundCall(advisedRequest);
    }
    
    private UserPreferences loadUserPreferences(String userId) {
        // 实际项目中从数据库或缓存加载
        return new UserPreferences(userId, 3, "zh-CN");
    }
    
    @Override
    public int getOrder() {
        return AdvisorOrder.CONTEXT;
    }
    
    @Override
    public String getName() {
        return "ContextEnrichAdvisor";
    }
}

// 领域对象
record UserPreferences(String userId, int level, String language) {
    public int getLevel() { return level; }
}

4. 完整的日志监控 Advisor

复制代码
@Component
@Slf4j
public class LoggingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    
    private final MeterRegistry meterRegistry;
    
    public LoggingAdvisor(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
    }
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, 
                                     CallAroundAdvisorChain chain) {
        long startTime = System.currentTimeMillis();
        String requestId = UUID.randomUUID().toString().substring(0, 8);
        
        try {
            // 读取整个链的上下文信息
            Map<String, Object> context = advisedRequest.adviseContext();
            boolean re2Enabled = Boolean.TRUE.equals(context.get("RE2_ENABLED"));
            String originalQuery = (String) context.get("RE2_ORIGINAL_QUERY");
            String userId = (String) context.get("AUTH_USER_ID");
            
            // 记录请求开始
            log.info("[{}] Request started - User: {}, Re2: {}, Query: {}", 
                     requestId, userId, re2Enabled, 
                     truncate(originalQuery != null ? originalQuery : advisedRequest.userText(), 100));
            
            AdvisedResponse response = chain.nextAroundCall(advisedRequest);
            
            // 记录请求完成
            long duration = System.currentTimeMillis() - startTime;
            log.info("[{}] Request completed - Duration: {}ms", requestId, duration);
            
            // 记录指标
            meterRegistry.timer("advisor.request.duration", 
                "advisor", "LoggingAdvisor",
                "re2_enabled", String.valueOf(re2Enabled))
                .record(duration, TimeUnit.MILLISECONDS);
            
            // 在响应中携带请求ID和耗时
            return enrichResponseWithMetadata(response, requestId, duration);
            
        } catch (Exception e) {
            log.error("[{}] Request failed - Error: {}", requestId, e.getMessage());
            meterRegistry.counter("advisor.request.errors").increment();
            throw e;
        }
    }
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, 
                                             StreamAroundAdvisorChain chain) {
        String requestId = UUID.randomUUID().toString().substring(0, 8);
        long startTime = System.currentTimeMillis();
        
        log.info("[{}] Stream request started", requestId);
        
        return chain.nextAroundStream(advisedRequest)
                .doOnComplete(() -> {
                    long duration = System.currentTimeMillis() - startTime;
                    log.info("[{}] Stream completed - Duration: {}ms", requestId, duration);
                })
                .doOnError(error -> {
                    log.error("[{}] Stream failed", requestId, error);
                });
    }
    
    private AdvisedResponse enrichResponseWithMetadata(
            AdvisedResponse response, String requestId, long duration) {
        Map<String, Object> metadata = new HashMap<>(response.adviseContext());
        metadata.put("REQUEST_ID", requestId);
        metadata.put("RESPONSE_DURATION", duration);
        
        return new AdvisedResponse(response.response(), metadata);
    }
    
    private String truncate(String text, int maxLength) {
        return text.length() > maxLength ? text.substring(0, maxLength) + "..." : text;
    }
    
    @Override
    public int getOrder() {
        return AdvisorOrder.LOGGING;
    }
    
    @Override
    public String getName() {
        return "LoggingAdvisor";
    }
}

三、流式处理的高级模式

针对流式场景,我们使用 Reactor 操作符实现复杂处理:

复制代码
@Component
public class AdvancedStreamAdvisor implements StreamAroundAdvisor {
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, 
                                             StreamAroundAdvisorChain chain) {
        return Mono.just(advisedRequest)
            // 1. 在不同线程池处理,避免阻塞主线程
            .publishOn(Schedulers.boundedElastic())
            
            // 2. 请求预处理
            .map(this::preprocessRequest)
            
            // 3. 转换为流式处理
            .flatMapMany(request -> chain.nextAroundStream(request)
                // 4. 对每个流块进行过滤
                .filter(response -> !isEmptyResponse(response))
                
                // 5. 转换响应内容
                .map(this::transformResponse)
                
                // 6. 限流控制
                .limitRate(10)
                
                // 7. 超时控制
                .timeout(Duration.ofSeconds(30))
                
                // 8. 错误重试
                .retryWhen(Retry.backoff(3, Duration.ofSeconds(1))
                    .maxBackoff(Duration.ofSeconds(10))
                    .doBeforeRetry(signal -> 
                        log.warn("Retrying stream processing: {}", signal.failure().getMessage()))
                )
                
                // 9. 监控每个块的延迟
                .elapsed()
                .map(tuple -> {
                    long elapsed = tuple.getT1();
                    AdvisedResponse response = tuple.getT2();
                    
                    // 记录延迟指标
                    recordStreamChunkLatency(elapsed);
                    
                    return response;
                })
            )
            
            // 10. 整体流程的错误处理
            .onErrorResume(this::handleStreamError)
            
            // 11. 确保资源清理
            .doFinally(signalType -> cleanup(signalType));
    }
    
    private AdvisedRequest preprocessRequest(AdvisedRequest request) {
        // 可以添加流式场景特有的预处理
        return request;
    }
    
    private boolean isEmptyResponse(AdvisedResponse response) {
        // 过滤空响应块
        String content = response.response().getContent();
        return content == null || content.trim().isEmpty();
    }
    
    private AdvisedResponse transformResponse(AdvisedResponse response) {
        // 可以对每个块的内容进行转换
        return response;
    }
    
    private Flux<AdvisedResponse> handleStreamError(Throwable error) {
        // 优雅降级:返回错误信息而不是中断流
        log.error("Stream processing error, returning fallback", error);
        return Flux.just(new AdvisedResponse(
            new AssistantMessage("处理过程中出现错误,请稍后重试")
        ));
    }
    
    private void recordStreamChunkLatency(long elapsedMillis) {
        // 记录流块的延迟
    }
    
    private void cleanup(SignalType signalType) {
        // 清理资源,如关闭连接等
        log.debug("Stream cleanup: {}", signalType);
    }
    
    @Override
    public int getOrder() {
        return 200;
    }
    
    @Override
    public String getName() {
        return "AdvancedStreamAdvisor";
    }
}

四、关键最佳实践与注意事项

1. 单一职责原则

错误示例

复制代码
// ❌ 职责混乱
public class BadAdvisor implements CallAroundAdvisor {
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        // 既做安全检查
        if (request.userText().contains("danger")) {
            throw new SecurityException();
        }
        
        // 又做日志记录
        log.info("Processing: {}", request.userText());
        
        // 还做内容转换
        String enhanced = "Enhanced: " + request.userText();
        
        // 甚至做缓存处理
        if (cache.contains(enhanced)) {
            return cache.get(enhanced);
        }
        
        return chain.nextAroundCall(request);
    }
}

正确示例

复制代码
// ✅ 职责分离
public class SecurityAdvisor implements CallAroundAdvisor {
    // 只做安全检查
}

public class LoggingAdvisor implements CallAroundAdvisor {
    // 只做日志记录
}

public class EnhanceAdvisor implements CallAroundAdvisor {
    // 只做内容增强
}

public class CacheAdvisor implements CallAroundAdvisor {
    // 只做缓存处理
}

2. 执行顺序的重要性

复制代码
// 场景演示:Advisor 执行顺序的影响
@SpringBootTest
class AdvisorOrderTest {
    
    @Test
    void testAdvisorOrderMatters() {
        // 错误的顺序:先增强后检查
        List<Advisor> wrongOrder = List.of(
            new ReReadingAdvisor(),    // 0: 先重读
            new SecurityCheckAdvisor() // -100: 后检查(但顺序错了)
        );
        
        // 正确的顺序:先检查后增强
        List<Advisor> correctOrder = List.of(
            new SecurityCheckAdvisor(), // -100: 先检查
            new ReReadingAdvisor()      // 0: 后增强
        );
        
        // 如果输入包含危险内容,错误顺序会导致:
        // 1. ReReadingAdvisor 先处理了危险输入
        // 2. SecurityCheckAdvisor 再检查时已经浪费了 Token
    }
    
    @Test
    void testOrderInRealScenario() {
        String dangerousInput = "DROP TABLE users; --";
        
        ChatClient client = ChatClient.builder()
            .defaultAdvisors(
                new SecurityCheckAdvisor(), // 应该先执行
                new ReReadingAdvisor(),     // 应该后执行
                new LoggingAdvisor()        // 最后记录
            )
            .build();
        
        // 这样的顺序确保:
        // 1. 安全检查最先执行,危险输入被拦截
        // 2. 只有安全的输入才会被增强处理
        // 3. 最后记录完整的处理过程
    }
}

3. 边界条件处理

复制代码
@Component
public class RobustAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        // 1. 空值检查
        if (request == null) {
            log.error("Received null request");
            return createErrorResponse("系统错误:空请求");
        }
        
        String userText = request.userText();
        
        // 2. 空内容检查
        if (userText == null || userText.isBlank()) {
            log.warn("Received empty user text");
            return createErrorResponse("请输入您的问题");
        }
        
        // 3. 超大输入检查
        if (userText.length() > 50_000) {
            log.warn("Input too large: {} characters", userText.length());
            return createErrorResponse("输入内容过长,请精简后重试");
        }
        
        // 4. 特殊字符处理
        if (containsControlCharacters(userText)) {
            userText = sanitizeInput(userText);
        }
        
        // 5. 并发安全
        Map<String, Object> params = new ConcurrentHashMap<>(request.userParams());
        
        try {
            // 6. 异常捕获
            AdvisedResponse response = chain.nextAroundCall(request);
            
            // 7. 响应空值检查
            if (response == null || response.response() == null) {
                log.error("Received null response from chain");
                return createErrorResponse("系统处理异常,请稍后重试");
            }
            
            return response;
            
        } catch (IllegalArgumentException e) {
            log.error("Invalid argument in processing", e);
            return createErrorResponse("请求参数不合法");
        } catch (Exception e) {
            log.error("Unexpected error in advisor", e);
            return createErrorResponse("系统内部错误");
        }
    }
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest request, 
                                             StreamAroundAdvisorChain chain) {
        // 流式场景的特殊边界处理
        if (request == null || request.userText() == null) {
            return Flux.just(createErrorResponse("Invalid stream request"));
        }
        
        return chain.nextAroundStream(request)
            .onErrorContinue((error, obj) -> {
                log.error("Error in stream chunk, continuing", error);
            })
            .switchIfEmpty(Flux.just(createErrorResponse("No response generated")))
            .timeout(Duration.ofSeconds(60))
            .onErrorResume(TimeoutException.class, e -> 
                Flux.just(createErrorResponse("Response timeout"))
            );
    }
    
    private boolean containsControlCharacters(String text) {
        return text.codePoints().anyMatch(cp -> cp < 32 && cp != 9 && cp != 10 && cp != 13);
    }
    
    private String sanitizeInput(String text) {
        return text.replaceAll("[\\x00-\\x08\\x0B\\x0C\\x0E-\\x1F]", "");
    }
    
    private AdvisedResponse createErrorResponse(String message) {
        return new AdvisedResponse(
            new AssistantMessage(message),
            Map.of("error", true, "type", "validation_error")
        );
    }
}

4. 上下文传递与协作

复制代码
@SpringBootTest
class AdvisorContextCooperationTest {
    
    @Test
    void testContextSharingBetweenAdvisors() {
        // 模拟复杂的上下文传递场景
        
        Map<String, Object> initialContext = new HashMap<>();
        
        // Advisor 1: 用户认证
        AdvisedRequest afterAuth = AdvisedRequest.from(originalRequest)
            .adviseContext(context -> {
                context.put("USER_ID", "12345");
                context.put("USER_ROLE", "PREMIUM");
                return context;
            })
            .build();
        
        // Advisor 2: 读取上下文并决策
        Map<String, Object> authContext = afterAuth.adviseContext();
        String userRole = (String) authContext.get("USER_ROLE");
        
        AdvisedRequest afterDecision;
        if ("PREMIUM".equals(userRole)) {
            // 高级用户使用增强策略
            afterDecision = AdvisedRequest.from(afterAuth)
                .adviseContext(context -> {
                    context.put("USE_RE2", true);
                    context.put("MODEL_LEVEL", "advanced");
                    return context;
                })
                .build();
        } else {
            // 普通用户使用基础策略
            afterDecision = AdvisedRequest.from(afterAuth)
                .adviseContext(context -> {
                    context.put("USE_RE2", false);
                    context.put("MODEL_LEVEL", "basic");
                    return context;
                })
                .build();
        }
        
        // Advisor 3: 根据前面 Advisors 的决策执行
        Map<String, Object> decisionContext = afterDecision.adviseContext();
        boolean useRe2 = Boolean.TRUE.equals(decisionContext.get("USE_RE2"));
        String modelLevel = (String) decisionContext.get("MODEL_LEVEL");
        
        // 记录整个决策链
        log.info("Final processing decision: useRe2={}, modelLevel={}, userRole={}", 
                 useRe2, modelLevel, userRole);
    }
    
    @Test
    void testContextFlowInCompleteChain() {
        // 完整的 Advisor 链上下文流转
        ChatClient client = ChatClient.builder()
            .defaultAdvisors(
                new ContextInjectionAdvisor(),  // 注入初始上下文
                new BusinessLogicAdvisor(),     // 业务逻辑处理
                new EnhancementAdvisor(),       // 根据上下文决定增强策略
                new MonitoringAdvisor()         // 基于完整上下文进行监控
            )
            .build();
        
        // 执行请求并验证上下文传递
        String response = client.prompt()
            .user("复杂业务问题")
            .advisors(spec -> spec
                .param("businessType", "financial")
                .param("riskLevel", "high")
            )
            .call()
            .content();
    }
}

5. 性能优化策略

复制代码
@Component
public class PerformanceOptimizedAdvisor implements CallAroundAdvisor {
    
    private final Cache<String, AdvisedResponse> responseCache;
    private final ExecutorService processingExecutor;
    
    public PerformanceOptimizedAdvisor() {
        // 使用 Caffeine 缓存
        this.responseCache = Caffeine.newBuilder()
            .maximumSize(1000)
            .expireAfterWrite(10, TimeUnit.MINUTES)
            .recordStats()
            .build();
        
        // 专用线程池
        this.processingExecutor = Executors.newFixedThreadPool(5);
    }
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        String cacheKey = generateCacheKey(request);
        
        // 1. 缓存优化
        AdvisedResponse cached = responseCache.getIfPresent(cacheKey);
        if (cached != null) {
            log.debug("Cache hit for key: {}", cacheKey);
            meterRegistry.counter("advisor.cache.hit").increment();
            return cached;
        }
        
        meterRegistry.counter("advisor.cache.miss").increment();
        
        // 2. 异步预处理(对于复杂预处理)
        CompletableFuture<AdvisedRequest> preprocessedFuture = CompletableFuture
            .supplyAsync(() -> expensivePreprocessing(request), processingExecutor);
        
        try {
            // 3. 超时控制
            AdvisedRequest preprocessed = preprocessedFuture.get(5, TimeUnit.SECONDS);
            AdvisedResponse response = chain.nextAroundCall(preprocessed);
            
            // 4. 缓存结果
            responseCache.put(cacheKey, response);
            
            return response;
            
        } catch (TimeoutException e) {
            log.warn("Preprocessing timeout, using original request");
            return chain.nextAroundCall(request);
        } catch (Exception e) {
            log.error("Error in performance advisor", e);
            return chain.nextAroundCall(request);
        }
    }
    
    private String generateCacheKey(AdvisedRequest request) {
        return DigestUtils.md5Hex(request.userText());
    }
    
    private AdvisedRequest expensivePreprocessing(AdvisedRequest request) {
        // 模拟耗时的预处理操作
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
        return request;
    }
    
    @Override
    public int getOrder() {
        return 0;
    }
    
    @Override
    public String getName() {
        return "PerformanceOptimizedAdvisor";
    }
}

6. 错误恢复与降级策略

复制代码
@Component
public class FallbackAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {
    
    private final CircuitBreaker circuitBreaker;
    private final Map<String, String> fallbackResponses;
    
    public FallbackAdvisor() {
        // 熔断器配置
        this.circuitBreaker = CircuitBreaker.of("advisor-fallback",
            CircuitBreakerConfig.custom()
                .failureRateThreshold(50)
                .waitDurationInOpenState(Duration.ofSeconds(30))
                .slidingWindowSize(10)
                .build()
        );
        
        // 预设降级响应
        this.fallbackResponses = Map.of(
            "timeout", "处理超时,请简化您的问题后重试",
            "rate_limit", "请求过于频繁,请稍后再试",
            "service_error", "服务暂时不可用,请稍后重试"
        );
    }
    
    @Override
    public AdvisedResponse aroundCall(AdvisedRequest request, CallAroundAdvisorChain chain) {
        return circuitBreaker.executeSupplier(() -> {
            try {
                return chain.nextAroundCall(request);
            } catch (TimeoutException e) {
                return createFallbackResponse("timeout", request);
            } catch (RateLimitExceededException e) {
                return createFallbackResponse("rate_limit", request);
            } catch (Exception e) {
                log.error("Service error, using fallback", e);
                return createFallbackResponse("service_error", request);
            }
        });
    }
    
    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest request, 
                                             StreamAroundAdvisorChain chain) {
        return chain.nextAroundStream(request)
            .timeout(Duration.ofSeconds(30))
            .onErrorResume(TimeoutException.class, e -> {
                log.warn("Stream timeout, using fallback");
                return Flux.just(createFallbackResponse("timeout", request));
            })
            .onErrorResume(e -> {
                log.error("Stream error, using fallback", e);
                return Flux.just(createFallbackResponse("service_error", request));
            })
            .switchIfEmpty(Flux.defer(() -> {
                log.warn("Empty stream response");
                return Flux.just(createFallbackResponse("service_error", request));
            }));
    }
    
    private AdvisedResponse createFallbackResponse(String reason, AdvisedRequest request) {
        String message = fallbackResponses.getOrDefault(reason, "处理失败,请稍后重试");
        
        return new AdvisedResponse(
            new AssistantMessage(message),
            Map.of(
                "fallback", true,
                "reason", reason,
                "original_query", request.userText()
            )
        );
    }
    
    @Override
    public int getOrder() {
        return AdvisorOrder.FALLBACK;
    }
    
    @Override
    public String getName() {
        return "FallbackAdvisor";
    }
}

五、测试与验证

复制代码
@SpringBootTest
class AdvisorIntegrationTest {
    
    @Autowired
    private ChatClient chatClient;
    
    @Test
    void testCompleteAdvisorChain() {
        // 测试完整的 Advisor 链协作
        String response = chatClient.prompt()
            .user("帮我分析一下这个复杂的逻辑问题:...")
            .call()
            .content();
        
        assertNotNull(response);
        // 验证响应中包含了 Re2 处理的特征
        assertTrue(response.contains("根据重新阅读理解"));
    }
    
    @Test
    void testSecurityAdvisorBlocksMaliciousInput() {
        assertThrows(SecurityException.class, () -> {
            chatClient.prompt()
                .user("DROP TABLE users")
                .call();
        });
    }
    
    @Test
    void testFallbackWhenServiceUnavailable() {
        // 模拟服务不可用的情况
        // 验证降级策略是否正常工作
    }
    
    @Test
    void testAdvisorOrderExecution() {
        // 验证 Advisors 按正确顺序执行
        List<String> executionOrder = new ArrayList<>();
        
        ChatClient testClient = ChatClient.builder()
            .defaultAdvisors(
                new OrderTrackingAdvisor("first", -100, executionOrder),
                new OrderTrackingAdvisor("second", 0, executionOrder),
                new OrderTrackingAdvisor("third", 100, executionOrder)
            )
            .build();
        
        testClient.prompt().user("test").call();
        
        assertEquals(List.of("first", "second", "third"), executionOrder);
    }
}

六、总结

构建严谨的 AI Agent Advisor 体系需要注意:

  1. 架构设计:合理规划 Advisor 的职责和执行顺序

  2. 上下文管理:充分利用 adviseContext 实现 Advisor 间协作

  3. 错误处理:完善的边界检查和降级策略

  4. 性能优化:缓存、异步处理等性能优化措施

  5. 可观测性:日志、指标、追踪的全面覆盖

  6. 测试覆盖:单元测试和集成测试确保可靠性

通过合理运用这些模式和最佳实践,可以构建出健壮、可扩展、易维护的 AI Agent 系统。


附录:完整的 Advisor 开发检查清单

  • □ 单一职责:每个 Advisor 只做一件事

  • □ 接口实现:同时支持 CallAroundAdvisor 和 StreamAroundAdvisor

  • □ 执行顺序:合理设置 getOrder() 返回值

  • □ 边界处理:空值、超长、特殊字符等异常输入

  • □ 上下文传递:使用 adviseContext 共享状态

  • □ 错误处理:完善的异常捕获和降级策略

  • □ 性能考虑:避免耗时操作,使用缓存和异步处理

  • □ 日志监控:关键节点的日志记录和指标采集

  • □ 测试用例:覆盖正常流程和边界情况

  • □ 文档注释:清晰的 Javadoc 和使用说明

相关推荐
aiqianzhan1 小时前
采购数字化选型:智慧采购平台五维对照与常见路线
大数据·人工智能
云空1 小时前
《软件专业完整学习路线图(本科4年+自学通用版,2026就业向)》
人工智能·科技·学习·计算机·编程·软件
2603_965148111 小时前
eBay商品数据API:寻找海外仓与价格洼地
大数据·人工智能·windows·python·microsoft
ruofu331 小时前
如何让docker使用提前下载好的镜像,而不是在线拉取
java·spring cloud·docker
爱敲键盘的猴子1 小时前
深入理解 Spring IoC -- 基于 XML 的 Bean 配置详解
xml·java·spring
艾莉丝努力练剑1 小时前
【Qt:问题解决】Qt程序入口点错误修复:MinGW ABI兼容问题
java·服务器·开发语言·qt·文件系统·动静态库
2601_956319881 小时前
TqSdk Tick 数据适合做什么?获取方法与使用边界
人工智能·python
程序员黑豆8 小时前
Java 注释详解:单行、多行与文档注释的完整指南
java·前端·ai编程