Advisors API

原文:https://docs.spring.io/spring-ai/reference/api/advisors.html

Spring AI Advisors API 提供了一种灵活而强大的方法来拦截、修改和增强 Spring 应用程序中的 AI 驱动交互。通过利用 Advisors API,开发人员可以创建更复杂、可重用且更易于维护的 AI 组件。

主要优势包括封装重复的生成式 AI 模式、转换发送到和来自大型语言模型 (LLM) 的数据,以及提供跨各种模型和用例的可移植性。

您可以使用 ChatClient API 配置现有的 Advisors ,如以下示例所示:

ChatMemory chatMemory = ... // Initialize your chat memory store
VectorStore vectorStore = ... // Initialize your vector store

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        MessageChatMemoryAdvisor.builder(chatMemory).build(), // chat-memory advisor
        QuestionAnswerAdvisor.builder(vectorStore).build()    // RAG advisor
    )
    .build();

var conversationId = "678";

String response = this.chatClient.prompt()
    // Set advisor parameters at runtime
    .advisors(advisor -> advisor.param(ChatMemory.CONVERSATION_ID, conversationId))
    .user(userText)
    .call()
    .content();

建议在构建时使用构建器的 defaultAdvisors() 方法注册通知(Advice)。

核心组件

该 API 由 CallAdvisorCallAdvisorChain(适用于非流式场景),以及 StreamAdvisor 和StreamAdvisorChain( 适用于流式场景)组成。它还包括 ChatClientRequest,用于表示未密封的 Prompt 请求,以及 ,ChatClientResponse 用于表示聊天完成响应。两者都持有一个 ,advise-context 用于在顾问链中共享状态。

adviseCall()adviseStream() 是通知(Advice)的 关键方法,通常执行诸如检查未密封的提示数据、自定义和扩充提示数据、调用顾问链中的下一个实体、可选地阻止请求、检查聊天完成响应以及抛出异常以指示处理错误等操作。

此外,getOrder()方法确定链中顾问(Advisor)的顺序,同时 getName() 提供唯一的顾问(Advisor)名称。

Spring AI 框架创建的Advisor Chain允许按 getOrder() 值的顺序调用多个 Advisor 。值较低的 Advisor 优先执行。最后一个 Advisor 自动添加,并将请求发送到 LLM。

以下流程图说明了顾问(Advisor)链和聊天模型之间的交互:

  1. Spring AI 框架创建一个ChatClientRequest来自用户的Prompt空顾问context对象。
  2. 链中的每个顾问都会处理请求,并可能对其进行修改。或者,它可以选择阻止请求,不调用下一个实体。在后一种情况下,顾问(Advisor)负责填写响应。
  3. 框架提供的最终顾问将请求发送给Chat Model。
  4. 然后,聊天模型的响应通过顾问链传回并转换为ChatClientResponse。稍后包含共享顾问context实例。
  5. 每个顾问都可以处理或修改回复。
  6. 最后ChatClientResponse通过提取返回给客户端ChatCompletion。

Advisor 顺序

链中 顾问(Advisor) 的执行顺序由 getOrder() 方法决定。需要理解的关键点:

  • 具有较低顺序值的 Advisor 将首先执行。
  • Advisor Chain 以堆栈的形式运行:
  • 链中的第一位 Advisor 是第一个处理请求的。
  • 它也是最后处理响应的。
  • 控制执行顺序:
    • 将顺序设置Ordered.HIGHEST_PRECEDENCE为确保Advisor在链中首先执行(第一个用于请求处理,最后一个用于响应处理)。
    • 将顺序设置Ordered.LOWEST_PRECEDENCE为确保Advisor在链中最后执行(请求处理最后执行,响应处理首先执行)。
  • 值越高,优先级越低。
  • 如果多个Advisor具有相同的Order值,则无法保证它们的执行顺序。

提醒一下,以下是 Spring 接口的语义Ordered:

public interface Ordered {

    /**
     * Constant for the highest precedence value.
     * @see java.lang.Integer#MIN_VALUE
     */
    int HIGHEST_PRECEDENCE = Integer.MIN_VALUE;

