Tool-Calling原理-大模型如何决定调用你的Java方法

12-Tool-Calling原理-大模型如何决定调用你的Java方法

很多刚接触 Agent 的 Java 同学会有个误会:“大模型不是只能聊天吗?它怎么可能去调我的 saveMemory 方法、去改我的数据库?”

答案就藏在一个叫 Tool Calling(工具调用) 的机制里。它不是让模型直接"执行"你的 Java 代码,而是让模型先决定"我要调哪个函数、参数填什么",然后由 LangChain4j 框架真的去跑你的方法,再把结果喂回去。你的方法永远跑在你的 JVM 里,模型只是个"指手画脚的指挥官"。

这篇文章把工具调用的完整回合拆成 7 步讲透,然后用 AI 伙伴里一句最日常的"明天八点提醒我吃药",串起那 13 个工具到底谁会被叫醒。最后聊聊 @Tool 描述文本有多要命,以及并行/多轮调用这些进阶玩法。


一、一次工具调用的完整 7 步回合

下面这张图是 AI 伙伴里 Agent 调用任意工具的通用流程。请把它刻进脑子里,后面所有分析都基于它。

┌──────────────────────────────────────────────────────────────────┐
│ 1. 注册:6 个 @Component 工具类被注入 AiServices.builder()         │
│ 2. 转 Schema:LangChain4j 把每个 @Tool 方法抽成 JSON Schema        │
│        (名字、描述、参数名、参数类型、是否必填)                    │
└───────────────────────────┬──────────────────────────────────────┘
                            │  (把这些 schema 随请求发给模型)
                            ▼
┌──────────────────────────────────────────────────────────────────┐
│ 3. 模型决策:LLM 读完 System/用户消息 + 工具列表,决定"调哪个"      │
│ 4. 参数填充:模型吐出 tool_call,内含方法名 + 填好的参数 JSON        │
└───────────────────────────┬──────────────────────────────────────┘
                            │
                            ▼
┌──────────────────────────────────────────────────────────────────┐
│ 5. Java 执行:LangChain4j 拿到 tool_call,反射调用你的真实方法       │
│        (如 ReminderTool.createReminder → ReminderService → 落库)  │
└───────────────────────────┬──────────────────────────────────────┘
                            │  (方法返回值 = 工具结果)
                            ▼
┌──────────────────────────────────────────────────────────────────┐
│ 6. 结果回灌:工具结果作为 assistant 的 tool 消息,再次发回模型       │
│ 7. 最终回答:模型结合工具结果,生成给用户的自然语言回复             │
└──────────────────────────────────────────────────────────────────┘

我逐条展开:

第 1 步 注册。 AI 伙伴在 LangChain4jConfig 里用 AiServices.builder(CompanionAssistant.class).tools(memoryTool, reminderTool, ...) 把 6 个工具类一次性注册进 Agent。这 6 个类都是 @Component,由 Spring 管理,方法上标了 @Tool

第 2 步 转 Schema。 框架会扫描每个 @Tool 方法,生成一段机器可读的"函数说明书"(JSON Schema),描述:函数叫什么、干啥用(来自 @Tool 的描述文本)、有哪些参数、每个参数什么类型、必填还是可选。模型读的是这份说明书,而不是你的 Java 源码。

第 3~4 步 模型决策 + 参数填充。 这是大模型的活。它根据 System 消息(人设 + 记忆)、用户这句话、以及工具说明书,判断"现在该不该调工具、调哪个、参数怎么填"。如果决定调,它会返回一个 tool_call 结构,里面是方法名和填好的参数(比如 {"title":"吃药","type":"medication","remindTimeStr":"明天8点"})。

第 5 步 Java 执行。 框架接到 tool_call,用反射真正调用你的方法。你的方法内部再去调 service、落库、发 MQTT——所有副作用都在这一步发生,完全在本地 JVM,模型碰不到你的数据库。

