FunctionCallback

The FunctionCallback interface in Spring AI provides a standardized way to implement Large Language Model (LLM) function calling capabilities. It allows developers to register custom functions that can be called by AI models when specific conditions or intents are detected in the prompts.spring-doc.cn

The FunctionCallback interface defines several key methods:spring-doc.cn

  • getName(): Returns the unique function name within the AI model contextspring-doc.cn

  • getDescription(): Provides a description that helps the model decide when to invoke the functionspring-doc.cn

  • getInputTypeSchema(): Defines the JSON schema for the function’s input parametersspring-doc.cn

  • call(String functionArguments): Handles the actual function executionspring-doc.cn

  • call(String functionArguments, ToolContext toolContext): Extended version that supports additional tool contextspring-doc.cn

Builder Pattern

Spring AI provides a fluent builder API for creating FunctionCallback implementations. This is particularly useful for defining function callbacks that you can register, pragmatically, on the fly, with your ChatClient or ChatModel model calls.spring-doc.cn

Use the FunctionCallback.builder() method to create a new builder instance and chain the configuration methods to set the function name, description, input type, and other properties. The FunctionCallback.Builder is a hierarchical with the following structure:spring-doc.cn

Function-Invoking Approach

Converts any java.util.function.Function, BiFunction, Supplier or Consumer into a FunctionCallback that can be called by the AI model.spring-doc.cn

You can use lambda expressions or method references to define the function logic but you must provide the input type of the function using the inputType(TYPE).

Function<I, O>

FunctionCallback callback = FunctionCallback.builder()
    .function("processOrder", (Order order) -> processOrderLogic(order))
    .description("Process a new order")
    .inputType(Order.class)
    .build();

BiFunction<I, ToolContext, O>

Using Function with input type <I> and additional ToolContext parameter:spring-doc.cn

FunctionCallback callback = FunctionCallback.builder()
    .function("processOrder", (Order order, ToolContext context) ->
        processOrderWithContext(order, context))
    .description("Process a new order with context")
    .inputType(Order.class)
    .build();

Supplier<O>

Use java.util.Supplier<O> or java.util.function.Function<Void, O> to define functions that don’t take any input:spring-doc.cn

FunctionCallback.builder()
	.function("turnsLight", () -> state.put("Light", "ON"))
    .description("Turns light on in the living room")
    .inputType(Void.class)
	.build();

Consumer<I>

Use java.util.Consumer<I> or java.util.function.Function<I, Void> to define functions that don’t produce output:spring-doc.cn

record LightInfo(String roomName, boolean isOn) {}

FunctionCallback.builder()
	.function("turnsLight", (LightInfo lightInfo) -> {
				logger.info("Turning light to [" + lightInfo.isOn + "] in " + lightInfo.roomName());
			})
    .description("Turns light on/off in a selected room")
    .inputType(LightInfo.class)
	.build();

Generics Input Type

Use the ParameterizedTypeReference to define functions with generic input types:spring-doc.cn

record TrainSearchRequest<T>(T data) {}

record TrainSearchSchedule(String from, String to, String date) {}

record TrainSearchScheduleResponse(String from, String to, String date, String trainNumber) {}

FunctionCallback.builder()
	.function("trainSchedule", (TrainSearchRequest<TrainSearchSchedule> request) -> {
        logger.info("Schedule: " + request.data().from() + " to " + request.data().to());
        return new TrainSearchScheduleResponse(request.data().from(), request.  data().to(), "", "123");
    })
    .description("Schedule a train reservation")
    .inputType(new ParameterizedTypeReference<TrainSearchRequest<TrainSearchSchedule>>() {})
	.build();

Method Invoking Approach

Enables method invocation through reflection while automatically handling JSON schema generation and parameter conversion. It’s particularly useful for integrating Java methods as callable functions within AI model interactions.spring-doc.cn

The method invoking implements the FunctionCallback interface and provides:spring-doc.cn

Static Method Invocation

