Large Language Models (LLMs) have traditionally been known for their ability to generate coherent and contextually relevant text. However, recent advancements have introduced a powerful extension to their capabilities, the ability to invoke external tools. This opens a new class of applications where LLMs not only interpret user inputs but also initiate actions such as running code, fetching live data, or performing calculations.
This post explores how this works, the concept of “tools” or “function calling,” and what developers need to consider when integrating such models.
What Are Tools in LLMs?
In the context of LLMs, tools are externally defined functions or services that the model can suggest calling based on the user’s input. These tools can be anything:
· A math computation engine
· A weather API
· A database query executor
· A function that performs specific logic
These tools are not "magically" executed by the LLM. Instead, the model outputs a structured representation of its intent to call a specific tool along with the necessary arguments. It's then up to the developer to detect this intent, execute the corresponding tool, and return the result to the LLM for further processing or final response generation.
LLMs Expressing Intent, Not Executing Code
It’s important to understand that LLMs don’t execute code or directly call APIs themselves. They generate an action plan in the form of a structured message like:
{ "tool": "calculate_area", "parameters": { "length": 5, "width": 3 } }
This means the developer needs to set up a framework that listens for such tool call outputs, processes them, and sends the result back to the model.
Is all LLM models support tools?
Not all LLMs support tool use equally. Some models:
· Don’t support tools at all
· Require extensive prompt engineering
· Support only predefined tool schemas
· Need special configuration via APIs or SDKs
So before designing a system with tool support, it’s critical to verify the capabilities of the LLM you plan to use.
For example, let me define three tools
@Description("All the tools related to mathematical operations are found here") public class MathTools { @Tool(""" Calculate the square root of a number, and return the result as a double value. """) public Double squareRoot(double number) { System.out.println("***Calculating the Square Root Of A Number***"); return Math.sqrt(number); } @Tool(""" Take radians as input and return the equivalant value in degrees """) public Double radiansToDegrees(double radians) { System.out.println("***Converting Radians to Degrees***"); return Math.toDegrees(radians); } @Tool(""" Take degress as input and return the equivalant value in radians """) public Double degreesToRadians(double degrees) { System.out.println("***Converting Degrees to Radians***"); return Math.toRadians(degrees); } }
As you see above snippet, each method in MathTools is:
· Annotated with @Tool(description): this signals that it's a callable tool for the AI.
· Designed to perform a simple math related function.
· Printing a log message for clarity/debugging.
· Returning a Double result for clarity and precision.
Method |
Input |
Output |
Usecase |
squareRoot(double number) |
number |
square root of the number |
What is the square root of 25? |
radiansToDegrees(double radians) |
angle in radians |
angle in degrees |
Convert π/2 radians to degrees |
degreesToRadians(double degrees) |
angle in degrees
|
angle in radians
|
Convert 180 degrees to radians |
ChatAssistant Interface
public interface ChatAssistant { String chat(@UserMessage String message); }
This is the abstraction of a chat interface. The @UserMessage annotation designates the input message from the user, which will be processed by the model.
Then, you instantiate the assistant by supplying all the tools.
ChatAssistant chatAssistant = AiServices .builder(ChatAssistant.class) .chatModel(chatModel) .tools(new MathTools()) .build();
Ideal Flow of Tool Use with an LLM
LLM cannot execute arbitrary code, it just suggests function calls. The ideal flow of execution looks like below.
1. User Message: "What is the square root of 49?"
2. LLM Interpretation: The model does not compute sqrt(49) itself. Instead, it says:
"To answer this, I need to use a tool called squareRoot with the argument number = 49."
3. Function Call Construction: The LLM returns something like below.
{
"tool_name": "squareRoot",
"arguments": { "number": 49 }
}
4. Host Application Executes the Tool: The host code (e.g., your Java runtime) recognizes this intent. It calls the actual squareRoot(49) method in MathTools.
5. Result Passed Back to LLM: The host receives 7.0 and sends it back to the LLM as a tool result.
6. LLM Responds to the User: The square root of 49 is 7.0
Follow below step-by-step procedure to build a working application.
Step 1: Create new maven project tools-demo.
Step 2: Update pom.xml with maven dependencies.
pom.xml
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.sample.app</groupId> <artifactId>tools-demo</artifactId> <version>0.0.1-SNAPSHOT</version> <properties> <maven.compiler.source>21</maven.compiler.source> <maven.compiler.target>21</maven.compiler.target> <java.version>21</java.version> </properties> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>3.3.10</version> </parent> <dependencyManagement> <dependencies> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-bom</artifactId> <version>1.0.1</version> <type>pom</type> <scope>import</scope> </dependency> </dependencies> </dependencyManagement> <dependencies> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>dev.langchain4j</groupId> <artifactId>langchain4j-ollama-spring-boot-starter</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springdoc</groupId> <artifactId>springdoc-openapi-starter-webmvc-ui</artifactId> <version>2.6.0</version> </dependency> <!-- https://mvnrepository.com/artifact/jakarta.validation/jakarta.validation-api --> <dependency> <groupId>jakarta.validation</groupId> <artifactId>jakarta.validation-api</artifactId> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> <executions> <execution> <goals> <goal>repackage</goal> <!-- Important --> </goals> </execution> </executions> </plugin> </plugins> </build> </project>
Step 3: Define MathTools.
MathTools.java
package com.sample.app.tools; import dev.langchain4j.agent.tool.Tool; import dev.langchain4j.model.output.structured.Description; @Description("All the tools related to mathematical operations are found here") public class MathTools { @Tool(""" Calculate the square root of a number, and return the result as a double value. """) public Double squareRoot(double number) { System.out.println("***Calculating the Square Root Of A Number***"); return Math.sqrt(number); } @Tool(""" Take radians as input and return the equivalant value in degrees """) public Double radiansToDegrees(double radians) { System.out.println("***Converting Radians to Degrees***"); return Math.toDegrees(radians); } @Tool(""" Take degress as input and return the equivalant value in radians """) public Double degreesToRadians(double degrees) { System.out.println("***Converting Degrees to Radians***"); return Math.toRadians(degrees); } }
Step 4: Define ChatAssistant interface,
ChatAssistant.java
package com.sample.app.interfaces; import dev.langchain4j.service.UserMessage; public interface ChatAssistant { String chat(@UserMessage String message); }
Step 5: Define ChatResponse dto.
ChatResponse.java
package com.sample.app.dto; public class ChatResponse { private String message; public ChatResponse() { } public ChatResponse(String message) { this.message = message; } public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } }
Step 6: Define ChatService class.
ChatService.java
package com.sample.app.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import com.sample.app.dto.ChatResponse; import com.sample.app.interfaces.ChatAssistant; import com.sample.app.tools.MathTools; import dev.langchain4j.model.ollama.OllamaChatModel; import dev.langchain4j.service.AiServices; @Service public class ChatService { @Autowired private OllamaChatModel chatModel; private ChatAssistant chatAssistant; public ChatResponse chat(String userMessage) { if (chatAssistant == null) { synchronized (ChatService.class) { if (chatAssistant == null) { chatAssistant = AiServices.builder(ChatAssistant.class).chatModel(chatModel).tools(new MathTools()) .build(); } } } String story = chatAssistant.chat(userMessage); return new ChatResponse(story); } }
Step 7: Define LangchainConfig and SwaggerConfig classes.
SwaggerConfig.java
package com.sample.app.config; import org.springframework.context.annotation.Configuration; import io.swagger.v3.oas.annotations.OpenAPIDefinition; import io.swagger.v3.oas.annotations.info.Info; @Configuration @OpenAPIDefinition(info = @Info(title = "Chat service Application", version = "v1")) public class SwaggerConfig { }
LangchainConfig.java
package com.sample.app.config; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; import dev.langchain4j.http.client.spring.restclient.SpringRestClientBuilderFactory; import dev.langchain4j.model.ollama.OllamaChatModel; @Configuration public class LangchainConfig { @Bean public OllamaChatModel ollamaLanguageModel() { return OllamaChatModel.builder().baseUrl("http://localhost:11434") .modelName("llama3.2") .logRequests(true) .httpClientBuilder(new SpringRestClientBuilderFactory().create()) // explicitly use Spring's HTTP client .build(); } }
Step 8: Define ChatController class.
ChatController.java
package com.sample.app.controller; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.CrossOrigin; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; import com.sample.app.dto.ChatResponse; import com.sample.app.service.ChatService; import io.swagger.v3.oas.annotations.tags.Tag; import jakarta.validation.Valid; import jakarta.validation.constraints.NotEmpty; @RestController @RequestMapping("/api/chat") @CrossOrigin("*") @Tag(name = "Chat Controller", description = "This section contains APIs related to Chat APIs Powered by Ollama") public class ChatController { @Autowired private ChatService chatService; @PostMapping public ChatResponse chat(@RequestBody @Valid ChatRequestBody chatRequestBody) { return chatService.chat(chatRequestBody.getMessage()); } private static class ChatRequestBody { @NotEmpty private String message; public String getMessage() { return message; } public void setMessage(String message) { this.message = message; } } }
Step 9: Define main application class.
App.java
package com.sample.app; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class App { public static void main(String[] args) { SpringApplication.run(App.class, args); } }
Build the project
Navigate to the project root directory where pom.xml is located and execute following command to generate the artifact.
mvn clean install
Upon successful execution of the command, you can see tools-demo-0.0.1-SNAPSHOT.jar file in the target folder.
$ ls ./target/ classes maven-archiver tools-demo-0.0.1-SNAPSHOT.jar generated-sources maven-status tools-demo-0.0.1-SNAPSHOT.jar.original generated-test-sources test-classes
Run the Application
Execute following command to run the application.
java -jar ./target/tools-demo-0.0.1-SNAPSHOT.jar --server.port=1235
Open the url http://localhost:1235/swagger-ui/index.html in browser.
Execute the api /api/chat with below payload.
{ "message": "What is the square root of 12345" }
You will receive below response.
{ "message": "The square root of 12345 is approximately 111.11." }
You will see following messages in the console.
2025-05-29T10:25:35.636+05:30 INFO 25628 --- [io-1235-exec-10] d.l.http.client.log.LoggingHttpClient : HTTP request: - method: POST - url: http://localhost:11434/api/chat - headers: [Content-Type: application/json] - body: { "model" : "llama3.2", "messages" : [ { "role" : "user", "content" : "What is the square root of 12345" } ], "options" : { "stop" : [ ] }, "stream" : false, "tools" : [ { "type" : "function", "function" : { "name" : "squareRoot", "description" : "Calculate the square root of a number, and return the result as a double value.\n", "parameters" : { "type" : "object", "properties" : { "number" : { "type" : "number" } }, "required" : [ "number" ] } } }, { "type" : "function", "function" : { "name" : "radiansToDegrees", "description" : "Take radians as input and return the equivalant value in degrees\n", "parameters" : { "type" : "object", "properties" : { "radians" : { "type" : "number" } }, "required" : [ "radians" ] } } }, { "type" : "function", "function" : { "name" : "degreesToRadians", "description" : "Take degress as input and return the equivalant value in radians\n", "parameters" : { "type" : "object", "properties" : { "degrees" : { "type" : "number" } }, "required" : [ "degrees" ] } } } ] } ***Calculating the Square Root Of A Number*** 2025-05-29T10:25:38.502+05:30 INFO 25628 --- [io-1235-exec-10] d.l.http.client.log.LoggingHttpClient : HTTP request: - method: POST - url: http://localhost:11434/api/chat - headers: [Content-Type: application/json] - body: { "model" : "llama3.2", "messages" : [ { "role" : "user", "content" : "What is the square root of 12345" }, { "role" : "assistant", "tool_calls" : [ { "function" : { "name" : "squareRoot", "arguments" : { "number" : "12345" } } } ] }, { "role" : "tool", "content" : "111.1080555135405" } ], "options" : { "stop" : [ ] }, "stream" : false, "tools" : [ { "type" : "function", "function" : { "name" : "squareRoot", "description" : "Calculate the square root of a number, and return the result as a double value.\n", "parameters" : { "type" : "object", "properties" : { "number" : { "type" : "number" } }, "required" : [ "number" ] } } }, { "type" : "function", "function" : { "name" : "radiansToDegrees", "description" : "Take radians as input and return the equivalant value in degrees\n", "parameters" : { "type" : "object", "properties" : { "radians" : { "type" : "number" } }, "required" : [ "radians" ] } } }, { "type" : "function", "function" : { "name" : "degreesToRadians", "description" : "Take degress as input and return the equivalant value in radians\n", "parameters" : { "type" : "object", "properties" : { "degrees" : { "type" : "number" } }, "required" : [ "degrees" ] } } } ] }
You can download the application from this link.
Previous Next Home
No comments:
Post a Comment