news 2026/4/23 21:21:14

Spring AI 框架介绍 - Advisor

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Spring AI 框架介绍 - Advisor

目录:
Spring AI 框架介绍
Spring AI 框架介绍 - Advisor
Spring AI 框架介绍 - 上下文记忆
SpringAI 框架介绍 - 工具调用
Spring AI 对话模型
Spring AI 图像模型
LLM 工具与 RAG 原理
AI Code Review 工具


Advisors API

正如前文介绍,Spring AI 中的Advisors是用于拦截和增强 AI 操作行为的组件。通过 Advisors API,开发者可以创建更多复杂的、可复用的、可维护的 AI 组件。

你可以对已经存在的 advisors 进行配置:

ChatMemorychatMemory=...VectorStorevectorStore=...varchatClient=ChatClient.builder(chatModel).defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build(),// chat-memory advisorQuestionAnswerAdvisor.builder(vectorStore).build()// RAG advisor).build();varconversationId="678";Stringresponse=this.chatClient.prompt()// Set advisor parameters at runtime.advisors(advisor->advisor.param(ChatMemory.CONVERSATION_ID,conversationId)).user(userText).call().content();

这里通过 Consumer 函数式接口对默认设置的 advisors 进行配置,等价于

for (Advisor advisor : advisors) { advisor.param(ChatMemory.CONVERSATION_ID, conversationId); }

推荐在 builder 的defaultAdvisors()方法中注册advisors。

核心组件

Advisors的核心组件包括:

  • CallAroundAdvisorCallAroundAdvisorChain:用于非流式调用场景
  • StreamAroundAdvisorStreamAroundAdvisorChain:用于流式调用场景
  • ChatClientRequest:表示原始客户端请求
  • ChatClientResponse:表示处理后的模型返回

如下图所示:


其中adviseCall()adviseStream()是关键的 advisor 方法,负责在请求和响应的整个生命周期中进行“拦截、修改、控制和异常处理”,类似于拦截器中的 intercept 方法。

下面举几个实际的使用示例,比如可以对原始的 prompt 进行日志记录或安全审计:

// 以下是伪代码 // 获取原始 prompt String prompt = req.getPrompt().getContent(); // 敏感词安全检查 checkPrompt()

也可以对 prompt 进行增强,如增加 system prompt、增加 memory、拼接上下文等:

req.getPrompt().addSystemMessage("You are a helpful assistant");

在顶层的 Ordered 类中,getOrder()方法的返回值决定了链上的 advisor 的执行顺序,值越小优先级越高。Advisor类的getName()方法返回了每个 advisor 的唯一名称。

Advisor 本质上是面向 LLM 调用链的 AOP 环绕拦截器。通过inspect(req)modify(req)进行 before 处理,chain.proceed(req)调用下一个节点、modify(resp)进行 after 处理。

实现一个Advisor

如果想实现一个advisor,只需要实现CallAroundAdvisorStreamAroundAdvisor接口即可。

下面实现一个日志 advisor 来记录AdvisedRequestAdvisedResponse的内容。

Logging Advisor

代码如下:

publicclassSimpleLoggerAdvisorimplementsCallAroundAdvisor,StreamAroundAdvisor{privatestaticfinalLoggerlogger=LoggerFactory.getLogger(SimpleLoggerAdvisor.class);@OverridepublicStringgetName(){returnthis.getClass().getSimpleName();}@OverridepublicintgetOrder(){return0;}@OverridepublicChatClientResponseadvisorCall(ChatClientRequestchatClientRequest,CallAdvisorChainchain){logger.debug("request: {}",chatClientRequest);ChatClientResponsechatClientResponse=callAdvisorChina.nextCall(chatClientRequest);logger.debug("AFTER: {}",chatClientResponse);returnchatChatResponse;}@OverridepublicFlux<ChatClientResponse>advisorStream(ChatClientRequestchatClientRequest,StreamAdvisorChainchain){logger.debug("BEFORE: {}",chatClientRequest);Flux<ChatClientResponse>chatClientResponses=chain.nextStream(chatClientRequest);returnnewChatClientMessageAggregator().aggregateChatClientResponse(chatClientResponses,resp->logger.debug("AFTER: {}",resp));}}

Re2 Advisor

"Re-Reading improvement Reasoning in Large Language Models"这篇文章介绍了一种称为Re-Reading (Re2)的技术,它可以提高大型语言模型的推理能力。Re2技术需要像下面这样增加输入提示:

