Spring AI Advisors API 提供了一种灵活而强大的方式,用于在 Spring 应用程序中拦截、修改和增强 AI 驱动的交互 。通过利用 Advisors API,开发者可以创建更复杂、可复用且可维护的 AI 组件。 其核心优势包括:

  • 封装重复的生成式 AI 模式 (如对话历史管理、敏感词过滤)
  • 转换与大语言模型(LLM)交互的数据 (如动态附加上下文或格式化输入/输出)
  • 实现跨模型和用例的可移植性 (通过统一接口适配不同 AI 服务)

核心组件

API 包含以下组件:

  • 非流式场景 :CallAroundAdvisor 和 CallAroundAdvisorChain
  • 流式场景 :StreamAroundAdvisor 和 StreamAroundAdvisorChain
  • AdvisedRequest :表示未密封的提示(Prompt)请求,包含可修改的输入数据
  • AdvisedResponse :封装聊天补全(Chat Completion)响应,包含生成结果和元数据

两者均通过 advise-context 在Advisor链中共享状态。

Advisors API 类

1、关键方法 nextAroundCall()(非流式)和 nextAroundStream()(流式)负责执行以下操作:

  • 检查或修改未密封的提示数据
  • 自定义或增强提示内容
  • 调用顾问链中的下一个节点
  • 可选择阻断请求(需自行填充响应)
  • 处理异常并抛出错误

2、控制方法

  • getOrder() :决定顾问链中的执行顺序(数值越小优先级越高)
  • getName() :提供唯一标识符以区分不同顾问

在这里插入图片描述

顾问链(Advisor Chain)

由 Spring AI 框架创建,按 getOrder() 值升序执行。最后一个内置顾问负责将请求发送至大语言模型(LLM)。 在这里插入图片描述 1、请求阶段

  • 框架将用户提示(Prompt)转换为 AdvisedRequest,并初始化空AdvisorContext。
  • 每个顾问依次处理请求,可修改数据或阻断流程(需自行生成响应)。
  • 最终顾问将请求发送至 LLM。

2、响应阶段

  • LLM 的响应通过 AdvisedResponse 封装,携带 AdvisorContext。
  • 顾问链反向处理响应,允许进一步修改或记录日志。
  • 最终结果提取为 ChatCompletion 返回客户端。

核心机制

  • 链式调用 :顾问按顺序执行,前一个顾问的输出作为下一个的输入。
  • 上下文共享 :通过 AdvisorContext 在链中传递状态(如对话ID、用户偏好)。
  • 流式支持 :StreamAroundAdvisor 处理响应式数据流(如逐字生成)。

顾问顺序(Advisor Order)

顾问链中的执行顺序由 getOrder() 方法决定,关键要点如下:

1、低值优先 getOrder() 返回值较低的顾问优先执行。例如:

  • Ordered.HIGHEST_PRECEDENCE(值为 Integer.MIN_VALUE)表示最高优先级。
  • Ordered.LOWEST_PRECEDENCE(值为 Integer.MAX_VALUE)表示最低优先级。

2、栈式链特性 顾问链的行为类似栈(后进先出):

  • 请求阶段 :高优先级顾问(低值)先处理请求。
  • 响应阶段 :高优先级顾问最后处理响应(类似栈的展开顺序)。

3、顺序控制策略

  • 需同时在请求/响应阶段优先的场景 : 使用两个独立顾问,分别设置不同顺序值,并通过 AdvisorContext 共享状态。
  • 示例 :
// 请求处理顾问(高优先级)
public class RequestAdvisor implements CallAroundAdvisor {
    @Override
    public int getOrder() { return Ordered.HIGHEST_PRECEDENCE; } // 优先处理请求
    // ...
}

// 响应处理顾问(低优先级)
public class ResponseAdvisor implements CallAroundAdvisor {
    @Override
    public int getOrder() { return Ordered.LOWEST_PRECEDENCE; } // 最后处理响应
    // ...
}

4、相同顺序值的处理 若多个顾问的 getOrder() 值相同,执行顺序不保证确定性。 Spring Ordered 接口语义

public interface Ordered {
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE; // 最高优先级
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;  // 最低优先级

    int getOrder(); // 值越低优先级越高,值越高优先级越低
}

优先级规则 :值越低的顾问越早处理请求,但越晚处理响应(栈特性)。

5、核心机制解释

  • 请求处理顺序 :顾问链按 getOrder() 升序执行(低值→高值)。
  • 响应处理顺序 :顾问链按 getOrder() 降序执行(高值→低值)。
  • 矛盾表象 :栈式结构导致高优先级顾问在请求阶段先执行,在响应阶段最后执行。

