从 FunctionCallback 迁移到 ToolCallback API

本指南帮助你从已弃用的 FunctionCallback API 迁移到 Spring AI 中的新 ToolCallback API。有关新 API 的更多信息,请查看 工具调用文档。

变更概述

这些变更是改进和扩展 Spring AI 中工具调用能力的更广泛努力的一部分。其中,新 API 从"函数"术语转向"工具"术语,以更好地符合行业惯例。这涉及几个 API 变更,同时通过已弃用的方法保持向后兼容性。

主要变更

  1. FunctionCallbackToolCallback

  2. FunctionCallback.builder().function()FunctionToolCallback.builder()

  3. FunctionCallback.builder().method()MethodToolCallback.builder()

  4. FunctionCallingOptionsToolCallingChatOptions

  5. ChatClient.builder().defaultFunctions()ChatClient.builder().defaultTools()

  6. ChatClient.functions()ChatClient.tools()

  7. FunctionCallingOptions.builder().functions()ToolCallingChatOptions.builder().toolNames()

  8. FunctionCallingOptions.builder().functionCallbacks()ToolCallingChatOptions.builder().toolCallbacks()

迁移示例

1. 基本函数回调

之前:

FunctionCallback.builder()
    .function("getCurrentWeather", new MockWeatherService())
    .description("Get the weather in location")
    .inputType(MockWeatherService.Request.class)
    .build()

之后:

FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
    .description("Get the weather in location")
    .inputType(MockWeatherService.Request.class)
    .build()

2. ChatClient 使用

之前:

String response = ChatClient.create(chatModel)
    .prompt()
    .user("What's the weather like in San Francisco?")
    .functions(FunctionCallback.builder()
        .function("getCurrentWeather", new MockWeatherService())
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .call()
    .content();

之后:

String response = ChatClient.create(chatModel)
    .prompt()
    .user("What's the weather like in San Francisco?")
    .tools(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .call()
    .content();

3. 基于方法的函数回调

之前:

FunctionCallback.builder()
    .method("getWeatherInLocation", String.class, Unit.class)
    .description("Get the weather in location")
    .targetClass(TestFunctionClass.class)
    .build()

之后:

var toolMethod = ReflectionUtils.findMethod(TestFunctionClass.class, "getWeatherInLocation");

MethodToolCallback.builder()
    .toolDefinition(ToolDefinition.builder(toolMethod)
        .description("Get the weather in location")
        .build())
    .toolMethod(toolMethod)
    .build()

或者使用声明式方法:

class WeatherTools {

    @Tool(description = "Get the weather in location")
    public void getWeatherInLocation(String location, Unit unit) {
        // ...
    }

}

你可以使用相同的 ChatClient#tools() API 来注册基于方法的工具回调:

String response = ChatClient.create(chatModel)
    .prompt()
    .user("What's the weather like in San Francisco?")
    .tools(MethodToolCallback.builder()
        .toolDefinition(ToolDefinition.builder(toolMethod)
            .description("Get the weather in location")
            .build())
        .toolMethod(toolMethod)
        .build())
    .call()
    .content();

或者使用声明式方法:

String response = ChatClient.create(chatModel)
    .prompt()
    .user("What's the weather like in San Francisco?")
    .tools(new WeatherTools())
    .call()
    .content();

4. 选项配置

之前:

FunctionCallingOptions.builder()
    .model(modelName)
    .function("weatherFunction")
    .build()

之后:

ToolCallingChatOptions.builder()
    .model(modelName)
    .toolNames("weatherFunction")
    .build()

5. ChatClient Builder 中的默认函数

之前:

ChatClient.builder(chatModel)
    .defaultFunctions(FunctionCallback.builder()
        .function("getCurrentWeather", new MockWeatherService())
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .build()

之后:

ChatClient.builder(chatModel)
    .defaultTools(FunctionToolCallback.builder("getCurrentWeather", new MockWeatherService())
        .description("Get the weather in location")
        .inputType(MockWeatherService.Request.class)
        .build())
    .build()

6. Spring Bean 配置

之前:

@Bean
public FunctionCallback weatherFunctionInfo() {
    return FunctionCallback.builder()
        .function("WeatherInfo", new MockWeatherService())
        .description("Get the current weather")
        .inputType(MockWeatherService.Request.class)
        .build();
}

之后:

@Bean
public ToolCallback weatherFunctionInfo() {
    return FunctionToolCallback.builder("WeatherInfo", new MockWeatherService())
        .description("Get the current weather")
        .inputType(MockWeatherService.Request.class)
        .build();
}

破坏性变更

  1. 函数回调中的 method() 配置已被替换为使用 ToolDefinitionMethodToolCallback 的更明确的方法工具配置。

  2. 使用基于方法的回调时,你现在需要显式地使用 ReflectionUtils 查找方法并将其提供给构建器。或者,你可以使用带有 @Tool 注解的声明式方法。

  3. 对于非静态方法,你现在必须同时提供方法和目标对象:

MethodToolCallback.builder()
    .toolDefinition(ToolDefinition.builder(toolMethod)
        .description("Description")
        .build())
    .toolMethod(toolMethod)
    .toolObject(targetObject)
    .build()

已弃用的方法

以下方法已被弃用,将在未来版本中删除:

  • ChatClient.Builder.defaultFunctions(String…​)

  • ChatClient.Builder.defaultFunctions(FunctionCallback…​)

  • ChatClient.RequestSpec.functions()

请使用它们的 tools 对应方法。

使用 @Tool 的声明式规范

现在你可以使用方法级注解(@Tool)来向 Spring AI 注册工具:

class Home {

    @Tool(description = "Turn light On or Off in a room.")
    void turnLight(String roomName, boolean on) {
        // ...
        logger.info("Turn light in room: {} to: {}", roomName, on);
    }
}

String response = ChatClient.create(this.chatModel).prompt()
        .user("Turn the light in the living room On.")
        .tools(new Home())
        .call()
        .content();

其他说明

  1. 新 API 提供了工具定义和实现之间更好的分离。

  2. 工具定义可以在不同的实现中重用。

  3. 构建器模式已针对常见用例进行了简化。

  4. 改进了对基于方法的工具的支持,并改进了错误处理。

时间线

已弃用的方法将在当前里程碑版本中保持向后兼容性,但将在下一个里程碑版本中删除。建议尽快迁移到新 API。