SpringAI中的模块化链式Advisor调用(源码学习)
SpringAI 使用通义千问进行聊天对话开发)之后,我发现 Spring AI 框架中的 Advisor 的链式调用挺有意思,而且可能应用到比较复杂的业务场景中。通过之前的AI 对话,我最终发现,调用链路中,最后一环拥有最低的优先级,也就是对话模型真正执行的advisor。基于此,在调用链路中模块化的增加一些 advisor 来处理对应的业务,或调整请求参数做一些业务逻辑。但是Spring AI
前言
在进行了Spring AI 的对话模型的实践(见文章:SpringAI 使用通义千问进行聊天对话开发)之后,我发现 Spring AI 框架中的 Advisor 的链式调用挺有意思,而且可能应用到比较复杂的业务场景中。
通过之前的AI 对话,我最终发现,调用链路中,最后一环拥有最低的优先级,也就是对话模型真正执行的advisor。基于此,在调用链路中模块化的增加一些 advisor 来处理对应的业务,或调整请求参数做一些业务逻辑。
但是Spring AI 本身的接口和类定义比较固定,都是基于对话的。我进行了简化,并且使用泛型来做通用化。可以包装任意请求和响应,包括最后一环使用 Function 接口进行执行后置。
完整代码在代码仓库:https://gitee.com/fengsoshuai/song-tools/tree/dev/advisor-tool
具体的就请看本文正文!!
正文
1.1 项目结构
- CallAdvisor :调用增强器,所有advisor的顶层接口。提供调用方法、排序方式,order 值越小,表示优先级越高,越先执行。
- CallAdvisorChain: 调用增强器链,提供获取下一个advisor,并执行该advisor的方法。
- ReqSpec:请求规则,提供构建 advisor,请求参数的方法,支持lambda调用。可以组装响应规则。
- RespSpec:响应规则,返回响应内容。
- AdvisorClient:增强器客户端,提供获取 ReqSpec 的方法。
- DefaultCallAdvisor:默认调用增强器,调用链的最后会调用。一般用于实际业务的最后一步请求处理。比如http请求,或请求数据库。
1.2 项目环境
项目环境基于 java 17,在maven 中进行如下配置:
<properties>
<java.version>17</java.version>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
<maven.compiler.source>17</maven.compiler.source>
<maven.compiler.target>17</maven.compiler.target>
</properties>
不使用用其他任何依赖,包括 lombok!! 方便被其他模块使用,真实项目中如果有业务场景,建议将advisor作为一个jar包,在其他模块引入。
1.3 完整代码
1.3.1 CallAdvisor
package com.song.tools.advisor;
/**
* 调用增强器
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:47
*/
public interface CallAdvisor<Req, Resp> extends Ordered {
/**
* 调用增强
*
* @param req 请求参数
* @param chain 调用链
* @return 响应结果
*/
Resp adviseCall(Req req, CallAdvisorChain<Req, Resp> chain);
/**
* 默认排序
*/
int DEFAULT_ORDER = HIGHEST_PRECEDENCE + 1000;
default String getName() {
return getClass().getSimpleName();
}
}
1.3.2 Ordered
package com.song.tools.advisor;
/**
* 排序接口:数值越小,优先级越高
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:45
*/
public interface Ordered {
int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;
int LOWEST_PRECEDENCE = Integer.MAX_VALUE;
int getOrder();
}
1.3.3 CallAdvisorChain
package com.song.tools.advisor;
import java.util.List;
/**
* 增强器调用链
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:49
*/
public interface CallAdvisorChain<Req, Resp> {
/**
* 获取下一个增强器,并执行
*
* @param req 请求参数
* @return 响应参数
*/
Resp nextCall(Req req);
/**
* 获取所有增强器
*
* @return 所有增强器
*/
List<CallAdvisor<Req, Resp>> getCallAdvisors();
}
1.3.4 ReqSpec
package com.song.tools.advisor;
import java.util.List;
/**
* 增强器req规则
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:41
*/
public interface ReqSpec<Req, Resp> {
ReqSpec<Req, Resp> advisor(CallAdvisor<Req, Resp> advisor);
ReqSpec<Req, Resp> advisors(List<CallAdvisor<Req, Resp>> advisors);
/**
* 请求参数:构建请求参数
*
* @param req 请求参数
* @return 增强器req规则
*/
ReqSpec<Req, Resp> requestParam(Req req);
/**
* 组装响应规则,不实际执行
*
* @return 响应规则
*/
RespSpec<Req, Resp> call();
}
1.3.5 RespSpec
package com.song.tools.advisor;
/**
* 增强器resp规则
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:41
*/
public interface RespSpec<Req, Resp> {
/**
* 获取响应结果的内容
*
* @return 响应结果的内容
*/
String content();
/**
* 获取响应结果
*
* @return 响应结果
*/
Resp responseEntity();
/**
* 获取响应结果为json
*
* @return json
*/
default String json() {
throw new UnsupportedOperationException();
}
}
1.3.6 AdvisorClient
package com.song.tools.advisor;
/**
* 增强器客户端
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:59
*/
public interface AdvisorClient<Req, Resp> {
/**
* 初始化:获取请求规则
*
* @return 请求规则
*/
ReqSpec<Req, Resp> reqSpec(DefaultCallAdvisor<Req, Resp> defaultCallAdvisor);
}
1.3.7 DefaultCallAdvisorChain
使用优先级队列对链中的advisor进行排序。
package com.song.tools.advisor;
import java.util.Comparator;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.PriorityBlockingQueue;
/**
* 默认执行增强器链
*
* @author pine
* @version v1.0
* @since 2025-08-16 10:07
*/
public class DefaultCallAdvisorChain<Req, Resp> implements CallAdvisorChain<Req, Resp> {
private final List<CallAdvisor<Req, Resp>> originalCallAdvisors;
private final PriorityBlockingQueue<CallAdvisor<Req, Resp>> callAdvisors;
private DefaultCallAdvisorChain(PriorityBlockingQueue<CallAdvisor<Req, Resp>> callAdvisors) {
this.callAdvisors = callAdvisors;
this.originalCallAdvisors = List.copyOf(callAdvisors);
}
/**
* 获取下一个增强器,并执行
*
* @param req 请求参数
* @return 响应参数
*/
@Override
public Resp nextCall(Req req) {
Objects.requireNonNull(req);
if (this.callAdvisors.isEmpty()) {
throw new IllegalStateException("No CallAdvisors available to execute");
}
// 获取下一个增强器,并从队列中移除
CallAdvisor<Req, Resp> currentAdvisor = callAdvisors.poll();
// 执行增强器
return currentAdvisor.adviseCall(req, this);
}
/**
* 获取所有增强器
*
* @return 所有增强器
*/
@Override
public List<CallAdvisor<Req, Resp>> getCallAdvisors() {
return originalCallAdvisors;
}
public static class Builder<Req, Resp> {
private final PriorityBlockingQueue<CallAdvisor<Req, Resp>> callAdvisors;
public Builder() {
this.callAdvisors = new PriorityBlockingQueue<>(20, Comparator.comparingInt(Ordered::getOrder));
}
public Builder<Req, Resp> addAdvisor(CallAdvisor<Req, Resp> advisor) {
return addAdvisors(List.of(advisor));
}
public Builder<Req, Resp> addAdvisors(List<CallAdvisor<Req, Resp>> advisors) {
this.callAdvisors.addAll(advisors);
return this;
}
public DefaultCallAdvisorChain<Req, Resp> build() {
return new DefaultCallAdvisorChain<>(callAdvisors);
}
}
}
1.3.8 DefaultAdvisorClient
定义内部静态类请求规则和响应规则的实现:DefaultReqSpec、DefaultRespSpec 。
package com.song.tools.advisor;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* 默认增强器客户端
*
* @author pine
* @version v1.0
* @since 2025-08-16 10:20
*/
public class DefaultAdvisorClient<Req, Resp> implements AdvisorClient<Req, Resp> {
/**
* 初始化:获取请求规则
*
* @return 请求规则
*/
@Override
public ReqSpec<Req, Resp> reqSpec(DefaultCallAdvisor<Req, Resp> defaultCallAdvisor) {
return new DefaultReqSpec<>(defaultCallAdvisor);
}
/**
* 默认请求规则
*
* @param <Req> 请求参数类型
* @param <Resp> 响应参数类型
*/
public static class DefaultReqSpec<Req, Resp> implements ReqSpec<Req, Resp> {
private final List<CallAdvisor<Req, Resp>> advisors = new ArrayList<>();
private Req req;
public DefaultReqSpec(DefaultCallAdvisor<Req, Resp> defaultCallAdvisor) {
Objects.requireNonNull(defaultCallAdvisor);
this.advisors.add(defaultCallAdvisor);
}
@Override
public ReqSpec<Req, Resp> advisor(CallAdvisor<Req, Resp> advisor) {
Objects.requireNonNull(advisors);
this.advisors.add(advisor);
return this;
}
@Override
public ReqSpec<Req, Resp> advisors(List<CallAdvisor<Req, Resp>> advisors) {
Objects.requireNonNull(advisors);
this.advisors.addAll(advisors);
return this;
}
@Override
public ReqSpec<Req, Resp> requestParam(Req req) {
this.req = req;
return this;
}
/**
* 组装响应规则
*
* @return 响应规则
*/
@Override
public RespSpec<Req, Resp> call() {
Objects.requireNonNull(req);
DefaultCallAdvisorChain<Req, Resp> advisorChain = buildAdvisorChain();
return new DefaultRespSpec<>(req, advisorChain);
}
private DefaultCallAdvisorChain<Req, Resp> buildAdvisorChain() {
return new DefaultCallAdvisorChain.Builder<Req, Resp>().addAdvisors(advisors).build();
}
}
/**
* 默认响应规则
*
* @param <Req> 请求参数类型
* @param <Resp> 响应参数类型
*/
public static class DefaultRespSpec<Req, Resp> implements RespSpec<Req, Resp> {
private final CallAdvisorChain<Req, Resp> advisorChain;
private final Req req;
public DefaultRespSpec(Req req, CallAdvisorChain<Req, Resp> advisorChain) {
Objects.requireNonNull(req);
Objects.requireNonNull(advisorChain);
this.req = req;
this.advisorChain = advisorChain;
}
@Override
public String content() {
return String.valueOf(advisorChain.nextCall(req));
}
@Override
public Resp responseEntity() {
return advisorChain.nextCall(req);
}
}
}
1.3.9 日志增强器:SimpleLoggerAdvisor
增强器的一种实现,可以选择使用,会在调用链的起始和结束打印请求和响应结果。
package com.song.tools.advisor;
/**
* 简单日志增强器
*
* @author pine
* @version v1.0
* @since 2025-08-16 09:53
*/
public class SimpleLoggerAdvisor<Req, Resp> implements CallAdvisor<Req, Resp> {
@Override
public Resp adviseCall(Req req, CallAdvisorChain<Req, Resp> chain) {
System.out.println("SimpleLoggerAdvisor=========");
logRequest(req);
// 继续调用下一个增强器
Resp resp = chain.nextCall(req);
logResponse(resp);
return resp;
}
/**
* 打印请求
*
* @param req 请求
*/
private void logRequest(Req req) {
System.out.println("SimpleLoggerAdvisor.logRequest=========" + req);
}
/**
* 打印响应
*
* @param resp 响应
*/
private void logResponse(Resp resp) {
System.out.println("SimpleLoggerAdvisor.logResponse=========" + resp);
}
@Override
public int getOrder() {
return DEFAULT_ORDER;
}
}
1.3.10 默认调用增强器:DefaultCallAdvisor
最终需要执行的业务,作为调用链的最后一个增强器,它会实际处理业务,并生成响应。比如实际调用http请求的逻辑就可以用它。
package com.song.tools.advisor;
import java.util.Objects;
import java.util.function.Function;
/**
* 默认执行增强器:作为调用链的最后一个
*
* @author pine
* @version v1.0
* @since 2025-08-16 10:58
*/
public class DefaultCallAdvisor<Req, Resp> implements CallAdvisor<Req, Resp> {
/**
* 调用函数:由调用方指定,作为业务逻辑。需要生成响应结果。
*/
private final Function<Req, Resp> callFunction;
public DefaultCallAdvisor(Function<Req, Resp> callFunction) {
Objects.requireNonNull(callFunction);
this.callFunction = callFunction;
}
@Override
public Resp adviseCall(Req req, CallAdvisorChain<Req, Resp> chain) {
System.out.println("DefaultCallAdvisor=========");
return callFunction.apply(req);
}
@Override
public int getOrder() {
return LOWEST_PRECEDENCE;
}
}
1.4 测试执行
如果你想增强某方面的业务逻辑,又不方便调整业务逻辑本身,可以实现自己的 CallAdvisor,然后设置其优先级。构建到执行链路中即可。
注意DefaultCallAdvisor必须只设置一个,不设置多次!而且必须要有!
1.4.1 执行流程
组装如下的逻辑:
- 定义业务逻辑,Function<Req, Resp>
- 定义请求参数
- 创建增强器客户端
- 客户端获取请求规则,请求规则装载多个advisor,请求规则装载请求参数
- 请求规则构建响应规则
- 响应规则执行请求,并组装响应结果
1.4.2 测试代码
package com.song.tools.advisor.test;
import com.song.tools.advisor.AdvisorClient;
import com.song.tools.advisor.DefaultAdvisorClient;
import com.song.tools.advisor.DefaultCallAdvisor;
import com.song.tools.advisor.SimpleLoggerAdvisor;
import java.util.List;
import java.util.function.Function;
/**
* 测试
*
* @author pine
* @version v1.0
* @since 2025-08-16 10:18
*/
public class Demo {
public static void main(String[] args) {
// 业务逻辑本身:后续如果用spring,可以是注入某个service的方法
Function<UserReq, UserResp> callFunction = req -> {
UserResp userResp = new UserResp();
userResp.name = req.name + "涨了10岁";
userResp.age = req.age + 10;
return userResp;
};
// 请求参数
UserReq userReq = new UserReq();
userReq.name = "张三";
userReq.age = 18;
AdvisorClient<UserReq, UserResp> client = new DefaultAdvisorClient<>();
UserResp userResp = client.reqSpec(new DefaultCallAdvisor<>(callFunction))
.advisors(List.of(new SimpleLoggerAdvisor<>()))
.requestParam(userReq)
.call()
.responseEntity();
System.out.println(userResp.name);
System.out.println(userResp.age);
}
public static class UserReq {
public String name;
public Integer age;
}
public static class UserResp {
public String name;
public Integer age;
}
}
1.4.3 控制台输出
SimpleLoggerAdvisor=========
SimpleLoggerAdvisor.logRequest=========com.song.tools.advisor.test.Demo$UserReq@1e80bfe8
DefaultCallAdvisor=========
SimpleLoggerAdvisor.logResponse=========com.song.tools.advisor.test.Demo$UserResp@cc34f4d
张三涨了10岁
28
可以观察到日志增强器在链路的起始和结束执行了,并打印了请求和响应。
其次DefaultCallAdvisor
在链路的最后执行,Function<UserReq, UserResp> callFunction
作为业务逻辑,会实际执行。
更多推荐
所有评论(0)