Skip to content

高频面试题

什么是工具调用 Tool Calling?如何利用 Spring AI 实现工具调用?

面试官问 Spring AI 里的工具调用,重点不是会不会写一个 @Tool,而是看你是否知道模型只提出调用请求,Spring AI 的 advisor 和 ToolCallingManager 才负责真正执行、回灌和控循环。

适合阶段:Java Agent 应用 / Spring AI 工程面核心能力:Tool Calling · Spring AI ChatClient · ToolCallback · 工具治理

面试官角度分析,想考什么

  • Tool Calling 是什么?Spring AI 里怎么声明?
    考模型只生成意图,应用层执行;@Tool / ToolCallback 如何暴露给 ChatClient。

  • 调用循环是谁驱动的?
    考 ToolCallingAdvisor / ToolCallingManager 自动执行并回灌,直到不再调工具。

  • 生产治理和框架价值是什么?
    考权限、异常、调用上限、trace;框架把 schema、循环和编排统一起来。

可直接抄走的 30 秒参考答案

text
Tool Calling 是让模型基于工具描述选择工具并生成 JSON 参数,但真正执行工具的是应用层。用 Spring AI 实现时,我一般用 @Tool 和 @ToolParam 声明业务方法,或者用 ToolCallback / FunctionToolCallback 做程序化注册,然后通过 ChatClient 的 .tools() 或 .defaultTools() 暴露给模型。Spring AI 2.x 里 ToolCallingAdvisor 会驱动工具调用循环,模型返回 tool call 后由 ToolCallingManager 执行工具、追加 tool result,再让模型继续推理,直到没有新的工具调用。生产里还要做工具白名单、权限、异常处理、tool context、调用上限和 trace。

面试回答详解,知其所以然

这道题要先讲清 Tool Calling 的通用边界,再落到 Spring AI 的具体类和工程配置。不要答成“Spring AI 加个注解就能让模型调接口”,那会漏掉工具循环、执行责任和风险控制。

1. Tool Calling 的核心边界

工具调用解决的是 LLM 无法直接访问外部世界的问题。模型可以根据上下文和工具描述决定“应该调用哪个工具、参数是什么”,但它不会真的发 HTTP 请求、查数据库或写文件。

完整链路是:

text
用户目标
  -> 应用把工具定义传给模型
  -> 模型返回 tool call(工具名 + JSON 参数)
  -> 应用层校验并执行工具
  -> 工具结果回灌给模型
  -> 模型继续调用工具或生成最终回答

所以工具调用的关键不是“模型会执行”,而是“模型会结构化表达动作意图”。真正的执行、鉴权、超时、重试、审计和人工确认都在应用层。

2. Spring AI 的核心抽象

Spring AI 把工具调用拆成几个层次:

  • @Tool:声明式方式,适合你拥有源码的方法。方法名默认就是工具名,description 告诉模型什么时候使用。
  • @ToolParam:描述参数含义、是否必填,帮助框架生成输入 JSON Schema。
  • ToolCallback:Spring AI 内部统一的工具接口,包含模型可见的 ToolDefinition 和真实执行逻辑。
  • MethodToolCallback:把某个 Java 方法包装成工具,适合运行时动态注册。
  • FunctionToolCallback:把 FunctionSupplierConsumer 等函数式对象包装成工具。
  • ToolCallbackProvider:批量提供工具,MCP 客户端工具也会通过 provider 暴露出来。
  • ToolCallingAdvisor:挂在 ChatClient advisor chain 里的工具调用循环控制器。
  • ToolCallingManager:负责解析 tool call、查找匹配工具、执行工具、生成工具响应,并执行调用上限策略。

面试里能说出这几个角色,基本就从“会用注解”进入“理解 Spring AI 工具体系”了。

3. 最常见实现:@Tool + ChatClient.tools()

一个最小实现通常这样写:

java
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

class OrderTools {

    @Tool(description = "Query an order by order id. Use this only when the user provides an order id.")
    OrderInfo getOrder(
            @ToolParam(description = "Order id, for example ORD-12345") String orderId) {
        return orderService.findById(orderId);
    }
}

调用时把工具对象传给 ChatClient

java
String answer = chatClient.prompt()
    .user("帮我查一下订单 ORD-12345 的状态")
    .tools(new OrderTools())
    .call()
    .content();

Spring AI 会做几件事:

  • 读取 @Tool 和方法签名,生成模型可见的工具定义和参数 schema。
  • 把工具定义随 chat request 发给底层模型。
  • 模型返回 tool call 后,交给 ToolCallingManager 找到对应 ToolCallback 并执行。
  • 把工具执行结果作为 tool response 回灌给模型。
  • 如果模型继续返回 tool call,就继续循环;如果不再返回工具调用,就把最终内容返回给业务代码。

4. 三种工具注册方式怎么选

  • 声明式 @Tool:最适合普通 Spring 业务方法,代码少、可读性好。注意生产里最好让工具类成为 Spring Bean,便于依赖注入、AOT/native image 和统一治理。
  • MethodToolCallback:适合你不想或不能改源码,但要把某个方法动态包装成工具,例如插件化工具或多租户工具目录。
  • FunctionToolCallback:适合函数式封装,比如把 weatherService::getWeatherSupplierConsumer 快速暴露成工具。
  • ToolCallback Bean:适合长期维护的生产工具。定义成 Bean 后可被 ToolCallbackResolver 统一解析,也方便加 wrapper、权限和观测。
  • MCP 工具:适合工具来源在外部进程或远端服务。Spring AI 的 MCP client starter 可以连接 MCP Server,把远端工具包装成 ToolCallbackProvider,再显式传给 ChatClient

