此版本仍在开发中,尚未被视为稳定版。为了获取最新的快照版本,请使用Spring AI 1.1.3spring-doc.cadn.net.cn

顾问API

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

核心优势包括封装重复的生成式AI模式,将数据转换为从和发送到大型语言模型(LLMs),以及提供在各种模型和使用场景中的可访问性。spring-doc.cadn.net.cn

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

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() 方法注册通知器。spring-doc.cadn.net.cn

通知器(Advisors)也会参与可观测性栈,因此您可以查看与其执行相关的指标和追踪信息。spring-doc.cadn.net.cn

核心组件

该API包含用于非流式场景的CallAdvisorCallAdvisorChain,以及用于流式场景的StreamAdvisorStreamAdvisorChain。 它还包括用于表示未密封的Prompt请求的ChatClientRequest,以及用于聊天补全响应的ChatClientResponse。两者都持有一个advise-context以在顾问链中共享状态。spring-doc.cadn.net.cn

Advisors API Classes

adviseCall()adviseStream() 是关键的顾问方法,通常执行诸如检查未密封的提示数据、自定义和增强提示数据、调用顾问链中的下一个实体、选择性地阻止请求、检查聊天补全响应以及抛出异常以指示处理错误等操作。spring-doc.cadn.net.cn

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

由Spring AI框架创建的Advisor链,允许按照其getOrder()值顺序依次调用多个advisor。 数值越小,优先级越高,越先执行。 最后一个advisor会自动将请求发送给大语言模型(LLM)。spring-doc.cadn.net.cn

下图展示了通知器链(advisor chain)与聊天模型(Chat Model)之间的交互:spring-doc.cadn.net.cn

Advisors API Flow
  1. Spring AI 框架根据用户的 Prompt 创建一个 ChatClientRequest,以及一个空的顾问 context 对象。spring-doc.cadn.net.cn

  2. 链中的每个通知器都会处理请求,可能会对其进行修改。或者,它也可以选择不调用下一个实体来阻止该请求。在后一种情况下,该通知器负责填充响应。spring-doc.cadn.net.cn

  3. 由框架提供的最终通知器,将请求发送到 Chat Modelspring-doc.cadn.net.cn

  4. 聊天模型的响应随后会通过顾问链回传,并被转换为ChatClientResponse。之后包含共享的顾问context实例。spring-doc.cadn.net.cn

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

  6. 最终的 ChatClientResponse 通过提取 ChatCompletion 返回给客户端。spring-doc.cadn.net.cn

Advisor Order

顾问在链中的执行顺序由getOrder()方法决定。需要理解的关键点:spring-doc.cadn.net.cn

看似矛盾的顺序与执行顺序之间的差异,源于顾问链的栈式特性: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. 使用不同的顺序值来配置它们。spring-doc.cadn.net.cn

  3. 使用顾问上下文在它们之间共享状态。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 CallAdvisor extends Advisor {

	ChatClientResponse adviseCall(
		ChatClientRequest chatClientRequest, CallAdvisorChain callAdvisorChain);

}
public interface StreamAdvisor extends Advisor {

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

}

要继续 Advice 的链式调用,请在您的 Advice 实现中使用 CallAdvisorChainStreamAdvisorChainspring-doc.cadn.net.cn

这些接口是spring-doc.cadn.net.cn

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

}

实现顾问

要创建一个顾问,实现CallAdvisorStreamAdvisor(或两者都实现)。需要实现的关键方法是对于非流式顾问为nextCall(),对于流式顾问为nextStream()spring-doc.cadn.net.cn

示例

我们将提供几个动手示例,以说明如何实现用于观察和增强用例的顾问。spring-doc.cadn.net.cn

日志顾问

我们可以实现一个简单的日志通知器,它会在调用链中的下一个通知器之前记录 ChatClientRequest,并在之后记录 ChatClientResponse。 请注意,该通知器仅观察请求和响应,而不会对其进行修改。 此实现同时支持非流式和流式场景。spring-doc.cadn.net.cn

public class SimpleLoggerAdvisor implements CallAdvisor, StreamAdvisor {

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

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

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

}
1 为顾问提供一个唯一名称。
2 您可以设置 order 值来控制执行顺序。较低的值会优先执行。
3 The MessageAggregator is a utility class that aggregates the Flux responses into a single ChatClientResponse. This can be useful for logging or other processing that observe the entire response rather than individual items in the stream. Note that you can not alter the response in the MessageAggregator as it is a read-only operation.

重新阅读(Re2)顾问

重读提升大型语言模型的推理能力”一文介绍了一种名为“重读”(Re2)的技术,该技术能够提升大型语言模型的推理能力。 Re2 技术需要对输入提示进行如下增强:spring-doc.cadn.net.cn

{Input_Query}
Read the question again: {Input_Query}

实现一个将 Re2 技术应用于用户输入查询的顾问,可以按如下方式完成:spring-doc.cadn.net.cn

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) { (1)
		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() { (2)
		return this.order;
	}

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

}
1 The before method augments the user’s input query applying the Re-Reading technique.
2 您可以设置 order 值来控制执行顺序。较低的值会优先执行。

Spring AI 内置顾问

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

聊天记忆顾问

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

问答顾问
  • QuestionAnswerAdvisorspring-doc.cadn.net.cn

    该顾问使用向量存储来提供问答功能,实现了朴素的 RAG(检索增强生成)模式。spring-doc.cadn.net.cn

  • RetrievalAugmentationAdvisorspring-doc.cadn.net.cn

    Advisor that implements common 检索增强生成(RAG) flows using the building blocks defined in the `org.springframework.ai.rag` package and following the Modular RAG Architecture.
推理顾问
内容安全顾问

流式传输与非流式传输

Advisors Streaming vs Non-Streaming Flow
  • 非流式通知器处理完整的请求和响应。spring-doc.cadn.net.cn

  • 流式顾问以连续流的形式处理请求和响应,使用响应式编程概念(例如,用于响应的 Flux)。spring-doc.cadn.net.cn

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

最佳实践

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

  2. 使用adviseContext在必要时在顾问之间共享状态。spring-doc.cadn.net.cn

  3. 实现顾问的流式和非流式两种版本,以获得最大的灵活性。spring-doc.cadn.net.cn

  4. 仔细考虑通知链中各个通知器的顺序,以确保数据流的正确性。spring-doc.cadn.net.cn

API 变更说明

顾问接口

上下文映射处理