Advisors API

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

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

您可以使用 ChatClient API 配置现有顾问,如以下示例所示:spring-doc.cadn.net.cn

var chatClient = ChatClient.builder(chatModel)
    .defaultAdvisors(
        new MessageChatMemoryAdvisor(chatMemory), // chat-memory advisor
        new QuestionAnswerAdvisor(vectorStore, SearchRequest.defaults()) // 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();

建议在构建时使用 builder 的defaultAdvisors()方法。spring-doc.cadn.net.cn

顾问还参与可观测性堆栈,因此您可以查看与其执行相关的指标和跟踪。spring-doc.cadn.net.cn

核心组件

API 包括CallAroundAdvisorCallAroundAdvisorChain对于非流式处理方案,以及StreamAroundAdvisorStreamAroundAdvisorChain用于流式处理方案。 它还包括AdvisedRequest来表示未封装的 Prompt 请求,AdvisedResponse以获取 Chat Completion 响应。两者都持有advise-context在 advisor 链中共享状态。spring-doc.cadn.net.cn

Advisor API 类

nextAroundCall()nextAroundStream()是关键的 advisor 方法,通常执行诸如检查未密封的 Prompt 数据、自定义和扩充 Prompt 数据、调用 advisor 链中的下一个实体、选择性地阻止请求、检查聊天完成响应以及引发异常以指示处理错误等作。spring-doc.cadn.net.cn

此外,getOrder()method 确定链中的 advisor 顺序,而getName()提供唯一的 advisor 名称。spring-doc.cadn.net.cn

由 Spring AI 框架创建的 Advisor 链允许按顺序调用多个 advisor,这些 advisor 按其getOrder()值。 首先执行较低的值。 自动添加的最后一个 advisor 将请求发送到 LLM。spring-doc.cadn.net.cn

以程图说明了顾问链和聊天模型之间的交互:spring-doc.cadn.net.cn

Advisors API 流程
  1. Spring AI 框架创建了一个AdvisedRequest从用户的Prompt以及一个空的AdvisorContext对象。spring-doc.cadn.net.cn

  2. 链中的每个 advisor 都会处理请求,并可能对其进行修改。或者,它也可以选择通过不调用下一个实体来阻止请求。在后一种情况下,顾问负责填写回复。spring-doc.cadn.net.cn

  3. 框架提供的 final advisor 将请求发送到Chat Model.spring-doc.cadn.net.cn

  4. 然后,聊天模型的响应通过 advisor 链传回并转换为AdvisedResponse.later 包括共享的AdvisorContext实例。spring-doc.cadn.net.cn

  5. 每个顾问都可以处理或修改响应。spring-doc.cadn.net.cn

  6. 决赛AdvisedResponse通过提取ChatCompletion.spring-doc.cadn.net.cn

顾问订单

链中 advisor 的执行顺序由getOrder()方法。需要了解的要点:spring-doc.cadn.net.cn

顺序和执行顺序之间看似矛盾是由于 advisor 链的堆栈性质: * 具有最高优先级 (最低订单价值) 的顾问被添加到堆栈顶部。 * 当堆栈展开时,它将第一个处理请求。 * 当堆栈倒带时,它将是最后一个处理响应的。spring-doc.cadn.net.cn

提醒一下,以下是 Spring 的语义Ordered接口:spring-doc.cadn.net.cn

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();
}

对于需要在输入和输出端都位于链中的先行用例:spring-doc.cadn.net.cn

  1. 为每一方使用单独的顾问。spring-doc.cadn.net.cn

  2. 使用不同的 order 值配置它们。spring-doc.cadn.net.cn

  3. 使用 advisor 上下文在它们之间共享状态。spring-doc.cadn.net.cn

API 概述

主要的 Advisor 界面位于软件包中org.springframework.ai.chat.client.advisor.api.以下是您在创建自己的 advisor 时会遇到的关键界面:spring-doc.cadn.net.cn

public interface Advisor extends Ordered {

	String getName();

}

同步和反应式 Advisor 的两个子接口是spring-doc.cadn.net.cn

public interface CallAroundAdvisor extends Advisor {

	/**
	 * Around advice that wraps the ChatModel#call(Prompt) method.
	 * @param advisedRequest the advised request
	 * @param chain the advisor chain
	 * @return the response
	 */
	AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain);

}
public interface StreamAroundAdvisor extends Advisor {

	/**
	 * Around advice that wraps the invocation of the advised request.
	 * @param advisedRequest the advised request
	 * @param chain the chain of advisors to execute
	 * @return the result of the advised request
	 */
	Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain);

}

要继续 Advice 链,请使用CallAroundAdvisorChainStreamAroundAdvisorChain在 Advice 实现中:spring-doc.cadn.net.cn

这些接口包括spring-doc.cadn.net.cn

public interface CallAroundAdvisorChain {

	AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);

}
public interface StreamAroundAdvisorChain {

	Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);

}

实施 Advisor

要创建 advisor,请实现CallAroundAdvisorStreamAroundAdvisor(或两者兼而有之)。要实现的关键方法是nextAroundCall()对于非流式处理或nextAroundStream()适用于流顾问。spring-doc.cadn.net.cn

例子

我们将提供一些动手实践示例来说明如何实施 advisor 来观察和增强用例。spring-doc.cadn.net.cn

日志记录顾问

