Spring AI框架升级:Tool Calling取代Function Calling

后端 潘老师 2周前 (04-06) 128 ℃ (0) 扫码查看

在最新发布的Spring AI 1.0.0.M6版本里,有个重大调整值得开发者们重点关注,那就是Function Calling被废弃,取而代之的是Tool Calling。接下来,本文就带大家深入了解Tool Calling的新特性,以及如何将原有的Function Calling迁移到Tool Calling。

一、Function Calling为何被弃用

Spring AI之所以做出这样的调整,主要有两方面原因。一方面,是为了让框架中工具调用功能得到更好的改进和扩展。另一方面,新的API将术语从“functions”改为“tools”,这能与行业通用术语保持一致,便于开发者理解和使用。

除此之外,新的设计在多方面进行了优化。从代码结构上看,新API实现了工具定义和实现之间更好的分离,这意味着工具定义可以在不同的实现场景中重复利用,提高了代码的复用性。在实际使用时,不仅简化了构造器模式,对基于方法的工具提供了更友好的支持,还优化了错误处理机制,让开发过程更加顺畅。

Function Calling和Tool Calling本质概念相同,底层实现原理也一致,只是对外提供的概念和部分API有所变动。官方建议开发者尽快将Function Calling相关API迁移到Tool Calling,因为在后续版本中,Function Calling API会被彻底移除。

二、Function Calling与Tool Calling的关键变化

这两者的变化主要体现在API的调整上,简单来说,就是把“Function”替换成“Tool”,同时为了方便方法使用还做了一些优化。具体如下:

  • 接口与构建器变化FunctionCallback变为ToolCallbackFunctionCallback.builder().function()改为FunctionToolCallback.builder()FunctionCallback.builder().method()改为MethodToolCallback.builder()
  • 选项类变化FunctionCallingOptions相关构建器方法变化,如FunctionCallingOptions.builder().functions()变为ToolCallbackChatOptions.builder().toolNames()FunctionCallingOptions.builder().functionCallbacks()变为ToolCallbackChatOptions.builder().toolCallbacks()
  • 聊天客户端变化ChatClient.builder().defaultFunctions变为ChatClient.builder().defaultTools()ChatClient.functions()变为ChatClient.tools()

若要进行迁移操作,可以参考官方文档获取更详细的指导。

三、深入了解Tool Calling

(一)什么是Tool Calling

Tool Calling(工具调用),也可以理解为函数调用,是AI应用程序中常用的一种模式。借助它,模型能够与一组API或者工具进行交互,进而增强自身的功能。

(二)Tool Calling的应用场景

  1. 信息检索:AI模型通常无法直接获取实时信息,比如当被问到当前日期、天气预报这类需要实时数据的问题时,模型往往无法回答。这时,就可以借助信息检索工具,让模型在需要时调用该工具,从外部资源(像数据库、Web服务、文件系统或者搜索引擎)获取相关信息。这样一来,模型的知识储备得到扩充,就能回答原本无法回答的问题了。比如利用工具查询指定位置的天气、检索最新的新闻文章,或者查询数据库等。
  2. 执行操作:这类工具主要用于在软件系统中执行各种操作,例如发送电子邮件、在数据库中创建新记录、提交表单,甚至触发工作流等。其目的是自动完成那些原本需要人工干预,或者必须通过显式编程才能实现的任务。举个例子,与机器人交互生成待办事项、创建会议安排等。

需要注意的是,虽然工具调用看起来像是模型自身具备的功能,但实际上,工具调用逻辑是由客户端应用程序提供的。模型只能请求调用工具并提供输入参数,真正执行工具调用的并不是模型本身。

四、Tool Calling示例演示

接下来,通过两个简单示例,带大家了解Spring AI中Tool Calling的具体用法。

(一)信息检索示例

以使用ollama qwen2.5大模型查询日期为例,比如想知道明天的年月日,具体实现步骤如下:

  1. 定义工具
package org.ivy.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 该类用于定义与日期时间相关的工具方法
public class DateTimeTools {
    // 使用@Tool注解将该方法标记为一个工具
    // description属性用于描述工具功能,方便模型理解何时调用该工具
    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        // 获取当前日期和时间,并根据用户所在时区进行格式化
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }
}
  1. 工具使用
