递归式 Advisors
什么是递归式 Advisor?
递归式 Advisor 是一种特殊类型的 Advisor,它可以多次循环调用下游的 Advisor 链。这种模式在需要不断调用 LLM 直到满足某个条件时非常有用,例如:
- 反复执行工具调用(tool calls),直到不再需要继续调用工具
- 验证结构化输出,如果验证失败则重试
- 在请求需要调整时实现 “评估(Evaluation)” 逻辑
- 在请求需要修改时实现重试逻辑
实现递归式 Advisor 的关键是 CallAdvisorChain.copy(CallAdvisor after) 方法。
该方法会基于原始链创建一个新的 Advisor 链,其中只包含指定 Advisor 之后的所有 Advisor,从而让递归式 Advisor 可以根据需要多次调用这个“子链”。
这种方式保证了:
- 递归式 Advisor 可以循环处理链中剩余的 Advisor
- 链中的其他 Advisor 仍然能够观察并拦截每一次循环
- Advisor 链的顺序性与可观察性被完整保留
- 递归式 Advisor 不会再次执行位于它之前的 Advisor

内置的递归式 Advisor
Spring AI 提供了两个内置的递归式 Advisor,用来示范这种模式:
ToolCallAdvisor
ToolCallAdvisor 实现了在 Advisor 链中循环调用工具的机制,而不是依赖模型内部的工具执行。这使得链中的其他 Advisor 能够拦截并观察工具调用的过程。
主要特性:
- 通过设置
setInternalToolExecutionEnabled(false)禁用模型内部的工具执行。 - 循环遍历 Advisor 链,直到没有更多工具调用为止。
- 支持 “return direct” 功能——当工具执行的
returnDirect=true时,它会中断工具调用循环,并将工具执行结果直接返回给客户端应用,而不是再次发送给 LLM。 - 使用
callAdvisorChain.copy(this)创建递归调用的子链。 - 包含空安全检查,以处理聊天响应可能为
null的情况。
示例用法:
var toolCallAdvisor = ToolCallAdvisor.builder()
.toolCallingManager(toolCallingManager)
.advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE + 300)
.build();
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(toolCallAdvisor)
.build();
Return Direct 功能
“return direct” 功能允许工具绕过 LLM,直接将结果返回给客户端应用。适用场景包括:
- 工具的输出就是最终答案,无需 LLM 处理。
- 希望通过避免额外的 LLM 调用来降低延迟。
- 工具结果应原样返回,无需解释。
当工具执行的 returnDirect=true 时,ToolCallAdvisor 会:
- 按正常流程执行工具调用。
- 检测
ToolExecutionResult中的returnDirect标志。 - 中断工具调用循环。
- 将工具执行结果直接作为 ChatResponse 返回给客户端应用,生成内容为工具输出。
StructuredOutputValidationAdvisor
StructuredOutputValidationAdvisor 会根据生成的 JSON schema 验证结构化 JSON 输出,如果验证失败,则会重试调用,最多达到指定的尝试次数。
主要特性:
- 根据期望的输出类型自动生成 JSON schema
- 验证 LLM 的响应是否符合 schema
- 如果验证失败,会重试调用,最多重试次数可配置
- 在重试时,将验证错误信息附加到 prompt 中,帮助 LLM 修正输出
- 使用
callAdvisorChain.copy(this)创建递归调用的子链 - 可选地支持自定义
ObjectMapper进行 JSON 处理
示例用法:
var validationAdvisor = StructuredOutputValidationAdvisor.builder()
.outputType(MyResponseType.class)
.maxRepeatAttempts(3)
.advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE + 1000)
.build();
var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(validationAdvisor)
.build();