{Input_Query}Readthe question again:{InputQuery}

下面是一个使用Re2的实例:

publicclassReReadingAdvisorimplementsBaseAdvisor{privatestaticfinalStringDEFAULT_RE2_ADVISE_TEMPLATE=""" {re2_intput_query} Read the question again: {re2_intput_query} """;privatefinalStringre2AdviseTemplate;privateintorder=0;publicReReadingAdvisor(){this(DEFAULT_RE2_ADVISE_TEMPLATE)};publicReReadingAdvisor(Stringre2AdviseTemplate){this.re2AdviseTemplate=re2AdviseTemplate;}@OverrideprivateChatClientRequestbefore(ChatClientRequestchatClientRequest,AdvisorChainadvisorChain){StringaugmentedUserText=PromptTemplate.builder().template(this.re2AdviseTemplate).variables(Map.of("re2_intput_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.order=order;returnthis;}}

这个例子在before方法中通过 Re2 的技巧来增强用户输入。

内置的 Advisors

Spring AI 框架内置了很多 advisors 用来增强与 AI 的交互。

Chat Memory Advisors

此类 advisors 管理对话历史,包括以下几种:

  • MessageChatMemoryAdvisor,获取对话记忆并以消息集合的方式添加到 prompt 中。需要注意的是,不是所有的 AI 模型都支持。
  • PromptChatMemoryAdvisor,获取记忆并将其合并到系统 prompt 文本中。
  • VectorStoreChatMemoryAdvisor,从向量存储中获取记忆并将其添加到系统 prompt 文本中。这个 advisor 对于从大数据集中查询和检索有关信息是非常有用的。

Question Answering Advisor

这类 advisros 用于检索增强,包括:

  • QuestionAnswerAdvisor:使用向量存储提供问答能力,实现了原生的 RAG 模式。
  • RetrievalAugmentationAdvisor:基于org.springframework.ai.rag提供的组件,按照模块化架构组织,把检索、拼装 prompt、调用 LLM 整个串起来的 RAG 流程实现器。

Reasoning Advisor

推理顾问主要是 ReReadingAdvisor,它实现了推理大模型的 Re2 策略。

Content Safety Advisor

内容安全顾问主要是 SafeGuardAdvisor,这是一个简单的、防止模型生成有害内容的 advisor。

API 兼容性

Spring AI 的Advisor 从 1.0 M2 升级到 M3 的过程经历了一些重大变更。

在1.0 M2中,RequestAdvisorResponseAdvisor是独立的接口,其中:

  • RequestAdvisorChatModel.callChatModel.stream方法之前调用。
  • ResponseAdvisor在这些方法之后调用。

而在 1.0 M3中,这些接口被CallAroundAdvisorStreamAroundAdvisor代替。另外,之前作为ResponseAdvisor的一部分的StreamResponseMode,在 M3 中被移除了。

在 1.0 M2 中,上下文 map 是一个独立的方法参数。且这个 map 是可变的,沿着 advisoor 链传递。在 1.0 M3 中,上下文 map 变成了AdvisedRequestAdvisedResponse的一部分,且是不可变的。如果要更新上下文,需要用updateContext方法,创建一个新的不可变map。

当然,本文是基于 1.1.3 版本的 API 介绍的,相比于 1.0 有了更多的的不同。

递归 Advisors

普通的 advisor 是线性执行的,如:
User Request -> Advisor A -> Advisor B -> LLM 调用 -> Response
而递归 advisor 可以循环调用自己:
User Request -> Advisor 触发 -> LLM 调用 -> Advisor 处理结果 -> 再次调用 LLM -> ... (多次循环此过程)

示意图如下:

为什么需要递归 advisor 呢?

在很多 AI 场景中,需要多步推理/多轮增强。典型的场景如:

  • 多跳 RAG(Multi-hop Retrieval),第一次检索的信息不够,根据结果再生成新的 query 重新检索。
  • 自我反思(self-reflection/refinement),LLM 先回答,advisor 判断其回复质量,如果觉得不好就让 LLM 重新回答。
  • Tool 调用链(Agent 行为),LLM 决定调用工具,将工具返回的结果再喂给 LLM。

对于递归 advisor 来说,已经不是拦截器了,而是变成了一个流程控制器(orchestrator)。

内置的递归 Advisors

SpringAI 提供了两种内置的递归 advisors。

ToolCallAdvisor

ToolCallAdvisor 主要功能如下:

  • 通过设置setInternalToolExecutionEnabled(false)可以禁用模型的内部工具执行。一般情况下,LLM 中的 tool calling 默认是自动执行的,无需用户来决策,这可能会带来预料之外的结果。关闭自动执行后,用户就可以自定义执行逻辑,比如加权限校验、做日志审计等等,还可以尝试多工具调度、增加重试和 fallback 策略等,变得更加灵活可控。
  • 遍历 advisor chain,直到没有其他工具调用为止。
  • 支持直接返回功能,换句话说,当工具执行设置了returnDirect=true,该 advisor 将会中断工具调用循环并直接将结果返回给客户端。
  • 使用callAdvisorChain.copy(this)来创建一个子 chain 用于递归调用。
  • 支持 null 值安全检查(大模型对话有可能返回 null)。
  • 支持可配置的对话历史管理(通过conversationHistoryEnabled)。

使用示例:

vartoolCallAdvisor=ToolCallAdvisor.builder().toolCallingManager(tooCallingManager).advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE+300).build()varchatClient=ChatClient.builder(chatModel).defaultAdvisors(toolCallAdvisor).build()

对话历史管理

ToolCallAdvisor 有一个conversationHistoryEnabled配置项,用于配置当进行工具调用迭代时如何管理对话历史。该参数默认设置为True,意味着会维护全部的对话历史,当每次与大模型交互时,所有的历史对话记录都会被带上,这会消耗大量的 token。

通过disableMemory()方法可以禁用对话历史管理,只有最近一次的工具返回的消息被用作下一轮的迭代。

使用示例:

vartoolCallAdvisor=ToolCallAdvisor.builder().toolCallingManager(toolCallingManager).disableMemory()// Disable internal history - let ChatMemory handle it.advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE+300).build();varchatMemoryAdvisor=MessageChatMemoryAdvisor.builder(chatMemory).advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE+200)// Positioned before ToolCallAdvisor.build();varchatClient=ChatClient.builder(chatModel).defaultAdvisors(chatMemoryAdvisor,toolCallAdvisor).build();

直接返回

直接返回允许工具跳过大预言模型,直接将结果返回给客户端。适合使用的场景如:工具的输出结果就是最终结果,无需再经过 LLM 的处理;不调用 LLM 来降低延迟等。

StructuredOutputValidationAdvisor

结构化输出校验顾问用于校验最终返回的结果是否是想要的 JSON 格式。使用示例:

varvalidationAdvisor=StructuredOutputValidationAdvisor.builder().outputType(MyResponseType.class).maxRepeatAttempts(3).advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE+1000).build()varchatClient=ChatClient.builder(chatModel).defaultAdvisors(validationAdvisor).build();

参考资料

[1]. https://docs.spring.io/spring-ai/reference/api/advisors-recursive.html

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/4/23 21:19:48

python deque

# Python deque&#xff1a;看似简单却藏着不少门道 很多人刚开始接触Python时&#xff0c;list几乎是万能的。装数据、切片、append、pop&#xff0c;用起来顺手得很。但当你开始处理大量数据&#xff0c;特别是需要在队列两端频繁操作时&#xff0c;list的真相就慢慢浮出来了…

作者头像 李华
网站建设 2026/4/23 21:18:42

机载侧视SAR雷达成像仿真系统完整解析

机载侧视SAR雷达成像仿真系统完整解析 文档说明 本文档完整解析了机载/侧视SAR&#xff08;合成孔径雷达&#xff09;二维雷达成像仿真系统的全流程&#xff0c;涵盖仿真核心原理、数据生成、信号处理、图像评估全链路&#xff0c;适合雷达算法学习、仿真逻辑理解、项目文档编写…

作者头像 李华
网站建设 2026/4/23 21:13:21

FPGA逻辑验证不求人:用WinDriver 10.21快速调试PCI板卡(附VS2015工程)

FPGA逻辑验证不求人&#xff1a;用WinDriver 10.21快速调试PCI板卡&#xff08;附VS2015工程&#xff09; 对于FPGA开发者而言&#xff0c;硬件逻辑验证往往是最令人头疼的环节之一。尤其是当设计涉及PCIe接口时&#xff0c;传统的验证方法需要编写复杂的底层驱动&#xff0c;这…

作者头像 李华