You can refer to a static method in a class by providing the method name, parameter types, and the target class.spring-doc.cn

public class WeatherService {
    public static String getWeather(String city, TemperatureUnit unit) {
        return "Temperature in " + city + ": 20" + unit;
    }
}

FunctionCallback callback = FunctionCallback.builder()
    .method("getWeather", String.class, TemperatureUnit.class)
    .description("Get weather information for a city")
    .targetClass(WeatherService.class)
    .build();

Object instance Method Invocation

You can refer to an instance method in a class by providing the method name, parameter types, and the target object instance.spring-doc.cn

public class DeviceController {
    public void setDeviceState(String deviceId, boolean state, ToolContext context) {
        Map<String, Object> contextData = context.getContext();
        // Implementation using context data
    }
}

DeviceController controller = new DeviceController();

String response = ChatClient.create(chatModel).prompt()
    .user("Turn on the living room lights")
    .functions(FunctionCallback.builder()
        .method("setDeviceState", String.class,boolean.class,ToolContext.class)
        .description("Control device state")
        .targetObject(controller)
        .build())
    .toolContext(Map.of("location", "home"))
    .call()
    .content();
Optionally, using the .name(), you can set a custom function name different from the method name.

Common Configurations

There are several common configurations that you can use to customize the function callbacks.spring-doc.cn

Schema Type

The framework supports different schema types for generating the schemas for the input parameters:spring-doc.cn

FunctionCallback.builder()
    .schemaType(SchemaType.OPEN_API_SCHEMA)
    // ... other configuration
    .build();

Custom Response Handling

You can provide a custom response converter to format the function response before sending it back to the AI model. Most AI Models expect an text response, so it is your responsibility to convert the function response to a text format. By default, the response is converted to a String.spring-doc.cn

Many models cope well with JSON responses, so you can return a JSON string.
FunctionCallback.builder()
    .responseConverter(response ->
        customResponseFormatter.format(response))
    // ... other configuration
    .build();

Custom Object Mapping

Spring AI uses ObjectMapper for JSON serialization and deserialization. You can provide a custom ObjectMapper to handle custom object mapping:spring-doc.cn

FunctionCallback.builder()
    .objectMapper(customObjectMapper)
    // ... other configuration
    .build();

Best Practices

Descriptive Names and Descriptions

  • Provide unique function namesspring-doc.cn

  • Write comprehensive descriptions to help the model understand when to invoke the functionspring-doc.cn

Input Type & Schema

  • For the function invoking approach, define input types explicitly and use ParameterizedTypeReference for generic types.spring-doc.cn

  • Consider using custom schema when auto-generated ones don’t meet requirements.spring-doc.cn

Error Handling

  • Implement proper error handling in function implementations and return the error message in the responsespring-doc.cn

  • You can use the ToolContext to provide additional error context when neededspring-doc.cn

Tool Context Usage

  • Use ToolContext when additional state or context is required that is provided from the User and not part of the function input generated by the AI model.spring-doc.cn

  • Use BiFunction<I, ToolContext, O> to access the ToolContext in the function invocation approach and add ToolContext parameter in the method invoking approach.spring-doc.cn

Notes on Schema Generation

  • The framework automatically generates JSON schemas from Java typesspring-doc.cn

  • For function invoking, the schema is generated based on the input type for the function that needs to be set using inputType(TYPE). Use ParameterizedTypeReference for generic types.spring-doc.cn

  • Generated schemas respect Jackson annotations on model classesspring-doc.cn

  • You can bypass the automatic generation by providing custom schemas using inputTypeSchema()spring-doc.cn

Common Pitfalls to Avoid

Lack of Description

  • Always provide explicit descriptions instead of relying on auto-generated onesspring-doc.cn

  • Clear descriptions improve model’s function selection accuracyspring-doc.cn

Schema Mismatches

  • Ensure input types match the Function’s input parameter types.spring-doc.cn

  • Use ParameterizedTypeReference for generic types.spring-doc.cn