统一环境:IDEA2026.1.1、JDK21、SpringBoot3.5.14、SpringAI1.1.6、智谱glm-4-flash、MySQL8.0

前置阅读:已掌握 Spring AI 分层记忆架构、JDBC 数据库持久化记忆、ChatClient 链式调用基础

一、前言

在前几期记忆实战中,我们借助官方提供的 MessageChatMemoryAdvisor,快速实现了 AI 多轮对话的上下文记忆与数据库持久化能力,解决了 AI 对话无状态、会话数据丢失的问题。

但在实际生产开发中,仅靠官方自带的记忆顾问远远不够,我们往往需要实现全局日志审计、自定义请求拦截、业务参数透传、会话权限校验等通用扩展能力。而支撑这些高阶功能的核心底层,正是 Spring AI 框架的核心扩展机制 —— Advisor 顾问机制

多数初学者在使用 Advisor 时,普遍存在两大痛点:

  • 不理解多 Advisor 共存时的 Order 优先级执行规则,经常出现功能失效、执行顺序错乱等问题

  • 不熟悉 param 上下文参数透传机制,无法实现跨顾问参数共享,难以拓展自定义业务能力

本篇将基于 SpringAI1.1.6 标准 BaseAdvisor 接口,以可动态配置的日志顾问为实战案例,结合 Spring 拦截器设计思想,讲透 Advisor 核心原理与落地用法。

二、Advisor 核心定位:类比 Spring MVC 拦截器

2.1 核心设计思想

Spring AI 的 Advisor 顾问机制,设计思路与 Spring MVC 拦截器(HandlerInterceptor) 高度一致,是框架提供的全局 AOP 扩展入口,核心作用是在不侵入业务代码的前提下,对 AI 对话全链路进行统一增强。

  • Spring MVC 拦截器:在 Controller 接口执行前后,完成权限校验、日志记录、参数预处理

  • Spring AI Advisor:在 大模型 LLM 调用前后,完成上下文处理、请求拦截、日志审计、参数透传

核心设计理念:业务与通用增强解耦,全局统一生效,灵活拓展 AI 对话能力

2.2 BaseAdvisor 标准执行流程

BaseAdvisor 是 Spring AI 官方标准顶层顾问接口,定义了 AI 对话全生命周期的拦截方法,核心包含两个核心阶段:

  • before() 前置执行:大模型调用前触发,可实现参数修改、请求拦截、上下文参数绑定等操作

  • after() 后置执行:大模型调用结束后触发,可实现响应结果处理、对话日志记录、数据持久化等操作

完整对话执行链路:客户端请求 → 多Advisor链式执行before前置逻辑 → LLM大模型调用 → 多Advisor链式执行after后置逻辑 → 响应结果返回客户端

三、Order 优先级核心机制(重点)

当项目中存在多个自定义顾问、官方顾问共存时,执行顺序完全由 getOrder() 返回值控制,这是解决多顾问冲突、功能失效的核心关键。

3.1 优先级通用规则

  • 前置 before 方法:Order 值越小,优先级越高,越先执行

  • 后置 after 方法:Order 值 越小,优先级越低,越晚执行(与前置执行顺序完全相反)

3.2 本篇实战优先级配置

本次实战同时启用「日志自定义顾问」和「官方记忆顾问」,采用生产级优先级配置:

  1. LogAdvisor(Order=-100):高优先级,最先执行前置日志拦截,最后执行后置日志打印

  2. MessageChatMemoryAdvisor(Order=0):默认优先级,负责对话记忆的加载与存储

最终完整执行链路:日志前置打印 → 加载历史对话记忆 → 调用大模型 → 保存本轮对话记忆 → 日志后置打印

3.3 动态 Order 配置优势

本次自定义日志顾问采用 Builder 构建器模式,支持动态传入 Order 优先级,摒弃硬编码方式。开发者可在配置类中灵活调整顾问执行顺序,适配不同业务场景,扩展性与灵活性更强。

四、核心知识点:param 上下文参数透传

4.1 核心作用

Spring AI 提供 .param(key,value) 方法,支持向 Advisor 全局上下文透传自定义参数,实现跨业务、跨顾问的数据共享,是实现会话隔离、权限控制、日志追踪的核心基础:

  • 透传会话 ID(conversationId),实现多用户、多会话对话隔离

  • 透传 Token、用户 ID、设备信息等自定义业务参数

  • 实现所有注册顾问全局共享参数,统一处理上下文数据

4.2 参数传递完整链路

