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模型仍然能够正确调用工具并返回结果。

相关推荐
StackNoOverflow2 小时前
Spring MVC零散知识点记录
java·spring·mvc
rainchestnut2 小时前
Spring AI 初步集成(2)-添加记忆
spring
koping_wu2 小时前
Java面试汇总:java基础、多线程、spring、jvm、分布式
java·spring·面试
tsyjjOvO2 小时前
SpringMVC 从入门到精通(续)
java·后端·spring
Binary-Jeff2 小时前
MySQL MVCC 原理解析:Undo Log、ReadView 与版本可见性机制
java·数据库·后端·mysql·spring
逆境不可逃2 小时前
【后端新手谈 04】Spring 依赖注入所有方式 + 构造器注入成官方推荐的原因
java·开发语言·spring boot·后端·算法·spring·注入方式
毅炼2 小时前
Spring 总结(1)
java·开发语言·spring
码界奇点3 小时前
基于Spring MVC和MyBatis的妖气山视频管理系统设计与实现
java·spring·毕业设计·mvc·mybatis·源代码管理
大傻^3 小时前
Spring AI 2.0 生产部署指南:从 1.x 迁移、性能调优与云原生实践
人工智能·spring·云原生·springai