    /**
     * Constant for the lowest precedence value.
     * @see java.lang.Integer#MAX_VALUE
     */
    int LOWEST_PRECEDENCE = Integer.MAX_VALUE;

    /**
     * Get the order value of this object.
     * <p>Higher values are interpreted as lower priority. As a consequence,
     * the object with the lowest value has the highest priority (somewhat
     * analogous to Servlet {@code load-on-startup} values).
     * <p>Same order values will result in arbitrary sort positions for the
     * affected objects.
     * @return the order value
     * @see #HIGHEST_PRECEDENCE
     * @see #LOWEST_PRECEDENCE
     */
    int getOrder();
}

API 概述

主要的 Advisor 接口位于org.springframework.ai.chat.client.advisor.api包中。以下是创建自己的 Advisor 时会遇到的关键接口:

public interface Advisor extends Ordered {

    String getName();

}

同步和响应式Advisor的两个子接口是

public interface CallAdvisor extends Advisor {

    ChatClientResponse adviseCall(
    ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);

}

public interface StreamAdvisor extends Advisor {

    Flux<ChatClientResponse> adviseStream(
    ChatClientRequest chatClientRequest, StreamAdvisorChain streamAdvisorChain);

}

继续看 Advice Chain,在你的实现中使用 CallAdvisorChainStreamAdvisorChainAdvice

接口是

public interface CallAdvisorChain extends AdvisorChain {

    /**
     * Invokes the next {@link CallAdvisor} in the {@link CallAdvisorChain} with the given
     * request.
     */
    ChatClientResponse nextCall(ChatClientRequest chatClientRequest);

    /**
     * Returns the list of all the {@link CallAdvisor} instances included in this chain at
     * the time of its creation.
     */
    List<CallAdvisor> getCallAdvisors();

}

public interface StreamAdvisorChain extends AdvisorChain {

    /**
     * Invokes the next {@link StreamAdvisor} in the {@link StreamAdvisorChain} with the
     * given request.
     */
    Flux<ChatClientResponse> nextStream(ChatClientRequest chatClientRequest);

    /**
     * Returns the list of all the {@link StreamAdvisor} instances included in this chain
     * at the time of its creation.
     */
    List<StreamAdvisor> getStreamAdvisors();

}

实现一个顾问(Advisor)

要创建顾问(Advisor)程序,请实现 CallAdvisor、StreamAdvisor(其中之一或两者)。关键的实现方法取决于nextCall() 非流式顾问程序或 nextStream() 流式顾问程序。

示例

我们将提供一些实际的例子来说明如何实现顾问来观察和扩展用例。

日志顾问(Advisor)

我们可以实现一个简单的日志顾问,用于记录调用链中下一个顾问的 ChatClientRequest 前后情况ChatClientResponse。请注意,该顾问仅观察请求和响应,而不会对其进行修改。此实现支持非流式和流式场景。

public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {

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

    @Override
    public String getName() {
        return this.getClass().getSimpleName();
    }

    @Override
    public int getOrder() {
        return 0;
    }


    @Override
    public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain) {
        logRequest(chatClientRequest);

        ChatClientResponse chatClientResponse = callAdvisorChain.nextCall(chatClientRequest);

        logResponse(chatClientResponse);

        return chatClientResponse;
    }

    @Override
    public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest,
            StreamAdvisorChain streamAdvisorChain) {
        logRequest(chatClientRequest);

        Flux<ChatClientResponse> chatClientResponses = streamAdvisorChain.nextStream(chatClientRequest);

        return new ChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses, this::logResponse);
    }

    private void logRequest(ChatClientRequest request) {
        logger.debug("request: {}", request);
    }

    private void logResponse(ChatClientResponse chatClientResponse) {
        logger.debug("response: {}", chatClientResponse);
    }

}
  1. 为顾问提供一个唯一的名称。
  2. 您可以通过设置 order 值来控制执行顺序。值较低的将优先执行。
  3. 是MessageAggregator一个实用程序类,它将 Flux 响应聚合成单个 ChatClientResponse。这对于日志记录或其他需要观察整个响应(而非流中的单个项目)的处理非常有用。请注意,MessageAggregator由于这是只读操作,因此您无法更改其中的响应。

