楼层: 首页/ 软件技术/ Spring AI/ Function Calling:让 AI 调你的 Java 方法
五

Function Calling:让 AI 调你的 Java 方法

Tool Calling

大模型的知识有截止日期,也不知道你数据库里有啥。用户问"我订单 12345 物流到哪了?",AI 自己答不上来。Function Calling(工具调用)就是:你把"查物流"这个 Java 方法注册给 AI,AI 自己判断什么时候该调它、传什么参数,调完再把结果组织成人话回答。

论一次工具调用的完整流程

第一步:你把 Java 方法(比如 trackLogistics(orderId))连同描述一起注册给 AI。

第二步:用户提问,AI 听完判断:"这问题我得查物流",于是返回一个"我要调用 trackLogistics,参数 orderId=12345"的指令。

第三步:Spring AI 自动执行你写的 Java 方法,拿到真实物流数据,再把结果喂回给 AI。

第四步:AI 根据真实数据组织成自然语言回答用户。整个过程你只写了那个 Java 方法,AI 决定何时调、调哪个。

用 @Tool 注解把 Java 方法暴露给 AI

import org.springframework.ai.tool.annotation.Tool; import org.springframework.stereotype.Component; @Component public class LogisticsTools { // @Tool 注解:这是一个 AI 可以调用的工具 @Tool(description = "根据订单号查询物流状态,输入订单号,返回物流节点") public String trackLogistics(@ToolParam(description = "订单号,例如 12345") String orderId) { // 这里真正查数据库/调物流接口 return "订单 " + orderId + " 已到达南京转运中心,预计明天送达"; } @Tool(description = "查询某个城市的天气") public String weather(@ToolParam(description = "城市名") String city) { return city + " 今天多云,25 度"; } }

让 AI 在对话时自动用这些工具

@GetMapping("/ask") public String ask(@RequestParam String q) { return chatClient.prompt(q) .tools(logisticsTools) // 把工具包传进去 .call() .content(); } // 用户问:"我订单 12345 到哪了?" // AI 自己决定调 trackLogistics("12345"),再回答用户 // 用户问:"南京天气咋样?" // AI 自己决定调 weather("南京")

多个工具,AI 自己挑哪个

注册了多个工具后,AI 会根据用户问题自己决定调哪个。你不用写 if-else 判断"用户问天气就调天气工具"——AI 看用户的话和工具描述,自己选。这就是 Function Calling 最爽的地方:工具越多,AI 越能干,你业务代码不用改。

注册多个工具,AI 自动选择

// 用户问"我订单12345到哪了,顺便报下南京天气" // AI 会先调 trackLogistics("12345"),再调 weather("南京") // 然后把两个结果拼成一段回答 @GetMapping("/ask") public String ask(@RequestParam String q) { return chatClient.prompt(q) .tools(logisticsTools, weatherTools) // 多个工具一起传 .call() .content(); }

工具描述怎么写,AI 才用得对

@Tool 的 description 不是写给你看的,是写给 AI 看的"招聘启事"——AI 靠它判断什么时候该请这个工具干活。好描述的标准:说清"能干啥、啥时候用、啥时候别用、参数啥意思"。

差的描述好的描述
"这是个工具""根据订单号查询物流状态,用户问『我的货到哪了』时用"
"查一下""入参是订单号字符串,格式如 12345;返回最新物流节点文字"
"天气""查询指定城市今日天气,输入中文城市名,如『南京』"

工具里报错了怎么办

你写的 Java 方法可能抛异常(数据库挂了、订单号不存在)。别让异常直接炸到模型,捕获后返回一段友好的错误描述,让 AI 把这个错误转告用户。比如"订单号不存在,请检查",而不是一段 Java 栈信息。

工具里优雅处理错误

@Tool(description = "根据订单号查询物流") public String trackLogistics(@ToolParam(description = "订单号") String orderId) { try { return logisticsService.query(orderId); } catch (OrderNotFoundException e) { // 不抛异常,返回友好描述,AI 会转告用户 return "没找到订单 " + orderId + ",请核对订单号"; } }
工具描述写得烂,AI 就用错

@Tool 的 description 是给 AI 看的"说明书"。你要是写"这是个工具",AI 根本不知道什么时候该用、参数怎么填。描述要写清楚"这个工具干嘛用、什么场景该调、参数啥意思"。参数绑定错了(比如把用户昵称当订单号传进去),查出来就是错的,AI 还一本正经地胡说。

完整案例:天气 + 计算器 + 查库三个工具,AI 自动挑

注册三个工具:查天气、算数、查数据库。用户一句话混着问,看 AI 怎么自己决定调哪个、按什么顺序调。

三个 @Tool 方法(天气 / 计算器 / 查库存)

@Component public class BizTools { @Tool(description = "查询城市今天的天气,入参中文城市名") public String weather(@ToolParam(description = "城市,如:南京") String city) { return city + " 今天多云 25℃,微风。"; } @Tool(description = "计算一个数学表达式,入参是如 '(3+5)*2' 的字符串") public double calculator(@ToolParam(description = "数学表达式") String expr) { // 真实项目用表达式引擎,这里示意 return 16.0; } @Tool(description = "查询某商品剩余库存,入参商品名") public int stock(@ToolParam(description = "商品名,如:键盘") String product) { if (!"键盘".equals(product)) throw new RuntimeException("没有这个商品"); return 42; } }

注册全部工具,让 AI 自己挑

@GetMapping("/ask") public String ask(@RequestParam String q) { return chatClient.prompt(q) .tools(bizTools) // 三个工具一起注册 .call() .content(); }

一次混合提问,完整对话过程

用户:"南京天气咋样?再帮我算下 (3+5)*2 等于几,键盘还有多少库存?" # AI 内部决策链(打开 SimpleLoggerAdvisor 能看到): # 1. 调 weather("南京") → "南京 今天多云 25℃,微风。" # 2. 调 calculator("(3+5)*2") → 16.0 # 3. 调 stock("键盘") → 42 # AI 把三个结果组织成人话返回: # "南京今天多云,25 度很舒服;(3+5)*2 等于 16; # 键盘目前库存还有 42 件,要下单趁早~" # 故意问不存在的商品,看错误兜底: 用户:"鼠标有货吗?" # stock 抛 RuntimeException("没有这个商品") # AI 拿到错误信息,转告用户:"抱歉,目前系统里没查到鼠标这个商品。"
工具报错别让异常炸到模型

上面 stock() 抛了异常,Spring AI 会把异常信息作为工具结果返回给模型,模型据此跟用户解释。但别把数据库连接串、SQL 全文这类敏感信息抛出去——模型可能原样念给用户。工具里要 catch 住,只返回友好的业务描述。

本章面试题

面试快答 · Function Calling

Q1. Function Calling 的完整流程?谁决定调哪个工具?

参考答案

① 你把 @Tool 方法+描述注册给模型;② 用户提问,模型判断需要工具,返回"要调 X、参数 Y"的指令;③ 框架执行你的 Java 方法,把结果喂回模型;④ 模型基于真实结果组织回答。何时调、调哪个由模型根据工具描述自己决定,不是你写 if-else。

Q2. RAG 和 Function Calling 怎么选?

参考答案

知识在动态业务系统里(订单、库存、实时天气)→ Function Calling;知识在静态文档里(制度、手册、FAQ)→ RAG。分不清就想:这数据会变吗?会变 → 调接口;不变 → 查文档。

Q3. @Tool 的 description 为什么重要?写烂了会怎样?

参考答案

description 是写给模型看的"招聘启事",模型靠它判断何时调用、怎么填参。写得烂("这是个工具")模型不知道何时用、乱填参数,结果就是一本正经地查错数据。要写清"干嘛用、何时用、参数啥意思"。