大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图

大模型怎么自己调函数?Spring AI 2.0 Tool Calling 自动循环原理 + 完整时序图

系列:Spring AI 深入浅出(第 13 篇)

版本:Spring AI 2.0.1 / Spring Boot 4.1.x / JDK 21

标签:#SpringAI #ToolCalling #Agent #函数调用

前置阅读:第 6 篇《Advisor 责任链原理》

预计字数:7000 字|可复制源码 + 循环时序图 + 踩坑 FAQ

本文将解决 :让模型"会动手"的机制,2.0 用递归 Advisor 链 重写了,还省了 34%~64% token。本文讲清 @Tool、ToolCallingAdvisor 自动循环,并给出完整时序图。

你将学到:

  • Tool Calling 是什么(模型决定调不调、调哪个、传什么参)
  • 2.0 的架构变化:工具循环从 ChatModel 内部提升为递归 Advisor 链
  • @Tool 注解与参数 Schema
  • 自动多轮循环的完整时序
  • 2.0 独有的 ToolSearchToolCallingAdvisor(海量工具渐进暴露)

一、版本标注

本文基于 Spring AI 2.0.1 / Boot 4.1 / JDK 21。2.0 重点变化 :工具调用循环从各 ChatModel 内部提升为递归 Advisor 链 ,ChatClient 会自动注册 ToolCallingAdvisor;新增 ToolSearchToolCallingAdvisor。精确签名以 2.0.1 官方文档为准。


二、Tool Calling 是什么

大模型本身只会"生成文本",不会真的执行代码。Tool Calling(函数调用)的机制是:模型先输出"我要调用某个函数,参数是这些"的指令,框架收到后真正执行函数,把结果回喂给模型,模型再基于结果继续生成。

关键点:模型决定"调不调、调哪个、传什么参",框架负责"真正执行"。这让 AI 从"会聊"变成"能查订单、能发消息、能操作业务"。


三、2.0 架构变化:递归 Advisor 链(重点)

这是 2.0 最重要的架构升级之一:

复制代码
1.x:工具调用循环写在各 ChatModel 实现内部(每家实现一份,割裂)
2.0:工具调用循环提升为 ToolCallingAdvisor,挂到递归 Advisor 链上(统一)

#mermaid-svg-orQo8lI2Mjg9AzoB{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-orQo8lI2Mjg9AzoB .error-icon{fill:#552222;}#mermaid-svg-orQo8lI2Mjg9AzoB .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-orQo8lI2Mjg9AzoB .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-orQo8lI2Mjg9AzoB .marker{fill:#333333;stroke:#333333;}#mermaid-svg-orQo8lI2Mjg9AzoB .marker.cross{stroke:#333333;}#mermaid-svg-orQo8lI2Mjg9AzoB svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-orQo8lI2Mjg9AzoB p{margin:0;}#mermaid-svg-orQo8lI2Mjg9AzoB .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster-label text{fill:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster-label span{color:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster-label span p{background-color:transparent;}#mermaid-svg-orQo8lI2Mjg9AzoB .label text,#mermaid-svg-orQo8lI2Mjg9AzoB span{fill:#333;color:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB .node rect,#mermaid-svg-orQo8lI2Mjg9AzoB .node circle,#mermaid-svg-orQo8lI2Mjg9AzoB .node ellipse,#mermaid-svg-orQo8lI2Mjg9AzoB .node polygon,#mermaid-svg-orQo8lI2Mjg9AzoB .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-orQo8lI2Mjg9AzoB .rough-node .label text,#mermaid-svg-orQo8lI2Mjg9AzoB .node .label text,#mermaid-svg-orQo8lI2Mjg9AzoB .image-shape .label,#mermaid-svg-orQo8lI2Mjg9AzoB .icon-shape .label{text-anchor:middle;}#mermaid-svg-orQo8lI2Mjg9AzoB .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-orQo8lI2Mjg9AzoB .rough-node .label,#mermaid-svg-orQo8lI2Mjg9AzoB .node .label,#mermaid-svg-orQo8lI2Mjg9AzoB .image-shape .label,#mermaid-svg-orQo8lI2Mjg9AzoB .icon-shape .label{text-align:center;}#mermaid-svg-orQo8lI2Mjg9AzoB .node.clickable{cursor:pointer;}#mermaid-svg-orQo8lI2Mjg9AzoB .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-orQo8lI2Mjg9AzoB .arrowheadPath{fill:#333333;}#mermaid-svg-orQo8lI2Mjg9AzoB .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-orQo8lI2Mjg9AzoB .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-orQo8lI2Mjg9AzoB .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-orQo8lI2Mjg9AzoB .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-orQo8lI2Mjg9AzoB .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-orQo8lI2Mjg9AzoB .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster text{fill:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB .cluster span{color:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-orQo8lI2Mjg9AzoB .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-orQo8lI2Mjg9AzoB rect.text{fill:none;stroke-width:0;}#mermaid-svg-orQo8lI2Mjg9AzoB .icon-shape,#mermaid-svg-orQo8lI2Mjg9AzoB .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-orQo8lI2Mjg9AzoB .icon-shape p,#mermaid-svg-orQo8lI2Mjg9AzoB .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-orQo8lI2Mjg9AzoB .icon-shape rect,#mermaid-svg-orQo8lI2Mjg9AzoB .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-orQo8lI2Mjg9AzoB .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-orQo8lI2Mjg9AzoB .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-orQo8lI2Mjg9AzoB :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 模型要调工具,重入链
ChatClient
Advisor 链
ToolCallingAdvisor

