Tool use

使用Spring AI做Function Call

前面介绍过了Function Call的主要概念和用法之后,深入Spring AI,看看在Spring AI中如何做工具调用。 已有方法转成工具 假如,现在我已经有个现成的服务了,我想把他变成一个Function(Tool),可以这…

TL;DR

前面介绍过了Function Call的主要概念和用法之后,深入Spring AI,看看在Spring AI中如何做工具调用。 已有方法转成工具 假如,现在我已经有个现成的服务了,我想把他变成一个Function(Tool),可以这…

前面介绍过了Function Call的主要概念和用法之后,深入Spring AI,看看在Spring AI中如何做工具调用。

已有方法转成工具

假如,现在我已经有个现成的服务了,我想把他变成一个Function(Tool),可以这样做:

@Configuration
public class FunctionCallConfiguration {
    @Bean
    @Description("根据用户输入的时区获取该时区的当前时间")
    public Function<TimeService.Request, TimeService.Response> getTimeFunction(TimeService timeService) {
        return timeService::getTimeByZoneId;
    }
}

也就是定义一个Bean,返回值是Function类型,构造函数中的参数是一个我们已经有的Spring中的服务,然后在这个方法中直接调用已有的方法即可。 记得需要增加一个清晰的描述,讲清楚这个Function是干什么的,这样才能让模型更好的知道什么时候可以调用这个工具。 以下是这个Function的定义,他有两个泛型类型参数,分别是T和R。

/**
 * Represents a function that accepts one argument and produces a result.
 *
 * <p>This is a <a href="package-summary.html">functional interface</a>
 * whose functional method is {@link #apply(Object)}.
 *
 * @param <T> the type of the input to the function
 * @param <R> the type of the result of the function
 *
 * @since 1.8
 */
@FunctionalInterface
public interface Function<T, R> {

}

T表示这个function的入参,R表示出参。如我们定义的入参Request和出参Response如下:

@Service
public class TimeService {
    public Response getTimeByZoneId(Request request) {
        ZoneId zid = ZoneId.of(request.zoneId);
        System.out.println("getTimeByZoneId");
        ZonedDateTime zonedDateTime = ZonedDateTime.now(zid);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
        return new Response(zonedDateTime.format(formatter));
    }


    public record Request(
            @JsonProperty(required = true, value = "zoneId") @JsonPropertyDescription("时区,比如 Asia/Shanghai") String zoneId) {
    }

    public record Response(String time) {

    }
}

这里在入参中,我们就可以加一些说明,也是为了让模型更清晰了解这些参数是什么,他才知道怎么调用。

定义新工具

如果是之前没有现成的工具,想要重新定义一个工具给LLM用的话,可以直接借助@Tool 注解:

public class TimeTools {
    @Tool(description = "Get time by zone id")
    public String getTimeByZoneId(@ToolParam(description = "Time zone id, such as Asia/Shanghai")
                                  String zoneId) {
        ZoneId zid = ZoneId.of(zoneId);
        System.out.println("getTimeByZoneId");
        ZonedDateTime zonedDateTime = ZonedDateTime.now(zid);
        DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss z");
        return zonedDateTime.format(formatter);
    }
}

用@Tool把一个方法声明一个工具,用@ToolParam 来定义每个参数的描述。 这样定义的tool之后,可以把他直接给到chatClient,用tools方法把对应的工具new出来就行了。

@GetMapping("/functionCall1")

public Flux<String> functionCall1(HttpServletResponse response, String city) {
    response.setCharacterEncoding("UTF-8");
    return chatClient
            .prompt().tools(new TimeTools())
            .user(city + "现在几点了?")
            .stream().content();
}

工具不自动执行

默认情况下,在Spring AI中,如果模型决策需要调用工具,Spring AI就会直接调用具体的工具了,如果你想自己控制工具的调用,比如在后续要讲的ReAct Agent中,要自己控制的话,可以通过这个参数控制:

internalToolExecutionEnabled

把他设置为false的话,Spring AI就不会自动调用工具了,就需要开发者自己控制工具的调用。(后续会展开,这里了解即可)。

调用外部工具

Spring AI Alibaba也提供了一些集成的第三方工具,可以方便的调用,避免重复开发。已有工具列表: https://java2ai.com/docs/1.0.0.2/practices/integrations/tool-calling/

版本提示

模型、框架与接口会持续变化。涉及版本号、参数与生产配置时,请在实践前对照对应官方文档。

LLMentor系统化学习大模型应用工程

内容来自个人课程知识库备份,并经过结构化整理。技术版本持续演进,生产使用前请结合官方文档验证。