1.ChatMemoryRepository****接⼝
ChatMemoryRepository 接⼝是对话记忆存储的抽象。⽀持多种存储⽅式,例如:内存⽅式、 JDBC ⽅式以及 Redis ⽅式等,每种实现⽅式都有特定的实现类。⽐如内存⽅式就是通过 InMemoryChatMemoryRepository 类实现的。
源码:
public interface ChatMemoryRepository {
// 获取所有活跃会话ID列表
List<String> findConversationIds();
// 根据会话ID获取消息列表
List<Message> findByConversationId(String conversationId);
// 根据会话ID全量替换存储,清除旧消息,录⼊新消息
void saveAll(String conversationId, List<Message> messages);
// 根据会话ID清除消息列表
void deleteByConversationId(String conversationId);
}
2.Advisor机制(拦截器)(面试可能会问到)
SpringAI Advisors 是 SpringAI 框架中⽤于拦截和增强 AI 交互的核⼼组件,其设计灵感类似
于 WebFilter ,通过链式调⽤实现对请求和响应的处理。
2.1什么是Advisor
SpringAI Advisors 是连接 AI 模型与业务逻辑的核⼼中间件,其设计理念与 SpringAOP 深度契
合。她提供了⼀种灵活⽽强⼤的⽅法来拦截、修改和增强 Spring 应⽤程序中的 AI 驱动的交互。
通过利⽤ Advisors API ,开发⼈员可以创建更复杂、可重⽤和可维护的 AI 组件。例如:我们可
能会创建聊天记录、排除敏感词或为每个请求添加额外的上下⽂。
例如:排除敏感词,对敏感违法的词,数据进行拦截
2.2大致流程

- 我们将提示词发送给 AI ⼤模型。
- ⼤模型附加的 Advisor ,会依次执⾏链中每个 Advisor 的 before ⽅法。
- 执⾏完所有的 before ⽅法后,交给⼤模型处理。
- ⼤模型处理后的响应,会依次执⾏链中每个 Advisor 的 after ⽅法。
- 执⾏完所有的 after ⽅法后,我们获得了最终的⼤模型响应内容,执⾏结束。
2.3核心功能
1. 请求和响应拦截
通过 AroundAdvisor 接⼝动态修改聊天请求和响应,⽀持⽇志记录、内容转换等场景。处理
流程遵循责任链模式,请求按顺序通过所有 Advisor ,响应则逆序返回。
2. 上下⽂共享
通过 AdvisorContext (会被所有的Advisor拦截器所共享的资源,例如在第一个Advisor中进行了一个操作,第二个Advisor中可以拿到第一个Advisor所做的操作的信息,会有一个共享容器)在 Advisor 链中传递数据,实现跨拦截器的状态共享。
3. 多模型兼容性
封装通⽤ AI 模式、例如如记忆管理、⽇志记录等,确保代码可复⽤且兼容不同⼤模型。
2.4基本架构

可以看到Advisor接口有两个子接口,分别是CallAdvisor和StreamAdvisor。
2.4.1Advisor接口
Advisor 接⼝是 advisor 体系中的顶层接⼝,它继承了 Ordered (只和顺序有关),⽤于⽅便制定
Advisor 链的执⾏顺序。
java
public interface Advisor extends Ordered {
int DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER = -2147482648;
String getName();
}
interface中自动加public static finial
该接⼝的主要作⽤就是⽤来声明当前的类是 Advisor 增强类,并可以声明⾃⼰的 advisor 名称。如果有多个 advisor 类,这些类按照其 getOrder() 值来排序。⾸先执⾏较低的值。⾃动添加的最后⼀个 advisor 将请求发送到⼤模型中。
java
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
注意:如果多个 advisor 的 getOrder() 返回的值相同,则不能保证执⾏顺序。
2.4.2CallAdvisor 和 StreamAdvisor ⼦接⼝
CallAdvisor 接⼝继承于 Advisor 接⼝,提供了⼀个环绕通知 adviseCall() ⽅法,可以在⽬标⽅法执⾏ 之前和执 ⾏ 之后都会执⾏逻辑,完全控制⽬标⽅法的执⾏流程。
java
public interface CallAdvisor extends Advisor {
ChatClientResponse adviseCall(ChatClientRequest chatClientRequest,
CallAdvisorChain callAdvisorChain);
}
StreamAdvisor 接⼝同样继承于 Advisor 接⼝,同样提供了⼀个环绕通知adviseStream() ⽅法,同样在⽬标⽅法执**⾏**前后执⾏逻辑,控制执⾏流程。
java
public interface StreamAdvisor extends Advisor {
Flux<ChatClientResponse> adviseStream(ChatClientRequest
chatClientRequest,
StreamAdvisorChain
streamAdvisorChain);
}
CallAdvisor 接⼝与 StreamAdvisor 接⼝不同的是: CallAdvisor 接⼝是同步的请求和
响应,⽽ StreamAdvisor 接⼝是流式请求和流失响应,通过返回 Flux<> 来增强流中的数
据操作。
2.4.3CallAdvisorChain 接⼝和 StreamAdvisorChain 接⼝
CallAdvisorChain 是 SpringAI 框架中⽤于链式调⽤多个 Advisor 的核⼼组件,实现请
求的拦截与增强功能。
java
public interface CallAdvisorChain extends AdvisorChain {
ChatClientResponse nextCall(ChatClientRequest chatClientRequest);
List<CallAdvisor> getCallAdvisors();
}
StreamAdvisorChain 是 SpringAI 框架中⽤于处理流式请求的链式拦截组件,其核⼼功能
是通过链式调⽤多个 StreamAdvisor 处理流式交互。
java
public interface StreamAdvisorChain extends AdvisorChain {
Flux<ChatClientResponse> nextStream(ChatClientRequest
chatClientRequest);
List<StreamAdvisor> getStreamAdvisors();
}
流程:

