1232 字
约 4 分钟
4
Spring AI Function Call:让大模型真正调用外部工具

Spring AI Function Call:让大模型真正调用外部工具

大模型本身只能根据已有上下文生成内容。

如果用户问:

帮我查一下今天的天气

模型自己不会访问天气接口。

Function Call 解决的就是:

让模型决定调用什么工具,由应用程序真正执行工具。


1. Function Call 是怎么工作的?

完整流程只有四步:

用户问题 + 工具列表
        ↓
       LLM
        ↓
toolName + arguments
        ↓
   应用执行工具
        ↓
    Tool Result
        ↓
       LLM
        ↓
    最终回答

比如模型可能返回:

{
  "toolName": "getWeather",
  "arguments": {
    "city": "北京"
  }
}

应用再真正执行:

getWeather("北京");

所以最重要的一点是:

模型负责决策,应用负责执行。

模型本身不会真的调用数据库、浏览器或第三方 API。


2. Spring AI 的 @Tool

Spring AI 可以直接把 Java 方法声明成工具:

@Tool(description = "获取当前日期和时间")
String getCurrentDateTime() {
    return LocalDateTime.now().toString();
}

调用时注册:

chatClient.prompt()
        .user(message)
        .tools(new DateTimeTools())
        .call()
        .content();

Spring AI 会把这个方法转换成模型能理解的:

工具名
+
工具描述
+
参数 JSON Schema

然后发送给模型。


3. 工具参数靠 JSON Schema

例如:

@Tool(description = "查询指定时区的时间")
String getTime(
    @ToolParam(description = "时区")
    ZoneId zoneId) {
    ...
}

Spring AI 会自动生成参数 Schema。

模型因此知道:

参数叫什么
参数是什么类型
参数有什么含义

然后自动构造参数并发起 Tool Call。

这和 Structured Output 很像:

Structured Output
Java 类型 → Schema → 约束输出

Function Call
Java 参数 → Schema → 约束工具参数

4. description 本质也是 Prompt

例如:

@Tool(description = "时间工具")

就比较模糊。

更好的写法是:

@Tool(
    description = "当用户询问当前日期或时间时调用,返回当前系统时间"
)

因为模型就是根据这些描述决定:

要不要调用?
调用哪个?
参数怎么填?

所以:

工具描述不是普通注释,而是工具选择阶段的 Prompt。


5. @Tool 和 ToolCallback

Spring AI 有两种工具定义方式。

简单场景:

@Tool

适合自己编写的普通 Java 方法。

复杂场景:

MethodToolCallback
FunctionToolCallback

适合:

第三方 SDK
动态工具
动态 Schema
运行时生成工具

可以简单记成:

@Tool
自动挡

ToolCallback
手动挡

最终都会变成统一的 ToolCallback。


6. ToolContext:传递业务身份

有些参数应该让模型生成:

city
keyword
jobName

但有些参数不能让模型决定:

userId
tenantId
权限
当前用户

这时候可以使用 ToolContext

@Tool(description = "创建任务")
String createTask(String name, ToolContext context) {

    String userId =
        (String) context.getContext().get("userId");

    return taskService.create(userId, name);
}

调用时注入:

.toolContext(Map.of(
    "userId", userId
))

于是参数可以分成:

LLM Controlled
模型生成

Application Controlled
程序注入

像用户 ID、权限、租户信息应该尽量由应用控制。


7. ReAct:连续调用多个工具

复杂 Agent 往往不是调用一次工具就结束。

可能是:

思考
 ↓
搜索岗位
 ↓
得到结果
 ↓
继续思考
 ↓
查询岗位详情
 ↓
最终回答

这就是:

Reason
 ↓
Act
 ↓
Observation
 ↓
Reason

也就是 ReAct

生产环境自己控制 ReAct Loop,可以增加:

最大调用次数
异常处理
超时
Token 统计
工具耗时
Tracing

避免模型一直重复调用工具进入死循环。


8. 多 Agent 要做工具隔离

不同 Agent 应该拥有不同的工具。

岗位 Agent
→ searchJob
→ getJobDetail

任务 Agent
→ createTask
→ deleteTask

采集 Agent
→ openPage
→ click
→ extract

不要把全部工具都交给所有 Agent。

因为:

工具集本身就是 Agent 的能力边界。

工具越精准,模型选错工具的概率也越低。


9. MCP 和 Function Call

可以简单理解:

Function Call
解决模型怎么调用工具

MCP
解决工具怎么被标准化提供和接入

未来 Agent 的工具可能来自:

本地 @Tool
+
远程 MCP Server

这样 Agent 的能力就可以动态扩展。


最终理解

整个调用链可以记成:

User
 ↓
Agent
 ↓
Prompt + Tools
 ↓
LLM
 ↓
toolName + arguments
 ↓
Tool Execution
 ↓
Tool Result
 ↓
LLM
 ↓
Final Answer

最值得记住的一句话:

Function Call 不是让大模型执行代码,而是让大模型决定“该调用什么工具”;真正的执行、权限、安全和异常处理始终由应用程序掌控。

Spring AI Function Call:让大模型真正调用外部工具
http://www.clxhxhhr.top/posts/658/
作者
clxstart
发布于
2026-09-17
许可协议
CC BY-NC-SA 4.0
评论
0 条
还没有评论,先写一条吧。