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

打印 上一主题 下一主题

主题 1722|帖子 1722|积分 5176

马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。

您需要 登录 才可以下载或查看,没有账号?立即注册

x
一、引言

随着大型语言模型(LLM)能力的飞速发展,它们在自然语言明确、文本生成、对话交互等方面展现出了令人惊叹的体现。然而,LLM 自己并不具备实行外部操作的能力,比如访问网页、调用第三方 API、实行精确数学运算等。在现实应用中,我们往往需要将 LLM 与各种工具(Tool)举行结合,让模型在生成文本时,能够在必要时发出“调用工具”的指令,由外部步伐实行该工具,并将效果反馈给模型,以得到精确、可实行的输出。
“工具调用”或“功能调用”(Function Calling)正是为了办理这一需求而筹划的一种机制。它允许我们在向模型发送请求时,声明一组可用的工具(或函数),并在模型的回复中,通过特别的字段告知“我想调用哪个工具,传入什么参数”。随后,应用步伐负责实行该工具,将实行效果再送回给模型,模型据此继承生成最终回复。
本文将从原理、示例、API 概览、Spring AI 集成、具体代码示例以及最佳实践等多个角度,深入解析 Function Calling 的利用方法,资助读者在自己的项目中顺遂落地这一强盛功能。

二、为什么需要工具调用


  • 弥补模型自身能力的范围

    • LLM 在常识、语言表达方面体现良好,但在精确盘算、实时信息检索、与外部系统交互等方面仍有短板。
    • 比方,让模型直接答复“475695037565 的平方根是多少?”往往只能给出近似值,且大概不够精确。

  • 降低 Prompt 复杂度,提升可维护性

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

  • 支持多样化的应用场景

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


三、Function Calling 的基本流程

下面以一个简单的数学盘算示例,说明 Function Calling 在消息交互中的具体工作流程。
3.1 普通对话(无工具调用)

  1. User: What is the square root of 475695037565?
  2. AI: The square root of 475695037565 is approximately 689710.
复制代码


  • 模型直接在文本中给出答案,但效果不够精确(现实应为 ~689706.486532)。
3.2 启用 Function Calling


  • 请求 1:声明工具
  1. {
  2.   "messages": [
  3.     { "role": "user", "content": "What is the square root of 475695037565?" }
  4.   ],
  5.   "functions": [
  6.     {
  7.       "name": "squareRoot",
  8.       "description": "Returns the square root of a given number",
  9.       "parameters": {
  10.         "type": "object",
  11.         "properties": {
  12.           "x": { "type": "number", "description": "The number to take the square root of" }
  13.         },
  14.         "required": ["x"]
  15.       }
  16.     }
  17.   ]
  18. }
复制代码

  • 模型相应:发起工具调用
  1. {
  2.   "role": "assistant",
  3.   "content": null,
  4.   "function_call": {
  5.     "name": "squareRoot",
  6.     "arguments": { "x": 475695037565 }
  7.   }
  8. }
复制代码

  • 实行工具:


  • 应用步伐吸收到 function_call,调用相应的工具(如内部实现或外部 API),得到效果 689706.486532。

  • 请求 2:将工具实行效果反馈给模型
  1. {
  2.   "messages": [
  3.     { "role": "user", "content": "What is the square root of 475695037565?" },
  4.     {
  5.       "role": "assistant",
  6.       "function_call": {
  7.         "name": "squareRoot",
  8.         "arguments": { "x": 475695037565 }
  9.       }
  10.     },
  11.     { "role": "tool", "name": "squareRoot", "content": "689706.486532" }
  12.   ]
  13. }
复制代码

  • 模型生成最终答复:
  1. The square root of 475695037565 is 689706.486532.
复制代码
通过上述流程,模型能够借助外部工具得到精确效果,并在对话中精确呈现。

四、API 概览

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


  • name:工具/函数的唯一标识,如 getWeather、searchWeb。
  • description:对该工具功能的简要说明,资助模型判断何时调用。
  • parameters:JSON Schema 格式,定义函数参数的范例、名称、描述、是否必填等。
