Thursday, 17 July 2025

Trigger Actions Using Tools in LangChain4j

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