自动注册
ChatModel

好处:

  • 统一可组合:工具调用和其他 Advisor(记忆、检索)在同一套链上协同
  • 可观测可扩展:工具调用成为链上标准环节,日志/拦截/审计统一
  • 省 token :官方数据,工具调用在 OpenAI/Anthropic/Gemini 上带来 34%~64% 的 token 节省(复用工具定义,减少重复发送)

四、@Tool 注解与参数 Schema

把 Java 方法暴露成工具,核心是 @Tool + @ToolParam:

java 复制代码
import org.springframework.ai.tool.annotation.Tool;
import org.springframework.ai.tool.annotation.ToolParam;

public class OrderTools {

    @Tool(description = "根据订单号查询订单信息")
    public Order getOrder(
            @ToolParam(description = "订单号") String orderId) {
        // 真实查询逻辑(查数据库等)
        return orderRepository.findById(orderId);
    }

    @Tool(description = "查询某用户的所有订单")
    public List<Order> listOrders(
            @ToolParam(description = "用户ID") String userId) {
        return orderRepository.findByUserId(userId);
    }
}

框架会把 @Tool 方法转成工具的 JSON Schema (方法名=工具名,@ToolParam 描述=参数说明),模型据此决定调哪个、传什么参。


五、自动多轮循环

挂上工具后,ChatClient 会自动完成"多轮循环",业务代码只写一次调用:

java 复制代码
@Bean
public ChatClient chatClient(ChatClient.Builder builder, OrderTools orderTools) {
    return builder
            .defaultTools(orderTools)   // 注册工具
            .build();
}

// 业务侧:一次调用,框架自动处理工具循环
String reply = chatClient.prompt()
        .user("帮我查一下订单 A001 的收货人是谁")
        .call()
        .content();

完整时序:
真实函数 模型 ToolCallingAdvisor ChatClient 用户 真实函数 模型 ToolCallingAdvisor ChatClient 用户 #mermaid-svg-ASeKVSUMPb4WKlgK{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-ASeKVSUMPb4WKlgK .error-icon{fill:#552222;}#mermaid-svg-ASeKVSUMPb4WKlgK .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-ASeKVSUMPb4WKlgK .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-ASeKVSUMPb4WKlgK .marker{fill:#333333;stroke:#333333;}#mermaid-svg-ASeKVSUMPb4WKlgK .marker.cross{stroke:#333333;}#mermaid-svg-ASeKVSUMPb4WKlgK svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-ASeKVSUMPb4WKlgK p{margin:0;}#mermaid-svg-ASeKVSUMPb4WKlgK .actor{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ASeKVSUMPb4WKlgK text.actor>tspan{fill:black;stroke:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .actor-line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ASeKVSUMPb4WKlgK .innerArc{stroke-width:1.5;stroke-dasharray:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .messageLine0{stroke-width:1.5;stroke-dasharray:none;stroke:#333;}#mermaid-svg-ASeKVSUMPb4WKlgK .messageLine1{stroke-width:1.5;stroke-dasharray:2,2;stroke:#333;}#mermaid-svg-ASeKVSUMPb4WKlgK #arrowhead path{fill:#333;stroke:#333;}#mermaid-svg-ASeKVSUMPb4WKlgK .sequenceNumber{fill:white;}#mermaid-svg-ASeKVSUMPb4WKlgK #sequencenumber{fill:#333;}#mermaid-svg-ASeKVSUMPb4WKlgK #crosshead path{fill:#333;stroke:#333;}#mermaid-svg-ASeKVSUMPb4WKlgK .messageText{fill:#333;stroke:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .labelBox{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ASeKVSUMPb4WKlgK .labelText,#mermaid-svg-ASeKVSUMPb4WKlgK .labelText>tspan{fill:black;stroke:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .loopText,#mermaid-svg-ASeKVSUMPb4WKlgK .loopText>tspan{fill:black;stroke:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .loopLine{stroke-width:2px;stroke-dasharray:2,2;stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);}#mermaid-svg-ASeKVSUMPb4WKlgK .note{stroke:#aaaa33;fill:#fff5ad;}#mermaid-svg-ASeKVSUMPb4WKlgK .noteText,#mermaid-svg-ASeKVSUMPb4WKlgK .noteText>tspan{fill:black;stroke:none;}#mermaid-svg-ASeKVSUMPb4WKlgK .activation0{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ASeKVSUMPb4WKlgK .activation1{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ASeKVSUMPb4WKlgK .activation2{fill:#f4f4f4;stroke:#666;}#mermaid-svg-ASeKVSUMPb4WKlgK .actorPopupMenu{position:absolute;}#mermaid-svg-ASeKVSUMPb4WKlgK .actorPopupMenuPanel{position:absolute;fill:#ECECFF;box-shadow:0px 8px 16px 0px rgba(0,0,0,0.2);filter:drop-shadow(3px 5px 2px rgb(0 0 0 / 0.4));}#mermaid-svg-ASeKVSUMPb4WKlgK .actor-man line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;}#mermaid-svg-ASeKVSUMPb4WKlgK .actor-man circle,#mermaid-svg-ASeKVSUMPb4WKlgK line{stroke:hsl(259.6261682243, 59.7765363128%, 87.9019607843%);fill:#ECECFF;stroke-width:2px;}#mermaid-svg-ASeKVSUMPb4WKlgK :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 查订单 A001 收货人请求第一轮:问题 + 工具列表指令:调用 getOrder("A001")执行 getOrder("A001")返回订单对象第二轮:问题 + 工具结果最终答案:收货人是张三回复收货人是张三

