前言

在进行了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 执行流程

组装如下的逻辑:

  1. 定义业务逻辑,Function<Req, Resp>
  2. 定义请求参数
  3. 创建增强器客户端
  4. 客户端获取请求规则,请求规则装载多个advisor,请求规则装载请求参数
  5. 请求规则构建响应规则
  6. 响应规则执行请求,并组装响应结果

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 作为业务逻辑,会实际执行。

Logo

有“AI”的1024 = 2048,欢迎大家加入2048 AI社区

更多推荐