Controller 接口通过 advisors().param() 绑定自定义参数 → 参数存入全局 AdvisorContext 上下文 → 所有 Advisor 的 before/after 方法可通过请求对象获取上下文参数 → 实现全局参数透传与复用。

五、实战一:自定义可配置日志顾问 LogAdvisor

基于标准 BaseAdvisor 接口,结合 Builder 模式实现可动态配置优先级的全局日志顾问,自动打印 AI 对话请求、响应及上下文参数,实现全链路日志审计。

package demo.ai.advisor;

import lombok.extern.slf4j.Slf4j;
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.*;
import org.springframework.stereotype.Component;

/**
 * 全局日志记录顾问
 * 基于 BaseAdvisor 标准接口开发
 * 支持Builder动态配置Order优先级,打印完整AI对话请求、响应与上下文参数
 */
@Slf4j
@Component
public class LogAdvisor implements BaseAdvisor {

    private int order;

    @Override
    public String getName() {
        return "Log Advisor";
    }

    /**
     * 模型调用前置执行
     * 打印请求信息、上下文透传参数
     */
    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        log.info("beforeLog: {}", chatClientRequest);
        log.info("beforeLog context: {}", chatClientRequest.context());
        return chatClientRequest;
    }
    /**
     * 模型调用后置执行
     * 打印响应结果、上下文参数
     */
    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        log.info("afterLog: {}", chatClientResponse);
        log.info("afterLog context: {}", chatClientResponse.context());
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return order;
    }

    public static LogAdvisor.Builder builder() {
        return new LogAdvisor.Builder();
    }

    public static final class Builder {
        private int order = 0;
        public LogAdvisor.Builder order(int order) {
            this.order = order;
            return this;
        }
        public LogAdvisor build() {
            LogAdvisor logAdvisor = new LogAdvisor();
            logAdvisor.order = this.order;
            return logAdvisor;
        }

    }
}


六、实战二:全局配置整合记忆与日志顾问

沿用前文 JDBC 数据库持久化记忆配置,同时注册官方记忆顾问与自定义日志顾问,手动指定优先级,保证多顾问有序执行,实现对话持久化+全局日志审计双重能力。

package demo.ai.conf;

import demo.ai.advisor.LogAdvisor;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.MessageWindowChatMemory;
import org.springframework.ai.chat.memory.repository.jdbc.JdbcChatMemoryRepository;
import org.springframework.ai.chat.memory.repository.jdbc.MysqlChatMemoryRepositoryDialect;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.jdbc.core.JdbcTemplate;

@Configuration
public class ChatMemoryConfig {

    /**
     * 1. 升级为JDBC数据库持久化仓库(MySQL方言)
     */
    @Bean
    public JdbcChatMemoryRepository chatMemoryRepository(JdbcTemplate jdbcTemplate) {
        // 指定MySQL方言,适配数据库语法
        return JdbcChatMemoryRepository.builder()
                .jdbcTemplate(jdbcTemplate)
                .dialect(new MysqlChatMemoryRepositoryDialect())
                .build();
    }

    /**
     * 2. 滑动窗口记忆策略(完全复用,无需改动)
     * 最大保留10条消息,自动裁剪过期消息
     */
    @Bean
    public MessageWindowChatMemory chatMemory(JdbcChatMemoryRepository repository) {
        return MessageWindowChatMemory.builder()
                .chatMemoryRepository(repository)
                .maxMessages(10)
                .build();
    }


    /**
     * 3. 构建全局ChatClient,批量注册顾问并指定优先级
     * LogAdvisor:Order=-100,优先级最高,优先完成日志拦截
     * MessageChatMemoryAdvisor:Order=0,默认优先级,处理对话记忆读写
     */
    @Bean
    public ChatClient chatClient(ChatClient.Builder builder, MessageWindowChatMemory chatMemory) {
        return builder
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).order(0).build(), LogAdvisor.builder().order(-100).build())
                               .build();
    }
}

七、实战三:Controller 上下文参数透传

在接口层通过 param 透传会话 ID、自定义 Token 参数,实现多会话隔离与全局参数共享,所有参数可在 Advisor 拦截逻辑中获取并使用。

package demo.ai.controller;

import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import reactor.core.publisher.Flux;

@RestController
@RequestMapping("/memory")
public class ChatMemoryController {

    @Resource
    private ChatClient chatClient;
    @Resource
    private ChatMemory chatMemory;

