Spring AI:使用 Advisor 组件 - 打印请求大模型出入参日志

考虑一个问题,AI 应用上线后,想要打印调用大模型的出入参日志(提示词、整体回答),以便后续能够跟踪调试,要如何实现呢?小伙伴们可能会说,自定义一个打印日志的注解 ,通过 AOP 环绕,不就行了吗?

确实,但如果是流式对话,想要一次性打印整个流式回答,而不是片段性的打印,要怎么办呢?貌似之前的方案不太好搞定,有没有什么优雅的方式呢?那就是本文的主题 ------ Advisor 组件。

可以先看下前几篇文章:
Spring AI:对接DeepSeek实战
Spring AI:对接官方 DeepSeek-R1 模型 ------ 实现推理效果
Spring AI:ChatClient实现对话效果

什么是 Advisor?

Advisor 是 Spring AI 中的核心组件,用于在 AI 模型交互过程中动态拦截和增强请求与响应流,类似于面向切面编程(AOP)的拦截器。其核心目标是通过模块化设计提升 AI 应用的灵活性、安全性和扩展性。

通过它,可以实现功能如下:

  • 请求与响应拦截:Advisor 能够拦截 AI 模型的输入和输出,允许开发者在请求发送前和响应返回后插入自定义逻辑,例如数据增强、敏感词过滤等;
  • 功能模块化:
    重复任务封装:例如日志记录、上下文记忆管理等通用功能可封装为可复用组件。
    数据转换:优化发送给模型的数据结构(如添加上下文信息),并标准化响应格式。
    业务规则注入:例如限制模型输出范围或根据业务需求引导生成内容。
  • 跨模型兼容性:通过抽象接口设计,Advisor 可适配不同 AI 模型(如 DeepSeek、OpenAI 等),提升代码的可移植性。

Advisor 处理流程

下图是 Spring AI 官方提供的 Advisor 处理流程图:

大致过程如下:

1、请求初始化: Spring AI 框架根据用户的输入(Prompt)创建一个 AdvisedRequest(请求),并初始化一个空的 AdvisorContext(Advisor 上下文) 对象。

2、Advisor 链路处理:

  • 整个链路中,可能包含多个 Advisor, 每个 Advisor 依次处理 AdvisedRequest,可以修改请求内容(例如添加上下文或过滤敏感词)。
  • 拦截机制:若某个 Advisor 决定阻止请求(如检测到违规内容),则不再调用后续 Advisor 组件,并直接生成响应(需自行填充 AdvisedResponse)。

3、调用聊天模型

  • 最后一个 Advisor(由框架提供)将处理后的请求发送给 聊天模型(Chat Model)(如 DeepSeek)。

4、大模型响应

  • 大模型的响应会通过 Advisor 链逆向回传,并被封装为 AdvisedResponse(响应),其中包含共享的 AdvisorContext 实例。

5、响应后处理

  • 每个 Advisor 可对响应进行二次处理或修改(例如格式化输出、添加日志等等)。

6、最终响应返回

  • 框架从最终的 AdvisedResponse 中提取 ChatCompletion(聊天完成的结果),并将其返回给客户端。

配置 Advisor

接下来,我们就来通过 Spring AI 内置提供的日志记录组件 SimpleLoggerAdvisor ,实现一下打印出入参日志功能。编辑 ChatClientConfig 配置类:

java 复制代码
@Configuration
public class ChatClientConfig {

    /**
     * 初始化 ChatClient 客户端
     * @param chatModel
     * @return
     */
    @Bean
    public ChatClient chatClient(DeepSeekChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("请你扮演一个智能客服")
                .defaultAdvisors(new SimpleLoggerAdvisor()) // 添加 Spring AI 内置的日志记录功能
                .build();
    }
}

开启 debug 模式

为了能够让 SimpleLoggerAdvisor 组件打印日志,还需要编辑 application.yml 配置文件,将 SimpleLoggerAdvisor 所在的包路径,设置为 debug 模式:

java 复制代码
logging:
  level:
    org.springframework.ai.chat.client.advisor: debug

TIP: SimpleLoggerAdvisor 组件的具体包路径,可以点击查看源码来获取,如下:

