当前位置: 首页 > article >正文

大型语言模型中的工具调用(Function Calling)技术详解

一、引言

随着大型语言模型(LLM)能力的飞速发展,它们在自然语言理解、文本生成、对话交互等方面展现出了令人惊叹的表现。然而,LLM 本身并不具备执行外部操作的能力,比如访问网页、调用第三方 API、执行精确数学运算等。在实际应用中,我们往往需要将 LLM 与各种工具(Tool)进行结合,让模型在生成文本时,能够在必要时发出“调用工具”的指令,由外部程序执行该工具,并将结果反馈给模型,以获得准确、可执行的输出。

“工具调用”或“功能调用”(Function Calling)正是为了解决这一需求而设计的一种机制。它允许我们在向模型发送请求时,声明一组可用的工具(或函数),并在模型的回复中,通过特殊的字段告知“我想调用哪个工具,传入什么参数”。随后,应用程序负责执行该工具,将执行结果再送回给模型,模型据此继续生成最终回复。

本文将从原理、示例、API 概览、Spring AI 集成、具体代码示例以及最佳实践等多个角度,深入解析 Function Calling 的使用方法,帮助读者在自己的项目中顺利落地这一强大功能。


二、为什么需要工具调用

  1. 弥补模型自身能力的局限

    • LLM 在常识、语言表达方面表现优异,但在精确计算、实时信息检索、与外部系统交互等方面仍有短板。
    • 例如,让模型直接回答“475695037565 的平方根是多少?”往往只能给出近似值,且可能不够准确。
  2. 降低 Prompt 复杂度,提升可维护性

    • 通过将外部逻辑封装为工具/函数,Prompt 中无需硬编码调用细节,只需告知模型“我有一个叫 squareRoot 的工具,你需要的时候就调用它”。
    • 工具逻辑集中管理,易于维护和升级。
  3. 支持多样化的应用场景

    • 网页搜索(googleSearch)、邮件发送(sendEmail)、数据库查询、业务系统接口调用等。
    • 只要定义好函数签名并注册给模型,就能让模型“像写 RPC 调用”一样使用这些工具。

三、Function Calling 的基本流程

下面以一个简单的数学计算示例,说明 Function Calling 在消息交互中的具体工作流程。

3.1 普通对话(无工具调用)

User: What is the square root of 475695037565?
AI: The square root of 475695037565 is approximately 689710.
  • 模型直接在文本中给出答案,但结果不够精确(实际应为 ~689706.486532)。

3.2 启用 Function Calling

  1. 请求 1:声明工具
{"messages": [{ "role": "user", "content": "What is the square root of 475695037565?" }],"functions": [{"name": "squareRoot","description": "Returns the square root of a given number","parameters": {"type": "object","properties": {"x": { "type": "number", "description": "The number to take the square root of" }},"required": ["x"]}}]
}
  1. 模型响应:发起工具调用
{"role": "assistant","content": null,"function_call": {"name": "squareRoot","arguments": { "x": 475695037565 }}
}
  1. 执行工具:
  • 应用程序接收到 function_call,调用相应的工具(如内部实现或外部 API),得到结果 689706.486532
  1. 请求 2:将工具执行结果反馈给模型
{"messages": [{ "role": "user", "content": "What is the square root of 475695037565?" },{"role": "assistant","function_call": {"name": "squareRoot","arguments": { "x": 475695037565 }}},{ "role": "tool", "name": "squareRoot", "content": "689706.486532" }]
}
  1. 模型生成最终回答:
The square root of 475695037565 is 689706.486532.

通过上述流程,模型能够借助外部工具得到精确结果,并在对话中正确呈现。


四、API 概览

要让 LLM 知道可调用哪些工具,需要在请求中提供每个工具的名称(name)、描述(description)和调用签名(parameters)。常见字段如下:

  • name:工具/函数的唯一标识,如 getWeathersearchWeb
  • description:对该工具功能的简要说明,帮助模型判断何时调用。
  • parameters:JSON Schema 格式,定义函数参数的类型、名称、描述、是否必填等。