2.4.4BaseAdvisor 接⼝
BaseAdvisor 接⼝同时实现同步拦截(CallAdvisor)和流式拦截(StreamAdvisor )。BaseAdvisor 接⼝,这个接⼝同时继承了 CallAdvisor 和 StreamAdvisor 接⼝,同时提供了同步和流式的链式默认实现。在默认实现的环绕通知⽅法 中,前置和后置⽅法调⽤ before 和 after ⽅法⽤于使⽤者⾃定义。因此我们在需要时仅仅 定义我们的 before 和 after 即可。

3自定义Advisor
要⾃定义⼀个 Advisor ,其实很简单。只需遵循以下步骤:
- 创建⼀个类,实现 CallAroundAdvisor 或 StreamAroundAdvisor 接⼝。
- 实现接⼝的aroundCall()|aroundStream() 、 getName() 以及 getOrder() ⽅法。
- 调⽤⼤模型时,将自定义的 advisor 类添加进去即可。
入门案例:计算大模型的耗时
config包
java
package com.jiazhong.mingxing.ai.advisor.glm.config;
import com.jiazhong.mingxing.ai.advisor.glm.advisor.TimerAdvisor;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfig {
@Resource
private OpenAiChatModel openAiChatModel;
@Resource
private TimerAdvisor timerAdvisor;
@Bean("openAiChatClient")
public ChatClient openAiChatClient(){
return ChatClient.builder(openAiChatModel)
//3.进行全局注册
.defaultAdvisors(timerAdvisor)
.build();
}
}
advisor包
java
package com.jiazhong.mingxing.ai.advisor.glm.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.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.stereotype.Component;
@Slf4j
@Component //将一个普通的 Java 类标记为 Spring 容器管理的 Bean
public class TimerAdvisor implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
//1.获取进来的时候的时间
long begin = System.currentTimeMillis();
log.info("开始时间:{}",begin);
//2.执行后面的操作(包含下一个Advisor(可能多个)以及大模型)
ChatClientResponse chatClientResponse = chain.nextCall(request);
//4.获取到出去的时候的时间
long end = System.currentTimeMillis();
log.info("结束时间:{}",end);
//5.计算时间
long time = end - begin;
log.info("总共花费了:"+time+"毫秒");
return chatClientResponse;
}
@Override
//Advisor的名字
public String getName() {
return "timer";
}
@Override
//Advisor的优先级,数字越小优先级越高
public int getOrder() {
return 0;
}
}
controller包
java
package com.jiazhong.mingxing.ai.advisor.glm.controller;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
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;
@RestController
@RequestMapping("/advisor")
public class AdvisorController {
@Resource
private ChatClient openAiChatClient;
@GetMapping(value = "/stream",produces = "text/html;charset=utf-8")
public String call(@RequestParam("question") String question){
return openAiChatClient.prompt()
.user(question)
.call().content();
}
}
AdvisorContext上下文
在TimerAdvisor类中进行添加
java
package com.jiazhong.mingxing.ai.advisor.glm.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.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.stereotype.Component;
@Slf4j
@Component //将一个普通的 Java 类标记为 Spring 容器管理的 Bean
public class TimerAdvisor implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
//1.获取进来的时候的时间
long begin = System.currentTimeMillis();
request.context().put("A","这个是我在上下文中存放的内容");
log.info("开始时间:{}",begin);
//2.执行后面的操作(包含下一个Advisor(可能多个)以及大模型)
ChatClientResponse chatClientResponse = chain.nextCall(request);
//4.获取到出去的时候的时间
long end = System.currentTimeMillis();
Object a = request.context().get("A");
log.info("上下文存放的内容是:{}",a);
log.info("结束时间:{}",end);
//5.计算时间
long time = end - begin;
log.info("总共花费了:"+time+"毫秒");
return chatClientResponse;
}
@Override
//Advisor的名字
public String getName() {
return "timer";
}
@Override
//Advisor的优先级,数字越小优先级越高
public int getOrder() {
return 0;
}
}
新建SecondAdvisor类
java
package com.jiazhong.mingxing.ai.advisor.glm.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.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
import org.springframework.stereotype.Component;
@Component
@Slf4j
public class SecondAdvisor implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
Object a = chatClientRequest.context().get("A");
log.info("secondAdvisor:{}",a);
return callAdvisorChain.nextCall(chatClientRequest);
}
@Override
public String getName() {
return "second";
}
@Override
public int getOrder() {
return 3;
}
}
修改Cinfig类
java
package com.jiazhong.mingxing.ai.advisor.glm.config;
import com.jiazhong.mingxing.ai.advisor.glm.advisor.SecondAdvisor;
import com.jiazhong.mingxing.ai.advisor.glm.advisor.TimerAdvisor;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.openai.OpenAiChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ChatClientConfig {
@Resource
private OpenAiChatModel openAiChatModel;
@Resource
private TimerAdvisor timerAdvisor;
@Resource
private SecondAdvisor secondAdvisor;
@Bean("openAiChatClient")
public ChatClient openAiChatClient(){
return ChatClient.builder(openAiChatModel)
//3.进行全局注册
.defaultAdvisors(timerAdvisor,secondAdvisor)
.build();
}
}
运行结果