重启项目,调用接口看下控制台是否有相关日志。

自定义 Advisor

另外,我们也可以自定义 Advisor 组件,接下来,我们尝试自己创建一个记录出入参日志的 Advisor 组件。

添加 Lombok

为了能够方便的输出日志,在 pom.xml 文件中,添加 Lombok 的依赖,如下:

java 复制代码
  <properties>
        // 省略
        <lombok.version>1.18.30</lombok.version>
    </properties>
    
    <dependencies>
        // 省略...

        <!-- 避免编写那些冗余的 Java 样板式代码,如 get、set 方法等 -->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>${lombok.version}</version>
        </dependency>
    </dependencies>

创建 Advisor

创建一个 /advisor 包,在里面创建 MyLoggerAdvisor 自定义组件:

java 复制代码
@Slf4j
public class MyLoggerAdvisor implements CallAdvisor {

    @Override
    public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
        log.info("## 请求入参: {}", chatClientRequest);
        ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);
        log.info("## 请求出参: {}", chatClientResponse);
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return 1; // order 值越小,越先执行
    }

    @Override
    public String getName() {
    	// 获取类名称
        return this.getClass().getSimpleName();
    }
}

解释一下,上述代码中核心的地方:

  • 添加 Lombok 提供的 @Slf4j 注解,可以方便的打印日志;
  • 注意,自定义的 Advisor 类实现了 CallAdvisor 接口。在 Spring AI 中,CallAdvisor 和 StreamAdvisor是两种不同类型的 Advisor,分别用于处理 同步调用 和 流式调用 场景。它们的设计目的是为了适应不同的 AI 模型交互模式,提供灵活的请求拦截与响应处理能力。咱们这个日志记录 Advisor 仅支持一下同步调用场景,故只实现了 CallAdvisor 接口;
    在 adviseCall() 方法中,打印调用大模型的出入参日志;
  • getOrder() : 支持配置多个 Advisor, 通过 Order 值来决定执行顺序,值越小,越先执行。进入到 SimpleLoggerAdvisor 源码中,会发现其 Order 值默认为 0, 我们这里设置为 1, 在 SimpleLoggerAdvisor 后面执行;

配置自定义 Advisor

自定义 Advisor 创建完成后,编辑 ChatClientConfig 类,配置上咱们自定义的 MyLoggerAdvisor 日志记录组件:

java 复制代码
@Configuration
public class ChatClientConfig {

    /**
     * 初始化 ChatClient 客户端
     * @param chatModel
     * @return
     */
    @Bean
    public ChatClient chatClient(DeepSeekChatModel chatModel) {
        return ChatClient.builder(chatModel)
                .defaultSystem("请你扮演一个智能客服")
                .defaultAdvisors(new SimpleLoggerAdvisor(), // 添加 Spring 内置的日志记录功能
                                 new MyLoggerAdvisor()) // 添加自定义的日志打印 Advisor
                .build();
    }
}

重启项目。调用接口测试。

相关推荐
良策金宝AI42 分钟前
在一个平台完成查规范+绘图:工程AI如何重构设计工作流?
人工智能·能源·ai助手·工程设计
XL's妃妃44 分钟前
Arthas:Java 应用诊断利器
java·开发语言
zhaodiandiandian1 小时前
2025 AI 技术革命:从工具进化到生态重构
人工智能·重构
z***67771 小时前
Spring Data 什么是Spring Data 理解
java·后端·spring
AI视觉网奇1 小时前
表情驱动 训练
人工智能·深度学习
百胜软件@百胜软件1 小时前
财务对账提速80%:高并发场景下的快消网销数据治理实践
大数据·人工智能·零售
一个处女座的程序猿1 小时前
LLMs之Agents:Graphiti(为 AI 代理构建实时知识图谱)的简介、安装和使用方法、案例应用之详细攻略
人工智能·知识图谱·llms·agents·graphiti
Tao____1 小时前
国产开源物联网基础平台
java·物联网·mqtt·开源·设备对接
f***a3461 小时前
SpringBoot 如何调用 WebService 接口
java·spring boot·后端