Spring AI Alibaba 工具(Function Calling)使用

news2025/3/29 13:00:24

一、工具(Function Calling)简介

Spring AI Alibaba工具(Function Calling):https://java2ai.com/docs/1.0.0-M6.1/tutorials/function-calling/

1、工具(Function Calling)

“工具(Tool)”或“功能调用(Function Calling)”允许大型语言模型(LLM)在必要时调用一个或多个可用的工具,这些工具通常由开发者定义。工具可以是任何东西:网页搜索、对外部 API 的调用,或特定代码的执行等。

LLM 本身不能实际调用工具;相反,它们会在响应中表达调用特定工具的意图(而不是以纯文本回应)。然后,我们应用程序应该执行这个工具,并报告工具执行的结果给模型。

通过在请求中声明一个或多个工具,当 LLM 可以访问工具时,它可以在合适的情况下决定调用其中一个工具,这是一个非常强大的功能。

比如说,googleSearch 和 sendEmail 工具,并且有一个查询像是“我的朋友想了解 AI 领域的最新新闻。将简短的总结发送到 email.com”,那么它可以使用 googleSearch 工具查找最新新闻,然后总结这些信息并通过 sendEmail 工具将总结发送到指定的邮箱。

2、API 概览

通常,自定义函数需要提供一个 name、description 和 function call signature,以便模型知道函数能做什么、期望的输入参数。

Spring AI 使这一过程变得简单,只需定义一个返回 java.util.Function 的 @Bean 定义,并在调用 ChatModel 时将 bean 名称作为选项进行注册。

在底层,Spring 会用适当的适配器代码包装你的 POJO(即函数),以便与 AI 模型进行交互,免去了编写繁琐的样板代码。FunctionCallback.java 接口和配套的 FunctionCallbackWrapper.java 工具类包含了底层实现代码,它们是简化 Java 回调函数的实现和注册的关键。

在这里插入图片描述

二、工具(Function Calling)使用

示例需求: 我们将创建一个聊天机器人,通过调用我们自己的函数来回答问题。

为了支持聊天机器人的响应,我们将注册一个自己的函数,该函数接受位置信息并返回该位置的天气。

当模型需要回答诸如 “What’s the weather like in Boston?” 这样的问题时,AI 模型将调用客户端,将位置值作为参数传递给函数。这种类似 RPC 的数据将以 JSON 格式传递。

我们的函数调用某个基于 SaaS 的天气服务 API,并将天气响应返回给模型以完成对话。

1、自定义函数

使用 ToolCallback接口。

这里定义一个 RealTimeWeatherFunction函数来获取实时天气信息。

/**
 * 获取实时天气信息
 */
@Slf4j
@Component
public class RealTimeWeatherFunction implements ToolCallback {


    @Override
    public ToolDefinition getToolDefinition() {
        ToolDefinition toolDefinition = DefaultToolDefinition.builder()
                //.name(getName())
                .name("realTimeWeatherFunction")
                .description("获取实时天气信息")
                // inputSchema值为:JSON字符串
                .inputSchema("""
                        {
                            "city": {
                                "type": "string",
                                "description": "城市名"
                            },
                            "days": {
                                "type": "integer",
                                "description": "天数"
                            },
                            "hours": {
                                "type": "integer",
                                "description": "小时数"
                            }
                        }
                        """)
                .build();

        log.info("RealTimeWeatherFunction --> getToolDefinition方法,toolDefinition = {}", toolDefinition);
        return toolDefinition;
    }

    /**
     * @param toolInput JSON字符串为 getToolDefinition方法中定义的 inputSchema
     *                  model.
     * @return
     */
    @NotNull
    @Override
    public String call(String toolInput) {
        log.info("RealTimeWeatherFunction --> call方法,toolInput = {}", toolInput);

        if (StringUtils.isBlank(toolInput)) {
            return null;
        }
        // TODO 模拟西安未来5天天气状况的数据
        String weatherData = getWeatherData();
        return weatherData;
    }

