Spring AI Chat Client provides a fluent API for building AI-powered applications with LLMs, supporting advisors, streaming, structured outputs, and conversation memory
Enable AI to call Java functions during processing.
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.chat.client.advisor.ToolCallAdvisor;
// Define request/response types
record WeatherRequest(String location) {}
record WeatherResponse(String location, double temp, String conditions) {}
// Implement tool function
public WeatherResponse getWeather(WeatherRequest request) {
// Fetch weather data
return new WeatherResponse(request.location(), 22.5, "Sunny");
}
// Create tool callback
ToolCallback weatherTool = ToolCallback.builder()
.function("getCurrentWeather", this::getWeather)
.description("Get the current weather for a location")
.inputType(WeatherRequest.class)
.build();
// Configure client with tool advisor
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(ToolCallAdvisor.builder().build())
.defaultTools(weatherTool)
.build();
// Use tool
String response = client
.prompt("What's the weather in Paris?")
.call()
.content();
// Response: "The weather in Paris is currently sunny with a temperature of 22.5°C."// Weather tool
ToolCallback weatherTool = ToolCallback.builder()
.function("getWeather", this::getWeather)
.description("Get weather")
.inputType(WeatherRequest.class)
.build();
// Calculator tool
ToolCallback calcTool = ToolCallback.builder()
.function("calculate", this::calculate)
.description("Perform calculations")
.inputType(CalculationRequest.class)
.build();
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(ToolCallAdvisor.builder().build())
.defaultTools(weatherTool, calcTool)
.build();
// AI can call multiple tools
String answer = client
.prompt("What's 2+2 and what's the weather in London?")
.call()
.content();ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(ToolCallAdvisor.builder().build())
.build();
// Tools specific to this request
String response = client
.prompt("Calculate expenses")
.tools(expenseTool, budgetTool)
.call()
.content();Share data between tools:
Map<String, Object> context = Map.of(
"userId", "user-123",
"apiKey", "secret-key",
"database", databaseConnection
);
chatClient
.prompt("Process my data")
.toolContext(context)
.toolCallbacks(dataTool)
.call()
.content();Tool accessing context:
public class DataTool {
@Tool(description = "Process user data")
public String processData(
@ToolParam("data") String data,
@ToolContext Map<String, Object> context
) {
String userId = (String) context.get("userId");
Database db = (Database) context.get("database");
return db.process(userId, data);
}
}@Component
class WeatherTools {
@Tool(description = "Get the current weather for a location")
public String getCurrentWeather(@ToolParam("location") String location) {
// Fetch weather
return "Sunny, 22°C";
}
@Tool(description = "Get weather forecast")
public List<String> getForecast(
@ToolParam("location") String location,
@ToolParam("days") int days
) {
return List.of("Day 1: Sunny", "Day 2: Cloudy");
}
}
// Use annotated tools
String response = chatClient
.prompt("What's the weather in Paris?")
.tools(weatherTools)
.call()
.content();Combine tool calling with retrieval:
record SearchRequest(String query) {}
record SearchResult(List<String> documents) {}
public SearchResult searchDocuments(SearchRequest request) {
// Search vector database
return vectorStore.similaritySearch(request.query());
}
ToolCallback searchTool = ToolCallback.builder()
.function("searchDocuments", this::searchDocuments)
.description("Search knowledge base")
.inputType(SearchRequest.class)
.build();
ChatClient client = ChatClient.builder(chatModel)
.defaultAdvisors(ToolCallAdvisor.builder().build())
.defaultTools(searchTool)
.build();
String answer = client
.prompt("What are the benefits of Spring Boot?")
.call()
.content();
// AI will call searchDocuments tool, then answer using retrieved docstry {
String response = client
.prompt("Call the weather tool")
.call()
.content();
} catch (ToolExecutionException e) {
System.err.println("Tool failed: " + e.getMessage());
System.err.println("Tool name: " + e.getToolName());
}// GOOD: Clear, specific description
.description("Get the current weather for a location. Returns temperature in Celsius and conditions.")
// BAD: Vague description
.description("Get weather")// GOOD: Use typed records
record WeatherRequest(String location) {}
// AVOID: Untyped maps
Map<String, Object> requestpublic WeatherResponse getWeather(WeatherRequest request) {
try {
return weatherApi.fetch(request.location());
} catch (Exception e) {
// Return error info for AI to handle
return new WeatherResponse(
request.location(),
0.0,
"Weather data unavailable"
);
}
}