要点:模型第一轮"决定调工具",框架执行后第二轮把结果回喂,模型再生成最终答案。循环由框架自动完成,业务代码无感。


六、ToolSearchToolCallingAdvisor(2.0 独有)

工具少(几个)时,把所有工具定义都发给模型没问题。但工具多(几十上百)时,全发给模型会撑爆上下文。

2.0 的 ToolSearchToolCallingAdvisor 解决这个问题:不把全部工具定义发过去,而是按需检索------根据用户当前意图,只把最相关的几个工具暴露给模型。

java 复制代码
// 海量工具场景:用 ToolSearchToolCallingAdvisor 做"检索式"工具暴露
// 精确构造方式以 2.0.1 官方文档为准

适用:工具数量几十上百的企业中台、把整个服务的方法都暴露给 AI 的场景。


七、原理图解:递归 Advisor 链中的工具循环

#mermaid-svg-XSFaidcArE8sfj7O{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-XSFaidcArE8sfj7O .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-XSFaidcArE8sfj7O .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-XSFaidcArE8sfj7O .error-icon{fill:#552222;}#mermaid-svg-XSFaidcArE8sfj7O .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-XSFaidcArE8sfj7O .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-XSFaidcArE8sfj7O .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-XSFaidcArE8sfj7O .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-XSFaidcArE8sfj7O .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-XSFaidcArE8sfj7O .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-XSFaidcArE8sfj7O .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-XSFaidcArE8sfj7O .marker{fill:#333333;stroke:#333333;}#mermaid-svg-XSFaidcArE8sfj7O .marker.cross{stroke:#333333;}#mermaid-svg-XSFaidcArE8sfj7O svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-XSFaidcArE8sfj7O p{margin:0;}#mermaid-svg-XSFaidcArE8sfj7O .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-XSFaidcArE8sfj7O .cluster-label text{fill:#333;}#mermaid-svg-XSFaidcArE8sfj7O .cluster-label span{color:#333;}#mermaid-svg-XSFaidcArE8sfj7O .cluster-label span p{background-color:transparent;}#mermaid-svg-XSFaidcArE8sfj7O .label text,#mermaid-svg-XSFaidcArE8sfj7O span{fill:#333;color:#333;}#mermaid-svg-XSFaidcArE8sfj7O .node rect,#mermaid-svg-XSFaidcArE8sfj7O .node circle,#mermaid-svg-XSFaidcArE8sfj7O .node ellipse,#mermaid-svg-XSFaidcArE8sfj7O .node polygon,#mermaid-svg-XSFaidcArE8sfj7O .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-XSFaidcArE8sfj7O .rough-node .label text,#mermaid-svg-XSFaidcArE8sfj7O .node .label text,#mermaid-svg-XSFaidcArE8sfj7O .image-shape .label,#mermaid-svg-XSFaidcArE8sfj7O .icon-shape .label{text-anchor:middle;}#mermaid-svg-XSFaidcArE8sfj7O .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-XSFaidcArE8sfj7O .rough-node .label,#mermaid-svg-XSFaidcArE8sfj7O .node .label,#mermaid-svg-XSFaidcArE8sfj7O .image-shape .label,#mermaid-svg-XSFaidcArE8sfj7O .icon-shape .label{text-align:center;}#mermaid-svg-XSFaidcArE8sfj7O .node.clickable{cursor:pointer;}#mermaid-svg-XSFaidcArE8sfj7O .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-XSFaidcArE8sfj7O .arrowheadPath{fill:#333333;}#mermaid-svg-XSFaidcArE8sfj7O .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-XSFaidcArE8sfj7O .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-XSFaidcArE8sfj7O .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XSFaidcArE8sfj7O .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-XSFaidcArE8sfj7O .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XSFaidcArE8sfj7O .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-XSFaidcArE8sfj7O .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-XSFaidcArE8sfj7O .cluster text{fill:#333;}#mermaid-svg-XSFaidcArE8sfj7O .cluster span{color:#333;}#mermaid-svg-XSFaidcArE8sfj7O div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-XSFaidcArE8sfj7O .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-XSFaidcArE8sfj7O rect.text{fill:none;stroke-width:0;}#mermaid-svg-XSFaidcArE8sfj7O .icon-shape,#mermaid-svg-XSFaidcArE8sfj7O .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-XSFaidcArE8sfj7O .icon-shape p,#mermaid-svg-XSFaidcArE8sfj7O .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-XSFaidcArE8sfj7O .icon-shape rect,#mermaid-svg-XSFaidcArE8sfj7O .image-shape rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-XSFaidcArE8sfj7O .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-XSFaidcArE8sfj7O .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-XSFaidcArE8sfj7O :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 生成答案
调用工具
用户请求
ToolCallingAdvisor
模型决定?
返回
执行真实函数
结果回喂

