SpringAI工具调用原理解析

目录

一、工具调用整体流程

二、工具调用检测机制

[1. 结构检测:AssistantMessage 与 ToolCall](#1. 结构检测:AssistantMessage 与 ToolCall)

[2. 元数据检测:验证 finishReason](#2. 元数据检测:验证 finishReason)

[3. 响应解析的核心:buildGeneration 方法](#3. 响应解析的核心:buildGeneration 方法)

三、工具执行机制

[四、无缝集成:ChatClient 的角色](#四、无缝集成:ChatClient 的角色)

在智能应用开发中,**工具调用(Tool Calling)**是大模型落地的重要能力。它让模型不仅能对话,还能主动调用外部函数或服务,从而具备"执行"能力。Spring AI 对此提供了完整的框架支持,本文将结合源码解析,深入剖析 Spring AI 是如何检测、解析并执行模型的工具调用的。

一、工具调用整体流程

    1. 当我们想要让模型可以使用某个工具时,我们会将其定义包含在聊天请求(Prompt)中,并调用ChatModel将请求发送到 AI 模型的 API。
  1. 当模型决定调用某个工具时,它会发送一个响应(ChatResponse),其中包含工具名称和根据定义的模式建模的输入参数。
  2. 将ChatModel工具调用请求发送到ToolCallingManagerAPI。
  3. 负责ToolCallingManager识别要调用的工具并使用提供的输入参数执行它。
  4. 工具调用的结果返回给ToolCallingManager。
  5. 将ToolCallingManager工具执行结果返回给ChatModel。
  6. 将ChatModel工具执行结果发送回AI模型(ToolResponseMessage)。

二、工具调用检测机制

1. 结构检测:AssistantMessageToolCall

当 AI 模型返回响应时,Spring AI 会将其封装在 AssistantMessage 对象中。这个类是检测工具调用的第一道关卡。

AssistantMessage.java:69-71

复制代码
public boolean hasToolCalls() {
    return!CollectionUtils.isEmpty(this.toolCalls);
}

通过检查 toolCalls 列表是否为空,框架可以快速判断响应中是否包含了结构化的工具调用请求。每一个工具调用请求都被解析为一个 ToolCall 记录(Record):

AssistantMessage.java:103-105

复制代码
public record ToolCall(String id, String type, String name, String arguments) {
}

这个记录清晰地定义了工具调用的所有要素:一个唯一的 id、类型(通常是 "function")、工具的 name 以及 JSON 格式的 arguments

2. 元数据检测:验证 finishReason

仅仅有 ToolCall 结构还不够。大语言模型在返回响应时,会附带一个 finishReason 元数据,用来说明生成停止的原因(例如,正常结束、达到长度限制,或是因为需要调用工具)。Spring AI 利用这个元数据作为第二重验证。

核心的检测逻辑位于 AbstractToolCallSupport 类中:

AbstractToolCallSupport.java:265-272

复制代码
protected boolean isToolCall(Generation generation, Set<String> toolCallFinishReasons) {
    var finishReason = (generation.getMetadata().getFinishReason()!= null)
           ? generation.getMetadata().getFinishReason() : "";
    return generation.getOutput().hasToolCalls() && toolCallFinishReasons.stream()
           .map(s -> s.toLowerCase())
           .toList()
           .contains(finishReason.toLowerCase());
}

这段代码清晰地展示了双重验证:

  • generation.getOutput().hasToolCalls(): 进行结构检测,确认 AssistantMessage 中存在 ToolCall 对象。
  • toolCallFinishReasons.stream()...contains(finishReason.toLowerCase()): 进行元数据检测,确认模型的 finishReason 是 "tool_calls" 或 "function_call" 等预期的值。

只有当这两个条件同时满足时,Spring AI 才会确认这是一个有效的工具调用意图。

3. 响应解析的核心:buildGeneration 方法

那么,AssistantMessage 中的 toolCalls 列表和 finishReason 是从何而来的呢?答案在于各个模型实现中的响应解析逻辑。以 OpenAI 为例,其核心转换逻辑如下:

复制代码
private Generation buildGeneration(Choice choice, Map<String, Object> metadata, ChatCompletionRequest request) {
    List<AssistantMessage.ToolCall> toolCalls = choice.message().toolCalls() == null? List.of()
            : choice.message()
               .toolCalls()
               .stream()
               .map(toolCall -> new AssistantMessage.ToolCall(toolCall.id(), "function",
                        toolCall.function().name(), toolCall.function().arguments()))
               .toList();

    String finishReason = (choice.finishReason()!= null? choice.finishReason().name() : "");
    
    var generationMetadataBuilder = ChatGenerationMetadata.builder().finishReason(finishReason);
    
    //... 其他逻辑...

    var assistantMessage = new AssistantMessage(textContent, metadata, toolCalls, media);
    return new Generation(assistantMessage, generationMetadataBuilder.build());
}

这段代码是连接模型原始输出和 Spring AI 内部模型的桥梁。它明确地从模型的原始响应(Choice 对象)中提取 toolCallsfinishReason,并将它们构建成 Spring AI 的标准 GenerationAssistantMessage 对象。

toolCallsfinishReason 是实现 Agent 功能不可或缺的两个关键要素。任何主流的大模型如果想要融入 Spring AI 生态,就必须支持这两个特性,因为它们是模型从一个纯粹的文本生成器转变为一个能够执行动作的智能代理的基础。

三、工具执行机制

一旦检测到工具调用意图,执行的接力棒就交给了 DefaultToolCallingManager

DefaultToolCallingManager.java:195-233

复制代码
for (AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) {
    String toolName = toolCall.name();
    String toolInputArguments = toolCall.arguments();
    
    // 查找匹配的工具回调
    FunctionCallback toolCallback = toolCallbacks.stream()
       .filter(tool -> toolName.equals(tool.getName()))
       .findFirst()
       .orElseGet(() -> toolCallbackResolver.resolve(toolName));

    // 执行工具并获取结果
    String toolResult = toolCallback.call(toolInputArguments, toolContext);
    
    toolResponses.add(new ToolResponseMessage.ToolResponse(toolCall.id(), toolName, toolResult));
}

这个流程清晰地展示了从解析到执行的每一步:

  • 遍历工具调用 :从 AssistantMessage 中获取所有 ToolCall 请求并逐一处理。
  • 解析参数:提取工具的名称和 JSON 格式的参数。
  • 解析与匹配 :根据 toolName 在已注册的 FunctionCallback 列表中查找并解析出对应的工具实例。
  • 执行调用 :调用 toolCallback.call() 方法,将参数传入,执行实际的 Java 函数。
  • 封装结果 :将执行结果封装成 ToolResponseMessage,准备在下一轮对话中返回给模型。

四、无缝集成:ChatClient 的角色

这一切复杂的内部机制,对于开发者而言,都被 DefaultChatClient 优雅地封装了起来。通过其内置的 advisor链,整个工具调用的检测、解析、执行和结果反馈流程被自动化处理。开发者只需简单地配置 ToolCallbackProvider,并在调用时使用 .tools().functions() 方法注册工具即可。

精心设计的责任分离

通过对源码的深入分析,我们可以看到 Spring AI 在工具调用功能上体现出的卓越设计思想:

  • 双重检测机制 :结合结构化数据(toolCalls)和元数据(finishReason)进行双重验证,保证了意图识别的准确性和鲁棒性。
  • 责任分离 :模型层(ChatModel)专注于将服务商的原始响应解析为统一的 ToolCall 对象,而 ChatClientToolCallingManager 则负责工具的解析、执行和生命周期管理。
相关推荐
毕设源码-钟学长2 小时前
【开题答辩全过程】以 基于Java的戏曲网站设计与实现为例,包含答辩的问题和答案
java·开发语言
苹果醋32 小时前
SpringCloud高可用集群搭建及负载均衡配置实战
java·运维·spring boot·mysql·nginx
2501_916007472 小时前
Java界面开发工具有哪些?常用Java GUI开发工具推荐、实战经验与对比分享
android·java·开发语言·ios·小程序·uni-app·iphone
ps酷教程3 小时前
spring-batch深入了解
java·spring·batch
C++chaofan3 小时前
通过Selenium实现网页截图来生成应用封面
java·spring boot·后端·selenium·测试工具·编程·截图
渣哥3 小时前
事务嵌套场景必问:Spring 传播机制如何真正发挥作用?
java
聪明的笨猪猪3 小时前
Java SE “概念与优势”面试清单(含超通俗生活案例与深度理解)
java·经验分享·笔记·面试
Yunfeng Peng3 小时前
2- 十大排序算法(希尔排序、计数排序、桶排序)
java·算法·排序算法
RainbowSea3 小时前
8. Spring AI tools/function-call
java·spring·ai编程