一个实战原则是:低风险、小工具可以 per-call .tools(...);全局只读工具可以 .defaultTools(...);高风险写操作不要默认挂到所有请求上,而是按场景动态挂载并加审批。

5. ChatClient 工具循环要怎么理解

Spring AI 2.x 的一个重点变化是:工具调用循环成为 ChatClient advisor chain 里的 first-class 组件。

可以这样理解:

text
ChatClient request
  -> Memory / 自定义 Advisor
  -> ToolCallingAdvisor
      -> 发模型请求
      -> 如果有 tool call:ToolCallingManager 执行
      -> 追加 tool result
      -> 再次进入模型请求
      -> 直到模型不再返回 tool call
  -> 返回最终 assistant 内容

这比你自己写 while(true) 有两个好处:

  • Spring AI 替你处理工具定义、结果回灌、stream/call 两种模式、结果转换和异常处理。
  • advisor chain 可以和 memory、observability、retry、自定义审批、预算控制组合。

但这不代表生产风险消失。框架负责调用循环,业务仍要负责工具设计、权限、参数校验和副作用控制。

6. 生产落地要补哪些治理

Spring AI 提供了几个很实用的治理点,面试里要主动提:

  • 工具描述要像 API 文档:写清用途、边界、参数格式、什么时候不要用。工具描述不清会直接导致误调用。
  • 工具数量要控制:工具过多会挤占上下文,也会降低模型选择准确率。Spring AI 提供 tool search advisor 思路,把大量工具按需检索出来,而不是全量塞给模型。
  • 敏感上下文不要给模型:例如租户 ID、内部用户 ID、鉴权信息,可以用 Spring AI 的 tool context 传给工具,让工具执行时拿到,但不暴露给模型。
  • 异常要转成可决策的 tool result:用 ToolExecutionExceptionProcessor 把失败翻译成模型能理解的错误类型和下一步,而不是直接吞异常或抛一屏堆栈。
  • 调用上限必须打开:配置每个工具和总工具调用次数,防止模型在一个 turn 里反复调用同一工具。
  • 高风险动作加人工确认:转账、删除、发邮件、改生产数据这类工具,即使模型参数合法,也要在执行层做审批。
  • trace 必须完整:记录 prompt 版本、工具名、参数、结果摘要、耗时、错误、调用次数和 stop reason,方便排查。

面试官追问3个问题

追问一:@Tool 的 description 为什么这么重要?

  • 考察点:是否理解工具描述就是模型的“接口文档”。
  • 回答方向:模型靠 description 判断何时使用工具、参数代表什么、边界在哪里。描述应写清用途、适用条件、不要使用的场景、参数格式和返回含义。相似工具多时,description 的区分度比方法名更重要。

追问二:工具异常是抛给业务代码,还是返回给模型?

  • 考察点:是否能区分系统异常和模型可恢复错误。
  • 回答方向:参数错误、业务规则拒绝、权限不足这类可以转成结构化 tool result,让模型换参数、换路径或向用户解释;严重系统异常、数据一致性风险或安全风险应该抛给调用方或中断流程。Spring AI 可通过 ToolExecutionExceptionProcessor 控制这件事。

追问三:Spring AI 里怎么避免工具调用死循环?

  • 考察点:是否了解框架调用上限和业务 stop condition。
  • 回答方向:先配置每个工具和总工具调用次数上限,防止单 turn 失控;再在工具结果里返回明确错误类型和 retryable 语义;高风险工具加审批;必要时自定义 advisor 记录 action signature,连续相同工具和参数就停止或追问用户。

扩展知识

Spring AI 里的 returnDirect

默认情况下,工具结果会回灌给模型,让模型再生成自然语言回答。returnDirect = true 则表示工具结果可以直接返回给调用方,适合结果本身就是最终答案的场景,比如确定性查询、固定格式检索结果或内部系统已经生成好的文案。

java
@Tool(description = "Retrieve customer information", returnDirect = true)
Customer getCustomerInfo(Long id) {
    return customerService.getCustomerInfo(id);
}

取舍是:少一次模型调用,延迟和成本更低;但也少了模型解释、整合和补充上下文的机会。

Tool Context 和普通参数的区别

普通参数来自模型生成,模型看得见,也可能生成错。Tool context 来自应用运行时,不需要暴露给模型。

text
模型参数:orderId、city、date
运行时上下文:tenantId、currentUserId、permissionScope、traceId

这对企业应用很重要。比如用户问“查这个客户的信息”,模型只需要生成客户 ID;租户 ID 和权限范围应该由后端从登录态注入工具,而不是让模型自己猜。

Spring AI 和 MCP 的关系

MCP 解决的是工具如何被 Agent 应用发现、连接和调用。Spring AI 可以消费 MCP Server 的工具,也可以把 Spring 工具暴露成 MCP Server。面试里要分清:

  • Spring AI Tool Calling:Java 应用内部如何定义工具并让模型调用。
  • MCP:跨进程、跨语言、跨应用的工具接入协议。
  • Function Calling:底层模型如何表达工具调用意图。

三者可以组合,不是互相替代。

基于 MIT 协议开源