Advisors用于拦截、修改和增强与AI的交互

By ref-nobody 创建时间 2025年5月4日 | 本文最后更新于 2025年5月9日 #advisor, #ai, #spring, #翻译

在spring ai中从官网可以看出advisor是一个很重要的概念,因此这一篇文章来学习一下。

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

其主要优势包括封装重复出现的生成式人工智能模式、转换发送到和从大型语言模型(LLMs)接收的数据,以及在各种模型和用例中提供可移植性。

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

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

String response = this.chatClient.prompt()
    // Set advisor parameters at runtime
    .advisors(advisor -> advisor.param("chat_memory_conversation_id", "678")
            .param("chat_memory_response_size", 100))
    .user(userText)
    .call()
	.content();

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

此外,Advisors也参与可观察性堆栈,因此您可以查看与其执行相关的指标和追踪信息。

核心组件

该 API 包括用于非流式场景的 CallAroundAdvisorCallAroundAdvisorChain,以及用于流式场景的 StreamAroundAdvisorStreamAroundAdvisorChain。它还包含用于表示未密封的 Prompt 请求的 AdvisedRequest 和用于表示 Chat Completion 响应的 AdvisedResponse。两者都持有一个 advise-context,用于在advisor链中共享状态。

nextAroundCall()nextAroundStream() 是顾问的核心方法,通常执行以下操作:检查未密封的 Prompt 数据,自定义和增强 Prompt 数据,调用顾问链中的下一个实体,可选地阻止请求,检查聊天完成响应,以及抛出异常以指示处理错误。

此外,getOrder() 方法决定了顾问在链中的顺序,而 getName() 方法提供了一个唯一的顾问名称。

由 Spring AI 框架创建的顾问链允许按 getOrder() 值排序的多个顾问依次调用。值越低的顾问越先执行。自动添加的最后一个顾问将请求发送到 LLM。

下面的流程图描述了顾问链和聊天模型的交互:

1. Spring AI 框架会根据用户的 Prompt 创建一个 AdvisedRequest,并附带一个空的 AdvisorContext 对象。
2. 链中的每个顾问都会处理该请求,可能会对其进行修改。或者,它可以选择通过不调用下一个实体来阻止请求。在这种情况下,顾问需要自行填充响应。
3. 框架提供的最后一个顾问会将请求发送到聊天模型(Chat Model)。
4. 聊天模型的响应随后会通过顾问链返回,并被转换为 AdvisedResponse。后者包含了共享的 AdvisorContext 实例。
5. 每个顾问都可以处理或修改响应。
6. 最终的 AdvisedResponse 会通过提取 ChatCompletion 返回给客户端。

Advisor在链中的顺序

advisor在执行链中的顺序由getOrder()方法来决定。理解的关键点如下:

  • 顺序值较低的顾问会优先执行
  • 顾问链的工作方式类似于栈:
    • 链中的第一个顾问是第一个处理请求的
    • 它也是最后一个处理响应的
  • 为了控制执行顺序:
    • 将顺序值设置为接近 Ordered.HIGHEST_PRECEDENCE,以确保顾问在链中首先执行(在请求处理时最先,在响应处理时最后)。
    • 将顺序值设置为接近 Ordered.LOWEST_PRECEDENCE,以确保顾问在链中最后执行(在请求处理时最后,在响应处理时最先)。
  • 较高的值被视为较低的优先级
  • 如果多个顾问具有相同的顺序值,则它们的执行顺序无法保证。

看似矛盾的顺序和执行序列之间的关系,是由于顾问链的栈式特性导致的:

  • 具有最高优先级(最低顺序值)的顾问被添加到栈的顶部。
  • 在栈展开时,它将是第一个处理请求的。
  • 在栈回绕时,它将是最后一个处理响应的。

对于需要在输入端和输出端都位于链首的用例:

  • 为每一端分别使用不同的顾问。
  • 为它们配置不同的顺序值。
  • 使用顾问上下文在它们之间共享状态。

自定义实现一个Advisor

spring的官网提到了一个Re-Reading (Re2) Advisor,《重读提升大型语言模型的推理能力》一文介绍了一种名为重读(Re-Reading,简称 Re2)的技术,该技术能够提升大型语言模型的推理能力。Re2 技术需要对输入的提示进行如下增强:

{Input_Query}
Read the question again: {Input_Query}

实现一个对用户输入查询应用 Re2 技术的顾问可以这样操作:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.ai.chat.client.advisor.api.AdvisedRequest;
import org.springframework.ai.chat.client.advisor.api.AdvisedResponse;
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAroundAdvisorChain;

import java.util.HashMap;
import java.util.Map;

public class ReReadingAdvisor implements CallAroundAdvisor {
    private final Logger logger = LoggerFactory.getLogger(ReReadingAdvisor.class);

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

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

    @Override
    public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {
        Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
        advisedUserParams.put("re2_input_query", advisedRequest.userText());
        AdvisedRequest request = AdvisedRequest.from(advisedRequest)
                .userText(
                        """
                        {re2_input_query}
                        Read the question again: {re2_input_query}
                        """
                )
                .userParams(advisedUserParams)
                .build();
        logger.info("ReReadingAdvisor request: {}", request);
        AdvisedResponse advisedResponse = chain.nextAroundCall(request);
        logger.info("ReReadingAdvisor aroundCall Request: {}", advisedResponse);
        return advisedResponse;
    }
}

运行程序可以看到对应的输出:

Spring AI中内置的Advisors

Spring AI 框架提供了几种内置的顾问来增强你的人工智能交互。以下是可用顾问的概述:

聊天记忆顾问(Chat Memory Advisors)
这些顾问在聊天记忆存储中管理对话历史:

  • MessageChatMemoryAdvisor
    检索记忆并将其作为消息集合添加到提示中。这种方法保留了对话历史的结构。注意,并非所有 AI 模型都支持这种方法。
  • PromptChatMemoryAdvisor
    检索记忆并将其整合到提示的系统文本中。
  • VectorStoreChatMemoryAdvisor
    从 VectorStore 检索记忆并将其添加到提示的系统文本中。这个顾问适用于从大型数据集中高效搜索和检索相关信息。

问答顾问(Question Answering Advisor)

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

内容安全顾问(Content Safety Advisor)

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

流式和非流式的对比

  • 非流式顾问处理完整的请求和响应。
  • 流式顾问以连续流的形式处理请求和响应,使用响应式编程概念(例如,使用 Flux 处理响应)。

最佳实践

1. 让顾问专注于特定任务,以实现更好的模块化。
2. 在必要时,使用 adviseContext 在顾问之间共享状态。
3. 为实现最大的灵活性,同时实现顾问的流式和非流式版本。
4. 仔细考虑顾问链中的顺序,以确保数据的正确流动。

Leave a Reply

Your email address will not be published. Required fields are marked *

目录