Skip to content

第 3 章:工具调用,让 Agent 从会说话变成会干活

上一章的小助手只会回答问题,这一章给它一双手。工具调用的核心不是“Java 方法可以被调用”这么简单,而是:模型先判断要不要用工具,框架再执行工具,最后模型把工具结果整理成用户能看懂的话。

本章边界

本章讲三件事:工具怎么声明,模型怎么选择工具,框架怎么执行工具。复杂工具平台、Sandbox 深入实现和生产级权限系统先不展开,但危险工具的边界会先讲清楚。

1. 生活类比:模型是前台,工具是后厨

用户问:“今天杭州天气怎样,适合跑步吗?”

前台服务员不会自己跑到窗外测天气。他会判断:这事要问天气系统。后厨拿到单子,查数据,回传结果;前台再把“温度、风力、是否适合跑步”组织成一句人话。

在 Agent 里也是三段式:

text
模型判断 -> 框架执行工具 -> 模型整理答案

你一定要分清这三件事。模型不是工具执行器,它只是提出 tool call;真正执行动作的是框架侧。

2. 最小工具示例

先做一个天气工具,简单到有点朴素,但足够说明问题:

java
record WeatherRequest(String city) {}

class WeatherTool implements Function<WeatherRequest, String> {
    @Override
    public String apply(WeatherRequest request) {
        return request.city() + ":晴,22 度,适合慢跑。";
    }
}

@Bean
ToolCallback weatherTool() {
    return FunctionToolCallback
            .builder("query_weather", new WeatherTool())
            .description("根据城市名查询当前天气和运动建议")
            .inputType(WeatherRequest.class)
            .build();
}

@Bean
ReactAgent weatherAgent(ChatModel chatModel, ToolCallback weatherTool) {
    return ReactAgent.builder()
            .name("weather_agent")
            .model(chatModel)
            .instruction("你是运动建议助手。需要实时天气时,优先使用工具。")
            .tools(weatherTool)
            .build();
}

这里最重要的不是 Java 语法,而是工具说明:

配置作用
工具名 query_weather给模型看的工具标识,名字越清楚越好
description(...)告诉模型什么时候该用这个工具
inputType(...)告诉模型工具需要什么参数
.tools(weatherTool)把工具挂到 Agent 的工具箱里

工具说明写得含糊,模型就像看到一个写着“杂物间”的门牌,很难判断该不该进去。

3. 工具调用的流程图

换成工程语言,就是:

  1. Agent 把工具定义交给模型。
  2. 模型判断是否需要工具,并生成 tool call。
  3. 框架读取 tool call,找到对应工具。
  4. 工具执行后返回结果。
  5. 结果作为消息回到模型,模型组织最终回答。

这也是为什么工具返回值不一定原样展示给用户。工具只负责交付原材料,最终上桌前还要经过模型整理。

4. 危险工具:别把电锯放进儿童玩具箱

天气工具很安全,因为它只返回字符串。但 Shell、Python、文件读取这类工具就不一样了。它们不是“小助手的手”,它们是电锯、钥匙和仓库门。

生产里至少要有这张安全表:

风险推荐做法
工具描述不清,模型误调用工具名、描述、入参 schema 写清楚;高风险工具加人工审批
工具超时设置超时时间;超时后返回可解释错误
Shell/Python 越权限定工作目录、禁用危险命令、限制网络和文件权限
文件读取泄露只允许读白名单目录,不允许模型自由读任意路径
工具失败返回降级回答,并记录 tool name、input、error、threadId
审计缺失记录谁触发、触发了哪个工具、参数是什么、结果是否成功

你可以把工具调用当成一次“让模型开工单”。模型可以申请,但系统必须审批、执行、记录和兜底。

5. 工程里怎么设计一个好工具

一个好工具像一个好接口:名字清楚、输入稳定、失败可解释。

设计点好写法坏写法
工具名query_weathersearch_ordertool1doSomething
描述“根据城市查询当前天气和运动建议”“查询信息”
入参小而明确的 record一个大 Map 什么都塞
返回可读、可解析、错误清晰成功失败都返回一坨字符串
权限白名单、超时、审计模型想干什么就干什么

先从一个工具开始,跑通、观察、调描述,再加第二个。工具不是越多越强,工具越多,模型选错的机会也越多。

6. 本章小结

工具调用的本质是:

text
模型负责判断,框架负责执行,工程负责边界。

从这一章开始,Agent 不再只是聊天对象,而是一个能调用外部能力的执行单元。下一章我们给它加记忆,让它不要每次都像第一次见你。

7. 练习题

  1. query_weather 增加字段 activity,让工具能根据城市和运动类型返回建议。
  2. 设计一个 query_order_status 工具,写出工具名、description、入参 record 和失败返回格式。
  3. 给 Shell 工具设计一张权限白名单表:允许哪些目录、禁止哪些命令、超时时间多少、失败日志记录哪些字段。
课后源码索引:想验证实现时再打开
你想验证的结论源码锚点
官方工具调用示例examples/documentation/src/main/java/com/alibaba/cloud/ai/examples/documentation/framework/tutorials/ToolsExample.java,类 ToolsExample,方法 programmaticToolSpecification()toolsInReactAgent()methodToolsExample()combinedToolProvisionExample()
Builder 如何收集工具spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/DefaultBuilder.java,类 DefaultBuilder,方法 gatherLocalTools()build()
模型节点如何把工具交给模型spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentLlmNode.java,类 AgentLlmNode,方法 apply(...)filterToolCallbacks(...)buildChatClientRequestSpec(...)
ReactAgent 如何在模型和工具间循环spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/ReactAgent.java,类 ReactAgent,方法 makeModelToTools(...)makeToolsToModelEdge(...)
工具节点如何真正执行工具spring-ai-alibaba-agent-framework/src/main/java/com/alibaba/cloud/ai/graph/agent/node/AgentToolNode.java,类 AgentToolNode,方法 apply(...)executeToolCallsSequential(...)executeToolCallsParallel(...)
Sandbox 工具边界spring-ai-alibaba-sandbox/src/main/java/com/alibaba/cloud/ai/sandbox/SandboxAwareTool.java,类 SandboxAwareTool,方法 getSandbox()setSandbox(...)getSandboxClass()

Built with VitePress. Deployed on Cloudflare Pages.