    /**
     * 带记忆的普通多轮对话
     * 透传会话ID实现多用户隔离,自定义Token实现业务参数追踪
     */
    @GetMapping("/chat")
    public String memoryChat(@RequestParam String sessionId,
                             @RequestParam String msg) {
        return chatClient.prompt()
                // 传入会话ID,隔离不同用户对话
                .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, sessionId).param("token", "token123"))
                .system("你是专业的医院挂号咨询助手,回答简洁、易懂、专业")
                .user(msg)
                .call()
                .content();
    }

    /**
     * 带记忆的流式多轮对话
     * 透传会话ID实现多用户隔离,自定义Token实现业务参数追踪
     */
    @GetMapping("/stream")
    public Flux<String> memoryStreamChat(@RequestParam String sessionId,
                                         @RequestParam String msg) {
        return chatClient.prompt()
                .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, sessionId).param("token", "token123"))
                .system("你是专业的医院挂号咨询助手,分段清晰、简洁回答问题")
                .user(msg)
                .stream()
                .content();
    }

    /**
     * 清空指定会话记忆
     */
    @GetMapping("/clear")
    public String clearMemory(@RequestParam String sessionId) {
        chatMemory.clear(sessionId);
        return "会话【" + sessionId + "】记忆已清空";
    }
}

pom.xml、application.yml参照上一次教程。

八、核心原理深度复盘

8.1 上下文Param传递原理

通过advisor.param(k,v) 绑定的自定义参数,会统一存入框架内置的 AdvisorContext 全局上下文,贯穿整个 AI 调用链路。所有已注册的 Advisor 均可读取上下文参数,完美实现业务参数与框架增强逻辑解耦,常用于会话隔离、权限校验、日志追踪、用户信息透传等场景。

8.2 多Advisor完整执行链路复盘

结合本次优先级配置,完整执行顺序清晰可控,无冲突、无逻辑遗漏:

  1. LogAdvisor 前置方法执行(Order=-100):打印请求信息与上下文参数

  2. MessageChatMemoryAdvisor 前置方法执行:从 MySQL 加载对应会话的历史对话记忆

  3. 框架调用大模型 LLM,生成 AI 响应结果

  4. MessageChatMemoryAdvisor 后置方法执行:保存本轮对话数据至数据库,自动裁剪消息窗口

  5. LogAdvisor 后置方法执行:打印响应结果与最终上下文参数

九、功能测试验证

9.1 全局日志打印测试

调用接口 http://localhost:8080/memory/chat?sessionId=user001&msg=咨询内科挂号时间,控制台可正常打印完整请求信息、sessionId、自定义 token 及 AI 响应结果,全链路日志完整,参数透传生效。

9.2 JDBC记忆持久化测试

多轮对话数据可正常存入 MySQL 数据表,重启服务后历史记忆不丢失,记忆顾问与日志顾问完美共存,功能互不冲突。

9.3 上下文参数透传测试

自定义 Token、会话 ID 可成功透传至 Advisor 上下文,开发者可基于该能力拓展权限校验、会话拦截、日志追踪等自定义功能。

十、踩坑总结

  • Order优先级错乱:多顾问共存场景必须手动指定 Order 值,默认 0 会导致执行顺序不可控,出现日志缺失、记忆加载异常等问题

  • 接口使用不规范:项目统一使用标准 BaseAdvisor 接口开发,保证代码规范、版本适配统一,避免框架兼容问题

  • 上下文参数获取为空:自定义参数必须在 advisors() 方法中绑定,直接在 prompt 中设置无法透传到 Advisor 上下文

  • 硬编码优先级扩展性差:推荐使用 Builder 模式动态配置 Order,灵活适配不同业务场景,便于后期维护迭代

十一、本篇学习总结

本篇聚焦 Spring AI 核心扩展能力,彻底吃透 Advisor 高阶机制,核心收获总结如下:

  • 明确 Advisor 核心定位:AI 对话场景的全局拦截扩展机制,与 Spring MVC 拦截器设计思想一致

  • 熟练掌握 Order 优先级执行规则,可自主管控多顾问执行顺序,解决共存冲突问题

  • 吃透 param 上下文参数透传机制,实现跨顾问全局参数共享

  • 掌握 BaseAdvisor 标准开发方式,实现可配置、可拓展的自定义顾问

  • 整合 JDBC 持久化记忆与日志拦截能力,搭建解耦、稳定的企业级 AI 对话架构

十二、结语

Advisor 是 Spring AI 框架的核心扩展基石,日志审计、权限拦截、限流监控、RAG 增强、AI Agent 等所有高阶能力,均基于该机制实现。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