以 OpenAI Chat API 为例,请求体中的 functions 字段示比方下:
  1. "functions": [
  2.   {
  3.     "name": "getWeather",
  4.     "description": "Get the weather in a given location",
  5.     "parameters": {
  6.       "type": "object",
  7.       "properties": {
  8.         "location": {
  9.           "type": "string",
  10.           "description": "The city and state, e.g. San Francisco, CA"
  11.         },
  12.         "unit": {
  13.           "type": "string",
  14.           "enum": ["C", "F"],
  15.           "description": "Temperature unit, Celsius or Fahrenheit"
  16.         }
  17.       },
  18.       "required": ["location"]
  19.     }
  20.   }
  21. ]
复制代码
当模型生成答复时,假如它判断需要调用 getWeather,就会在相应中输出 function_call 而非直接文本。

五、Spring AI 中的 Function Calling

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

  1. @Configuration
  2. public class FunctionConfig {
  3.   
  4.   @Bean
  5.   @Description("Get the weather in location") // 函数描述,帮助模型选择调用
  6.   public Function<MockWeatherService.Request, MockWeatherService.Response> weatherFunction() {
  7.     return new MockWeatherService();
  8.   }
  9. }
复制代码


  • Bean 名称(默以为方法名 weatherFunction)即作为函数名传给模型。
  • @Description 注解为该函数提供说明,模型据此判断何时调用。
5.2 MockWeatherService 示例

下面是一个模仿天气服务的简单实现,硬编码返回温度:
  1. public class MockWeatherService implements Function<MockWeatherService.Request, MockWeatherService.Response> {
  2.   public enum Unit { C, F }
  3.   public record Request(String location, Unit unit) {}
  4.   public record Response(double temp, Unit unit) {}
  5.   @Override
  6.   public Response apply(Request request) {
  7.     // 简单返回固定温度,真实场景可调用第三方天气 API
  8.     return new Response(30.0, Unit.C);
  9.   }
  10. }
复制代码
5.3 注册并调用

在构建 ChatClient/ChatModel 时,将函数 Bean 名称传入:
  1. ChatClient chatClient = ChatClient.builder(dashScopeChatModel)
  2.     .defaultFunctions("weatherFunction") // 注册函数
  3.     .build();
  4. ChatResponse response = chatClient.prompt()
  5.     .user("What's the weather like in Boston?")
  6.     .call()
  7.     .chatResponse();
复制代码
当模型判断需要调用天气服务时,框架会自动将 function_call 转为对 weatherFunction Bean 的调用,并将效果封装回模型继承生成对话。

六、函数调用流程详解

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

  • 函数定义:在 Spring 应用上下文中,通过 @Bean 定义 Function<Req, Res>,并用 @Description 或 @JsonClassDescription 注解提供描述。
  • 函数注册:在创建 ChatClient/ChatModel 时,将 Bean 名称通过 defaultFunctions(...) 或 withFunctionCallbacks(...) 传入模型选项。
  • 构造 Prompt 请求:请求中包罗用户消息和 functions 列表(由框架自动根据已注册 Bean 生成)。
  • 模型相应 Function Call:模型在生成时,若判断需要调用某个工具,就输出 function_call 而非文本回复。
  • 实行函数:框架捕捉 function_call,将其中的 JSON 参数反序列化为对应 Bean 的 Request 对象,调用 Bean(即 Function.apply),得到 Response 对象。
  • 反馈效果给模型:将函数调用效果作为新的消息(role="tool")附加到消息列表中,再次调用模型,让它基于工具返回值生成最终文本回复。
整个过程对开发者透明,大大简化了与模型的交互和工具调用逻辑。

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

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

  1. @Service
  2. public class MockOrderService {
  3.     public Response getOrder(Request request) {
  4.         String productName = "尤尼克斯羽毛球拍";
  5.         return new Response(
  6.             String.format("%s 的订单编号为 %s, 购买的商品为: %s",
  7.                           request.userId, request.orderId, productName)
  8.         );
  9.     }
  10.     @JsonInclude(JsonInclude.Include.NON_NULL)
  11.     public record Request(
  12.         @JsonProperty(required = true, value = "orderId")
  13.         @JsonPropertyDescription("订单编号, 比如1001***")
  14.         String orderId,
  15.         @JsonProperty(required = true, value = "userId")
  16.         @JsonPropertyDescription("用户编号, 比如2001***")
  17.         String userId
  18.     ) {}
  19.     public record Response(String description) {}
  20. }