说明各个Advisor是互通的,根据优先级确定执行顺序。
4内置的Advisor
4.1. MessageChatMemoryAdvisor
管理多轮会话上下⽂,使⽤ MessageChatMemoryAdvisor ,我们可以通过 messages 属性
提供聊天客户端调⽤的聊天历史记录。我们可以将所有消息保存在 ChatMemory 实现中,并
控制历史记录的⼤⼩。
我们多轮会话中不管是内存⽅式还是 JDBC ⽅式使⽤的就是这种效果:

4.2PromptChatMemoryAdvisor
PromptChatMemoryAdvisor 与 MessageChatMemoryAdvisor 都能实现类似效果。不同的
是 PromptChatMemoryAdvisor 会将对话历史封装到系统提示词( System Prompt )中,兼容
不⽀持多轮上下⽂的⼤模型。
这⾥我们直接把 MessageChatMemoryAdvisor 替换成 PromptChatMemoryAdvisor 效果也
是⼀样的。
java
@Bean("ollamaChatClient")
public ChatClient chatClient() {
return ChatClient
.builder(ollamaChatModel)
//.defaultAdvisors(new MessageChatMemoryAdvisor(chatMemory))
// 内存⽅式
//.defaultAdvisors(new
MessageChatMemoryAdvisor(jdbcChatMemory)) // JDBC⽅式
.defaultAdvisors(new
PromptChatMemoryAdvisor(jdbcChatMemory)) // 替换成PromptChatMemoryAdvisor
.build();
}
4.3. SimpleLoggerAdvisor
SimpleLoggerAdvisor 是 SpringAI 框架中⽤于⽇志记录的内置组件,主要⽤于拦截并记
录聊天请求与响应的详细信息,⽀持调试和监控应⽤程序运⾏状态。
核⼼功能
- ⽇志记录:默认记录 Debug 级别的⽇志,包括⽤户输⼊⽂本、模型响应等内容,但默认不显示。
- 可配置性:⽀持通过配置⽂件调整⽇志级别(如改为 Info 级别),或下载源码⾃定义逻辑。
项⽬搭配
- 添加 SimpleLoggerAdvisor

- 修改配置⽂件
- 在application.yml文件中加入下述代码

- 测试结果

4.44. SafeGuardAdvisor
该 Advisor 基于关键词或正则表达式过滤敏感内容,拦截⾮法请求。
在config类中

输入敏感词的结果如下图
