ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

Spring AI 2.0 Tool Calling 深度实战:从 @Tool 注解到 ToolCallingAdvisor 源码分析与 Agent 工具编排落地

Spring AI 2.0 Tool Calling 深度实战:从 @Tool 注解到 ToolCallingAdvisor 源码分析与 Agent 工具编排落地 模型只是系统中的一个组件而 Agent 的核心不是模型能力而是工具调用循环。一、为什么 Tool Calling 是 Java Agent 的生死线2026 年 6 月 12 日Spring AI 2.0.0 GA 正式发布。Azul《2026 State of Java Survey》报告显示62% 的企业已使用 Java 开发 AI 功能较去年 50% 显著提升。但真正让 Java 工程师兴奋的不是能调大模型 API——这是任何语言都能做的事——而是 Spring AI 2.0 把工具调用循环从 ChatModel 内部搬到了 Advisor 链变成了一等公民。这意味着什么在 Spring AI 1.x 中每个 ChatModel 实现内部都藏着一个私有的工具执行循环。功能上能用但开发者无法介入、观察或扩展。你能调用工具但不能在工具调用之上构建更复杂的行为——比如授权拦截、审计日志、重试策略、迭代次数限制。一个真正的 Agent 不是带系统提示词的聊天端点而是一个循环选择工具 → 执行代码 → 检查结果 → 继续推理直到能回答或必须停止。这个循环如果在模型实现的黑盒里你做不了任何工程化管控。Spring AI 2.0 把这个循环提升为 Advisor 链中的标准组件ToolCallingAdvisor——可组合、可观测、可扩展。这是 Java 从调 API走向构建 Agent的关键一跃。本文将从源码级别拆解这套机制围绕一个企业知识库 Agent项目展开实战最后给出生产环境踩坑清单。二、核心原理ToolCallingAdvisor 的循环机制源码级拆解2.1 整体架构Advisor 链与循环入口Spring AI 2.0 的 ChatClient 将每个请求通过一条有序的 Advisor 链传递并支持循环——一个 Advisor 可以重新进入下游链。这套机制同时驱动工具调用循环、结构化输出重试循环和评估循环。Advisor 链的最底部固定压两个终结 Advisor非流式的ChatModelCallAdvisor和流式的ChatModelStreamAdvisor它们的getOrder()都是最低优先级永远排最后负责真正调用ChatModel.call(prompt)。ToolCallingAdvisor是一条递归 Advisor——它会反复重新进入下游链直到满足停止条件模型产生不包含工具调用的响应。DefaultChatClient自动将其添加到 Advisor 链中且整个链中只允许存在一个 ToolAdvisor。2.2 核心循环do...while 而非递归很多文章说 ToolCallingAdvisor 是递归 Advisor但翻开源码循环主体是一个显式的do...while(isToolCall)结构不是方法递归// ToolCallingAdvisor 核心循环改写自源码省略部分细节 do { // 1. 前置处理注入工具定义、合并对话历史 processedChatClientRequest this.doBeforeCall(processedChatClientRequest, callAdvisorChain); // 2. 调下游链 → ChatModelCallAdvisor → ChatModel.call() chatClientResponse callAdvisorChain.copy(this).nextCall(processedChatClientRequest); // 3. 后置处理检查响应 chatClientResponse this.doAfterCall(chatClientResponse, callAdvisorChain); // 4. 判断是否需要继续工具调用 isToolCall this.toolExecutionEligibilityChecker.isToolCallResponse(chatResponse); if (isToolCall) { // 5. 执行工具调用 ToolExecutionResult toolExecutionResult this.toolCallingManager.executeToolCalls( processedChatClientRequest.prompt(), chatResponse); // 6. 如果工具标记为直接返回跳出循环 if (toolExecutionResult.returnDirect()) break; // 7. 拼接下一轮指令消息 instructions this.doGetNextInstructionsForToolCall( processedChatClientRequest, chatClientResponse, toolExecutionResult); } } while (isToolCall);这里有几个关键设计决策为什么用 do...while 而不是递归递归在深度工具调用时会栈溢出而 do...while 是堆上的循环没有深度限制。一个复杂的 Agent 任务可能需要 10 轮工具调用递归方案不可靠。四个 Hook 方法——ToolCallingAdvisor暴露了四个扩展点供子类覆盖Hook 方法调用时机典型用途doInitializeLoop循环开始前初始化工具索引、设置上下文doBeforeCall每次调 LLM 前注入授权信息、记录审计日志、限制迭代次数doAfterCall每次 LLM 响应后检查响应合规性、记录工具调用链路doFinalizeLoop循环结束时清理资源、汇总调用统计2.3 Tool 注解体系从方法到 JSON SchemaSpring AI 2.0 用Tool注解替代了 1.x 的Function核心注解定义Target({ElementType.METHOD}) Retention(RetentionPolicy.RUNTIME) public interface Tool { String name() default ; String description() default ; boolean schemaDynamic() default true; // 2.0 新增动态 Schema }工具自动解析的核心类DefaultToolCallbackResolverpublic class DefaultToolCallbackResolver implements ToolCallbackResolver { Override public ListToolCallback resolve(Object bean) { return MethodIntrospector.selectMethods( bean.getClass(), ToolAnnotationUtils::isToolMethod) .stream() .map(this::createToolCallback) .toList(); } }它通过MethodIntrospector.selectMethods扫描带Tool注解的方法封装为MethodToolCallback。Spring AI 自动生成 JSON Schema 作为工具定义传递给大模型。ToolParam注解为每个参数添加描述和可选/必填提示Nullable标注的参数默认被视为可选。一个完整的工具定义示例Component public class KnowledgeBaseTools { private final DocumentService documentService; private final VectorStore vectorStore; public KnowledgeBaseTools(DocumentService documentService, VectorStore vectorStore) { this.documentService documentService; this.vectorStore vectorStore; } Tool(description 在企业知识库中搜索相关文档支持自然语言查询) public String searchKnowledgeBase( ToolParam(description 用户的自然语言查询) String query, ToolParam(description 返回文档数量默认5) Nullable Integer topK) { int limit topK ! null ? topK : 5; ListDocument docs vectorStore.similaritySearch( SearchRequest.builder() .query(query) .topK(limit) .similarityThreshold(0.7) .build()); return docs.stream() .map(d - 标题: d.getMetadata().get(title) \n内容: d.getText()) .collect(Collectors.joining(\n---\n)); } Tool(description 将新文档上传到知识库自动分块并向量化存储) public String uploadDocument( ToolParam(description 文档标题) String title, ToolParam(description 文档正文内容) String content, ToolParam(description 文档分类标签) Nullable String category) { Document doc new Document(content, Map.of( title, title, category, category ! null ? category : 未分类, uploadTime, Instant.now().toString() )); vectorStore.add(List.of(doc)); return 文档《 title 》已成功上传并索引; } }2.4 ToolCallingManager工具执行的编排中枢ToolCallingManager默认实现DefaultToolCallingManager是工具执行的核心编排器位于org.springframework.ai.model.tool包。它的核心职责public class DefaultToolCallingManager implements ToolCallingManager { private final ToolCallbackResolver toolCallbackResolver; // 1. 解析工具定义从请求选项中提取工具定义 public ListToolDefinition resolveToolDefinitions(ToolCallingChatOptions options) { // 从 ToolCallback 中提取 name、description、inputSchema // 转换为模型特定的工具格式OpenAI FunctionTool / Anthropic Tool 等 } // 2. 执行工具调用根据模型响应执行对应工具 public ToolExecutionResult executeToolCalls(Prompt prompt, ChatResponse chatResponse) { ListToolCall toolCalls chatResponse.getMetadata().getToolCalls(); ListMessage toolResponseMessages new ArrayList(); for (ToolCall toolCall : toolCalls) { // 通过 Resolver 查找对应的 ToolCallback ToolCallback callback toolCallbackResolver.resolve(toolCall.name()); if (callback null) { toolResponseMessages.add(new ToolResponseMessage( toolCall.id(), 工具未找到: toolCall.name())); continue; } // 构建工具上下文包含历史消息记录 ToolContext toolContext buildToolContext(prompt, toolCall); // 执行工具 String result callback.call(toolCall.arguments(), toolContext); toolResponseMessages.add( new ToolResponseMessage(toolCall.id(), result)); } return new ToolExecutionResult(toolResponseMessages); } // 3. 构建工具上下文 private ToolContext buildToolContext(Prompt prompt, ToolCall toolCall) { // 提供上下文信息和历史 Message 记录 // 工具方法可通过 ToolContext 获取整个上下文 } }关键设计点ToolCallbackResolver是工具查找的入口。DelegatingToolCallbackResolver采用组合模式先查StaticToolCallbackResolver从ToolCallbackProvider获取的静态注册工具再查SpringBeanToolCallbackResolver从 Spring 上下文按 name 动态获取。ToolCallResultConverter负责把工具返回的对象转为字符串——如果是复杂对象会通过 Jackson 序列化为 JSON。returnDirect标记如果工具标记了直接返回循环立即跳出工具结果直接作为最终响应返回给用户。这适用于不需要模型再加工的场景比如文件下载、图片生成。2.5 ToolExecutionEligibilityChecker循环的守门人ToolExecutionEligibilityChecker是一个函数式接口决定模型响应是否应该触发下一轮工具调用FunctionalInterface public interface ToolExecutionEligibilityChecker { boolean isToolCallResponse(Nullable ChatResponse chatResponse); }默认实现很简单——只要响应携带工具调用就继续// 默认 Checker chatResponse - chatResponse ! null chatResponse.hasToolCalls()但生产环境中这个默认值不够用。不同模型提供商的finishReason语义不一致OpenAI 用tool_callsAnthropic 用tool_use某些模型在停止时也会返回工具调用结构但 finishReason 标记为stop。一个更严格的 CheckerToolExecutionEligibilityChecker strictChecker response - response ! null response.hasToolCalls() tool_calls.equals( response.getResult().getMetadata().getFinishReason()); var advisor ToolCallingAdvisor.builder() .toolExecutionEligibilityChecker(strictChecker) .build();不配这个你在某些模型上会遇到finishReason 是 stop 但 hasToolCalls 返回 true的边界 case导致无意义的空工具调用循环。三、代码实战企业知识库 Agent 工具编排3.1 项目背景我们构建一个企业知识库 Agent支持以下能力-searchKnowledgeBase语义搜索内部文档-uploadDocument上传新文档到知识库-getProjectStatus查询项目管理系统中的项目进度-sendNotification通过企业微信发送通知用户通过自然语言与 Agent 对话Agent 自主决定调用哪些工具、以什么顺序调用。3.2 完整配置与工具定义Configuration public class AgentConfig { Bean public ChatClient chatClient(ChatModel chatModel, KnowledgeBaseTools knowledgeTools, ProjectTools projectTools, NotificationTools notificationTools) { return ChatClient.builder(chatModel) .defaultSystem( 你是企业知识库助手。你可以搜索文档、上传文档、查询项目状态和发送通知。 请根据用户需求自主选择工具必要时可以组合使用多个工具。 工具调用前请说明你的计划调用后请总结结果。 ) .defaultTools(knowledgeTools, projectTools, notificationTools) // ToolCallingAdvisor 由 DefaultChatClient 自动注册 // 无需手动添加 .build(); } } Component public class ProjectTools { private final ProjectService projectService; public ProjectTools(ProjectService projectService) { this.projectService projectService; } Tool(description 查询指定项目的当前进度、里程碑和风险状态) public String getProjectStatus( ToolParam(description 项目名称或项目编号) String projectName) { Project project projectService.findByName(projectName); if (project null) { return 未找到项目: projectName; } return String.format( 项目: %s 状态: %s 进度: %d%% 当前里程碑: %s 风险项: %d 个 , project.getName(), project.getStatus(), project.getProgress(), project.getCurrentMilestone(), project.getRisks().size()); } } Component public class NotificationTools { private final WeChatWorkClient weChatClient; private final AuditLogService auditLog; public NotificationTools(WeChatWorkClient weChatClient, AuditLogService auditLog) { this.weChatClient weChatClient; this.auditLog auditLog; } Tool(description 通过企业微信群机器人发送通知消息) public String sendNotification( ToolParam(description 目标群聊名称) String groupName, ToolParam(description 通知内容) String message) { try { // 审计日志记录谁在什么时候通过AI发送了什么消息 auditLog.record(AI_NOTIFICATION, groupName, message); weChatClient.sendToGroup(groupName, message); return 通知已发送到群: groupName; } catch (Exception e) { return 发送失败: e.getMessage(); } } }3.3 调用 AgentRestController RequestMapping(/api/agent) public class AgentController { private final ChatClient chatClient; public AgentController(ChatClient chatClient) { this.chatClient chatClient; } PostMapping(/chat) public String chat(RequestBody ChatRequest request) { return chatClient.prompt() .user(request.getMessage()) .advisors(a - a.param( ChatMemory.CONVERSATION_ID, request.getSessionId())) .call() .content(); } }用户输入帮我查一下订单系统的项目进度然后把风险项通知到技术群Agent 会1. 调用getProjectStatus(订单系统)获取项目信息2. 从返回的风险项中提取关键信息3. 调用sendNotification(技术群, 订单系统风险: ...)发送通知4. 综合两步结果返回用户整个多步工具调用编排由ToolCallingAdvisor的循环自动完成开发者不需要手写编排逻辑。3.4 生产级增强自定义 ToolCallingAdvisor 子类默认的ToolCallingAdvisor没有迭代次数限制。在生产环境中模型可能陷入反复调用同一个工具但不改参数的死循环。我们需要自定义一个有界的 Advisorpublic class BoundedToolCallingAdvisor extends ToolCallingAdvisor { private final int maxIterations; private static final String ITERATION_COUNT_KEY tool.call.iteration; public BoundedToolCallingAdvisor(ToolCallingManager manager, ToolExecutionEligibilityChecker checker, int maxIterations) { super(manager, checker, BaseAdvisor.HIGHEST_PRECEDENCE 300, true); this.maxIterations maxIterations; } Override protected ChatClientRequest doBeforeCall( ChatClientRequest request, CallAdvisorChain chain) { // 获取当前迭代次数 Integer iteration request.context() .get(ITERATION_COUNT_KEY, Integer.class); int current iteration ! null ? iteration : 0; if (current maxIterations) { throw new ToolCallLimitExceededException( 工具调用超过最大迭代次数: maxIterations); } // 递增计数 request.context().put(ITERATION_COUNT_KEY, current 1); // 审计日志 log.info(Tool call iteration {}/{} for session {}, current 1, maxIterations, request.context().get(ChatMemory.CONVERSATION_ID)); return request; } Override protected ChatClientResponse doAfterCall( ChatClientResponse response, CallAdvisorChain chain) { // 检测重复工具调用相同工具相同参数连续出现3次 if (isRepeatedToolCall(response)) { log.warn(检测到重复工具调用强制终止循环); // 返回一个不含工具调用的响应来终止循环 return ChatClientResponse.builder() .content(检测到重复工具调用已终止。) .build(); } return response; } private boolean isRepeatedToolCall(ChatClientResponse response) { // 实现检查最近3次工具调用的namearguments是否完全相同 // 需要从 conversation history 中提取 return false; // 简化 } }注册到 ChatClientBean public ChatClient chatClient(ChatModel chatModel, ToolCallingManager toolCallingManager, KnowledgeBaseTools tools) { var boundedAdvisor new BoundedToolCallingAdvisor( toolCallingManager, new StrictToolEligibilityChecker(), // 严格 finishReason 检查 20 // 最多 20 轮工具调用 ); return ChatClient.builder(chatModel) .defaultSystem(SYSTEM_PROMPT) .defaultTools(tools) .defaultAdvisors(boundedAdvisor) .build(); }四、进阶ToolSearchToolCallingAdvisor 渐进式工具披露4.1 Schema 通胀问题当 Agent 连接的服务多了——Slack、GitHub、Jira、MCP 服务器——工具库迅速膨胀。Spring AI 官方警告典型的多服务器配置轻松聚合 50 个工具在对话开始前就消耗 55,000 tokens。更糟的是模型面对 30 个相似名称的工具时选择准确率显著下降。ToolSearchToolCallingAdvisor通过渐进式工具披露解决不为全量工具建索引每次请求只把与当前问题最相关的子集发给模型模型确有需要再按需检索补充。官方基准测试显示跨 OpenAI、Anthropic 和 Gemini 模型token 消耗降低34%-64%且无明显精度损失。4.2 三种索引策略选型索引类型适用场景依赖Token 节省中文支持regex (默认)工具少、描述规范、英文场景零依赖中等差lucene工具多、关键词检索、英文为主lucene-core较高一般vector工具间语义相近、用户口语化、多语言VectorStore Bean最高好配置方式spring: ai: chat: client: tool-search-advisor: enabled: true tool-index-type: vector # regex | lucene | vector max-results: 5 # 每次返回最多5个工具定义手动配置方式Bean ToolIndex toolIndex(VectorStore vectorStore) { return new VectorToolIndex(vectorStore); } Bean ToolSearchToolCallingAdvisor toolSearchAdvisor(ToolIndex toolIndex) { return ToolSearchToolCallingAdvisor.builder() .toolIndex(toolIndex) .maxResults(5) .sessionIdKeyName(tenantId) // 多租户隔离 .build(); }4.3 记忆与工具循环的位置关系这是 Spring AI 2.0 最容易被误解的设计。MessageChatMemoryAdvisor相对于ToolCallingAdvisor的位置决定了记忆仓库捕获多少对话上下文记忆在循环外默认order HIGHEST_PRECEDENCE 200小于 ToolCallingAdvisor 的 300- 循环开始前加载一次历史- 只持久化最终的用户消息和助手消息- 工具请求和响应消息不写入仓库- 对所有 ChatMemoryRepository 实现都安全- 匹配 1.x 行为记忆在循环内order ToolCallingAdvisor.DEFAULT_ORDER如 400- 每次迭代都会触发记忆 Advisor- 持久化完整的工具请求/响应记录- LLM 在后续对话中能推理之前试了什么、调了哪些工具-必须禁用 ToolCallingAdvisor 的内部对话历史disableInternalConversationHistory()- 不是所有 ChatMemoryRepository 都支持工具消息类型2.0 中支持完整消息集的内置仓库InMemoryChatMemoryRepository、RedisChatMemoryRepository、Neo4jChatMemoryRepository。JDBC 仓库默认不支持工具消息序列化——如果你在循环内用 JDBC 仓库工具消息会被丢弃。// 记忆在循环内——完整审计模式 var toolCallingAdvisor ToolCallingAdvisor.builder() .disableInternalConversationHistory() // 让记忆 Advisor 接管历史 .advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE 300) .build(); var chatMemoryAdvisor MessageChatMemoryAdvisor.builder(chatMemory) .order(BaseAdvisor.HIGHEST_PRECEDENCE 400) // 在循环内 .build(); var chatClient ChatClient.builder(chatModel) .defaultAdvisors(chatMemoryAdvisor, toolCallingAdvisor) .build();使用自动注册的 ToolCallingAdvisor 时如果 DefaultChatClient 检测到有 MemoryAdvisor 放在循环内会自动禁用内部历史——无需额外配置。但手动构建时必须自己调用。五、生产踩坑清单坑 1中文工具描述 regex 索引 工具消失现象工具明明存在模型却不会了——昨天还会的技能今天突然找不到。根因ToolSearchToolCallingAdvisor默认使用 regex 索引对中文工具描述的召回能力极差。regex 本质是正则匹配中文分词机制导致关键词无法正确匹配。解决方案中文工具集必须使用 vector 索引。如果你复用了 RAG 流程中的向量库直接注入即可Bean ToolIndex toolIndex(VectorStore vectorStore) { // 复用已有的向量存储零额外成本 return new VectorToolIndex(vectorStore); }坑 2JDBC 记忆仓库静默丢弃工具消息现象Agent 在循环内模式下JDBC 仓库写入正常无报错但后续对话中 LLM 完全不记得之前调过什么工具。根因JDBC 仓库的默认 schema 只建模了普通 User/Assistant 消息不支持ToolResponseMessage和工具调用请求消息的序列化。写入时不报错但静默丢弃。解决方案- 循环内模式只使用InMemoryChatMemoryRepository、RedisChatMemoryRepository或Neo4jChatMemoryRepository- 如果必须用 JDBC 且需要完整工具消息记录使用社区项目 Spring-AI-Session计划纳入 2.1坑 3工具循环无限迭代烧 Token现象模型反复调用同一个工具参数不变循环不停直到 Token 耗尽或超时。根因默认ToolCallingAdvisor没有迭代次数上限。模型在复杂任务中可能卡住——尤其是工具返回的结果与模型预期不符时。解决方案使用自定义子类如上文BoundedToolCallingAdvisor在doBeforeCall中计数并限制最大迭代次数。同时在doAfterCall中检测重复工具调用模式。坑 4finishReason 语义不一致导致空转现象某些模型在 finishReason 为stop表示我应该停止了时hasToolCalls() 仍返回 true导致循环继续空转。根因不同模型提供商的 finishReason 语义不一致。默认ToolExecutionEligibilityChecker只检查hasToolCalls()不看 finishReason。解决方案配置严格 Checker同时检查 finishReasonToolExecutionEligibilityChecker strictChecker response - response ! null response.hasToolCalls() !stop.equals( response.getResult().getMetadata().getFinishReason());坑 5工具缺少授权和输入校验现象用户通过自然语言诱导 Agent 调用危险工具删库、发全员通知没有拦截。根因模型从不执行代码——它只是请求调用工具。实际执行权在应用代码。但很多实现直接把工具方法暴露给模型没有任何授权层。解决方案利用AugmentedToolCallbackProvider注入内思维参数在工具执行前记录模型推理过程同时在工具方法内部做权限检查// 1. 定义内思维记录 public record AgentThinking( ToolParam(description 调用此工具的推理过程) String innerThought ) {} // 2. 包装工具 AugmentedToolCallbackProvider toolProvider AugmentedToolCallbackProvider.builder() .toolObject(new NotificationTools(weChatClient, auditLog)) .argumentType(AgentThinking.class) .argumentConsumer(event - auditLog.record(AI_REASONING, event.toolDefinition().name(), event.arguments().innerThought())) .build(); // 3. 工具方法内部做权限检查 Tool(description 发送全员通知需要管理员权限) public String sendAllStaffNotification( ToolParam(description 通知内容) String message, ToolContext context) { String userId context.getContext().get(userId).toString(); if (!authService.isAdmin(userId)) { return 权限不足需要管理员权限; } // ... 发送逻辑 }坑 6流式模式下工具调用行为不一致现象阻塞模式.call()工具调用正常但切到流式.stream()后工具不执行或行为异常。根因ToolCallingAdvisor同时实现了CallAdvisor和StreamAdvisor但流式模式下的工具调用循环通过doAfterStream和doFinalizeLoopStream处理逻辑路径不同。某些自定义子类只覆盖了doAfterCall忘了doAfterStream。解决方案如果自定义了 ToolCallingAdvisor必须同时覆盖 Call 和 Stream 两套 Hook 方法。如果只需要阻塞模式在adviseStream中直接透传给下游即可。六、AugmentedToolCallbackProvider结构化思维链这是 Spring AI 2.0 一个容易被忽略但极具价值的功能。AugmentedToolCallbackProvider包装一个工具对象拦截模型的工具参数在不修改工具实现的前提下动态扩展输入 Schema。核心用法——强制模型在调用工具前表达推理过程public record AgentThinking( ToolParam(description 你调用此工具的推理过程包括为什么需要调用、期望得到什么结果) String innerThought ) {} AugmentedToolCallbackProvider toolProvider AugmentedToolCallbackProvider.builder() .toolObject(knowledgeBaseTools) .argumentType(AgentThinking.class) .argumentConsumer(event - { // 记录模型的推理过程到审计日志 auditLog.record(AI_REASONING, event.toolDefinition().name(), event.arguments().innerThought()); // 可选基于推理过程做风控判断 if (containsDangerousIntent(event.arguments().innerThought())) { throw new ToolExecutionBlockedException( 检测到危险意图工具调用被拦截); } }) .build(); ChatClient chatClient ChatClient.builder(chatModel) .defaultTools(toolProvider) .build();模型看到的是增强后的 Schema包含 innerThought 字段但原始工具方法只接收自己的参数——AugmentedToolCallbackProvider 在中间做了拆分。这等于给每个工具调用加了一层结构化的思维链比从响应文本里正则提取推理可靠得多。七、总结Spring AI 2.0 的 Tool Calling 架构可以浓缩为三层┌───────────────────────────────────────────────────┐ │ ChatClient用户入口 │ ├───────────────────────────────────────────────────┤ │ Advisor Chain有序链 循环支持 │ │ ┌─────────┐ ┌─────────────────────┐ ┌────────┐│ │ │ Memory │→ │ ToolCallingAdvisor │→ │ChatModel││ │ │Advisor │ │ do...while(loop) │ │ Call ││ │ └─────────┘ └─────────────────────┘ └────────┘│ │ │ │ │ ┌──────┴──────┐ │ │ │ToolCalling │ │ │ │ Manager │ │ │ └──────┬──────┘ │ │ │ │ │ ┌──────┴──────┐ │ │ │ Tool 方法 │ │ │ │业务逻辑 │ │ │ └─────────────┘ │ └───────────────────────────────────────────────────┘对 Java 工程师的意义当 AI Agent 需要在凌晨 3 点调用你的支付网关——带着重试逻辑、熔断器、类型安全校验和审计日志——这是一个分布式系统问题。模型只是系统中的一个组件而 Java 在过去二十年里一直在解决这类问题。Spring AI 2.0 的 Tool Calling 架构让 Java 工程师能用最熟悉的 Spring 模式构建生产级 Agent——依赖注入、AOP 切面、类型安全、可观测性一个都不少。从 1.x 迁移的关键动作1.toolNames()和SpringBeanToolCallbackResolver已移除——工具必须注册为 ToolCallback Bean 并通过.tools()显式传递2. 各 ChatModel 内部的工具循环不再存在——全部由 ToolCallingAdvisor 接管3. 工具定义方式从Function迁移到Tool参数描述用ToolParam4. 如果之前依赖模型内部的工具循环行为迁移后需要测试记忆持久化和流式模式的行为变化一句话总结未来的竞争不在于会不会调 API而在于能不能把 AI 能力融入业务。Spring AI 2.0 的 Tool Calling 就是 Java 开发者抓住这波机会的桥梁。
返回列表