以 OpenAI Chat API 为例,请求体中的 functions 字段示例如下:

"functions": [{"name": "getWeather","description": "Get the weather in a given location","parameters": {"type": "object","properties": {"location": {"type": "string","description": "The city and state, e.g. San Francisco, CA"},"unit": {"type": "string","enum": ["C", "F"],"description": "Temperature unit, Celsius or Fahrenheit"}},"required": ["location"]}}
]

当模型生成回答时,如果它判断需要调用 getWeather,就会在响应中输出 function_call 而非直接文本。


五、Spring AI 中的 Function Calling

在 Java 生态中,Spring AI(或类似框架)极大简化了工具注册和调用流程。我们只需将函数定义为 Spring Bean,并通过注解或配置将其暴露给 ChatModel。

5.1 定义函数 Bean

@Configuration
public class FunctionConfig {@Bean@Description("Get the weather in location") // 函数描述,帮助模型选择调用public Function<MockWeatherService.Request, MockWeatherService.Response> weatherFunction() {return new MockWeatherService();}}
  • Bean 名称(默认为方法名 weatherFunction)即作为函数名传给模型。
  • @Description 注解为该函数提供说明,模型据此判断何时调用。

5.2 MockWeatherService 示例

下面是一个模拟天气服务的简单实现,硬编码返回温度:

public class MockWeatherService implements Function<MockWeatherService.Request, MockWeatherService.Response> {public enum Unit { C, F }public record Request(String location, Unit unit) {}public record Response(double temp, Unit unit) {}@Overridepublic Response apply(Request request) {// 简单返回固定温度,真实场景可调用第三方天气 APIreturn new Response(30.0, Unit.C);}
}

5.3 注册并调用

在构建 ChatClient/ChatModel 时,将函数 Bean 名称传入:

ChatClient chatClient = ChatClient.builder(dashScopeChatModel).defaultFunctions("weatherFunction") // 注册函数.build();ChatResponse response = chatClient.prompt().user("What's the weather like in Boston?").call().chatResponse();

当模型判断需要调用天气服务时,框架会自动将 function_call 转为对 weatherFunction Bean 的调用,并将结果封装回模型继续生成对话。


六、函数调用流程详解

结合 Spring AI,整个 Function Calling 流程可分为以下几个步骤:

  1. 函数定义:在 Spring 应用上下文中,通过 @Bean 定义 Function<Req, Res>,并用 @Description 或 @JsonClassDescription 注解提供描述。

  2. 函数注册:在创建 ChatClient/ChatModel 时,将 Bean 名称通过 defaultFunctions(...) 或 withFunctionCallbacks(...) 传入模型选项。

  3. 构造 Prompt 请求:请求中包含用户消息和 functions 列表(由框架自动根据已注册 Bean 生成)。

  4. 模型响应 Function Call:模型在生成时,若判断需要调用某个工具,就输出 function_call 而非文本回复。

  5. 执行函数:框架捕获 function_call,将其中的 JSON 参数反序列化为对应 Bean 的 Request 对象,调用 Bean(即 Function.apply),得到 Response 对象。