package org.ivy.controller;

import org.ivy.tools.DateTimeTools;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.ollama.OllamaChatModel;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
// 该控制器类用于处理与工具调用相关的请求
@RestController
public class ToolsController {
    // 注入OllamaChatModel实例,用于与ollama qwen2.5大模型进行交互
    private final OllamaChatModel ollamaChatModel;

    public ToolsController(OllamaChatModel ollamaChatModel) {
        this.ollamaChatModel = ollamaChatModel;
    }

    // 处理GET请求,根据用户提供的prompt调用工具并返回结果
    @GetMapping("/search-tool")
    public String get(@RequestParam(value = "prompt", required = false) String prompt) {
        return ChatClient.create(ollamaChatModel)
                .prompt(prompt)
                // 将DateTimeTools工具添加到聊天客户端,使模型可以调用
                .tools(new DateTimeTools())
                .call()
                // 获取模型生成的最终响应内容
                .content();
    }
}
  1. 测试:访问http://localhost:8808/search-tool ,在请求参数中传入相应的prompt,就可以看到模型调用工具后返回的日期信息。

(二)执行功能示例

接下来定义一个用于设置闹钟的工具,这个工具基于前面获取当前时间的工具,在当前时间基础上设置闹钟。

  1. 工具定义
package org.ivy.tools;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.context.i18n.LocaleContextHolder;

import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
// 该类继续扩展日期时间相关工具,新增设置闹钟功能
public class DateTimeTools {
    // 获取当前日期和时间的工具方法
    @Tool(description = "Get the current date and time in the user's timezone")
    String getCurrentDateTime() {
        return LocalDateTime.now().atZone(LocaleContextHolder.getTimeZone().toZoneId()).toString();
    }

    // 设置闹钟的工具方法
    @Tool(description = "Set a user alarm for the given time, provided in ISO-8601 format")
    LocalDateTime setAlarm(String time) {
        // 将传入的时间字符串按照ISO-8601格式解析为LocalDateTime对象
        LocalDateTime alarmTime = LocalDateTime.parse(time, DateTimeFormatter.ISO_DATE_TIME);
        // 打印设置的闹钟时间
        System.out.println("Alarm set for " + alarmTime);
        return alarmTime;
    }
}
  1. 测试:访问http://localhost:8808/search-tool?prompt=Can you set an alarm 10 minutes from now? ,可以看到模型调用工具后返回的设置闹钟结果。

五、Tool Calling的原理剖析

当我们希望某个工具能被大模型使用时,需要在聊天请求中包含工具的定义信息,这些信息包括工具名称、工具参数以及工具描述。

大模型在处理请求过程中,如果决定调用工具,会返回一个包含工具名称和输入参数的响应。

接下来,应用程序会根据工具名称识别要调用的工具,并使用提供的输入参数执行该工具。执行完成后,应用程序负责处理工具调用的结果。

处理完结果后,应用程序再将工具调用结果发送给大模型。大模型会把这个结果作为额外的上下文信息,生成最终的响应内容返回给用户。

六、Tool Calling的源码解读

(一)方法即工具(Methods as Tools)

在Spring AI中,有两种方式可以将方法指定为工具,分别是声明式和编程式。

  1. 声明式定义工具
    • @Tool注解:用于标记方法为工具,它有多个属性。name属性用于指定工具名称,如果不设置,则默认使用方法名,要注意方法名的唯一性;description属性非常重要,用于详细描述工具功能,模型会根据这个描述来判断何时以及如何调用工具;returnDirect属性用于指定工具执行结果是直接返回给调用方,还是先发送给大模型,默认是发送给大模型;resultConverter属性用于指定工具执行结果的转换器,Spring AI内置了默认的转换为String的转换器,如果有特殊业务需求,开发者可以自行实现。
    • @ToolParam注解:用于定义工具参数。required属性指定参数是否必填,默认是必填;description属性用于描述参数作用,让模型更好地理解参数含义。此外,还有@Nullable注解用于指定参数可选,@Schema(Swagger)和@JsonProperty (jackson)都与json schema相关。
  2. 编程式定义工具
    • ToolCallback接口:定义了工具的基本行为。getToolDefinition方法用于获取工具定义,这是AI大模型决定何时、如何使用工具的依据;getToolMetadata方法用于获取工具的其他设置,默认返回一个空的工具元数据;call方法用于执行工具,并将结果发送给大模型。
    • MethodToolCallback类:实现了ToolCallback接口,它有多个重要成员变量,包括工具定义、工具元数据、工具方法以及工具对象。其中,ToolDefinition比较关键,包含工具名称、描述以及输入参数的json schema,如果未提供json schema,会根据方法参数自动生成。

