Recursive Advisor
This page comes from my Spring AI book.
Prior to Spring AI 1.1, for a given request, an advisor in the advisor chain would be executed at most once. Spring AI 1.1 introduced the concept of recursive advisors, allowing a subset of advisors in the chain to be executed repeatedly, thus enabling complex agent flows. For example, in the agentic pattern Evaluator-Optimizer, the generation process can be repeated multiple times. This pattern can be implemented using recursive advisors.
From an implementation perspective, a new method copy was added to CallAdvisorChain. This method can be used to copy the remaining CallAdvisors following the specified CallAdvisor in the current CallAdvisorChain, returning a new chain containing only these CallAdvisors. Calling the nextCall method of the new CallAdvisorChain executes this newly created chain.
Let's see an example of using recursive advisors. In the TellJokeAdvisor shown as below, the goal is to tell jokes about a topic for three times. In a loop, the original prompt is updated to include an instruction about telling a joke.
The advisor chain is copied and called with updated prompt. The response from the new advisor chain is collected into a list. All three responses are combined together as the response returned by the current advisor.
public class TellJokeAdvisor implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest chatClientRequest,
CallAdvisorChain callAdvisorChain) {
int count = 3;
var responses = new ArrayList<String>();
for (int i = 0; i < count; i++) {
var prompt = chatClientRequest.prompt().augmentUserMessage(
userMessage -> userMessage.mutate()
.text("Tell a joke about " + userMessage.getText())
.build()
);
var response = callAdvisorChain.copy(this)
.nextCall(chatClientRequest.mutate().prompt(prompt).build())
.chatResponse();
if (response != null) {
responses.add(response.getResult().getOutput().getText());
}
}
return ChatClientResponse.builder()
.chatResponse(ChatResponse.builder()
.generations(List.of(new Generation(AssistantMessage.builder()
.content(String.join("\n", responses))
.build())))
.build())
.build();
}
@Override
public String getName() {
return "Tell Joke";
}
@Override
public int getOrder() {
return 0;
}
}
Now we can add a REST controller to use this advisor. The screenshot below shows the result of testing the REST API using Swagger UI. The topic is programmer. The response contains three jokes about programmer.