    public String getWeatherData(){
        return """
                嗯,用户问的是西安未来24小时和15天的天气情况,我得先看看搜索结果里的各个网页内容。首先,今天是2025年3月25日,所以得找最新的信息。
                                
                网页1提到从3月26日开始陕西会有寒潮,大风沙尘天气,最高气温降幅可达20℃。不过用户关心的是未来24小时,也就是到3月26日的天气。网页1里的西安三日天气显示,今天(25日)晴,14℃~29℃;明天(26日)多云,14℃~25℃。不过这个网页的发布时间是3月25日8点21分,应该是最新的。
                                
                网页2是天气网的7天预报,显示25日(今天)晴,27/13℃,也就是最高27℃,最低13℃?不过网页1里的今天温度是14℃~29℃,这可能存在矛盾。需要确认哪个更准确。网页3发布于3月25日0点,提到今天(25日)晴天间多云,16时气温21.2℃~32℃,可能用户所在时间不同,但今天白天的最高气温在网页3里提到是29℃。而网页7提到3月25日西安最高气温可达29℃,所以可能网页1的今天温度是14℃~29℃,但实际可能更高,比如网页7提到24日最高气温32℃,25日可达29℃。但用户问的是未来24小时,也就是从今天3月25日到3月26日的天气。
                                
                网页2的天气预报显示26日多云,26/11℃,也就是最高26℃,最低11℃,但网页1里的明天(26日)是14℃~25℃,这里也有不一致。可能网页1的数据更准确,但需要结合其他网页。
                                
                网页3提到26日夜间至28日有寒潮,26日夜间开始降温,风力加大到4-5级,阵风6级以上,并伴有沙尘。所以未来24小时可能包括26日白天,而26日白天可能多云,温度在25℃左右,但到夜间开始降温。网页3的具体预报显示26日晴转多云,夜间有5级阵风,浮尘,12℃~27℃。所以未来24小时到26日晚上,温度可能在12℃到27℃之间,但夜间开始降温,并有大风和沙尘。
                                
                关于15天的天气,网页2提供了7天的预报,但用户需要15天。不过搜索结果里没有直接的15天数据,可能需要根据现有的7天预报和趋势来推断。网页2的7天预报显示到31日都是多云或阴天,气温逐渐回升,但可能之后会有更多变化,但具体无法确定。网页6提到寒潮影响持续到3月30日左右,最低气温出现在29日夜间到30日凌晨。网页3的预报显示到31日气温回升到4℃~18℃,但可能之后继续回升。不过只能根据现有数据尽量总结。
                                
                此外,沙尘和霜冻的情况需要注意。网页1、3、5、6都提到沙尘天气,尤其是26日至27日。霜冻出现在27日至31日,尤其是山区。森林火险红色预警在网页4提到,需要注意防火。
                                
                综合以上信息,未来24小时应该是今天(25日)晴,高温29℃,夜间转多云,26日白天多云,可能有浮尘,温度在14℃到25℃之间,但夜间开始剧烈降温,风力加大。而15天的天气可能前7天有详细数据,后续只能根据趋势推测,但需要说明可能的不确定性。
                """;
    }
}

函数调用某个天气服务 API逻辑替换为 同样的用户问题返回 deepseek的推理信息。

deepseek的推理信息如下:

在这里插入图片描述

2、编写 Controller接口

在 Prompt 请求中指定函数有几种方式:

  • 全局设置默认函数集合
  • ChatModel请求中指定函数集合

在普通 Controller Bean 中注入 ChatModel 实例,并为 Prompt 指定函数,实现下面几个功能:

  • 简单调用
  • 流式调用
/**
 * “工具(Tool)”或“功能调用(Function Calling)”允许大型语言模型(LLM)在必要时调用一个或多个可用的工具,这些工具通常由开发者定义。
 *
 */
@Slf4j
@RestController
@RequestMapping("/dashscope/function-calling")
public class DashScopeFunctionCallingController {

    private final ChatModel dashScopeChatModel;

    /**
     * 使用如下的方式自动注入 ChatModel
     *
     * @param chatModel
     */
    public DashScopeFunctionCallingController(ChatModel chatModel) {
        this.dashScopeChatModel = chatModel;
    }