API 概览

主要的顾问(Advisor)接口位于包 org.springframework.ai.chat.client.advisor.api 中。以下是创建自定义顾问时需了解的核心接口:

1、基础接口 Advisor

public interface Advisor extends Ordered {
    String getName(); // 获取顾问的唯一名称
}

继承自 Ordered 接口,通过 getOrder() 控制执行顺序。

2、同步顾问接口 CallAroundAdvisor 用于拦截和增强同步请求(非流式):

public interface CallAroundAdvisor extends Advisor {
    /**
     * 包裹 {@link ChatModel#call(Prompt)} 方法的环绕通知。
     * @param advisedRequest 封装的请求对象
     * @param chain 顾问链
     * @return 增强后的响应
     */
    AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);
}

通过 CallAroundAdvisorChain 继续执行顾问链:

public interface CallAroundAdvisorChain {
    AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest); // 调用链中下一个顾问
}

3、响应式顾问接口 StreamAroundAdvisor 用于拦截和增强流式请求(如异步响应):

public interface StreamAroundAdvisor extends Advisor {
    /**
     * 包裹流式请求的环绕通知。
     * @param advisedRequest 封装的请求对象
     * @param chain 顾问链
     * @return 响应式数据流
     */
    Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);
}

通过 StreamAroundAdvisorChain 继续执行流式顾问链:

public interface StreamAroundAdvisorChain {
    Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest); // 调用链中下一个顾问
}

4、核心机制

  • 责任链模式 :顾问按 getOrder() 值顺序执行,前一个顾问的输出作为下一个的输入。
  • 同步与流式分离 :通过 CallAroundAdvisor 和 StreamAroundAdvisor 分别处理不同编程模型。
  • 上下文传递 :AdvisedRequest 和 AdvisedResponse 携带共享状态(如对话ID、用户参数)。

实现顾问(Implementing an Advisor)

要创建顾问,需实现 CallAroundAdvisor(同步场景)或 StreamAroundAdvisor(流式场景),或两者同时实现。核心方法为:

  • 同步场景 :实现 nextAroundCall()
  • 流式场景 :实现 nextAroundStream()

示例

以下通过具体案例说明如何实现用于观测(如日志)和增强(如数据修改)的顾问。

日志记录顾问(Logging Advisor)

实现一个简单的日志顾问,在调用下一个顾问前后记录 AdvisedRequest 和 AdvisedResponse。此顾问仅观测数据,不修改内容,同时支持同步和流式场景

public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

    private static final Logger logger = LoggerFactory.getLogger(SimpleLoggerAdvisor.class);

    @Override
    public String getName() { 
        return this.getClass().getSimpleName(); // 提供唯一名称 [[1]][[10]]
    }

    @Override
    public int getOrder() { 
        return 0; // 控制执行顺序,值越低优先级越高 [[1]][[10]]
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        logger.debug("调用前 - 请求: {}", advisedRequest); // 记录请求
        AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest); // 继续链式调用
        logger.debug("调用后 - 响应: {}", advisedResponse); // 记录响应
        return advisedResponse;
    }

    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
        logger.debug("流式调用前 - 请求: {}", advisedRequest);
        Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest); // 继续流式链
        return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,
                advisedResponse -> logger.debug("流式调用后 - 响应: {}", advisedResponse)); 
    }
}

1、名称与顺序

  • getName():返回顾问的唯一标识(如类名)。
  • getOrder():值越低越早执行(同步阶段优先,流式阶段最后处理响应)。

2、流式聚合工具 MessageAggregator 是一个实用工具类,用于将流式响应(Flux)聚合为单个 AdvisedResponse。

  • 适用于需要观测完整响应的场景(如日志记录)。
  • 不可修改响应 :聚合操作是只读的,仅用于观测。

重新阅读(Re2)顾问

根据《Re-Reading Improves Reasoning in Large Language Models》一文介绍,Re-Reading(Re2)技术 通过增强输入提示(Prompt)提升大语言模型的推理能力。Re2的核心思想是让模型在回答前重新阅读问题 ,从而减少因遗漏细节导致的错误。具体提示格式如下:

{Input_Query}
Read the question again: {Input_Query}

