This is a personal note that I decided to share. It reflects my understanding of a subject and may contain errors and approximations. Feel free to contribute by contacting me here. Any help will be credited!
We are in the agentic AI era. More and more people are building agents to delegate some of their tasks. This is possible thanks to the function calling
pattern. It allows models to connect to and use external tools, thereby augmenting their capabilities.
The use of external tools allows the model to :
- Retrieve information: model can fetch an API, query a database, scrape a web page.
- Take action: model can act, send an email, save records in database, trigger a workflow.
How AI model call an external tool?
Basically, a tool is a classic code function. It can take parameters, and it has a return type. To let a model call this function, the user needs to provide a description of the function to the model.
Let’s say we want a model to summarize blog posts for us. We need to give it a tool to fetch blog posts autonomously. This tool is a function called getPosts
which takes an url
as a parameter.
The example below illustrates the request content we send to the model.
{
"model": "mistral-small3.1:latest",
"messages": [
{
"role": "user",
"content": "Get the latest posts on 'https://spring.io/blog.atom' using the 'getPosts' tool and summarize them."
}
],
"tools": [
{
"type": "function",
"function": {
"name": "getPosts",
"description": "Get blog posts.",
"parameters": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"type": "object",
"properties": {
"arg0": {
"type": "string",
"description": "URL to get blog posts"
}
},
"required": [
"arg0"
]
}
}
}
],
"stream": false,
"options": {}
}
We can see the function description in the tools
array. It’s important to describe the function’s purpose as well as the function parameters, so the model can understand the tool’s capabilities. Some parameters can be set as required.
We can see that there is no code sent to the model, and most models can’t execute code. Instead, the model will request the caller to execute the function on its behalf. The example below shows the model answer from the previous request.
{
"model": "mistral-small3.1:latest",
"created_at": "2025-05-15T14:17:58.362527Z",
"message": {
"role": "assistant",
"content": "",
"tool_calls": [
{
"function": {
"name": "getPosts",
"arguments": "{\"arg0\":\"https://spring.io/blog.atom\"}"
}
}
]
},
"done_reason": "stop",
"done": true,
"total_duration": 9153001084,
"load_duration": 37651750,
"prompt_eval_count": 214,
"prompt_eval_duration": 96136167,
"eval_count": 23,
"eval_duration": 1952835458
}
There is no content in the model message. Instead, there is a tool_calls
array. It gives instructions to call the function getPosts
with the arguments that it chooses.
Following the model answer, we execute the getPosts
function and we send back the function’s return to the model.
The example below illustrates the new request content we send after getPosts
execution.
{
"model": "mistral-small3.1:latest",
"messages": [
{
"role": "user",
"content": "Get the latest posts on 'https://spring.io/blog.atom' using the 'getPosts' tool and summarize them."
},
{
"role": "assistant",
"content": "",
"tool_calls": [
{
"function": {
"name": "getPosts",
"arguments": "{\"arg0\":\"https://spring.io/blog.atom\"}"
}
}
]
},
{
"role": "tool",
"content": "[{\"title\":\"Spring Framework 7.0.0-M5 available now\",\"description\":null,\"content\":\"<p>On behalf of the team and everyone who has contributed, I am pleased to announce a new milestone for the next Spring Framework generation.\\nThe fifth milestone continues delivering new features and refinements on top of <a href=\\\"https://spring.io/blog/2025/01/23/spring-framework-7-0-0-M1-available-now\\\">7.0.0-M1</a>, [TRUNCATED FOR READABILITY PURPOSE]"
}
],
"stream": false,
"tools": [
{
"type": "function",
"function": {
"name": "getPosts",
"description": "Get blog posts.",
"parameters": {
"$schema": "https://json-schema.org/draft/2020-12/schema",
"additionalProperties": false,
"type": "object",
"properties": {
"arg0": {
"type": "string",
"description": "URL to get blog posts"
}
},
"required": [
"arg0"
]
}
}
}
],
"options": {}
}
We can see that the interaction history with the model is sent. The latest message has a tool
role and contains the return of the function call. In our example, the model will then be able to summarize the blog posts based on the tool’s response.
This is how a model interacts with a tool. It does not execute the function code; it asks the caller to do it and then returns the result. The example is executed manually to understand the workflow. In a real use case, this logic is often hidden and orchestrated by a framework such as Spring AI in Java.
Spring AI implementation of Function Calling : Tools
Spring AI is an application framework from the Spring ecosystem. It aims to simplify the integration of AI capabilities into Spring-based applications. It provides abstraction to develop AI applications with interchangeable implementations. It supports most of the features (Chat, Embedding, Text to Image, RAG, Function Calling, Model Evaluation, Generative AI patterns, and more).
Let’s take a look at the function calling
pattern implementation of the framework: tools.
Spring AI Basics: ChatModel and ChatClient
Spring AI defines two abstractions at the core of the framework :
ChatModel
: a low-level API to interact with the model, like a model driver. Each model provider integrated to Spring AI extends theChatModel
interface (OllamaChatModel
,BedrockChatModel
,OpenAIChatModel
…)ChatClient
: a fluent API wrapping aChatModel
. It is a high-level API to interact with a model. It provides plumbing to use tools and other features such as prompt templating.
Basically, to integrate an AI model into a Spring AI application, you need a ChatModel
implementation such as org.springframework.ai:spring-ai-openai
, and a ChatClient
to interact with it. The model and the client are then available as Spring Beans in the application context.
Tools Modeling in Spring AI
Tools are modeled by the ToolCallback
interface.
The interface declares:
- The tool definition,
ToolDefinition
, that contains the name, definition, and schema of the tool. - The tool metadata,
ToolMetadata
, that contains additional properties on the tool.
ToolCallback
interface also has a call(String input)
method to execute the tool. The input is a String
because parameters are serialized in JSON (cf the example above).
Spring AI supports both methods as tools
and functions as tools
. So there two implementations of ToolCallback
, MethodToolCallback
and FunctionToolCallback
.
Methods as tools are tied to Java class methods, while functions as tools can be standalone functional beans.
Methods as tools declaration
Declarative Specification
Methods can be turned into tools with declarative specification using the @Tool
annotation. The annotation takes various parameters, including name
and description
. Method parameters can be annotated with the @ToolParam
annotation to specify parameter information, such as the name and whether it is required or not.
@Tool(description = "Get blog posts.")
public BlogPostsRssResponse getPosts(@ToolParam(description="URL to get blog posts") final String blogRssUrl) throws Exception {
// Logic to fetch blog posts...
return new BlogPostsRssResponse();
}
Programmatic Specification
Methods can be exposed as tools, also with a programmatic specification. To do so, the MethodToolCallback.Builder
should be used.
Method method = ReflectionUtils.findMethod(BlogPostTools.class, "getPosts");
ToolCallback toolCallback = MethodToolCallback.builder()
.toolDefinition(ToolDefinition.builder(method)
.description("Get blog posts.")
.build())
.toolMethod(method).toolObject(new BlogPostTools())
.build();
Function as tools declaration
Dynamic Specification with @Bean Functions as tools can be declared as Spring Beans, so it lets Spring AI to resolve them.
@Bean
@Description("Get Blog Posts.")
Function<BlogPostsRssRequest, BlogPostsRssResponse> getPosts() {
...
}
public record BlogPostsRssRequest(String url) {}
public record BlogPostsRssResponse(String title, String description, String content) {}
Programmatic Specification
Same as methods as tools programmatic specification, the FunctionToolCallback.Builder
allows programmatic specification.
public class BlogPostsService implements Function<BlogPostsRssRequest, BlogPostsRssResponse> {
public BlogPostsRssResponse apply(BlogPostsRssRequest request) {
// Logic to fetch blog posts...
return new BlogPostsRssResponse();
}
}
The framework provides various specification possibilities. Once tools are specified, they should be passed to the model.
Pass Tools to the Model
In the ChatClient.Builder
, we can pass tools as defaultTools
. They will be passed to the model by default at each exchange.
We can also use the ChatClient
tools()
methods to pass tools at the exchange level.
Both defaultTools
and tools
have several declaration methods depending on the tool specification, for example :
tools(String... toolNames)
: For Methods as tools with declarative specification. AMethodToolCallbackProvider
uses reflections to find methods annotated with@Tool
tools(FunctionCallback... toolCallbacks)
: For functions as tools with programmatic specification. Other methods can be found in the source code and documentation.
At this point, tools are passed to the model during model invocation, as we saw in the first part of this post.
Tools Execution in Spring AI
A ToolCallingManager
manages the execution. Its goal is to resolve the Tool (ToolCallback
), execute it, convert the result, and return a ChatResponse
.
The ToolCallingManager
is invoked by the ChatModel
under the ChatClient
. When the ChatModel
receives a ChatResponse
from the model, it checks with a predicate if a tool execution is required by the model. Then it calls the ToolCallingManager
if necessary.
ToolCallback
execution returns are then processed by a ToolCallResultConverter
. By default, DefaultToolCallResultConverter
converts the tool execution result to JSON.

A Spring AI Tool Calling diagram to understand how it works (probably inaccurate)
To go Further
This note presents the core of the function calling
logic. Other possibilities and features are available, such as ToolContext,
which is a map containing context information that can be injected into the tool at execution.
There’s also the possibility of directly returning to the user after tool execution without having to answer back to the model. It can be useful, for example, when sending an email or notification.
You can refer directly to the documentation for more information on all the possibilities.
Conclusion
This note provides a high-level overview of the function calling
pattern and how it’s implemented in Spring AI.
For those interested in pushing the boundaries even further, it’s worth exploring the MCP (Model Context Protocol), a protocol designed to support dynamic discovery, coordination, and execution of tools by AI models.