    /**
     * 编程方式自行创建一个 ChatClient.Builder 实例.
     * 使用自动注入 ChatModel
     *
     * @param chatModel
     */
    //public DashScopeFunctionCallingController(ChatModel chatModel) {
    //
    //    this.dashScopeChatModel = chatModel;
    //
    //    // 构造时,可以设置 ChatClient 的参数
    //    // {@link org.springframework.ai.chat.client.ChatClient};
    //    this.dashScopeChatClient = ChatClient.builder(chatModel)
    //            // 实现 Chat Memory 的 Advisor
    //            // 在使用 Chat Memory 时,需要指定对话 ID,以便 Spring AI 处理上下文。
    //            .defaultAdvisors(
    //                    new MessageChatMemoryAdvisor(new InMemoryChatMemory())
    //            )
    //            // 实现 Logger 的 Advisor
    //            .defaultAdvisors(
    //                    new SimpleLoggerAdvisor()
    //            )
    //            // 设置 ChatClient 中 ChatModel 的 Options 参数
    //            .defaultOptions(
    //                    DashScopeChatOptions.builder()
    //                            .withTopP(0.7)
    //                            .build()
    //            )
    //            // 设置默认 System Message
    //            //.defaultSystem("你是一个AI搞笑段子手,以搞笑,幽默,风趣的风格回答所有的问题。")
    //            // 设置默认函数集合
    //            //.defaultFunctions("")
    //            .build();
    //}

    /**
     * 简单调用方式
     */
    @GetMapping("/simple/chat")
    public String simpleChat(@RequestParam(defaultValue = "西安未来5天天气状况") String userInputPrompt) {

        // 指定参数
        DashScopeChatOptions chatOptions = DashScopeChatOptions.builder()
                // 指定使用的模型
                .withModel(DashScopeApi.ChatModel.QWEN_PLUS.getModel())
                // 指定函数调用
                .withFunction("realTimeWeatherFunction")
                .build();

        ChatResponse chatResponse = dashScopeChatModel.call(
                new Prompt(userInputPrompt, chatOptions)
        );
        String aiOutput = chatResponse.getResult().getOutput().getText();

        log.info("simpleChat --> userInputPrompt = {}, aiOutput = {}", userInputPrompt, aiOutput);
        return aiOutput;
    }

    /**
     * Stream 流式调用。
     * 可以使大模型的输出信息实现打字机效果。
     */
    @GetMapping("/stream/chat")
    public Flux<String> streamChat(HttpServletResponse response, @RequestParam(defaultValue = "西安未来5天天气状况") String userInputPrompt) {
        // 避免接口返回乱码
        response.setCharacterEncoding("UTF-8");

        // 指定参数
        DashScopeChatOptions chatOptions = DashScopeChatOptions.builder()
                // 指定使用的模型
                .withModel(DashScopeApi.ChatModel.QWEN_PLUS.getModel())
                // 指定函数调用
                .withFunction("realTimeWeatherFunction")
                .build();

        log.info("streamChat -->  userInputPrompt = {},", userInputPrompt);

        Flux<ChatResponse> stream = dashScopeChatModel.stream(
                new Prompt(userInputPrompt, chatOptions)
        );

        Flux<String> flux = stream.map(chatResponse -> chatResponse.getResult().getOutput().getText());
        return flux;
    }

}

deepseek的回答如下:

在这里插入图片描述

启动项目,访问接口与 AI 大模型智能对话。

在这里插入图片描述

自定义函数的调用信息:

  • getToolDefinition方法:项目启动会调用一次,接口访问一次会调用多次。
  • call方法:项目启动没有调用,接口访问一次会调用一次,并接收到 String toolInput参数(inputSchema的Json数据)。

在这里插入图片描述

– 求知若饥,虚心若愚。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2322323.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Touch Diver:Weart为XR和机器人遥操作专属设计的触觉反馈动捕手套