循环的边界:设最大轮次,防止模型反复调工具不收敛(见踩坑 FAQ)。


八、踩坑 FAQ

坑 1:工具描述差,模型调错工具

现象 :模型调了错误的工具,或传错参数。

原因 :@Tool/@ToolParam 的 description 写得含糊。

解决:描述要具体("根据订单号查询订单"而非"查东西"),参数描述写清格式(第 14 篇展开)。

坑 2:工具循环不收敛

现象 :模型反复调工具,请求迟迟不结束。

原因 :没有轮次上限,或工具返回格式模型看不懂。

解决:设最大循环轮次;让工具返回清晰、可读的结果。

坑 3:工具抛异常直接 500

现象 :工具内部报错,整个请求失败。

原因 :工具异常没捕获,直接抛给框架。

解决:工具方法里 try-catch,把错误信息作为结果返回给模型,让模型知道"这一步失败了"(第 14 篇展开)。

坑 4:参数 Schema 缺 required 标注

现象 :模型调用时漏传必填参数。

原因 :参数描述里没明确必填性。

解决 :@ToolParam 里写明"必填",必要时用 required 标注。

坑 5:工具太多撑爆上下文

现象 :工具多时,请求 token 暴涨甚至超限。

原因 :所有工具定义都发给了模型。

解决 :用 ToolSearchToolCallingAdvisor 按需暴露相关工具。


九、小结与合集导航

本篇讲透了 2.0 的 Tool Calling:

  • 机制:模型决定、框架执行、结果回喂
  • 2.0 架构:递归 Advisor 链 + ToolCallingAdvisor 自动注册,省 34%~64% token
  • @Tool + @ToolParam 定义工具 Schema
  • 自动多轮循环 + ToolSearchToolCallingAdvisor 应对海量工具

下一篇:《自定义工具实战》------把现有 Java Bean 方法暴露给 AI,重点讲"工具描述工程"。


九、互动钩子

投票:你用过 Tool Calling 吗?

  • 还没用过
  • 用过,几个简单工具
  • 用过,工具很多

评论区聊聊你想把什么业务暴露给 AI;源码见置顶评论。

相关推荐
深频率1 小时前
苹果AI入华:11月4日开测,千问只是个选项
人工智能
一枕槐安4871 小时前
大模型 API 调用封装:统一适配 GPT、文心一言、通义千问
人工智能·gpt·文心一言·大模型api·流式输出·统一封装·多模态路由
neocheng_5221 小时前
校招时间有限,先补英语、计算机还是AI应用能力?从目标岗位判断优先级
人工智能
布吉岛的石头1 小时前
Java 程序员第 50 阶段13:对齐税 alignment tax 是什么
人工智能·深度学习·transformer
fpcc2 小时前
AI和大模型—神经网络的学习
人工智能·神经网络
乌暮2 小时前
请求进门先安检:Spring 拦截器与统一功能实战指南
java·开发语言·后端·学习·spring
乌恩大侠2 小时前
「AI-RAN」Aerial,使用ORAN C1模式
人工智能·5g·spark·o-ru·ai-ran