复制代码
7.2 注册为 Function Call

  1. @Configuration
  2. public class FunctionCallConfiguration {
  3.   @Bean
  4.   @Description("根据用户编号和订单编号查询订单信息")
  5.   public Function<MockOrderService.Request, MockOrderService.Response> getOrderFunction(
  6.       MockOrderService mockOrderService) {
  7.     // 将 Service 方法引用作为 Function Bean
  8.     return mockOrderService::getOrder;
  9.   }
  10. }
复制代码
7.3 调用示例

  1. ChatClient chatClient = ChatClient.builder(dashScopeChatModel)
  2.     .defaultFunctions("getOrderFunction")
  3.     .build();
  4. ChatResponse response = chatClient.prompt()
  5.     .user("帮我查询订单, 用户编号为1001, 订单编号为2001")
  6.     .call()
  7.     .chatResponse();
  8. String content = response.getResult().getOutput().getContent();
  9. System.out.println("模型回复: " + content);
复制代码
如许,模型在收到查询订单的请求时,就会自动触发对 getOrderFunction 的调用,并将效果融入最终回复。

八、为 Prompt 动态指定函数

在某些场景下,我们希望根据上下文动态选择可用函数,而非在应用启动时一次性注册所有函数。Spring AI 支持通过 FunctionCallbackWrapper 在 Prompt 级别动态注入函数。
  1. // 构造 FunctionCallbackWrapper 列表
  2. List<FunctionCallbackWrapper<?, ?>> callbacks = List.of(
  3.   new FunctionCallbackWrapper<>(
  4.     "CurrentWeather",               // name
  5.     "Get the weather in location",  // description
  6.     new MockWeatherService()        // function 实现
  7.   )
  8. );
  9. var promptOptions = OpenAiChatOptions.builder()
  10.     .withFunctionCallbacks(callbacks)
  11.     .build();
  12. ChatResponse response = chatModel.call(
  13.   new Prompt(List.of(new UserMessage("What's the weather like in Tokyo?")),
  14.              promptOptions)
  15. );
复制代码
如许,每次调用时都可机动传入差别的函数聚集,无需预先在 Spring 容器中定义 Bean。

九、最佳实践与注意事项


  • 函数描述要精准:利用 @Description 或 @JsonClassDescription 为函数和参数添加清晰说明,资助模型精确选择。
  • 参数模式要完整:在 JSON Schema 中为每个参数提供 type、description,并对必填字段利用 required,避免模型生成错误调用。
  • 公道拆分工具:将功能独立的操作拆分为差别工具,避免“万能”函数导致模型调用紊乱。
  • 调用效果校验:在将工具实行效果反馈给模型前,最好举行必要的校验或清洗,防止异常数据影响后续生成。
  • 安全与权限控制:对敏感操作(如发送邮件、数据库写入)要做好鉴权与审计,避免模型被滥用。
  • 限流与降级:对外部 API 调用要做好限流,碰到失败时可筹划降级方案,让模型给出公道的错误提示。

十、总结

“工具调用(Function Calling)”为 LLM 提供了与外部系统、API、代码逻辑无缝对接的能力,大大拓展了模型在现实应用中的边界。通过在请求中声明工具、让模型在合适时机发起调用、由应用步伐实行并将效果反馈给模型,整个流程如同一次透明的 RPC 调用,让模型既保留了强盛的自然语言生成能力,又具备了实行外部操作的大概。
在 Java/Spring 生态中,借助 Spring AI、FunctionCallbackWrapper 等框架,我们只需将函数以 Bean 情势定义,并通过注解或配置举行注册,便可轻松实现 Function Calling。无论是简单的数学运算、天气查询,还是复杂的业务系统交互,都能通过这种方式优雅地集成到对话流程中。
希望本文能资助读者全面了解并掌握 Function Calling 的原理与实践,快速在自己的项目中落地这一功能,打造更智能、更实用的 AI 应用。未来,随着 LLM 与工具生态的不停完善,我们将见证更加丰富、多样的智能化场景落地,值得连续关注与探索。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

泉缘泉

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表