图片

让Java开发者像写Spring Boot一样开发AI应用——第二阶段第二篇

写在前面

第七天,我们学会了如何给Agent装上第一个工具——让它从“只会说”变成了“会做一件事”。

但现实世界的问题,从来不是靠一个工具就能解决的。

一个旅行规划Agent,需要查天气、搜酒店、找景点;一个智能客服Agent,需要查订单、退换货、转人工;一个数据分析Agent,需要查数据库、算指标、出图表。

如果只给Agent一个工具,它就像一个只有一把锤子的工匠——看什么都像钉子。

今天的主题,就是给Agent配齐“全套工具箱”——多个工具如何注册、如何协同、如何让Agent在它们之间自动做出正确选择。

一、多工具注册:让Agent拥有“全套工具箱”

为什么需要多工具?

第七天的Agent只有一个工具,就像一个只有一把锤子的工匠——什么都是钉子。今天的Agent有多个工具,就像拥有全套工具箱的工匠——锯子、扳手、螺丝刀各司其职。

在真实的业务场景中,一个Agent往往需要多种能力:

| 场景

|

需要的工具

旅行规划

|

天气查询、酒店搜索、景点推荐、交通查询

| |

智能客服

|

订单查询、退换货处理、人工转接、知识库检索

| |

数据分析

|

数据库查询、指标计算、图表生成、报告撰写

|

两种注册方式

方式一:批量注册(推荐)

<span leaf=""><span>// 使用 methodTools() 一次注册多个工具对象</span></span>

方式二:混合注册

<span leaf=""><span>// 同时使用 @Tool 注解和 FunctionToolCallback</span></span>

💡 两种注册方式可以混合使用——不需要把所有工具统一成一种风格,可以根据实际情况灵活选择。

二、Agent如何自动选择工具?

当Agent拥有多个工具时,模型需要决定“调用哪个工具”。这个决策过程由模型自动完成,核心依据是工具的描述(description)。

决策流程

<span leaf="">用户输入:”帮我规划一下杭州三日游”</span>

影响模型选择的关键因素

| 因素

|

权重

|

说明

工具描述

★★★★★

|

模型通过描述判断工具是否匹配当前任务

| | 参数名称与描述 |

★★★★

|

模型通过参数判断能否从用户问题中提取正确参数

| | 工具名称 |

★★★

|

语义清晰的名称帮助模型快速识别

|

⚠️ 工具描述是影响模型选择准确性的最关键因素。描述写得好,模型选得准;描述写得差,模型可能选错甚至不选。

三、工具依赖链:流水线上的接力赛

什么是工具依赖链?

在实际业务中,工具之间往往存在依赖关系——一个工具的输出,是另一个工具的输入。

典型场景:旅行规划

<span leaf="">用户:”帮我规划杭州三日游”</span>

💡 工具依赖链就像工厂的流水线——上一道工序的产品,是下一道工序的原材料。

Spring AI Alibaba中的三种实现方式

方式一:通过State传递(ReAct Agent自动处理)

ReAct Agent的State机制天然支持工具间的数据传递。当模型连续调用多个工具时,前一个工具的输出会自动存入State,后一个工具可以从State中读取。开发者不需要手动编写粘合代码。

方式二:使用占位符传递(多Agent场景)

<span leaf=""><span>// 第一个Agent:获取位置</span></span>

方式三:在工具内部组合调用

如果依赖关系比较复杂,也可以在工具方法内部直接调用其他工具。

依赖链设计原则

| 原则

|

说明

单一职责

每个工具只做一件事

| | 接口清晰 |

输入输出类型明确

| | 依赖方向 |

单向依赖,避免循环

| | 可测试性 |

每个工具可以独立测试

| | 容错设计 |

任何一环失败都要有处理

|

四、异常处理与降级:生产环境的必修课

在生产环境中,工具调用失败是常态而不是异常。设计时就要把“失败”当作一种正常情况来考虑。

常见的工具调用异常

| 异常类型

|

原因