  6. 反馈结果给模型:将函数调用结果作为新的消息(role="tool")附加到消息列表中,再次调用模型,让它基于工具返回值生成最终文本回复。

整个过程对开发者透明,大大简化了与模型的交互和工具调用逻辑。


七、使用已有 Service 的函数注册

除了新建 Bean,也可以将现有的 Service 方法通过函数调用暴露给模型。以下示例展示如何将一个已有的 MockOrderService 注册为 Function Call。

7.1 现有 Service

@Service
public class MockOrderService {public Response getOrder(Request request) {String productName = "尤尼克斯羽毛球拍";return new Response(String.format("%s 的订单编号为 %s, 购买的商品为: %s",request.userId, request.orderId, productName));}@JsonInclude(JsonInclude.Include.NON_NULL)public record Request(@JsonProperty(required = true, value = "orderId")@JsonPropertyDescription("订单编号, 比如1001***")String orderId,@JsonProperty(required = true, value = "userId")@JsonPropertyDescription("用户编号, 比如2001***")String userId) {}public record Response(String description) {}
}

7.2 注册为 Function Call

@Configuration
public class FunctionCallConfiguration {@Bean@Description("根据用户编号和订单编号查询订单信息")public Function<MockOrderService.Request, MockOrderService.Response> getOrderFunction(MockOrderService mockOrderService) {// 将 Service 方法引用作为 Function Beanreturn mockOrderService::getOrder;}}

7.3 调用示例

ChatClient chatClient = ChatClient.builder(dashScopeChatModel).defaultFunctions("getOrderFunction").build();ChatResponse response = chatClient.prompt().user("帮我查询订单, 用户编号为1001, 订单编号为2001").call().chatResponse();String content = response.getResult().getOutput().getContent();
System.out.println("模型回复: " + content);

这样,模型在收到查询订单的请求时,就会自动触发对 getOrderFunction 的调用,并将结果融入最终回复。


八、为 Prompt 动态指定函数

在某些场景下,我们希望根据上下文动态选择可用函数,而非在应用启动时一次性注册所有函数。Spring AI 支持通过 FunctionCallbackWrapper 在 Prompt 级别动态注入函数。

// 构造 FunctionCallbackWrapper 列表
List<FunctionCallbackWrapper<?, ?>> callbacks = List.of(new FunctionCallbackWrapper<>("CurrentWeather",               // name"Get the weather in location",  // descriptionnew MockWeatherService()        // function 实现)
);var promptOptions = OpenAiChatOptions.builder().withFunctionCallbacks(callbacks).build();ChatResponse response = chatModel.call(new Prompt(List.of(new UserMessage("What's the weather like in Tokyo?")),promptOptions)
);

这样,每次调用时都可灵活传入不同的函数集合,无需预先在 Spring 容器中定义 Bean。


九、最佳实践与注意事项

  1. 函数描述要精准:使用 @Description 或 @JsonClassDescription 为函数和参数添加清晰说明,帮助模型准确选择。

  2. 参数模式要完整:在 JSON Schema 中为每个参数提供 typedescription,并对必填字段使用 required,避免模型生成错误调用。

  3. 合理拆分工具:将功能独立的操作拆分为不同工具,避免“万能”函数导致模型调用混乱。

  4. 调用结果校验:在将工具执行结果反馈给模型前,最好进行必要的校验或清洗,防止异常数据影响后续生成。

  5. 安全与权限控制:对敏感操作(如发送邮件、数据库写入)要做好鉴权与审计,避免模型被滥用。

  6. 限流与降级:对外部 API 调用要做好限流,遇到失败时可设计降级方案,让模型给出合理的错误提示。


十、总结

“工具调用(Function Calling)”为 LLM 提供了与外部系统、API、代码逻辑无缝对接的能力,大大拓展了模型在实际应用中的边界。通过在请求中声明工具、让模型在合适时机发起调用、由应用程序执行并将结果反馈给模型,整个流程犹如一次透明的 RPC 调用,让模型既保留了强大的自然语言生成能力,又具备了执行外部操作的可能。

在 Java/Spring 生态中,借助 Spring AI、FunctionCallbackWrapper 等框架,我们只需将函数以 Bean 形式定义,并通过注解或配置进行注册,便可轻松实现 Function Calling。无论是简单的数学运算、天气查询,还是复杂的业务系统交互,都能通过这种方式优雅地集成到对话流程中。

希望本文能帮助读者全面了解并掌握 Function Calling 的原理与实践,快速在自己的项目中落地这一功能,打造更智能、更实用的 AI 应用。未来,随着 LLM 与工具生态的不断完善,我们将见证更加丰富、多样的智能化场景落地,值得持续关注与探索。

相关文章:

大型语言模型中的工具调用(Function Calling)技术详解

一、引言 随着大型语言模型&#xff08;LLM&#xff09;能力的飞速发展&#xff0c;它们在自然语言理解、文本生成、对话交互等方面展现出了令人惊叹的表现。然而&#xff0c;LLM 本身并不具备执行外部操作的能力&#xff0c;比如访问网页、调用第三方 API、执行精确数学运算等…...

Vue3.5 企业级管理系统实战(十四):动态主题切换

动态主题切换是针对用户体验的常见的功能之一&#xff0c;我们可以自己实现如暗黑模式、明亮模式的切换&#xff0c;也可以利用 Element Plus 默认支持的强大动态主题方案实现。这里我们探讨的是后者通过 CSS 变量设置的方案。 1 组件准备 1.1 修改 Navbar 组件 在 src/layo…...

解决Ubuntu20.04安装ROS2的问题(操作记录)

一、ROS 系统安装版本选择 每版的Ubuntu系统版本都有与之对应ROS版本&#xff0c;每一版ROS都有其对应版本的Ubuntu版本&#xff0c;切记不可随便装。ROS 和Ubuntu之间的版本对应关系如下&#xff1a;&#xff08; 可以从这个网站查看ROS2的各个发行版本的介绍信息。&#xff…...

C# 设置Excel中文本的对齐方式、换行、和旋转

在 Excel 中&#xff0c;对齐、换行和旋转是用于设置单元格内容显示方式的功能。合理的设置这些文本选项可以帮助用户更好地组织和展示 Excel 表格中的数据&#xff0c;使表格更加清晰、易读&#xff0c;提高数据的可视化效果。本文将介绍如何在.NET 程序中通过C# 设置Excel单元…...

Python 的 re.split()

文章目录 栗子&#xff1a;关键点&#xff1a;进阶用法&#xff1a;对比普通 split()&#xff1a;典型应用场景&#xff1a; 如何使用 Python 的 re.split() 方法通过正则表达式分割字符串。 栗子&#xff1a; import re s "apple123banana456orange" print(re.sp…...

大数据(6)【Kettle入门指南】从零开始掌握ETL工具:基础操作与实战案例解析

目录 为什么需要Kettle&#xff1f;‌一、Kettle基础概念与核心功能‌1.1 什么是Kettle&#xff1f;‌‌1.2 核心组件‌1.3 优势亮点‌ 二、Kettle安装与快速上手‌‌2.1 环境准备‌‌2.2 启动Spoon‌ ‌‌三、实战案例&#xff1a;从CSV到MySQL的数据迁移与清洗‌‌3.1 创建转…...

5.DJI-PSDK:Psdk开发负载与Msdk的应用app进行交互:

DJI-PSDK:Psdk开发负载与Msdk的应用app进行交互: 负载设备和无人机使用数据传输模块,在控制命令传输通道上以透传的方式在PSDK和MSDK间传输控制指令。在高速数据传输通道上以透传的方式在PSDK和MSDK间传输数据信息以及用户自定义的数据。使用数据传输功能,不仅可以设置不同…...

RPA VS AI Agent

图片来源网络 RPA&#xff08;机器人流程自动化&#xff09;和AI Agent&#xff08;人工智能代理&#xff09;在自动化和智能化领域各自扮演着重要角色&#xff0c;但它们之间存在显著的区别。以下是对两者区别的详细分析&#xff1a; 一、定义与核心功能 RPA&#xff08;机…...

第三节:React 基础篇-React组件通信方案

React 组件通信方案详解及使用场景 以下是 React 组件通信的常用方法及其适用场景&#xff0c;以层级结构呈现&#xff1a; 一、父子组件通信 1. Props 传递 • 实现方式&#xff1a; • 父组件通过 props 向子组件传递数据。 • 子组件通过回调函数 (onEvent) 通知父组件更…...

uniapp大文件分包

1. 在pages.json中配置 "subPackages":[{"root":pagesUser,"pages":[{"path":mine/xxx,"style":xxx },{"path":mine/xxx,"style":xxx}]},{"root":pagesIndex,"pages":[{"p…...

Spark-core编程

sortByKey 函数说明 join 函数说明 leftOuterJoin 函数说明 cogroup 函数说明 RDD行动算子&#xff1a; 行动算子就是会触发action的算子&#xff0c;触发action的含义就是真正的计算数据。 reduce 函数说明 collect 函数说明 foreach 函数说明 count 函数说明 first …...

2025年的Android NDK 快速开发入门

十年前写过一篇介绍NDK开发的文章《Android实战技巧之二十三&#xff1a;Android Studio的NDK开发》&#xff0c;今天看来已经发生了很多变化&#xff0c;NDK开发变得更加容易了。下面就写一篇当下NDK开发快速入门。 **原生开发套件 (NDK) **是一套工具&#xff0c;使开发者能…...

基于springboot的“嗨玩旅游网站”的设计与实现(源码+数据库+文档+PPT)

基于springboot的“嗨玩旅游网站”的设计与实现&#xff08;源码数据库文档PPT) 开发语言&#xff1a;Java 数据库&#xff1a;MySQL 技术&#xff1a;springboot 工具&#xff1a;IDEA/Ecilpse、Navicat、Maven 系统展示 系统功能结构图 局部E-R图 系统首页界面 系统注册…...

React 之 Redux 第三十一节 useDispatch() 和 useSelector()使用以及详细案例

使用 Redux 实现购物车案例 由于 redux 5.0 已经将 createStore 废弃&#xff0c;我们需要先将 reduxjs/toolkit 安装一下&#xff1b; yarn add reduxjs/toolkit// 或者 npm install reduxjs/toolkit使用 vite 创建 React 项目时候 配置路径别名 &#xff1a; // 第一种写法…...

6.1es新特性解构赋值

解构赋值是 ES6&#xff08;ECMAScript 2015&#xff09;引入的语法&#xff0c;通过模式匹配从数组或对象中提取值并赋值给变量。&#xff1a; 功能实现 数组解构&#xff1a;按位置匹配值&#xff0c;如 let [a, b] [1, 2]。对象解构&#xff1a;按属性名匹配值&#xff0c;…...

4月12日随笔

今天大风天气的第一天&#xff0c;周六&#xff0c;早上九点半起来听了排球技术台培训。结果一天都没顾得上看教学视频。黄老师说有排球基础的可以试试当主裁&#xff0c;那一定要争取一下&#xff01; 上午看了两集小排球&#xff0c;然后开始了解一些中介相关信息。因为下午…...

MCP遇见Web3:从边缘计算到去中心化的无限想象

MCP遇见Web3:从边缘计算到去中心化的无限想象 在数字化转型的浪潮中,边缘计算(MCP,Micro Control Protocol)和Web3技术分别在计算效率与去中心化架构上发挥着各自的优势。当两者融合,会碰撞出哪些火花?作为一名技术极客,我最近开始深度研究MCP与Web3工具的集成,试图探…...

Llama 4全面评测:官方数据亮眼,社区测试显不足之处

引言 2025年4月&#xff0c;Meta正式发布了全新的Llama 4系列模型&#xff0c;这标志着Llama生态系统进入了一个全新的时代。Llama 4不仅是Meta首个原生多模态模型&#xff0c;还采用了混合专家(MoE)架构&#xff0c;并提供了前所未有的上下文长度支持。本文将详细介绍Llama 4…...

【C++】函数直接返回bool值和返回bool变量差异

函数直接返回bool值和返回bool变量差异 背景 在工作中遇到一个比较诡异的问题&#xff0c;场景是给业务方提供的SDK有一个获取状态的函数GetStatus&#xff0c;函数的返回值类型是bool&#xff0c;在测试过程中发现&#xff0c;SDK返回的是false&#xff0c;但是业务方拿到的…...

游戏盾IP可以被破解吗

游戏盾IP&#xff08;如上海云盾SDK、腾讯云游戏盾&#xff09;是专为游戏行业设计的高防服务&#xff0c;旨在抵御DDoS攻击、CC攻击等威胁。其安全性取决于​​技术架构、防护能力​​以及​​运维策略​​。虽然理论上没有绝对“无法破解”的系统&#xff0c;但游戏盾IP在合理…...

第1节:计算机视觉发展简史

计算机视觉与图像分类概述&#xff1a;计算机视觉发展简史 计算机视觉&#xff08;Computer Vision&#xff09;作为人工智能领域的重要分支&#xff0c;是一门研究如何使机器"看"的科学&#xff0c;更具体地说&#xff0c;是指用摄影机和计算机代替人眼对目标进行识…...

ARM内核与寄存器

ARM内核与寄存器详解 目录 ARM架构概述ARM处理器模式 Cortex-M3内核的处理器模式Cortex-A系列处理器模式 ARM寄存器集 通用寄存器程序计数器(PC)链接寄存器(LR)堆栈指针(SP)状态寄存器(CPSR/SPSR) 协处理器寄存器NEON和VFP寄存器寄存器使用规范常见ARM指令与寄存器操作 ARM架…...

Hibernate:让对象与数据库无缝对话的全自动ORM框架

一、为什么需要全自动ORM&#xff1f; 在手动编写SQL的时代&#xff0c;开发者需要在Java代码和数据库表之间来回切换&#xff1a; // Java对象 public class User {private Long id;private String name;// getters and setters }// SQL语句 SELECT * FROM user WHERE id ?…...

TDengine 语言连接器(C/C++)

简介 C/C 开发人员可以使用 TDengine 的客户端驱动&#xff0c;即 C/C 连接器&#xff08;以下都用 TDengine 客户端驱动表示&#xff09;&#xff0c;开发自己的应用来连接 TDengine 集群完成数据存储、查询以及其他功能。TDengine 客户端驱动的 API 类似于 MySQL 的 C API。…...

英伟达Llama-3.1-Nemotron-Ultra-253B-v1语言模型论文快读:FFN Fusion

FFN Fusion: Rethinking Sequential Computation in Large Language Models 代表模型&#xff1a;Llama-3.1-Nemotron-Ultra-253B-v1 1. 摘要 本文介绍了一种名为 FFN Fusion 的架构优化技术&#xff0c;旨在通过识别和利用自然并行化机会来减少大型语言模型&#xff08;LLM…...

云曦月末断网考核复现

Web 先看一个BUUCTF中的文件一个上传题 [BUUCTF] 2020新生赛 Upload 打开后是一个文件上传页面 随便上传一个txt一句话木马后出现js弹窗&#xff0c;提示只能上传图片格式文件 说明有前端验证。我的做法是把一句话改为.jpg格式&#xff0c; 然后上传 访问发现虽然上传成功了…...

Flutter常用组件实践

Flutter常用组件实践 1、MaterialApp 和 Center(组件居中)2、Scaffold3、Container(容器)4、BoxDecoration(装饰器)5、Column(纵向布局)及Icon(图标)6、Column/Row(横向/横向布局)+CloseButton/BackButton/IconButton(简单按钮)7、Expanded和Flexible8、Stack和Po…...

MySQL MVCC 机制详解

MySQL MVCC 机制详解 1. MVCC 基本概念 MVCC 是一种并发控制的方法&#xff0c;主要用于数据库管理系统&#xff0c;允许多个事务同时读取数据库中的同一个数据项&#xff0c;而不需要加锁&#xff0c;从而提高了数据库的并发性能。 ┌──────────────────…...

【面试】封装、继承、多态的具象示例 模板编程的理解与应用场景 链表适用的场景

文章目录 C面试&#xff1a;封装、继承、多态的具象示例1. 封装 (Encapsulation)2. 继承 (Inheritance)3. 多态 (Polymorphism)综合示例&#xff1a;封装、继承、多态 C模板编程的理解与应用场景我对模板编程的理解C中最常用的模板编程场景1. STL (标准模板库)2. 通用容器实现3…...

0.机器学习基础

0.人工智能概述&#xff1a; &#xff08;1&#xff09;必备三要素&#xff1a; 数据算法计算力 CPU、GPU、TPUGPU和CPU对比&#xff1a; GPU主要适合计算密集型任务&#xff1b;CPU主要适合I/O密集型任务&#xff1b; 【笔试问题】什么类型程序适合在GPU上运行&#xff1…...