第 6~7 步 结果回灌 + 最终回答。 方法返回的那个 String(比如"提醒已创建,ID=12")会被包成一条工具消息,再次发给模型。模型看到"哦,提醒建好了",于是用自然语言回复用户:“好的,已经帮你记下明天早上8点吃药~”


二、用"明天八点提醒我吃药"串起 13 个工具

现在上主菜。假设老人对着 AI 伙伴说了一句:

“明天八点提醒我吃药。”

我们跟着第 3~4 步走一遍,看看那 13 个 @Tool 方法里,到底谁会被叫醒。

先亮结论: 这句话最直接命中的是 ReminderTool.createReminder,其余 12 个工具在这一句里基本不会被调用。但这中间有个有意思的细节。

模型的内心戏(示意还原):

  1. 用户说"明天八点提醒我吃药"。意图很明确:建一个提醒。
  2. 模型扫一眼 13 个工具说明书,发现 ReminderTool.createReminder 的描述是"为用户创建一条提醒(吃药/日程/喝水/生日/自定义)"——完美匹配。
  3. 参数怎么填?
    • title → “吃药”(或更具体的"吃降压药")
    • content → 可空,或填"吃药"
    • remindTimeStr → “明天8点”(自然语言,交给 NaturalTimeParser 去算)
    • type → “medication”(吃药对应 medication 类型)
    • repeatCron → 空(一次性提醒)
    • deviceCode → 空(没指定哪个设备播报,调度器会自己找在线设备)
    • userId → 由框架从 @ToolMemoryId 自动注入,模型不用管

为什么不是别的工具?

  • TimeTool.getCurrentTime:理论上模型可能想先确认"今天周几"以便定位"明天"。但 NaturalTimeParser 在计算"明天8点"时是直接基于 LocalDate.now() 的,不需要模型先问时间,所以多数情况下模型会跳过这一步,直接调 createReminder
  • MemoryTool.*:这句话没有透露稳定偏好或事实,不需要存记忆。
  • EmotionTool.*:没涉及情绪。
  • HealthTool.*:虽然"吃药"沾健康边,但用户说的是"提醒吃药"不是"记录已服药",语义上是提醒不是记录,所以不调 recordHealth
  • DeviceTool.*:没要求现在就播报或控制设备。
  • ReminderTool 的另外两个(listReminders / cancelReminder):用户是"创建"不是"查询/取消",不匹配。

执行阶段(第 5 步)真发生了什么?

// 项目源码:ReminderTool.createReminder(节选)
Long reminderId = reminderService.createFromAgent(
        userId, title, content, remindTimeStr, type, repeatCron, deviceCode);
return "提醒已创建,ID=" + reminderId + ":" + title + "(" + remindTimeStr + ")";

createFromAgent 内部会用 NaturalTimeParser.parse("明天8点") 把自然语言变成明早 8 点的 LocalDateTime,落进 t_reminder 表,返回一个 ID。这个 ID 连同"提醒已创建"会作为工具结果回灌(第 6 步),模型据此生成最终那句温暖的回复。

一句话总结这个案例:一句话往往只唤醒 1 个工具,工具选择高度依赖"用户意图 × 工具描述"的匹配度。13 个工具是 Agent 的"技能栏",但大多数时候它只点其中一个。


三、@Tool 描述文本为什么直接决定调用准确率

回到第 2 步转成的那份"函数说明书"。模型选不选这个工具、参数填得对不对,九成取决于 @Tool 括号里那段描述文字

看 AI 伙伴里的真实写法:

// 项目源码:ReminderTool.createReminder
@Tool("为用户创建一条提醒(吃药/日程/喝水/生日/自定义),返回创建结果和提醒 ID")
public String createReminder(...) { ... }

// 项目源码:MemoryTool.saveMemory
@Tool("保存一条关于用户的长期记忆,例如:用户喜欢喝绿茶、用户有一只叫团团的猫、用户本周三要去医院复查。返回保存结果")
public String saveMemory(...) { ... }

