Spring AI / Chat Client API / Advisors

Spring AI / Chat Client API / Advisors
Spring AI 参考文档 - Chat Client API 与 Advisors APISpring AI Advisors API 概述Spring AI Advisors API 提供了一种灵活而强大的方式用于在Spring应用程序中拦截、修改和增强AI驱动的交互。通过使用Advisors API开发者可以创建更复杂、可重用且易于维护的AI组件。主要优势包括封装常见的生成式AI模式、转换发送至大语言模型LLM和从LLM接收的数据以及提供跨各种模型和用例的可移植性。您可以使用ChatClient API配置现有的advisor如下例所示ChatMemorychatMemory...// 初始化您的聊天内存存储VectorStorevectorStore...// 初始化您的向量存储varchatClientChatClient.builder(chatModel).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(),// 聊天内存advisorQuestionAnswerAdvisor.builder(vectorStore).build()// RAG advisor).build();varconversationId678;Stringresponsethis.chatClient.prompt()// 在运行时设置advisor参数.advisors(advisor-advisor.param(ChatMemory.CONVERSATION_ID,conversationId)).user(userText).call().content();建议在构建时使用builder的defaultAdvisors()方法注册advisor。Advisors也参与可观测性Observability栈因此您可以查看与其执行相关的指标和追踪信息。了解Question Answer Advisor了解Chat Memory Advisor核心组件该API包含用于非流式场景的CallAdvisor和CallAdvisorChain以及用于流式场景的StreamAdvisor和StreamAdvisorChain。它还包括表示未封装的Prompt请求的ChatClientRequest以及表示Chat Completion响应的ChatClientResponse。两者都持有一个advise-context用于在advisor链中共享状态。Advisors API 类图adviseCall()和adviseStream()是关键的advisor方法通常执行以下操作检查未封装的Prompt数据定制和增强Prompt数据调用advisor链中的下一个实体可选地阻止请求检查聊天完成响应抛出异常以指示处理错误此外getOrder()方法决定advisor在链中的顺序而getName()提供唯一的advisor名称。Advisor链由Spring AI框架创建允许按getOrder()值顺序依次调用多个advisor。值较低的优先执行。最后一个advisor是自动添加的负责将请求发送给LLM。以下流程图说明了advisor链与Chat Model之间的交互Advisors API 流程图Spring AI框架根据用户的Prompt创建一个ChatClientRequest并附带一个空的advisor上下文对象。链中的每个advisor处理请求可能会修改它。或者它可以选择通过不调用下一个实体来阻止请求。在后一种情况下advisor负责填充响应。由框架提供的最后一个advisor将请求发送给Chat Model。Chat Model的响应随后通过advisor链传回并转换为ChatClientResponse。后者包含共享的advisor上下文实例。每个advisor可以处理或修改响应。最终的ChatClientResponse通过提取ChatCompletion返回给客户端。Advisor 顺序advisor在链中的执行顺序由getOrder()方法决定。需要理解的关键点order值较低的advisor优先执行。advisor链以栈stack的方式运作链中的第一个advisor是第一个处理请求的。它也是最后一个处理响应的。控制执行顺序将order设置为接近Ordered.HIGHEST_PRECEDENCE确保advisor在链中首先执行请求处理优先响应处理最后。将order设置为接近Ordered.LOWEST_PRECEDENCE确保advisor在链中最后执行请求处理最后响应处理优先。较高的值被解释为较低的优先级。如果多个advisor具有相同的order值则它们的执行顺序无法保证。order与执行序列之间的看似矛盾是由于advisor链的栈式特性具有最高优先级最低order值的advisor被添加到栈顶。当栈展开时它将首先处理请求。当栈回卷时它将最后处理响应。以下是SpringOrdered接口的语义publicinterfaceOrdered{/** * 最高优先级值的常量。 * see java.lang.Integer#MIN_VALUE */intHIGHEST_PRECEDENCEInteger.MIN_VALUE;/** * 最低优先级值的常量。 * see java.lang.Integer#MAX_VALUE */intLOWEST_PRECEDENCEInteger.MAX_VALUE;/** * 获取此对象的顺序值。 * p较高的值被解释为较低的优先级。因此 * 具有最低值的对象具有最高优先级有点类似于Servlet的{code load-on-startup}值。 * p相同的order值将导致受影响对象的排序位置不确定。 * return order值 * see #HIGHEST_PRECEDENCE * see #LOWEST_PRECEDENCE */intgetOrder();}对于需要在输入和输出两侧都位于链首的用例为每一侧使用单独的advisor。为它们配置不同的order值。使用advisor上下文在它们之间共享状态。API 概览主要的Advisor接口位于org.springframework.ai.chat.client.advisor.api包中。以下是您在创建自己的advisor时将遇到的关键接口publicinterfaceAdvisorextendsOrdered{StringgetName();}同步和响应式Advisor的两个子接口是publicinterfaceCallAdvisorextendsAdvisor{ChatClientResponseadviseCall(ChatClientRequestchatClientRequest,CallAdvisorChaincallAdvisorChain);}和publicinterfaceStreamAdvisorextendsAdvisor{FluxChatClientResponseadviseStream(ChatClientRequestchatClientRequest,StreamAdvisorChainstreamAdvisorChain);}要在您的Advice实现中继续执行Advice链请使用CallAdvisorChain和StreamAdvisorChain接口定义如下publicinterfaceCallAdvisorChainextendsAdvisorChain{/** * 使用给定的请求调用{link CallAdvisorChain}中的下一个{link CallAdvisor}。 */ChatClientResponsenextCall(ChatClientRequestchatClientRequest);/** * 返回创建时此链中包含的所有{link CallAdvisor}实例的列表。 */ListCallAdvisorgetCallAdvisors();}和publicinterfaceStreamAdvisorChainextendsAdvisorChain{/** * 使用给定的请求调用{link StreamAdvisorChain}中的下一个{link StreamAdvisor}。 */FluxChatClientResponsenextStream(ChatClientRequestchatClientRequest);/** * 返回创建时此链中包含的所有{link StreamAdvisor}实例的列表。 */ListStreamAdvisorgetStreamAdvisors();}实现 Advisor要创建一个advisor请实现CallAdvisor或StreamAdvisor或两者都实现。需要实现的关键方法是用于非流式的nextCall()或用于流式的nextStream()。示例我们将提供几个实践示例以说明如何实现用于观察和增强用例的advisor。日志记录 Advisor我们可以实现一个简单的日志记录advisor在调用链中下一个advisor之前记录ChatClientRequest之后记录ChatClientResponse。请注意该advisor仅观察请求和响应并不修改它们。此实现同时支持非流式和流式场景。publicclassSimpleLoggerAdvisorimplementsCallAdvisor,StreamAdvisor{privatestaticfinalLoggerloggerLoggerFactory.getLogger(SimpleLoggerAdvisor.class);OverridepublicStringgetName(){returnthis.getClass().getSimpleName();}OverridepublicintgetOrder(){return0;}OverridepublicChatClientResponseadviseCall(ChatClientRequestchatClientRequest,CallAdvisorChaincallAdvisorChain){logRequest(chatClientRequest);ChatClientResponsechatClientResponsecallAdvisorChain.nextCall(chatClientRequest);logResponse(chatClientResponse);returnchatClientResponse;}OverridepublicFluxChatClientResponseadviseStream(ChatClientRequestchatClientRequest,StreamAdvisorChainstreamAdvisorChain){logRequest(chatClientRequest);FluxChatClientResponsechatClientResponsesstreamAdvisorChain.nextStream(chatClientRequest);returnnewChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses,this::logResponse);}privatevoidlogRequest(ChatClientRequestrequest){logger.debug(request: {},request);}privatevoidlogResponse(ChatClientResponsechatClientResponse){logger.debug(response: {},chatClientResponse);}}为advisor提供唯一名称。您可以通过设置order值来控制执行顺序。较低的值优先执行。MessageAggregator是一个工具类用于将Flux响应聚合成单个ChatClientResponse。这对于需要观察整个响应而非流中单个项目的日志记录或其他处理非常有用。请注意您不能在MessageAggregator中修改响应因为它是只读操作。重读Re2Advisor《Re-Reading Improves Reasoning in Large Language Models》一文介绍了一种称为重读Re2的技术该技术可以提高大语言模型的推理能力。Re2技术需要像这样增强输入提示{Input_Query} Read the question again: {Input_Query}实现一个将Re2技术应用于用户输入查询的advisor可以这样做publicclassReReadingAdvisorimplementsBaseAdvisor{privatestaticfinalStringDEFAULT_RE2_ADVISE_TEMPLATE {re2_input_query} Read the question again: {re2_input_query} ;privatefinalStringre2AdviseTemplate;privateintorder0;publicReReadingAdvisor(){this(DEFAULT_RE2_ADVISE_TEMPLATE);}publicReReadingAdvisor(Stringre2AdviseTemplate){this.re2AdviseTemplatere2AdviseTemplate;}OverridepublicChatClientRequestbefore(ChatClientRequestchatClientRequest,AdvisorChainadvisorChain){StringaugmentedUserTextPromptTemplate.builder().template(this.re2AdviseTemplate).variables(Map.of(re2_input_query,chatClientRequest.prompt().getUserMessage().getText())).build().render();returnchatClientRequest.mutate().prompt(chatClientRequest.prompt().augmentUserMessage(augmentedUserText)).build();}OverridepublicChatClientResponseafter(ChatClientResponsechatClientResponse,AdvisorChainadvisorChain){returnchatClientResponse;}OverridepublicintgetOrder(){returnthis.order;}publicReReadingAdvisorwithOrder(intorder){this.orderorder;returnthis;}}before方法通过应用重读技术来增强用户的输入查询。您可以通过设置order值来控制执行顺序。较低的值优先执行。Spring AI 内置 AdvisorsSpring AI框架提供了几个内置advisor以增强您的AI交互。以下是可用advisor的概述聊天内存 Advisors这些advisor在聊天内存存储中管理对话历史MessageChatMemoryAdvisor检索内存并将其作为消息集合添加到提示中。这种方法保持了对话历史的结构。请注意并非所有AI模型都支持这种方法。VectorStoreChatMemoryAdvisor从VectorStore检索内存并将其添加到提示的系统文本中。此advisor对于高效搜索和检索大型数据集中的相关信息非常有用。问答 AdvisorQuestionAnswerAdvisor此advisor使用向量存储提供问答能力实现了朴素RAG检索增强生成模式。RetrievalAugmentationAdvisor使用org.springframework.ai.rag包中定义的构建块实现常见的检索增强生成RAG流程遵循模块化RAG架构。推理 AdvisorReReadingAdvisor实现了LLM推理的重读策略称为RE2以增强输入阶段的理解。基于文章《Re-Reading Improves Reasoning in LLMs》。工具调用 AdvisorToolCallingAdvisor处理作为advisor链一部分的工具调用循环。此advisor始终由ChatClient自动注册除非显式禁用因此即使在未配置静态工具的情况下由另一个advisor在运行时注入的工具也能得到支持。它执行模型请求的工具调用并将结果发送回去迭代直到不再需要工具调用。实现了ToolAdvisor这是一个标记接口用于防止自动注册第二个ToolCallingAdvisor。完整文档请参见递归Advisors - ToolCallingAdvisor 和 ChatClient - 工具调用。内容安全 AdvisorSafeGuardAdvisor一个简单的advisor旨在防止模型生成有害或不适当的内容。流式 vs 非流式Advisors 流式与非流式流程图非流式advisor处理完整的请求和响应。流式advisor将请求和响应作为连续流处理使用响应式编程概念例如使用Flux处理响应。OverridepublicFluxChatClientResponseadviseStream(ChatClientRequestchatClientRequest,StreamAdvisorChainchain){returnMono.just(chatClientRequest).publishOn(Schedulers.boundedElastic()).map(request-{// 此处可由阻塞和非阻塞线程执行。// Advisor在下一节之前的操作}).flatMapMany(request-chain.nextStream(request)).map(response-{// Advisor在下一节之后的操作});}最佳实践保持advisor专注于特定任务以提高模块化。在必要时使用adviseContext在advisor之间共享状态。同时实现流式和非流式版本的advisor以获得最大的灵活性。仔细考虑advisor在链中的顺序以确保正确的数据流。重大 API 变更Advisor 接口在1.0 M2中有独立的RequestAdvisor和ResponseAdvisor接口。RequestAdvisor在ChatModel.call和ChatModel.stream方法之前调用。ResponseAdvisor在这些方法之后调用。在1.0 M3中这些接口已被替换为CallAroundAdvisorStreamAroundAdvisor以前属于ResponseAdvisor的StreamResponseMode已被移除。在1.0.0中这些接口已被替换CallAroundAdvisor→CallAdvisorStreamAroundAdvisor→StreamAdvisorCallAroundAdvisorChain→CallAdvisorChainStreamAroundAdvisorChain→StreamAdvisorChainAdvisedRequest→ChatClientRequestAdvisedResponse→ChatClientResponse上下文映射处理在1.0 M2中上下文映射是一个单独的方法参数。该映射是可变的并在链中传递。在1.0 M3中上下文映射现在是AdvisedRequest和AdvisedResponse记录的一部分。该映射是不可变的。要更新上下文请使用updateContext方法该方法会创建一个包含更新内容的新不可修改映射。