Building a Minimal Spring AI Tool Chain for Multi-Tool Calls
This tutorial demonstrates how to integrate Spring AI with Ollama, define @Tool‑annotated weather and translation utilities, register them for automatic chaining, and let a large language model answer queries like “fetch Beijing weather and reply in English” using a concise end‑to‑end example.
Scenario
Spring AI can call external tools from a large language model (LLM) and automatically compose their execution order, enabling agents to complete multi‑step tasks such as "query Beijing weather and answer in English".
Core Concepts
Tool Calling : The LLM generates a function‑call request (function name and parameters); Spring AI intercepts the request, invokes the corresponding Java method, and returns the result to the model.
@Tool annotation : Placed on a method, Spring AI parses the method signature and JavaDoc to produce a JSON Schema that the model can understand.
Multiple Tool Registration : Use defaultTools(...) to register several tools at once; the model decides the order and combination of calls.
Chain Calling : Within a single response the model can invoke multiple tools sequentially, feeding the output of one tool as the input to the next, forming a chain.
Implementation
pom.xml
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.3.3</version> <!-- downgrade to stable version -->
</parent>
<groupId>com.example</groupId>
<artifactId>spring-ai-ollama-tool-chain</artifactId>
<version>1.0</version>
<properties>
<java.version>17</java.version>
<spring-ai.version>1.1.2</spring-ai.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- Spring AI Ollama core -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-starter-model-ollama</artifactId>
<version>${spring-ai.version}</version>
</dependency>
</dependencies>application.yml
server:
port: 886
spring:
ai:
ollama:
base-url: http://localhost:11434
chat:
model: qwen2.5:7b-instruct
options:
temperature: 0.7
num-ctx: 4096 # context window size
logging:
level:
org.springframework.ai.chat.client: DEBUG # view tool‑call detailsWeather Tool
package com.badao.ai.tools;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class WeatherTool {
@Tool(name = "get_weather", description = "查询指定城市的实时天气")
public String getWeather(@ToolParam(description = "城市名称") String city) {
System.out.println("调用了天气工具");
// Simulated weather data
return String.format("%s当前天气:晴,温度22℃,湿度45%%。", city);
}
}Translate Tool
package com.badao.ai.tools;
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Component;
@Component
public class TranslateTool {
@Tool(name = "translate_to_english", description = "将中文文本翻译成英文")
public String translate(@ToolParam(description = "待翻译的中文文本") String text) {
System.out.println("调用了翻译工具");
// Simulated translation; real implementation could call a translation API
return "Translated: " + text + " (This is the English version.)";
}
}Tool Registration Configuration
package com.badao.ai.config;
import com.badao.ai.tools.WeatherTool;
import com.badao.ai.tools.TranslateTool;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.model.ChatModel;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ToolConfig {
@Bean
public ChatClient chatClient(ChatModel chatModel, WeatherTool weatherTool, TranslateTool translateTool) {
return ChatClient.builder(chatModel)
.defaultTools(weatherTool, translateTool) // register weather and translation tools
.build();
}
}Agent Service Layer
package com.badao.ai.service;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.stereotype.Service;
@Service
public class AgentService {
private final ChatClient chatClient;
public AgentService(ChatClient chatClient) {
this.chatClient = chatClient;
}
public String ask(String question) {
return chatClient.prompt()
.user(question + "(请先用天气工具,再用翻译工具)")
.call()
.content();
}
}Controller
package com.badao.ai.controller;
import com.badao.ai.service.AgentService;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/api")
public class AgentController {
private final AgentService agentService;
public AgentController(AgentService agentService) {
this.agentService = agentService;
}
@PostMapping("/agent")
public String ask(@RequestBody String question) {
return agentService.ask(question);
}
}Testing
Test a single tool call and a chained tool call using the provided REST endpoint. The screenshots below show the console output for each scenario.
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
The Dominant Programmer
Resources and tutorials for programmers' advanced learning journey. Advanced tracks in Java, Python, and C#. Blog: https://blog.csdn.net/badao_liumang_qizhi
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