在虚拟现实&#xff08;VR&#xff09;和扩展现实&#xff08;XR&#xff09;领域&#xff0c;触觉反馈技术正逐渐成为提升沉浸感和交互体验的重要因素。Weart作为这一领域的创新者&#xff0c;凭借其TouchDIVER Pro和TouchDIVER G1触觉手套&#xff0c;为用户带来了高度逼真的…

[深度学习]图片分类任务

图片分类任务 文章目录 图片分类任务分类任务回归和分类如何做分类的输出 图片分类卷积神经网络保持特征图大小不变更大的卷积核和更多的卷积核层数特征图怎么变小卷积神经网络中特征图改变卷积到全连接分类任务的LOSS一个基本的分类神经网络 经典神经网络AlexNetVggNetResNet …

Nodejs 项目打包部署方式

方式一&#xff1a;PM2 一、准备工作 确保服务器上已安装 Node.js 环境建议使用 PM2 进行进程管理&#xff08;需要额外安装&#xff09; 二、部署步骤 1.首先在服务器上安装 PM2&#xff08;推荐&#xff09;&#xff1a; npm install -g pm22.将项目代码上传到服务器&…

C++类与对象的的第三个简单的实战练习-3.25笔记

哔哩哔哩C面向对象高级语言程序设计教程&#xff08;118集全&#xff09; 简单实战三 创建项目 打开VS&#xff0c;点击创建一个新项目 创建一个空项目 点击下一步 点击工程名称&#xff0c;选择添加 选择新建项 选择C类 取名 点击确定&#xff0c;这时候还需要一个main.cpp …

HarmonyOS-ArkUI Grip组件

我们在学习List的时候&#xff0c;已经捎带引入了Grid。讲解如下图所示&#xff1a; 也就是&#xff0c;如果一个表&#xff0c;长宽基本都是一致的&#xff0c;那么此时可以完全不用Grid也可以实现&#xff0c;并且&#xff0c;优先考虑的就是List。 如果List实现不了的情况下…

2025清华大学:DeepSeek教程全集(PDF+视频精讲,共10份).zip

一、资料列表 第一课&#xff1a;Deepseek基础入门 第二课&#xff1a;DeepSeek赋能职场 第三课&#xff1a;普通人如何抓住DeepSeek红利 第四课&#xff1a;让科研像聊天一样简单 第五课&#xff1a;DeepSeek与AI幻觉 第六课&#xff1a;基于DeepSeek的AI音乐词曲的创造法 第…

mac vim命令快捷键

目录 移动光标插入模式复制/粘贴删除搜索/替换退出 移动光标 快捷键说明0 / ^跳到行首&#xff0c;移动到光标所在行的"行首"$跳到行末&#xff0c;移动到光标所在行的"行尾"gg跳到文件第一行G移动到文章的最后[n]G跳到第n行w光标跳到下个字的开头e光标跳…

低代码配置式Web组态解析

低代码配置式Web组态技术通过可视化操作和预置组件库&#xff0c;大幅降低开发门槛&#xff0c;适用于工业控制、物联网监控、数据可视化等场景。以下是综合行业实践和产品特性的分析&#xff1a; ‌一、核心功能与优势‌ ‌可视化编辑与拖拽布局‌ 提供图形化编辑器&#xff0…

基于web的家政服务网站

内容摘要 由于互联网的使用&#xff0c;人们在管理、应用、服务等领域使用数据更加简洁、方便&#xff0c;大大提高了工作效率。互联网正逐渐融入我们的生活&#xff0c;影响和改变我们的生活。 家政服务管理系统是典型的信息管理系统&#xff08;MIS&#xff09;。其开发主要…

聚水潭数据集成到MySQL的最佳实践分享

聚水潭数据集成到MySQL的技术案例分享 在本次技术案例中&#xff0c;我们将探讨如何通过轻易云数据集成平台&#xff0c;将聚水潭的数据高效、可靠地集成到MySQL数据库中。具体的集成方案为“聚水潭-商品信息查询-->BI初本-商品信息表_copy”。该方案旨在实现从聚水潭获取商…

线性代数核心概念与NumPy科学计算实战全解析

