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();
    }
}

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

相关推荐
那个村的李富贵5 小时前
光影魔术师:CANN加速实时图像风格迁移,让每张照片秒变大师画作
人工智能·aigc·cann
侠客行03176 小时前
Mybatis连接池实现及池化模式
java·mybatis·源码阅读
蛇皮划水怪6 小时前
深入浅出LangChain4J
java·langchain·llm
腾讯云开发者6 小时前
“痛点”到“通点”!一份让 AI 真正落地产生真金白银的实战指南
人工智能
CareyWYR6 小时前
每周AI论文速递(260202-260206)
人工智能
hopsky7 小时前
大模型生成PPT的技术原理
人工智能
禁默8 小时前
打通 AI 与信号处理的“任督二脉”:Ascend SIP Boost 加速库深度实战
人工智能·信号处理·cann
老毛肚8 小时前
MyBatis体系结构与工作原理 上篇
java·mybatis
心疼你的一切8 小时前
昇腾CANN实战落地:从智慧城市到AIGC,解锁五大行业AI应用的算力密码
数据仓库·人工智能·深度学习·aigc·智慧城市·cann
AI绘画哇哒哒8 小时前
【干货收藏】深度解析AI Agent框架:设计原理+主流选型+项目实操,一站式学习指南
人工智能·学习·ai·程序员·大模型·产品经理·转行