SpringAI - Advisor(一)
什么是Advisor
官方定义
下面是官方文档对
Advisor
定义描述。Spring AI Advisor API 为拦截、修改和增强 Spring 应用中的 AI 交互提供了灵活强大的方式。通过该 API,开发者能构建更复杂、可复用且易维护的 AI 组件。
核心优势包括:封装可复用的生成式 AI 模式、转换与大语言模型(LLM)交互的数据、实现跨模型与用例的可移植性。
更多的可以自行去看官方文档,在这就先不多赘述了。
那么这里直接先看看如何自定义一个
Advisor
,在案例后再记录是什么、有什么用。
创建一个Advisor
如何创建Advisor
- 官方提供了两个
Advisor
接口,分别是CallAdvisor、StreamAdvisor
接口。这两接口有什么作用?作用就是实现它们之后,使用ChatClient调用AI的过程中会执行实现这俩接口的方法。 - 那么从命名上就能看出来它们一个是call调用、一个是stream调用相对应的接口了。
- 所以我们定义一个自己Advisor类,实现这两个接口,并重写他们所有的方法。方法里面就日志输出一下就是了。
创建一个Log输出的Advisor
实现
CallAdvisor、StreamAdvisor
接口,并重写所有方法1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class LogAdvisor implements CallAdvisor, StreamAdvisor {
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
return null;
}
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {
return null;
}
public String getName() {
return "";
}
public int getOrder() {
return 0;
}
}上面可以看到,一共重写了四个方法
getOrder()
:这个是排序用的,当有多个Advisor的时候,决定执行顺序。这里我们先不管getName()
就定定义一个名称,这里就用类名做名称好了。adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain)
:这个其实是CallAdvisor
接口的方法,也就是使用call调用时会进入到这个方法里面。adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain)
:这个就相对应时stream调用了。
两个adviseXXX方法我们就用log输出一下提示词的内容就好了,如下:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
log.info("\nadviseCall -> {}", chatClientRequest.prompt().getContents());
return callAdvisorChain.nextCall(chatClientRequest);
}
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain) {
log.info("\nadviseStream -> {}", chatClientRequest.prompt().getContents());
return streamAdvisorChain.nextStream(chatClientRequest);
}
public String getName() {
return this.getClass().getSimpleName();
}
public int getOrder() {
return 0;
}- 在上面我们可以看到两个方法的
return
都是xxxAdvisorChain.nextXxx(chatClientRequest)
,看着有种似曾相识的感觉。 - 到这里,自己创建的Advisor就可以了。那怎么让它在ChatClient调用AI过程中被执行呢?又没有注入到Spring容器。
- 在上面我们可以看到两个方法的
添加Advisor到ChatClient
在创建ChatClient的时候,添加需要的Advisor。如下
1
2
3
4
5
6
7
8
9
10
11
12
public class LogAdvisorClientConfiguration {
public ChatClient logAdvisorClient(DeepSeekChatModel chatModel) {
return ChatClient.builder(chatModel)
// 添加默认的 Advisors
.defaultAdvisors(List.of(new LogAdvisor()))
.build();
}
}这样就可以了,然后写个调用的案例。如下
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
public class LogAdvisorClientExample {
public final ChatClient logAdvisorClient;
private static final String SYSTEM_PROMPT = "你是一个Java专家,请帮忙解答提出的Java相关问题。";
public void logExample() {
String content = logAdvisorClient.prompt()
.system(SYSTEM_PROMPT)
.user("Java 之父的全称叫什么?")
.call()
.content();
log.info("\n[call] Java 之父 -> {}", content);
Flux<String> contentFlux = logAdvisorClient.prompt()
.system(SYSTEM_PROMPT)
.user("Java 之父的全称叫什么?")
.stream()
.content();
content = contentFlux.collectList().block().stream().collect(Collectors.joining());
log.info("\n[stream] Java 之父 -> {}", content);
}
}结果。从输出上看,执行了 LogAdvisor 中的log输出。
adviseCall
1
22025-06-26T19:26:48.694+08:00 INFO 77686 ---
adviseStream
1
22025-06-26T19:27:02.136+08:00 INFO 77686 ---
重新回到什么是Advisor
从上面的案例中可以看出来,在使用
ChatClient
调用AI的过程中,会执行添加在ChatClient
中的Advisor
的adviseCall
或adviseStream
方法。从
getOrder()
方法,以及在ChatClient
中添加Advisor
的.defaultAdvisors(List.of(new LogAdvisor()))
方法,可以看出来,是允许添加多个Advisor
的。多个
Advisor
那么就会形成一个调用链,所以在上面案例中可以看到如下两行代码的,这个xxxAdvisorChain
就是调用链。return callAdvisorChain.nextCall(chatClientRequest);
return streamAdvisorChain.nextStream(chatClientRequest);
那么简单来说,SpringAI会把添加在
ChatClient
中的Advisor
按照getOrder()
来排序,形成AdvisorChain
调用连。并在ChatClient
调用AI的过程中执行这个AdvisorChain
。这就能理解官网说的
Advisor 为拦截、修改和增强应用中的 AI 交互提供了灵活强大的方式。通过该 API,开发者能构建更复杂、可复用且易维护的 AI 组件
。确实如此!
Advisor上下文
是
Advisor
执行过程中的上下文,可以将一些参数放在里面进行共享传递。其实就是一个Map<String, Object>
。在创建
ChatClient
或者进行AI调用的时候就可以将参数放入上下文进行传递。下面将创建ChatClient的过程修改了一下。1
2
3
4
5
6
7
8
9
10
11
12
13
14
public ChatClient logAdvisorClient(DeepSeekChatModel chatModel) {
return ChatClient.builder(chatModel)
.defaultAdvisors(spec -> {
// 参数传递
spec.params(Map.of(
"ClientName", "multiAdvisorClient"
));
// 添加 advisors
spec.advisors(List.of(new LogAdvisor()));
})
.build();
}然后在
Advisor
之中通过ChatClientRequest
获取或者添加参数1
2
3
4// 获取
Object clientName = chatClientRequest.context().get("ClientName");
// 添加
chatClientRequest.context().put("K", "V");并不复杂,这里就不放运行结果了。
最后
- 如果对Web或者Spring比较了解的话,从
Advisor
的使用及功能上看,是不是觉得似曾相识? - 下一篇先记录一下
Advisor
的核心实现及执行流程。 - 后续会继续记录几个简单的案例来尝试拦截、增强与AI的交互。
- 所有案例的源码,都会提交在GitHub上。包:
com.spring.ai.example.advisor.one