跳到主要内容

递归顾问

Deepseek 3.2 中英对照 Recursive Advisors

什么是递归顾问?

Advisors Recursive 递归顾问是一种特殊类型的顾问,它可以多次遍历下游的顾问链。这种模式在需要反复调用 LLM 直到满足某个条件时非常有用,例如:

  • 在循环中执行工具调用,直到不再需要调用任何工具

  • 验证结构化输出,如果验证失败则重试

  • 实现评估逻辑,包含对请求的修改

  • 实现重试逻辑,包含对请求的修改

CallAdvisorChain.copy(CallAdvisor after) 方法是实现递归 Advisor 模式的关键工具。它创建一个新的 Advisor 链,其中仅包含原始链中位于指定 Advisor 之后的所有 Advisor,并允许递归 Advisor 根据需要调用这个子链。这种方法确保了:

  • 递归顾问可以在链中循环遍历剩余的顾问

  • 链中的其他顾问可以观察并拦截每次迭代

  • 顾问链保持正确的排序和可观察性

  • 递归顾问不会重新执行其之前的顾问

内置递归建议器

Spring AI 提供了两个内置的递归顾问(recursive advisor)来演示这种模式:

ToolCallAdvisor

ToolCallAdvisor 将工具调用循环作为 advisor 链的一部分来实现,而不是依赖模型的内部工具执行机制。这使得链中的其他 advisor 能够拦截并观察工具调用过程。

主要功能:

  • 通过设置 setInternalToolExecutionEnabled(false) 来禁用模型的内部工具执行

  • 循环遍历顾问链,直到不再出现工具调用

  • 支持“直接返回”功能——当工具执行设置了 returnDirect=true 时,它会中断工具调用循环,并将工具执行结果直接返回给客户端应用程序,而不是将其发送回给 LLM

  • 使用 callAdvisorChain.copy(this) 为递归调用创建子链

  • 包含空安全检查,以处理聊天响应可能为空的情况

示例用法:

var toolCallAdvisor = ToolCallAdvisor.builder()
.toolCallingManager(toolCallingManager)
.advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE + 300)
.build();

var chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(toolCallAdvisor)
.build();

返回直接功能

"return direct" 功能允许工具绕过 LLM,将结果直接返回给客户端应用程序。这在以下场景中尤为实用:

  • 该工具的输出即为最终答案,无需LLM处理

  • 为避免额外的LLM调用,需要降低延迟

  • 工具结果应原样返回,无需解释

当工具执行具有 returnDirect=true 时,ToolCallAdvisor 将:

  1. 正常执行工具调用

  2. 检测 ToolExecutionResult 中的 returnDirect 标志

  3. 跳出工具调用循环

  4. 将工具执行结果直接作为 ChatResponse 返回给客户端应用程序,其中工具的输出作为生成内容

StructuredOutputValidationAdvisor

StructuredOutputValidationAdvisor 会根据生成的 JSON 模式验证结构化的 JSON 输出,如果验证失败,将在指定的尝试次数内重试调用。

关键特性:

  • 根据期望的输出类型自动生成 JSON 模式

  • 根据模式验证 LLM 响应

  • 如果验证失败,会重试调用,最多可配置尝试次数

  • 在重试时,通过向提示中添加验证错误信息来帮助 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();