注意 saveMemory 的描述里举了三个具体例子:喜欢绿茶、有只叫团团的猫、周三复查。这不是凑字数,而是给模型"打样"——告诉它"什么样的输入算一条记忆"。例子越贴近真实场景,模型越容易在用户说出类似话时,正确联想到该调 saveMemory 而不是别的。

反过来说,如果描述写得太含糊,比如只写"保存数据",模型就会犯迷糊:这到底是存记忆、存健康、还是存提醒?调用准确率直接跳水。所以给工具写描述,是 Agent 工程里性价比最高的一件事:把"这个工具干什么、什么时候用、参数是啥意思"用大白话写清楚,最好带 1~3 个真实例子。

参数名和参数注释同样重要。比如 remindTimeStr 的 JavaDoc 写着"触发时间,自然语言,例如’明天早上8点’‘每天晚上9点’",模型读了就知道该传自然语言而不是 ISO 时间串——因为后面有 NaturalTimeParser 兜底解析。


四、并行工具调用:一句话能同时点两个技能

刚才的"吃药"案例只唤醒了 1 个工具,但有些话会同时命中多个。比如用户说:

“记住我爱吃甜食,顺便看看我最近血压怎么样。”

这句话里有两个独立意图:存一条偏好记忆 + 查健康趋势。现代大模型(DeepSeek、通义等 OpenAI 兼容模型都支持)可以在同一个回合里并行返回多个 tool_call

tool_call #1 → MemoryTool.saveMemory(content="爱吃甜食", type="preference", ...)
tool_call #2 → HealthTool.getHealthTrend(type="blood_pressure", ...)

框架会并行(或快速串行)执行这两个方法,把两个结果一起回灌,模型再综合生成回复。这对用户体验很关键:一句复合指令不用拆成两轮对话。

AI 伙伴的 6 个工具之间没有强制的先后依赖,因此天然支持这种并行。但要注意:并行执行时,如果两个工具都去写库且涉及同一行数据,才可能撞车——在陪伴场景里各写各的表,冲突概率极低。


五、多轮工具调用:工具调完还不够,再调一次

还有一种更"绕"的情况:模型调了一个工具,看了结果,发现信息不够,于是主动再调一次甚至调另一个工具。这叫多轮(multi-step)工具调用。

举个例子(示意):

用户:“帮我把明天的吃药提醒取消。”

模型可能这样走:

  1. 第一回合:先调 ReminderTool.listReminders(userId),看看用户有哪些待办提醒。
  2. 看到列表里有个"吃药"提醒 ID=12,但用户没说取消哪个。模型可能直接选 ID=12,或反问确认。
  3. 第二回合:调 ReminderTool.cancelReminder(reminderId=12, userId),完成取消。

这种"先查再改"的两段式,就是多轮工具调用的典型形态。框架层面,LangChain4j 会在"工具结果回灌 → 模型再决策"这一步循环,直到模型认为信息齐全、不再需要调工具,才输出最终自然语言回答。

需要提醒的是:AI 伙伴目前只有 ReminderTool.createReminder 一个方法有 try-catch。这意味着如果某个工具方法在 Java 执行阶段(第 5 步)抛了异常,而又没有 try-catch 兜住,这次 Agent 调用就可能整体失败,被 ChatService 捕获后返回"AI 服务暂时不可用"。这也是第 14 篇要专门聊的"统一异常处理"话题。


六、把原理收口:工具调用不是魔法,是"说明书写得好"

回顾一下,Tool Calling 的本质就三句话:

  1. 模型不碰你的代码,它只吐出"调哪个函数、参数填啥"的 JSON;
  2. 框架拿着 JSON 真的去调你的 Java 方法,副作用(落库、发消息)全在你的 JVM 里发生;
  3. 方法返回值会被回灌给模型,模型再组织成人类能懂的话。

所以,想让 AI 伙伴更聪明,功夫往往不在模型本身,而在两处:@Tool 描述写清楚(让模型选得准),以及把工具方法写稳健(让执行不翻车)。下一讲我们把那 13 个工具逐个摊开,看看 AI 伙伴到底有哪些"技能栏"。

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值