Tool Calling and Agent Advisors
Extend models with callable Java tools and compose behavior using request and response advisors.
Tool Calling and Agent Advisors is a free Spring Boot 4 Complete Guide lesson on CoddyKit — lesson 4 of 4. You can read the complete lesson below for free — then practise it hands-on in the browser with a built-in code editor and a 24/7 AI tutor. It is part of the Spring Boot 4 Complete Guide learning path, one of 4 lessons in the course, and your progress syncs across the web and the CoddyKit app.
Why Tool Calling Matters
Large language models can reason over text, but they cannot fetch a live order status, query your database, or call a payment gateway on their own. Tool calling (also called function calling) bridges that gap.
- The model decides when a tool is needed based on the user's request.
- Spring AI invokes the matching Java method and feeds the result back into the conversation.
- The model then produces a grounded, final answer.
In Spring AI, a tool is just an ordinary Java method annotated with @Tool. The framework reads the method signature and JavaDoc-style description, generates a JSON schema, and exposes it to the model.
Defining a Tool with @Tool
Annotate a method with @Tool and give it a clear description. The model uses that description to decide whether to call it, so write it as if you were instructing a junior developer.
- Use
@ToolParamto describe individual arguments. - Return types are serialized to JSON automatically.
- Keep the method deterministic and side-effect-aware.
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
import java.time.LocalDate;
@Component
class DateTools {
@Tool(description = "Get the number of days between today and a given target date")
int daysUntil(@ToolParam(description = "Target date in ISO-8601 format, e.g. 2026-12-31") String targetDate) {
LocalDate target = LocalDate.parse(targetDate);
return (int) java.time.temporal.ChronoUnit.DAYS.between(LocalDate.now(), target);
}
}Registering Tools on a ChatClient Call
Tools are attached per request through the fluent ChatClient API. Pass an instance whose annotated methods become callable tools for that exchange.
.tools(Object...)registers one or more tool-bearing beans.- The model may issue zero, one, or several tool calls before answering.
- Spring AI runs the full call loop transparently and returns the final text.
import org.springframework.ai.chat.client.ChatClient;
class AssistantService {
private final ChatClient chatClient;
AssistantService(ChatClient.Builder builder, DateTools dateTools) {
this.chatClient = builder.build();
}
String ask(String userText, DateTools dateTools) {
return chatClient.prompt()
.user(userText)
.tools(dateTools)
.call()
.content();
}
}The Tool Call Loop
Understanding the loop is essential for C2-level work. When the model requests a tool, Spring AI does not return control to you by default; it executes the tool and continues the conversation automatically.
- The model returns an assistant message containing one or more tool call requests.
- Spring AI matches each request to a registered method and invokes it.
- Results are wrapped as tool response messages and appended to the prompt.
- The model is called again with the enriched context until it emits a normal text answer.
This default behavior is driven by the framework's ToolCallingManager.
Programmatic Tools with FunctionToolCallback
Annotations are convenient, but sometimes you need tools defined at runtime or from a lambda. Use FunctionToolCallback to register a Function with an explicit name, description, and input type.
- The input type drives JSON schema generation.
- You stay in full control of serialization and naming.
- Useful for dynamically discovered capabilities.
import org.springframework.ai.tool.function.FunctionToolCallback;
import java.util.function.Function;
record WeatherRequest(String city) {}
record WeatherResponse(String city, double tempC) {}
class WeatherTool {
static FunctionToolCallback<WeatherRequest, WeatherResponse> callback() {
Function<WeatherRequest, WeatherResponse> fn =
req -> new WeatherResponse(req.city(), 21.5);
return FunctionToolCallback.builder("currentWeather", fn)
.description("Get the current temperature in Celsius for a city")
.inputType(WeatherRequest.class)
.build();
}
}Controlling Execution: returnDirect
Sometimes a tool's raw result should be returned straight to the caller without another model round-trip, for example when the tool already produced the final user-facing payload.
- Set
returnDirect = trueon@Toolto short-circuit the loop. - The framework returns the tool result instead of feeding it back to the model.
- This saves tokens and latency but skips the model's natural-language framing.
import org.springframework.ai.tool.annotation.Tool;
class TicketTools {
@Tool(description = "Open a support ticket and return its tracking id", returnDirect = true)
String openTicket(String summary) {
// Persist and return immediately; the id is the final answer
return "TICKET-" + Math.abs(summary.hashCode() % 100000);
}
}Introducing Advisors
Where tools extend what the model can do, advisors intercept and shape how each request and response flows. An advisor is a middleware in the ChatClient pipeline.
- Request advisors mutate the prompt before it reaches the model (inject context, retrieve documents, add system instructions).
- Response advisors post-process the model's output (logging, redaction, safety checks).
- Advisors are chained and ordered, much like servlet filters.
Spring AI ships several built-in advisors and lets you write your own by implementing the advisor interfaces.
Built-in Memory and RAG Advisors
Two of the most used built-in advisors:
MessageChatMemoryAdvisor— injects prior conversation turns so the model has memory across requests, backed by aChatMemorystore.QuestionAnswerAdvisor— performs retrieval-augmented generation by querying aVectorStoreand prepending relevant documents.
You register them on the builder so they apply to every call, or per-prompt for one-off behavior.
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.chat.client.advisor.vectorstore.QuestionAnswerAdvisor;
import org.springframework.ai.vectorstore.VectorStore;
class RagChatConfig {
ChatClient chatClient(ChatClient.Builder builder, ChatMemory memory, VectorStore store) {
return builder
.defaultAdvisors(
MessageChatMemoryAdvisor.builder(memory).build(),
QuestionAnswerAdvisor.builder(store).build())
.build();
}
}Writing a Custom Advisor
Implement CallAdvisor to participate in the synchronous call chain. The key method receives the request, calls chain.nextCall(...), and can transform the response.
getName()identifies the advisor.getOrder()controls position; lower runs earlier on the request side.- Wrap
nextCallto add logging, timing, or content filtering.
import org.springframework.ai.chat.client.ChatClientRequest;
import org.springframework.ai.chat.client.ChatClientResponse;
import org.springframework.ai.chat.client.advisor.api.CallAdvisor;
import org.springframework.ai.chat.client.advisor.api.CallAdvisorChain;
class LoggingAdvisor implements CallAdvisor {
@Override
public ChatClientResponse adviseCall(ChatClientRequest request, CallAdvisorChain chain) {
long start = System.nanoTime();
ChatClientResponse response = chain.nextCall(request);
long ms = (System.nanoTime() - start) / 1_000_000;
System.out.println("[advisor] call took " + ms + "ms");
return response;
}
@Override
public String getName() { return "logging"; }
@Override
public int getOrder() { return 0; }
}Advisor Ordering and the Chain
Advisors form an ordered chain. Picture an onion: on the way in advisors run from lowest order to highest, and on the way out the responses unwind in reverse.
- A memory advisor with a low order injects history early so RAG and tools see it.
- A safety/redaction advisor often sits at a high order to inspect the final text last.
- Order ties are resolved by registration sequence; be explicit with
getOrder()to avoid surprises.
Because tools and advisors both operate on the same prompt, design their interaction deliberately: advisors prepare context, tools fetch live data, and a final response advisor can sanitize the merged result.
Composing Tools and Advisors Together
Real agents combine both mechanisms in a single fluent call. Here the client carries default memory and RAG advisors, while a domain tool is attached for this request.
- Advisors enrich the prompt with history and retrieved knowledge.
- The model may still call the tool to obtain live, authoritative data.
- The result is an agent that remembers, grounds, and acts.
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.api.Advisor;
class OrderAgent {
private final ChatClient chatClient;
OrderAgent(ChatClient chatClient) { this.chatClient = chatClient; }
String handle(String conversationId, String userText, OrderTools tools) {
return chatClient.prompt()
.user(userText)
.advisors(a -> a.param("chat_memory_conversation_id", conversationId))
.tools(tools)
.call()
.content();
}
}
class OrderTools { /* @Tool methods omitted */ }Quick Check
Test your understanding of the tool call loop and advisor design.
Recap
You now know how to extend models with callable Java code and compose request/response behavior:
- Tools are Java methods marked with
@Tool(or built viaFunctionToolCallback) that the model invokes to fetch data or act. - Spring AI runs the tool call loop automatically;
returnDirect = trueshort-circuits it. - Advisors are pipeline middleware: request advisors enrich the prompt (memory, RAG), response advisors post-process output (logging, safety).
- Advisors are ordered and unwind like an onion; design tool/advisor interaction deliberately.
- Combine memory + RAG advisors with domain tools to build agents that remember, ground, and act.
Frequently asked questions
Is the “Tool Calling and Agent Advisors” lesson free?
Yes — the full text of “Tool Calling and Agent Advisors” is free to read here on the web, and the Spring Boot 4 Complete Guide course includes 4 lessons in total. To practise it interactively (a built-in code editor and a 24/7 AI tutor) and unlock the rest of the Spring Boot 4 Complete Guide course, upgrade to CoddyKit PRO.
What will I learn in “Tool Calling and Agent Advisors”?
Extend models with callable Java tools and compose behavior using request and response advisors. You practise Spring Boot 4 Complete Guide with hands-on code you run directly in the browser, and a 24/7 AI tutor answers your questions as you work through the lesson.
Do I need any experience to start Spring Boot 4 Complete Guide?
No prior experience is required. Spring Boot 4 Complete Guide on CoddyKit is structured for beginners through advanced learners; this is — lesson 4 of 4, so you can start here or from the beginning and move at your own pace.
How long does the “Tool Calling and Agent Advisors” lesson take?
Most CoddyKit lessons take about 5–10 minutes. Each one is bite-sized and interactive, so you make steady progress and pick up exactly where you left off across the web and the app.
Can I write and run code in this Spring Boot 4 Complete Guide lesson?
Yes. Every Spring Boot 4 Complete Guide lesson includes a built-in code editor, so you write and run real code right in your browser and get instant AI feedback — no local setup required.
All lessons in this course
- ChatClient, Prompts, and Structured Output
- Embeddings and Vector Store Retrieval
- Retrieval-Augmented Generation Pipelines
- Tool Calling and Agent Advisors