重读(Re2)顾问(Advisor)

“重读提升大型语言模型的推理能力”一文介绍了一种名为重读(Re2)的技术,它可以提升大型语言模型的推理能力。Re2 技术需要像这样扩充输入提示:

{Input_Query}
Read the question again: {Input_Query}

实现将 Re2 技术应用于用户输入查询的顾问可以如下完成:

public class ReReadingAdvisor implements BaseAdvisor {

    private static final String DEFAULT_RE2_ADVISE_TEMPLATE = """
            {re2_input_query}
            Read the question again: {re2_input_query}
            """;

    private final String re2AdviseTemplate;

    private int order = 0;

    public ReReadingAdvisor() {
        this(DEFAULT_RE2_ADVISE_TEMPLATE);
    }

    public ReReadingAdvisor(String re2AdviseTemplate) {
        this.re2AdviseTemplate = re2AdviseTemplate;
    }

    @Override
    public ChatClientRequest before(ChatClientRequest chatClientRequest, AdvisorChain advisorChain) {
        String augmentedUserText = PromptTemplate.builder()
            .template(this.re2AdviseTemplate)
            .variables(Map.of("re2_input_query", chatClientRequest.prompt().getUserMessage().getText()))
            .build()
            .render();

        return chatClientRequest.mutate()
            .prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText))
            .build();
    }

    @Override
    public ChatClientResponse after(ChatClientResponse chatClientResponse, AdvisorChain advisorChain) {
        return chatClientResponse;
    }

    @Override
    public int getOrder() {
        return this.order;
    }

    public ReReadingAdvisor withOrder(int order) {
        this.order = order;
        return this;
    }

}

Spring AI 内置顾问(Advisor)

Spring AI 框架提供了多个内置顾问(Advisor),以增强您的 AI 交互。以下是可用顾问(Advisor)的概述:

问答顾问(Advisor)

这些顾问在聊天记忆库中管理对话历史记录:

  • MessageChatMemoryAdvisor
    检索记忆并将其作为消息集合添加到提示中。此方法可以维护对话历史记录的结构。请注意,并非所有 AI 模型都支持此方法。

  • PromptChatMemoryAdvisor
    检索内存并将其合并到提示的系统文本中。

  • VectorStoreChatMemoryAdvisor
    从 VectorStore 检索内存并将其添加到提示的系统文本中。此建议器有助于高效地从大型数据集中搜索和检索相关信息。

问答顾问(Advisor)

  • QuestionAnswerAdvisor
    该顾问使用向量存储来提供问答功能,实现了 Naive RAG(检索增强生成)模式。

  • RetrievalAugmentationAdvisor
    顾问使用 org.springframework.ai.rag 包中定义的构建块并遵循模块化 RAG 架构来实现常见的检索增强生成 (RAG) 流程。

推理顾问
内容安全顾问
  • SafeGuardAdvisor
    一个简单的顾问,旨在防止模型生成有害或不适当的内容。

流式传输 vs. 非流式传输

  • 非流式顾问处理完整的请求和响应。
  • 流顾问使用反应式编程概念(例如,用于响应的 Flux)将请求和响应作为连续流进行处理。
@Override
public Flux<ChatClientResponse> adviseStream(ChatClientRequest chatClientRequest, StreamAdvisorChain chain) {

    return  Mono.just(chatClientRequest)
            .publishOn(Schedulers.boundedElastic())
            .map(request -> {
                // This can be executed by blocking and non-blocking Threads.
                // Advisor before next section
            })
            .flatMapMany(request -> chain.nextStream(request))
            .map(response -> {
                // Advisor after next section
            });
}

最佳实践

  • 让顾问专注于特定任务,以实现更好的模块化。
  • 必要时使用adviseContext在顾问之间共享状态。
  • 实施顾问的流式和非流式版本,以实现最大的灵活性。
  • 仔细考虑链中顾问的顺序,以确保正确的数据流。
作者:Jeebiz  创建时间:2025-08-04 23:39
最后编辑:Jeebiz  更新时间:2025-08-08 00:47