前言 学习方法&#xff1a; 思维导图&#xff0c;梳理 多记忆&#xff0c;函数名和功能&#xff0c;参数 学会应用&#xff0c;不要钻牛角尖 一、浅解线性代数 1.1标量 标量是一个只有大小没有方向的量。在数学上&#xff0c;标量通常表示为一个普通的数字&#xff0c;如‌质量…

C#自定义曲线便器功能实现(简化版)

目录 一、曲线编辑器实现功能 二、实现方法说明 三、关键代码说明 1、绘制背景板和曲线 2、绘制坐标系面板 3、绘制曲线 四、工程下载连接 一、曲线编辑器实现功能 添加或者删除控制点&#xff0c;通过移动控制点来修改曲线形状 二、实现方法说明 1、坐标系系统&#x…

解锁U盘属性0字节困境,重获数据生机

在数字化浪潮中&#xff0c;U盘宛如一位忠诚的“数据信使”&#xff0c;频繁穿梭于各种设备之间&#xff0c;为我们存储和传输着重要信息。然而&#xff0c;当U盘突然显示属性为0字节时&#xff0c;就如同这位信使突然“失声”&#xff0c;让我们陷入了数据丢失的恐慌之中。U盘…

⭐算法OJ⭐二叉树的直径【树】(C++实现)Binary Tree Paths

543. Binary Tree Paths&#xff08;二叉树的直径&#xff09; Given the root of a binary tree, return the length of the diameter of the tree. The diameter of a binary tree is the length of the longest path between any two nodes in a tree. This path may or m…

字典树与01trie

字典树简介 当我们通过字典查一个字或单词的时候&#xff0c;我们会通过前缀或关键字的来快速定位一个字的位置&#xff0c;进行快速查找。 字典树就是类似字典中索引表的一种数据结构&#xff0c;能够帮助我们快速定位一个字符串的位置。 字典树是一种存储字符串的数据结构…

vue - [Vue warn]: Duplicate keys detected: ‘0‘. This may cause an update error.

问题描述&#xff1a; vue项目中&#xff0c;对表单数组赋值时&#xff0c;控制台抛出警告&#xff1a; 问题代码&#xff1a; 问题分析&#xff1a; 1、Vue 要求每个虚拟 DOM 节点必须有唯一的 key。该警告信息通常出现在使用v-for循环的场景中&#xff0c;多个同级节点使用…

各类神经网络学习:(三)RNN 循环神经网络(中集),同步多对多结构的详细解释

上一篇下一篇RNN&#xff08;上集&#xff09;RNN&#xff08;下集&#xff09; 同步多对多结构 1&#xff09;结构详解 ①图解&#xff1a; ②参数含义&#xff1a; x t x_t xt​ &#xff1a;表示每一个时刻的输入&#xff1b; o t o_t ot​ &#xff1a;表示每一个时刻的输…

UR5e机器人位姿

UR5e 作为一款 6 自由度协作机器人&#xff0c;其末端执行器的位姿&#xff08;位置与姿态的组合&#xff09;控制是实现精准操作的核心。在笛卡尔坐标系中&#xff0c;位姿通常用齐次变换矩阵表示&#xff0c;包含末端的三维位置&#xff08;x, y, z&#xff09;和三维姿态&am…

导入 Excel 规则批量修改或删除 PDF 文档内容

需要对 PDF 文档内容进行修改的时候&#xff0c;通常我们会需要借助一些专业的工具来帮我们完成。那我们如果需要修改的 PDF 文档较多的时候&#xff0c;有什么方法可以帮我们实现批量操作呢&#xff1f;今天这篇文章就给大家介绍一下当我们需要批量修改多个 PDF 文档的时候&am…

ISIS-1 ISIS概述

前面几章我们介绍了OSPF的基础工作原理以及怎样交互LSA形成LSDB链路状态数据库的 这一章我们来介绍另一个链路状态路由协议,ISIS路由协议 一、概述 ISIS(Intermediate System to Intermediate System,中间系统到中间系统)是由ISO(International Organization for Standardiza…