(二)函数即工具(Function as Tools)

除了通过方法定义工具外,Spring AI还支持将函数类型(如FunctionSupplierConsumerBiFunction)转换为工具,通过构建FunctionToolCallback来实现。同样,在定义工具时,需要定义好ToolDefinition 。不过,函数工具在返回值及参数方面有一些限制,不支持基本类型、集合类型、异步类型以及响应式类型。

(三)Tool Specification(工具规范)

  1. Tool CallbackToolCallback接口为定义可被AI模型调用的工具提供了方法,包括工具的定义和执行逻辑。Spring AI内置了MethodToolCallbackFunctionToolCallback两种实现方式。
  2. Tool DefinitionToolDefinition接口用于提供可被AI模型解析的工具定义,包括工具名称、描述和输入的Json schema。每个ToolCallback都必须提供一个ToolDefinition实例。例如:
ToolDefinition toolDefinition = ToolDefinition.builder()
    .name("currentWeather")
    .description("Get the weather in location")
    .inputSchema("""
        {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string"
                },
                "unit": {
                    "type": "string",
                    "enum": ["C", "F"]
                }
            },
            "required": ["location", "unit"]
        }
    """)
    .build();
  1. Tool Context:Spring AI支持通过ToolContext API向工具传递额外的上下文信息。这一功能允许开发者提供更多用户信息,这些信息可以和工具参数一起使用。例如:
class CustomerTools {
    // 使用@Tool注解定义工具,该工具用于检索客户信息
    @Tool(description = "Retrieve customer information")
    Customer getCustomerInfo(Long id, ToolContext toolContext) {
        // 根据传入的客户ID和租户ID从数据库中获取客户信息
        return customerRepository.findById(id, toolContext.get("tenantId"));
    }
}

ChatModel chatModel =...

String response = ChatClient.create(chatModel)
        .prompt("Tell me more about the customer with ID 42")
        .tools(new CustomerTools())
        // 设置额外的上下文信息,这里是租户ID
        .toolContext(Map.of("tenantId", "acme"))
        .call()
        .content();

System.out.println(response);
  1. Return Direct:默认情况下,工具调用结果会发送给大模型,由模型继续生成对话。但在某些业务场景下,可能需要将结果直接返回给调用方,而不经过大模型。这就需要在定义工具时,根据实际需求指定returnDirect=true
  2. Tool Execution:工具的执行过程就是使用提供的输入参数和工具名称调用工具,并返回结果的过程。这一过程由ToolCallingManager接口负责管理,该接口主要包含两个方法:resolveToolDefinitions用于解析工具定义;executeToolCalls用于执行工具调用并返回结果。其默认实现类是DefaultToolCallingManager

七、总结

本文对Spring AI框架中Tool Calling的相关内容进行了较为全面的介绍,涵盖了它取代Function Calling的原因、自身的新特性、具体的使用示例、原理以及源码解读等方面。不过,像ToolContext、工具执行过程中的异常处理等内容,本文并未深入涉及,这些部分同样重要,感兴趣的读者可以参考官方文档或者github源码进一步学习。

 


版权声明:本站文章,如无说明,均为本站原创,转载请注明文章来源。如有侵权,请联系博主删除。
本文链接:https://www.panziye.com/back/16794.html
喜欢 (0)
请潘老师喝杯Coffee吧!】
分享 (0)
用户头像
发表我的评论
取消评论
表情 贴图 签到 代码

Hi,您需要填写昵称和邮箱!

  • 昵称【必填】
  • 邮箱【必填】
  • 网址【可选】