Spring AI 集成(3)-使用工具

集成工具

工具调用也称为函数调用,是指在与模型交互时,模型可以调用外部函数或API来获取数据或执行操作。Spring AI提供了对工具调用的支持,使得模型可以在对话中动态地调用外部函数。

尽管我们通常将工具调用视为一种模型能力,但实际上提供工具调用逻辑的是客户端应用程序。模型只能请求工具调用并提供输入参数,而应用程序则负责根据输入参数执行工具调用并返回结果。模型永远无法访问作为工具提供的任何 API,这是一个关键的安全考量。Spring AI 提供了便捷的 API 来定义工具、解析来自模型的工具调用请求并执行工具调用.

工具用途

根据Spring AI 文档,工具调用的主要用于:

  • 信息检索. 此类工具可用于从外部源(如数据库、网络服务、文件系统或网络搜索引擎)检索信息。其目标是增强模型的知识,使其能够回答原本无法回答的问题。因此,它们可用于检索增强生成(RAG)场景。例如,工具可用于检索给定地点的当前天气、获取最新新闻文章,或查询数据库以获取特定记录。

  • 执行操作. 此类工具可用于在软件系统中执行操作,例如发送电子邮件、在数据库中创建新记录、提交表单或触发工作流。其目标是自动化那些原本需要人工干预或显式编程的任务。例如,工具可用于为与聊天机器人交互的客户预订航班、在网页上填写表单,或在代码生成场景中基于自动化测试(TDD)实现一个Java类。

将方法作为工具

在Spring AI中,我们可以将Java方法作为工具来使用。Spring AI会自动将方法转换为工具,并在模型请求工具调用时执行该方法。 我们可以通过在方法上添加@Tool注解来将其标记为工具。以下是一个示例:

src/main/java/com/example/canaan/tool目录下创建一个新的工具类 UserTools.java,并添加以下内容:

java 复制代码
package com.chestnut.canaan.tool;

import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.stereotype.Service;

import java.util.Objects;

@Service
public class UserTools {

    @Tool(
        name = "getUserFrom",
        description = "根据用户名获取用户来源"
    )
    public String getUserFrom(@ToolParam(description = "用户名称") String userName) {
        if (userName == null || userName.isEmpty()) {
            throw new IllegalArgumentException("用户名不能为空");
        }
        // 模拟获取用户来源的逻辑
        if (Objects.equals(userName, "Canaan")) {
            return "中国西北";
        }
        if (Objects.equals(userName, "Tifa")) {
            return "中国东北";
        }
        return "未知来源";
    }
}

在这个示例中,我们定义了一个名为 getUserFrom 的工具方法,它接受一个用户名作为参数,并返回该用户的来源。我们使用 @Tool 注解来标记这个方法为工具,并提供了工具的名称和描述。 接下来,我们需要修改一下 src/main/java/com/example/canaan/config/ai/ChatConfiguration.java,使其支持工具调用:

java 复制代码
package com.chestnut.canaan.config.ai;

import com.chestnut.canaan.tool.UserTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ChatConfiguration {

    @Resource
    private ChatMemory chatMemory;

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder) {
        return builder
                .defaultTools(new UserTools())
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .defaultSystem("you are a friendly assistant!").build();
    }
}

在这个配置中,我们使用 defaultTools 方法将 UserTools 类注册为工具类。这样,当模型根据描述命中到 getUserFrom 工具时,Spring AI将能够调用该方法。

现在,我们可以通过访问 /ai/chat?input=用户输入 来与模型进行交互,并使用工具获取用户来源。例如,输入 Canaan此人来自哪里 将返回 中国西北

将函数作为工具

我们可以通过 FunctionToolCallback 将函数式类型(Function、Supplier、Consumer 或 BiFunction)转换为工具,例如:

java 复制代码
package com.chestnut.canaan.config.ai;

import com.chestnut.canaan.tool.UserTools;
import jakarta.annotation.Resource;
import org.springframework.ai.chat.client.ChatClient;
import org.springframework.ai.chat.client.advisor.MessageChatMemoryAdvisor;
import org.springframework.ai.chat.memory.ChatMemory;
import org.springframework.ai.tool.ToolCallback;
import org.springframework.ai.tool.annotation.ToolParam;
import org.springframework.ai.tool.function.FunctionToolCallback;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.util.function.Function;


@Configuration
public class ChatConfiguration {

    @Resource
    private ChatMemory chatMemory;

    @Bean
    public ChatClient chatClient(ChatClient.Builder builder) {
        ToolCallback weatherTool = FunctionToolCallback
                .builder("currentWeather", (Function<WeatherRequest, WeatherResponse>) _ -> new WeatherResponse(25.0, Unit.C))
                .description("Get the weather in location")
                .inputType(WeatherRequest.class)
                .build();
        return builder
                .defaultTools(new UserTools())
                .defaultToolCallbacks(weatherTool)
                .defaultAdvisors(MessageChatMemoryAdvisor.builder(chatMemory).build())
                .defaultSystem("you are a friendly assistant!").build();
    }
}


enum Unit { C, F }

record WeatherRequest(@ToolParam(description = "位置名称,例如:上海") String location, Unit unit) {}

record WeatherResponse(double temp, Unit unit) {}

注意

如果使用函数作为工具时,必须确保函数的输入和输出可以是Void或POJO。输入和输出的POJO必须是可序列化的,因为结果将被序列化并发送回模型。以上述示例为例,WeatherRequestWeatherResponse 都是可序列化的 POJO。简单来说,如果序列化为json,则必须是用{}包裹的对象。单个的字符串或数字等基本类型不能作为输入或输出。

java 复制代码
// 可以
(Function<WeatherRequest, WeatherResponse>) _ -> new WeatherResponse(25.0, Unit.C)
// 不可以
(Function<String, String>) _ -> "25.0"

额外说明

Spring AI官方文档中,DeepSeek的start包并不支持工具调用。

但是在上述尝试中可以发现,无论是使用方法还是使用函数作为工具时,DeepSeek模型仍然能够正确调用工具并返回结果。

相关推荐
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【6】ReactAgent 同步执行 & 流式执行
java·人工智能·spring
Java成神之路-1 天前
SpringMVC 响应实战指南:页面、文本、JSON 返回全流程(Spring系列13)
java·spring·json
砍材农夫1 天前
spring-ai 第六模型介绍-聊天模型
java·人工智能·spring
云烟成雨TD1 天前
Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析
java·人工智能·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot
Flittly1 天前
【SpringAIAlibaba新手村系列】(14)MCP 本地服务与工具集成
java·spring boot·笔记·spring·ai
mfxcyh1 天前
基于xml、注解、JavaConfig实现spring的ioc
xml·java·spring
Flittly1 天前
【SpringAIAlibaba新手村系列】(13)Tool Calling 函数工具调用技术
java·spring boot·spring·ai
xdscode1 天前
Spring 依赖注入方式全景解析
java·后端·spring