实现Re2顾问 以下顾问类通过修改用户输入,将Re2技术应用于请求流程:

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

    private AdvisedRequest before(AdvisedRequest advisedRequest) { 
        // 复制用户参数并添加Re2输入占位符
        Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
        advisedUserParams.put("re2_input_query", advisedRequest.userText());

        // 构建增强后的请求对象
        return AdvisedRequest.from(advisedRequest)
            .userText("""
                {re2_input_query}
                Read the question again: {re2_input_query}
                """) // 应用Re2模板 [[7]][[9]]
            .userParams(advisedUserParams)
            .build();
    }

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { 
        return chain.nextAroundCall(this.before(advisedRequest)); // 同步场景调用链 [[1]][[3]]
    }

    @Override
    public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { 
        return chain.nextAroundStream(this.before(advisedRequest)); // 流式场景调用链 [[3]][[10]]
    }

    @Override
    public int getOrder() { 
        return 0; // 控制执行顺序,值越低优先级越高 [[1]][[10]]
    }

    @Override
    public String getName() { 
        return this.getClass().getSimpleName(); // 顾问唯一名称 [[1]][[10]]
    }
}

核心方法说明: 1、before() 方法

  • 增强用户输入,插入Re2提示模板,引导模型重新阅读问题。
  • 通过 userParams 传递占位符参数(如 {re2_input_query})。

2、aroundCall() 和 aroundStream()

  • 同步场景 :拦截请求并应用Re2增强,然后继续调用后续顾问链。
  • 流式场景 :对响应式流执行相同增强逻辑。

3、顺序控制

  • 通过 getOrder() 设置执行优先级(值越低越早执行)。

Spring AI 内置顾问

Spring AI 框架提供了多种内置顾问以增强 AI 交互能力。以下是可用顾问的概述:

聊天记忆顾问(Chat Memory Advisors)

此类顾问通过聊天记忆存储管理对话历史:

  • MessageChatMemoryAdvisor 从记忆存储中检索历史记录,并作为消息集合附加到提示中。此方法保留对话历史的结构,但并非所有AI模型均支持此方式 。

  • PromptChatMemoryAdvisor 将记忆检索后合并到提示的系统文本(System Text)中,适用于需要全局上下文的场景。

  • VectorStoreChatMemoryAdvisor 从向量存储(Vector Store)中检索记忆并合并到系统文本中,适合从大规模数据集中高效搜索相关信息,支持检索增强生成(RAG)模式 。

问答顾问(Question Answering Advisor)

  • QuestionAnswerAdvisor 利用向量存储实现问答能力,通过检索外部数据增强生成结果,是RAG(检索增强生成)模式 的核心组件。

内容安全顾问(Content Safety Advisor)

  • SafeGuardAdvisor 用于过滤或阻止模型生成有害或不适当的内容,提供基础的内容安全防护。

核心机制与适用场景

  • 对话历史管理 :MessageChatMemoryAdvisor 和 PromptChatMemoryAdvisor 分别通过消息集合或系统文本传递历史记录,需根据模型兼容性选择。

  • RAG 支持 :VectorStoreChatMemoryAdvisor 和 QuestionAnswerAdvisor 结合外部数据源,提升生成结果的准确性和相关性。

  • 安全性增强 :SafeGuardAdvisor 可与其他顾问链式组合,确保输出内容符合规范。

流式与非流式顾问

在这里插入图片描述

流式与非流式顾问的流程差异

  • 非流式顾问 :处理完整的请求与响应对象。
  • 流式顾问 :基于响应式编程概念(如使用 Flux 处理响应流)处理连续的数据流。

流式顾问示例

@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {
    return Mono.just(advisedRequest)
        .publishOn(Schedulers.boundedElastic()) // 切换调度器以支持阻塞操作[[6]][[9]]
        .map(request -> {
            // 执行前置逻辑(可在阻塞或非阻塞线程中运行)
            return request;
        })
        .flatMapMany(request -> chain.nextAroundStream(request)) // 继续流式调用链[[3]][[10]]
        .map(response -> {
            // 执行后置逻辑(如日志记录或响应修改)
            return response;
        });
}

最佳实践

  • 保持顾问职责单一 每个顾问应专注于特定任务(如日志记录、内容过滤),以提升模块化和可维护性。

  • 使用 adviseContext 共享状态 通过上下文对象在顾问间传递数据(如对话ID、用户参数)。

  • 实现双模式支持 同时提供流式(StreamAroundAdvisor)和非流式(CallAroundAdvisor)实现,以适配不同场景。

  • 谨慎设计顾问顺序 低 getOrder() 值的顾问优先处理请求,但最后处理响应(栈式执行顺序)。

核心机制

  • 响应式编程 :流式顾问依赖 Flux 和 Mono 实现异步数据流处理,适合高并发场景。
  • 线程管理 :通过 publishOn(Schedulers.boundedElastic()) 切换线程池,避免阻塞操作影响性能。