Spring AI 1.x 系列【15】AI Agent 基石:Tool Calling 标准与 Spring AI 集成

文章目录

  • [1.Tool Calling](#1.Tool Calling)
    • [1.1 Function Calling](#1.1 Function Calling)
    • [1.2 工作流程](#1.2 工作流程)
    • [1.3 工具定义规范](#1.3 工具定义规范)
    • [1.4 响应格式规范](#1.4 响应格式规范)
    • [1.6 结果回传规范](#1.6 结果回传规范)
  • [2. Spring AI Tool Calling](#2. Spring AI Tool Calling)
    • [2.1 整体流程](#2.1 整体流程)
    • [2.2 核心组件](#2.2 核心组件)
      • [2.2.1 ToolCallback](#2.2.1 ToolCallback)
      • [2.2.2 ToolCallingManager](#2.2.2 ToolCallingManager)
      • [2.2.3 ChatModel/ChatClient](#2.2.3 ChatModel/ChatClient)
      • [2.2.4 ToolCallingChatOptions](#2.2.4 ToolCallingChatOptions)
      • [2.2.5 ToolCallbackResolver](#2.2.5 ToolCallbackResolver)
      • [2.2.6 ToolContext](#2.2.6 ToolContext)
      • [2.2.7 ToolCallResultConverter](#2.2.7 ToolCallResultConverter)

1.Tool Calling

工具调用Tool Calling)允许 AI 模型调用外部函数和 API,极大扩展了智能体的能力边界,使其能够执行具体操作和获取实时数据。

解决幻觉、知识滞后、无法操作现实世界的问题,是大模型从对话走向实用化、工程化、智能化的核心技术,也是 AI Agent 能够自主完成复杂任务的基础。

工具的主要用途:

  1. 信息检索 (查询天气、获取最新新闻、检索数据库指定记录等):
    • 从数据库、网络服务、文件系统、搜索引擎等外部渠道获取信息
    • 用于扩充模型知识,回答模型自身无法解答的问题
    • 适配检索增强生成(RAG)场景
  2. 执行操作 (发送邮件、新建数据库记录、提交表单、触发工作流、预订机票等):
    • 在软件系统中完成实际操作与任务执行
    • 实现原本需要人工或代码完成的自动化操作

提示 :工具调用看似是模型能力,实则模型只负责发起调用请求并提供参数,真正执行工具调用、访问 API 的是客户端应用,这种权限隔离对安全至关重要。

1.1 Function Calling

20236 月,OpenAI 首次推出原生 Function Calling 能力,基本内容如下:

  • 向模型发起请求时,你可传入模型可调用的函数(工具)列表。
  • 模型会判断是否需要调用其中一个或多个函数,若需要,将返回包含函数名称和参数的 JSON 对象。
  • 应用程序可执行该函数,并将结果回传给模型做进一步处理。

随后该标准被各大模型厂商和框架广泛采纳,20243 月,OpenAI API 进行了重构,Function Calling 全面升级为 Tool Calling ,主要是一些参数名称的变动,实际上他们是同一个东东。

提示 :大多模型都支持 OpenAI 定义的行业标准,极少数厂商可能存在少许差异。

1.2 工作流程

工作流程示意图如下所示:

流程相关说明:

  1. 发起第一次模型调用:应用程序首先向大模型发起一个包含用户问题与模型可调用工具清单的请求。
  2. 接收模型的工具调用指令 :若模型判断需要调用外部工具,会返回一个 JSON 格式的指令,用于告知应用程序需要执行的函数与入参。若模型判断无需调用工具,会返回自然语言格式的回复。
  3. 在应用端运行工具:应用程序接收到工具指令后,需要运行工具,获得工具输出结果。
  4. 发起第二次模型调用:获取到工具输出结果后,需添加至模型的上下文,再次发起模型调用。
  5. 接收来自模型的最终响应:模型将工具输出结果和用户问题信息整合,生成自然语言格式的回复。

1.3 工具定义规范

向大模型发送请求时,明确告知模型「可用的工具列表、每个工具的名称 / 功能 / 参数规则」,让模型判断 "是否调用工具、调用哪个工具"。

工具定义核心字段规范:

字段名 数据类型 固定值/核心说明 备注
type 字符串 "function" 标识该工具为函数类型,固定值不可修改
function Object - 工具的核心定义容器,包含名称、描述、参数等核心信息
name 字符串 自定义工具函数名称 建议与实际函数名一致(如get_current_weather、get_current_time),需唯一
description 字符串 工具函数功能描述 大模型依据此字段判断是否调用该工具
parameters Object 工具函数入参描述 无入参时可省略该字段;包含type、properties、required等子字段
type 字符串 "object" 固定值,标识入参整体为对象类型
properties Object 入参的名称、数据类型与描述 Key=入参名称,Value=入参的类型与描述(例:{ "location": { "type": "string", "description": "城市名称" } })
required Array 必填参数名称列表 数组元素为字符串类型,对应properties中的Key;无必填参数时可省略

对于天气查询工具来说,工具描述信息的格式如下:

json 复制代码
{
    "type": "function",
    "function": {
        "name": "get_current_weather",
        "description": "当你想查询指定城市的天气时非常有用。",
        "parameters": {
            "type": "object",
            "properties": {
                "location": {
                    "type": "string",
                    "description": "城市或县区,比如北京市、杭州市、余杭区等。"
                }
            },
            "required": ["location"]
        }
    }
}

1.4 响应格式规范

模型返回响应,告知应用「是否调用工具;若调用,需明确工具名称、参数、唯一标识」。

模型响应分两种情况:

  • 调用工具。
  • 不调用工具(直接返回文本)。

调用工具响应字段说明:

层级路径 字段名 数据类型 必选 核心说明
choices[0].message role 字符串 固定为 assistant(模型角色)
choices[0].message content 字符串 固定为 null(无文本响应)
choices[0].message tool_calls 数组 工具调用指令列表(支持批量调用多个工具)
choices[0].message.tool_calls[0] id 字符串 工具调用唯一 ID(结果回传时需匹配)
choices[0].message.tool_calls[0] type 字符串 固定为 function
choices[0].message.tool_calls[0].function name 字符串 要调用的工具名称(需与请求中的工具名称完全一致)
choices[0].message.tool_calls[0].function arguments 字符串 工具入参的 JSON 字符串(需符合请求中 parameters 定义的规则)
choices[0] finish_reason 字符串 固定为 tool_calls(标识响应为工具调用)

对于天气查询工具来说,提问"北京上海的天气如何" 时,模型会识别到需要调用工具,返回:

json 复制代码
{
    "content": "",
    "refusal": null,
    "role": "assistant",
    "audio": null,
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_61a2bbd82a8042289f1ff2",
            "function": {
                "arguments": "{\"location\": \"北京市\"}",
                "name": "get_current_weather"
            },
            "type": "function",
            "index": 0
        }
    ]
}

如果不需要调用工具,则没有 tool_calls 字段返回:

json 复制代码
{
  "id": "chatcmpl-9XYZ",
  "object": "chat.completion",
  "created": 1710000001,
  "model": "gpt-4o",
  "choices": [
    {
      "index": 0,
      "message": {
        "role": "assistant",
        "content": "北京今天的天气是晴天,气温30摄氏度。"
      },
      "finish_reason": "stop"
    }
  ],
  "usage": {
    "prompt_tokens": 20,
    "completion_tokens": 15,
    "total_tokens": 35
  }
}
复制代码
                         |

1.6 结果回传规范

应用执行完工具后,将「工具执行结果 + 匹配的调用 ID」回传给模型,让模型基于结果生成最终的用户响应。

结果回传工具结果相关字段说明:

字段 / 结构 类型 必填 说明 & 约束
role 字符串 固定值 tool
tool_call_id 字符串 匹配模型响应中的 tool_calls[].id(核心!确保结果与调用指令对应)
content 字符串 工具执行结果(需序列化为 JSON 字符串,支持结构化数据)

对于天气查询工具来说,之前的用户消息、助手消息、工具响应消息(天气查询结果),需要全部返回给大模型:

java 复制代码
{
  "model": "gpt-4o",
  "messages": [
    // 1. 原始用户消息
    {"role": "user", "content": "查询北京今天的天气,单位摄氏度"},
    // 2. 模型的工具调用响应(原样带回)
    {"role": "assistant", "content": null, "tool_calls": [{"id":"call_123456","type":"function","function":{"name":"currentWeather","arguments":"{\"location\":\"北京\",\"unit\":\"C\"}"}}]},
    // 3. 新增tool消息:回传工具执行结果
    {"role": "tool", "tool_call_id": "call_123456", "content": "{\"temp\":30.0,\"unit\":\"C\",\"condition\":\"晴\"}"}
  ]
}

2. Spring AI Tool Calling

2.1 整体流程

Spring AI 提供了一套灵活的象接口实现工具调用,能让你用统一、标准的方式完成工具的定义、解析和执行:

工具调用的完整执行流程

  1. 向模型开放工具 :将工具的名称、功能描述、入参格式(Schema) 放进聊天请求里,模型才知道有哪些工具可用。
  2. 模型决定调用:模型如果判断需要用工具,会返回:工具名 + 按格式生成的入参。
  3. 应用执行工具:由你的应用(不是模型)根据工具名找到对应工具,并用参数真正执行工具 / API。
  4. 应用处理结果:应用拿到工具执行的返回结果。
  5. 结果回传给模型:应用把执行结果再发给模型。
  6. 模型生成最终回答:模型把工具结果当作额外上下文,生成自然语言的最终答案。

2.2 核心组件

组件职责与关系对照表:

组件 职责 被...调用 调用...
ChatClient 入口协调,组装流程 用户 ChatModel, ToolCallingManager
ChatModel 与 AI 模型通信 ChatClient -
ToolCallingChatOptions 存储配置 ChatClient, ToolCallingManager -
ToolCallingManager 解析定义、执行工具 ChatClient ToolCallbackResolver, ToolCallback
ToolCallbackResolver 名称→ToolCallback ToolCallingManager -
ToolCallback 工具定义+执行 ToolCallingManager -

2.2.1 ToolCallback

ToolCallback 是所有可被 AI 模型调用工具的最小 / 基础单元,同时包含工具定义与执行逻辑。

接口定义如下:

java 复制代码
public interface ToolCallback {
    Logger logger = LoggerFactory.getLogger(ToolCallback.class);

    ToolDefinition getToolDefinition();

    default ToolMetadata getToolMetadata() {
        return ToolMetadata.builder().build();
    }

    String call(String toolInput);

    default String call(String toolInput, @Nullable ToolContext toolContext) {
        if (toolContext != null && !toolContext.getContext().isEmpty()) {
            logger.info("By default the tool context is not used,  override the method 'call(String toolInput, ToolContext toolcontext)' to support the use of tool context.Review the ToolCallback implementation for {}", this.getToolDefinition().name());
        }

        return this.call(toolInput);
    }
}

方法介绍

  • getToolDefinition():返回工具的定义信息(ToolDefinition),是 AI 模型识别工具的唯一依据。
  • getToolMetadata():返回工具的附加元数据(ToolMetadata),比如结果转换器、执行超时时间、工具分组等。
  • call(String toolInput):工具的核心执行逻辑,接收 AI 模型传入的参数(JSON 字符串),返回执行结果(字符串)。
  • call(String toolInput, @Nullable ToolContext toolContext):支持工具接收本地上下文(ToolContext),比如租户 ID、用户身份、请求 ID 等(这些数据不传给 AI 模型,仅本地使用)。

默认提供了两种实现:

  • MethodToolCallback:基于普通 Java 方法,框架自动把方法转成 ToolCallback,无需手动实现接口。
  • FunctionToolCallback:基于函数式接口,适配函数式开发风格,无需依赖反射(比 Method 更轻量)。

提示:如果需要从零开始定义工具,这是需要实现的核心接口。

2.2.2 ToolCallingManager

工具执行指的是使用传入的入参调用工具并返回结果的过程。工具执行由 ToolCallingManager 接口负责处理,该接口管控着工具执行的完整生命周期。

接口定义

java 复制代码
public interface ToolCallingManager {
    List<ToolDefinition> resolveToolDefinitions(ToolCallingChatOptions chatOptions);

    ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse);

    static DefaultToolCallingManager.Builder builder() {
        return DefaultToolCallingManager.builder();
    }
}

方法介绍

  • resolveToolDefinitions:解析工具定义。
  • executeToolCalls:执行工具调用。

DefaultToolCallingManagerToolCallingManager 接口提供的官方默认实现类,完整实现了接口的两个核心方法,并内置了日志、可观测性、工具解析、异常处理等通用能力。

核心源码:

java 复制代码
public final class DefaultToolCallingManager implements ToolCallingManager {
   private static final Logger logger = LoggerFactory.getLogger(DefaultToolCallingManager.class);
   private static final ObservationRegistry DEFAULT_OBSERVATION_REGISTRY;
   private static final ToolCallingObservationConvention DEFAULT_OBSERVATION_CONVENTION;
   private static final ToolCallbackResolver DEFAULT_TOOL_CALLBACK_RESOLVER;
   private static final ToolExecutionExceptionProcessor DEFAULT_TOOL_EXECUTION_EXCEPTION_PROCESSOR;
   private static final String POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING = "LLM may have adapted the tool name '{}', especially if the name was truncated due to length limits. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator";
   private final ObservationRegistry observationRegistry;
   private final ToolCallbackResolver toolCallbackResolver;
   private final ToolExecutionExceptionProcessor toolExecutionExceptionProcessor;
   private ToolCallingObservationConvention observationConvention;
   //..........
}

引入 Spring AI Spring Boot Starter 后,DefaultToolCallingManager 会作为 ToolCallingManager 接口的自动配置实现类生效。也可以通过自定义 ToolCallingManager Bean 的方式,定制工具执行的行为:

java 复制代码
@Bean
ToolCallingManager toolCallingManager() {
    return ToolCallingManager.builder().build();
}

2.2.3 ChatModel/ChatClient

ChatModel(底层模型接口)和 ChatClient(上层易用封装)是连接 AI 模型与本地工具的核心,是 Tool Calling 的 "入口和出口"。

核心作用

  • 工具注册 : 接收开发者传入的 ToolCallback 列表,提取每个工具的 ToolDefinition
  • 模型通信 : 把 ToolDefinition 转为 AI 模型能识别的格式,随 Prompt 发给模型。
  • 指令解析 : 接收模型返回的响应,解析出工具调用指令(tool_calls 数组)。
  • 请求转发 : 把工具调用指令(工具名 + 参数)转发给 ToolCallingManager 执行。
  • 结果回传 : 把 ToolCallingManager 返回的工具执行结果,转为模型能识别的格式(如 tool_results 数组),回传给模型。
  • 最终响应: 接收模型基于工具结果生成的自然语言响应,返回给开发者。

简单使用示例:

java 复制代码
// 1. 创建 ChatModel(以 OpenAI 为例)
OpenAiChatModel chatModel = new OpenAiChatModel(new OpenAiApi(System.getenv("OPENAI_API_KEY")));

// 2. 注册工具 + 发起请求
String response = ChatClient.create(chatModel)
        .prompt("告诉我当前系统时间")
        .tools(new CurrentTimeToolCallback()) // 注册 ToolCallback
        .toolContext(Map.of("timezone", "Asia/Shanghai")) // 传入上下文
        .call()
        .content();

System.out.println(response); // 输出:当前系统时间是2026-03-11 15:30:00

2.2.4 ToolCallingChatOptions

ToolCallingChatOptions 扩展了 ChatOptions,专门用于配置工具调用(Function Calling) 相关的选项。

核心属性:

属性 类型 说明
toolCallbacks List 注册到 ChatModel 的工具回调列表
toolNames Set 按名称注册的工具集合(运行时解析)
toolContext Map<String, Object> 工具执行时传递的上下文数据
internalToolExecutionEnabled Boolean 是否由 ChatModel 自动执行工具(默认true)

2.2.5 ToolCallbackResolver

工具解析的标准接口,支持通过 ToolCallbackResolver 接口在运行时动态解析工具。

只有一个核心功能:

java 复制代码
public interface ToolCallbackResolver {
    // 根据工具名称,解析出对应的工具
    @Nullable
    ToolCallback resolve(String toolName);
}

使用该方案时:

  • 客户端侧:向聊天客户端 / 模型传递工具名称,而非 ToolCallback 实例;
  • 服务端侧:由 ToolCallbackResolver 实现类将工具名称解析为对应的 ToolCallback实例。

实现类:

2.2.6 ToolContext

ToolContextSpring AI 中用于在工具执行时传递额外上下文信息的机制。它允许将用户身份、会话信息、权限等数据传递给工具实现,使工具能够根据上下文执行不同的逻辑。

直接设置工具上下文:

java 复制代码
DefaultToolCallingChatOptions options = new DefaultToolCallingChatOptions();
options.setToolContext(Map.of(
    "userId", "user123",
    "role", "admin"
));

2.2.7 ToolCallResultConverter

ToolCallResultConverter 接口提供了一种将工具调用结果转换为 String 对象的标准化方式。

接口定义:

java 复制代码
@FunctionalInterface
public interface ToolCallResultConverter {

    /**
     * 根据工具返回的对象,将其转换为与指定类类型兼容的字符串。
     * @param result 工具返回的对象(可为空)
     * @param returnType 目标返回类型(可为空)
     * @return 转换后的字符串
    */
    String convert(@Nullable Object result, @Nullable Type returnType);
}

工具返回的结果必须是可序列化类型,结果会通过 ToolCallResultConverter 进行序列化,随后发送回 AI 模型。

Spirng AI 提供了默认实现:

java 复制代码
public final class DefaultToolCallResultConverter implements ToolCallResultConverter {
    private static final Logger logger = LoggerFactory.getLogger(DefaultToolCallResultConverter.class);

    public DefaultToolCallResultConverter() {
    }

    public String convert(@Nullable Object result, @Nullable Type returnType) {
        if (returnType == Void.TYPE) {
            logger.debug("The tool has no return type. Converting to conventional response.");
            return JsonParser.toJson("Done");
        } else if (result instanceof RenderedImage) {
            ByteArrayOutputStream buf = new ByteArrayOutputStream(4096);

            try {
                ImageIO.write((RenderedImage)result, "PNG", buf);
            } catch (IOException var5) {
                return "Failed to convert tool result to a base64 image: " + var5.getMessage();
            }

            String imgB64 = Base64.getEncoder().encodeToString(buf.toByteArray());
            return JsonParser.toJson(Map.of("mimeType", "image/png", "data", imgB64));
        } else {
            logger.debug("Converting tool result to JSON.");
            return JsonParser.toJson(result);
        }
    }
}

DefaultToolCallResultConverte 会通过 Jackson 库将结果序列化为 JSON 格式,也可以通过自定义的实现类,灵活定制序列化的逻辑和格式。

相关推荐
科学创新前沿1 小时前
逆向设计新范式:深度学习驱动的声学超材料智能优化!
人工智能·python·深度学习·声学·逆向设计·声学超材料
咸鱼2.01 小时前
【java入门到放弃】杂记
java·开发语言
铮铭1 小时前
上海交大 RoboClaw VS EmbodiedAgentsSys 两个框架对比分析
人工智能·机器人·ai编程·具身智能·vla
rgb2gray1 小时前
论文详解:基于POI数据的城市功能区动态演化分析——以北京为例
人工智能·算法·机器学习·回归·gwr
不懂网络的坤坤1 小时前
2026中关村论坛AI主题日深度解读
人工智能
产业家1 小时前
“龙虾思想”背后:把AI战火正式推向软件层
人工智能
新缸中之脑1 小时前
将Autoresearch转化为通用技能
人工智能
Yao.Li2 小时前
LINEMOD 训练流程与实施细节
人工智能·深度学习·机器学习
AORUO奥偌2 小时前
奥偌医用气体系统——全链条一站式服务商 | 中心供氧/负压吸引/压缩空气源头厂家
人工智能·数字化·智慧医院·医用气体系统·中心供氧系统工程