此版本仍在开发中,尚未被视为稳定版本。最新的快照版本请使用 Spring AI 1.0.0-SNAPSHOT!spring-doc.cn

Advisors API

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

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

您可以使用 ChatClient API 配置现有顾问,如以下示例所示:spring-doc.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 的方法注册 advisor。defaultAdvisors()spring-doc.cn

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

核心组件

API 由非流式处理方案和 和 流式处理方案组成。 它还包括表示 Chat Completion 响应的未密封 Prompt 请求。两者都在 advisor 链中持有 to share 状态。CallAroundAdvisorCallAroundAdvisorChainStreamAroundAdvisorStreamAroundAdvisorChainAdvisedRequestAdvisedResponseadvise-contextspring-doc.cn

Advisor API 类

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

此外,该方法确定链中的 advisor 顺序,同时提供唯一的 advisor 名称。getOrder()getName()spring-doc.cn

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

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

Advisors API 流程
  1. Spring AI 框架创建一个 from user's 以及一个空对象。AdvisedRequestPromptAdvisorContextspring-doc.cn

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

  3. 框架提供的最后一个 advisor 将请求发送到 .Chat Modelspring-doc.cn

  4. 然后,聊天模型的响应将通过顾问链传回并转换为 .later 包括共享实例。AdvisedResponseAdvisorContextspring-doc.cn

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

  6. final 通过提取 .AdvisedResponseChatCompletionspring-doc.cn

顾问订单

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

  • 首先执行订单值较低的顾问。spring-doc.cn

  • advisor 链以堆栈的形式运行:spring-doc.cn

    • 链中的第一个 advisor 是第一个处理请求的人。spring-doc.cn

    • 它也是最后一个处理响应的服务器。spring-doc.cn

  • 要控制执行顺序:spring-doc.cn

    • 将 order close 设置为 以确保 advisor 在链中首先执行 (first 用于请求处理,last 用于响应处理)。Ordered.HIGHEST_PRECEDENCEspring-doc.cn

    • 将订单 close 设置为 以确保 advisor 在链中最后执行 (last 用于请求处理,first 用于响应处理)。Ordered.LOWEST_PRECEDENCEspring-doc.cn

  • 较高的值被解释为较低的优先级。spring-doc.cn

  • 如果多个 advisor 具有相同的订单价值,则不能保证他们的执行顺序。spring-doc.cn

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

提醒一下,以下是 Spring 接口的语义:Orderedspring-doc.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.cn

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

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

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

API 概述

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

public interface Advisor extends Ordered {

	String getName();

}

同步和反应式 Advisor 的两个子接口是spring-doc.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 链,请在您的 Advice 实现中使用 and:CallAroundAdvisorChainStreamAroundAdvisorChainspring-doc.cn

这些接口包括spring-doc.cn

public interface CallAroundAdvisorChain {

	AdvisedResponse nextAroundCall(AdvisedRequest advisedRequest);

}
public interface StreamAroundAdvisorChain {

	Flux<AdvisedResponse> nextAroundStream(AdvisedRequest advisedRequest);

}

实施 Advisor

要创建 advisor,请实施 or (或两者)。要实现的关键方法是针对非流式处理或流式处理顾问。CallAroundAdvisorStreamAroundAdvisornextAroundCall()nextAroundStream()spring-doc.cn

例子

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

日志记录顾问

我们可以实现一个简单的日志 advisor,记录对链中下一个 advisor 的调用之前和之后。 请注意,advisor 仅观察请求和响应,不会修改它们。 此实现支持非流式处理和流式处理方案。AdvisedRequestAdvisedResponsespring-doc.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 它是一个实用程序类,用于将 Flux 响应聚合到单个 AdvisedResponse 中。 这对于记录或其他观察整个响应而不是流中单个项目的处理非常有用。 请注意,您不能更改 中的响应,因为它是只读操作。MessageAggregatorMessageAggregator

重读 (Re2) 顾问

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

{Input_Query}
Read the question again: {Input_Query}

实现将 Re2 技术应用于用户输入查询的 advisor 可以像这样完成:spring-doc.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 该方法应用 Re-Reading 技术来增强用户的输入查询。before
2 该方法拦截非流式处理请求并应用 Re-Reading 技术。aroundCall
3 该方法拦截流请求并应用 Re-Reading 技术。aroundStream
4 您可以通过设置 order 值来控制执行顺序。较低的值首先执行。
5 为 advisor 提供唯一名称。

Spring AI 内置顾问程序

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

聊天记忆顾问

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

  • MessageChatMemoryAdvisorspring-doc.cn

    检索内存并将其作为消息集合添加到提示符中。此方法维护会话历史记录的结构。请注意,并非所有 AI 模型都支持此方法。spring-doc.cn

  • PromptChatMemoryAdvisorspring-doc.cn

    检索内存并将其合并到提示的系统文本中。spring-doc.cn

  • VectorStoreChatMemoryAdvisorspring-doc.cn

    从 VectorStore 中检索内存,并将其添加到提示符的系统文本中。此 advisor 可用于从大型数据集中高效搜索和检索相关信息。spring-doc.cn

问题解答顾问
  • QuestionAnswerAdvisorspring-doc.cn

    此 advisor 使用向量存储来提供问答功能,实现 RAG(检索增强生成)模式。spring-doc.cn

内容安全顾问

流式处理与非流式处理

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

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

  2. 必要时使用 to share state between advisors。adviseContextspring-doc.cn

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

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

向后兼容性

该类将移动到新包中。 虽然该接口仍然可用,但它被标记为已弃用,并将在 M3 版本期间删除。 建议将 new 和 interfaces 用于新实施。AdvisedRequestRequestResponseAdvisorCallAroundAdvisorStreamAroundAdvisor

重大 API 更改

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

Advisor 接口

上下文映射处理

在 1.0 M3 中更新上下文的示例:spring-doc.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...
}