网络超时

外部API响应慢或不可达

| | 参数错误 |

模型提取的参数格式不正确

| | 服务不可用 |

依赖的第三方服务宕机

| | 数据异常 |

返回的数据格式不符合预期

|

策略一:工具内部捕获异常

<span leaf=""><span>@Tool</span>(description = ”查询城市天气”)</span>

策略二:使用ModelRetryInterceptor自动重试

Spring AI Alibaba提供了ModelRetryInterceptor,可以自动重试失败的模型调用:

<span leaf=""><span>ModelRetryInterceptor</span>&nbsp;<span>retryInterceptor</span>&nbsp;<span>=</span>&nbsp;ModelRetryInterceptor.builder()</span>

重试策略:第1次重试等待1000ms,第2次等待2000ms,第3次等待4000ms。

策略三:降级方案

| 降级方案

|

说明

返回默认值

返回缓存数据或合理的默认结果

| | 跳过该工具 |

让模型用自己的知识回答

| | 使用备用数据源 |

切换到备用的第三方API

|

五、工具描述优化:让模型“看得懂”

为什么工具描述如此重要?

模型的工具选择完全依赖于工具描述。描述是模型与工具之间的唯一“桥梁”。

💡 工具描述就像招聘广告——写得好,合适的人(模型)会主动投简历(调用工具);写得差,要么没人来,要么来一堆不合适的人。

优秀描述的四个规范

规范一:明确“何时调用”

<span leaf=""><span>// ❌ 差:太模糊</span></span>

规范二:详细说明参数

<span leaf=""><span>// ❌ 差</span></span>

规范三:说明返回值格式

<span leaf=""><span>@Tool(</span><span><span>description = ”””</span></span></span>

规范四:区分相似工具

<span leaf=""><span>// 工具A:查询实时天气</span></span>

工具数量控制的建议

当一个Agent注册的工具太多时:

  • 工具越多,提示词越臃肿,Token成本线性增长

  • 工具越多,模型选择越不稳定

  • 工具越多,团队协作越脆弱

建议:单个Agent的工具数量控制在5个以内。

如果确实需要很多工具,可以考虑:

  1. 使用RoutingAgent按领域分组分发

  2. 使用Skill机制按需发现和加载工具

  3. 使用Tool Search动态匹配工具

六、实战:旅行规划工具集

系统设计

构建一个“旅行规划工具集”,包含三个协同工作的工具:

| 工具

|

职责

|

依赖

getLocation

根据城市名获取经纬度

|

| | getWeather |

根据经纬度查询天气

|

依赖getLocation的输出

| | recommendAttractions |

根据城市和天气推荐景点

|

依赖前两个工具的输出

|

核心代码

<span leaf=""><span>@Component</span></span>

构建Agent

<span leaf=""><span>@Configuration</span></span>

运行效果

<span leaf=""><span>[用户]</span>&nbsp;帮我规划杭州三日游</span>

七、本日核心收获

  1. 多工具让Agent从“专才”变“通才” ——一个Agent可以拥有多种能力

  2. 模型自动选择工具——核心依据是工具的描述(description),描述写得越好,模型选得越准

  3. 工具依赖链——一个工具的输出可以作为另一个工具的输入,State机制自动处理数据传递

  4. 异常处理是生产环境的必修课——工具内部捕获异常、ModelRetryInterceptor自动重试、降级方案三管齐下

  5. 工具数量控制在5个以内——太多会导致模型选择不稳定、Token成本增加

📌 本文是第二阶段“核心能力实战篇”的第二篇。第七天我们学会了给Agent装第一个工具,今天学会了装“全套工具箱”——多个工具如何注册、如何协同、如何让Agent自动选择。下一篇,我们将进入RAG(检索增强生成),让Agent能够访问私有知识库,回答专属问题。

有任何问题,欢迎在评论区留言交流!


作者:Java老兵搞AI,专注Java生态下的AI应用开发

如果觉得有用,点个「在看」支持一下吧,下期见!