DevFix
Spring AI已验证

Spring AI Tool Calling 不生效?三个静默失效的坑

@debug_master更新于 3 天前阅读 6 min0

一句话先说结论:Spring AI 的 Tool Calling 失效,最坑的是它往往“静默”——编译不报错、启动不报错,只是模型死活不调你的工具。常见三因:defaultOptions 用了不支持的 DefaultChatOptions.tools(...) 的重载把 FunctionToolCallback 吞了;以及工具执行阶段换了个新的 options 实例导致 callback 丢失。

背景

Tool Calling(函数调用)让 LLM 能反过来调用你定义的方法:@Tool 注解声明方法,框架收集成 ToolCallback,把工具描述发给模型,模型决定要不要调、调哪个,框架再执行对应方法回传结果。这条链路里任何一环断掉,表现都是“模型不调工具”,而且没有显式报错。

现象

典型表现:

  1. 配置了 defaultOptions 之后,工具回调再也触发不了;删掉 defaultOptions 又恢复正常。
  2. 注册了 FunctionToolCallback,但模型完全无视它。
  3. 工具明明注册了,模型一调用就抛 IllegalStateException: No ToolCallback found for tool name: xxx

根因分析

坑 1:defaultOptions 用了 DefaultChatOptions

ChatOptions.builder() 生成的是 DefaultChatOptions,而它不支持 Tool Calling。把工具回调包进去后,构建请求时 tools 列表被丢弃。

// ❌ 工具调用失效
.defaultOptions(ChatOptions.builder()
    .model(model)
    .temperature(0.7)
    .build())

// ✅ 用支持工具调用的选项
.defaultOptions(ToolCallingChatOptions.builder()
    .model(model)
    .temperature(0.7)
    .build())

这是 Spring AI 1.0.0 的一个已知问题(issue #3392)。需要用 ToolCallingChatOptions(或模型特化的 options,如 DeepSeekChatOptions)来搭 defaultOptions。

坑 2:.tools(Object…) 重载吞掉 FunctionToolCallback

ChatClient.tools(...) 有多个重载。当你传的是 FunctionToolCallback 实例时,编译器可能匹配到 tools(Object... toolObjects) 这个重载——它内部 ToolCallbacks.from(toolObjects) 只接受 Method toolFunctionToolCallback 被静默忽略,且没有编译错误(issue #2495)。

// ❌ 可能被匹配到 tools(Object...),FunctionToolCallback 不生效
FunctionToolCallback cb1 = ...;
FunctionToolCallback cb2 = ...;
client.prompt().tools(cb1, cb2).call();

// ✅ 用 ToolCallback 类型,或分开逐个注册
ToolCallback cb1 = ...;
ToolCallback cb2 = ...;
client.prompt().tools(cb1, cb2).call();
// 或者
client.prompt().tools(cb1).tools(cb2).call();

坑 3:中途换了 options 实例,callback 丢失

工具回调只在 .call() 阶段被塞进“ChatClient 初始的那个 options 对象”。如果你在自定义的 tool 执行逻辑里,为模型换了一个全新的 options 实例,新实例里没有 callback,框架转成 ToolCallingOptions 想取回调时就是空的,于是 No ToolCallback found(issue #4734)。排查这种问题要看“options 是不是又新建了一份”。

解决方案

快速诊断:看请求里有没有 tools 字段

工具调用失效时的第一件事,是确认发给模型的请求里带没带 tools。最简单是开 DEBUG 日志,或临时打印请求。没带 tools,问题在注册/选项;带了 tools 但没执行,问题在解析或回调。

用 ToolCallingChatOptions 配 defaultOptions

需要 tool calling 就别用裸 ChatOptions.builder(),换 ToolCallingChatOptions

工具注册用 ToolCallback 类型,避免重载歧义

声明变量时用 ToolCallback 类型,而不是 FunctionToolCallback;或者干脆 .tools(...) 逐个调用。

别在 tool 执行链路里新建 options

复用最初构建 ChatClient 时的那个 options 对象(或通过 .copy() 复制它保留回调),不要 new 一个全新的。

小结

  • Tool Calling 失效是典型的“静默失效”,先查请求里有没有 tools 字段。
  • 三个高发坑:DefaultChatOptions 不支持工具、.tools(Object...) 重载吞 FunctionToolCallback、换 options 丢回调。
  • 记一句:要函数调用,用 ToolCallingChatOptions,注册时用 ToolCallback 类型,别中途换 options。

来源

最后更新于 2026-08-22

这篇帮到你了吗?

刚解决了一个棘手的报错?花两分钟记录下来,帮助下一个遇到同样问题的开发者。

贡献一条解法