我们可以实现一个简单的日志顾问程序,它记录AdvisedRequestbefore 和AdvisedResponse在调用链中的下一个 advisor 之后。 请注意,advisor 仅观察请求和响应,不会修改它们。 此实现支持非流式处理和流式处理方案。spring-doc.cadn.net.cn

public class SimpleLoggerAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {

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

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

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

	@Override
	public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

		logger.debug("BEFORE: {}", advisedRequest);

		AdvisedResponse advisedResponse = chain.nextAroundCall(advisedRequest);

		logger.debug("AFTER: {}", advisedResponse);

		return advisedResponse;
	}

	@Override
	public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

		logger.debug("BEFORE: {}", advisedRequest);

		Flux<AdvisedResponse> advisedResponses = chain.nextAroundStream(advisedRequest);

        return new MessageAggregator().aggregateAdvisedResponse(advisedResponses,
                    advisedResponse -> logger.debug("AFTER: {}", advisedResponse)); (3)
	}
}
1 为 advisor 提供唯一名称。
2 您可以通过设置 order 值来控制执行顺序。较低的值首先执行。
3 MessageAggregator是一个实用程序类,用于将 Flux 响应聚合到单个 AdvisedResponse 中。 这对于记录或其他观察整个响应而不是流中单个项目的处理非常有用。 请注意,您不能更改MessageAggregator因为它是只读作。

重读 (Re2) 顾问

Re-Reading Improves Reasoning in Large Language Models” 一文介绍了一种称为 Re-Reading (Re2) 的技术,该技术可以提高大型语言模型的推理能力。 Re2 技术需要像这样扩充输入提示:spring-doc.cadn.net.cn

{Input_Query}
Read the question again: {Input_Query}

实现将 Re2 技术应用于用户输入查询的 advisor 可以像这样完成:spring-doc.cadn.net.cn

public class ReReadingAdvisor implements CallAroundAdvisor, StreamAroundAdvisor {


	private AdvisedRequest before(AdvisedRequest advisedRequest) { (1)

		Map<String, Object> advisedUserParams = new HashMap<>(advisedRequest.userParams());
		advisedUserParams.put("re2_input_query", advisedRequest.userText());

		return AdvisedRequest.from(advisedRequest)
			.withUserText("""
			    {re2_input_query}
			    Read the question again: {re2_input_query}
			    """)
			.withUserParams(advisedUserParams)
			.build();
	}

	@Override
	public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) { (2)
		return chain.nextAroundCall(this.before(advisedRequest));
	}

	@Override
	public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) { (3)
		return chain.nextAroundStream(this.before(advisedRequest));
	}

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

    @Override
    public String getName() { (5)
		return this.getClass().getSimpleName();
	}
}
1 before方法扩充用户的输入查询,并应用 Re-Reading 技术。
2 aroundCall方法拦截非流式处理请求并应用 Re-Reading 技术。
3 aroundStreammethod 拦截流请求并应用 Re-Reading 技术。
4 您可以通过设置 order 值来控制执行顺序。较低的值首先执行。
5 为 advisor 提供唯一名称。

Spring AI 内置顾问程序

Spring AI 框架提供了几个内置的顾问程序来增强您的 AI 交互。以下是可用顾问的概述:spring-doc.cadn.net.cn

聊天记忆顾问

这些顾问在聊天内存存储中管理对话历史记录:spring-doc.cadn.net.cn

问题解答顾问
内容安全顾问

流式处理与非流式处理

Advisors 流式与非流式
  • 非流式处理顾问处理完整的请求和响应。spring-doc.cadn.net.cn

  • Streaming advisor 使用反应式编程概念(例如,用于响应的 Flux)将请求和响应作为连续流处理。spring-doc.cadn.net.cn

@Override
public Flux<AdvisedResponse> aroundStream(AdvisedRequest advisedRequest, StreamAroundAdvisorChain chain) {

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

最佳实践

  1. 让顾问专注于特定任务,以实现更好的模块化。spring-doc.cadn.net.cn

  2. 使用adviseContext在必要时在 advisor 之间共享 state。spring-doc.cadn.net.cn

  3. 实施 advisor 的流式处理和非流式处理版本,以实现最大的灵活性。spring-doc.cadn.net.cn

  4. 仔细考虑 Advisor 在供应链中的顺序,以确保数据正常流动。spring-doc.cadn.net.cn

向后兼容性

AdvisedRequestclass 被移动到新 package 中。 虽然RequestResponseAdvisorinterface 仍然可用,它被标记为已弃用,并将在 M3 版本附近删除。 建议使用新的CallAroundAdvisorStreamAroundAdvisorinterfaces 的新实现。

重大 API 更改

Spring AI Advisor 链从 1.0 M2 版本到 1.0 M3 发生了重大变化。以下是主要修改:spring-doc.cadn.net.cn

Advisor 接口

上下文映射处理

在 1.0 M3 中更新上下文的示例:spring-doc.cadn.net.cn

@Override
public AdvisedResponse aroundCall(AdvisedRequest advisedRequest, CallAroundAdvisorChain chain) {

    this.advisedRequest = advisedRequest.updateContext(context -> {
        context.put("aroundCallBefore" + getName(), "AROUND_CALL_BEFORE " + getName());  // Add multiple key-value pairs
        context.put("lastBefore", getName());  // Add a single key-value pair
        return context;
    });

    // Method implementation continues...
}