目录:
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的核心组件包括:
CallAroundAdvisor和CallAroundAdvisorChain:用于非流式调用场景StreamAroundAdvisor和StreamAroundAdvisorChain:用于流式调用场景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,只需要实现CallAroundAdvisor或StreamAroundAdvisor接口即可。
下面实现一个日志 advisor 来记录AdvisedRequest和AdvisedResponse的内容。
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中,RequestAdvisor和ResponseAdvisor是独立的接口,其中:
RequestAdvisor在ChatModel.call和ChatModel.stream方法之前调用。ResponseAdvisor在这些方法之后调用。
而在 1.0 M3中,这些接口被CallAroundAdvisor和StreamAroundAdvisor代替。另外,之前作为ResponseAdvisor的一部分的StreamResponseMode,在 M3 中被移除了。
在 1.0 M2 中,上下文 map 是一个独立的方法参数。且这个 map 是可变的,沿着 advisoor 链传递。在 1.0 M3 中,上下文 map 变成了AdvisedRequest和AdvisedResponse的一部分,且是不可变的。如果要更新上下文,需要用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