尧图网站设计 尧图网站设计YAOTU DESIGN
ARTICLE DETAIL

资讯详情

深耕网站设计与一线实操的经验洞察。

Spring AI 工具调用:ChatClient 和 ChatModel 到底有什么区别?

Spring AI 工具调用:ChatClient 和 ChatModel 到底有什么区别? 前言很多刚上手 Spring AI 的同学会搞混ChatClient和底层ChatModel在工具调用上的区别。尤其在 Spring AI 2.0 正式发布之后底层ChatModel已经彻底移除了内置的工具自动循环逻辑两个 API 的行为发生了根本性变化。如果你是从 1.x 升级上来的老代码大概率已经无法正常工作。一句话核心结论先放前面ChatModel.call()底层原始接口不会自动循环执行工具上层ChatClient.prompt().call()才封装好了完整工具调用循环由内部ToolCallingAdvisor拦截处理。并且在 Spring AI 2.x 中internalToolExecutionEnabled属性已被彻底移除工具执行必须通过 ChatClient 或用户自行控制。1. 为什么 Spring AI 2.x 要重构工具调用在 Spring AI 1.x 中每个ChatModel实现OpenAI、Ollama、Anthropic、MistralAI 等都各自内嵌了一套私有的工具执行循环。代码功能重复、行为不一致、bug 各不相同而且开发者无法介入、观察或扩展这个循环——它是一个黑盒。Spring AI 2.0 对此进行了彻底的架构重构将工具调用循环从 ChatModel 内部提升到 Advisor 链中作为一等公民组件。这意味着工具调用不再是黑盒而是可组合、可观测、可扩展的标准组件。每次请求都经过有序的 Advisor 链工具循环由ToolCallingAdvisor这个递归 Advisor 驱动链上的其他 Advisor 可以拦截和观察每一次迭代。这一设计的直接结果就是ChatModel不再承担工具自动执行职责internalToolExecutionEnabled属性被移除所有 ChatModel 实现的内置工具循环全部被移除。工具执行要么交给 ChatClient 的ToolCallingAdvisor推荐方式要么由开发者通过DefaultToolCallingManager手动控制循环。2. ChatClient推荐使用自动工具执行模式ChatClient是 Spring AI 官方推荐的上层入口。在 Spring AI 2.x 中它默认通过自动注册的ToolCallingAdvisor来驱动工具调用循环。开发者只需要写一次.call()工具调用的多轮交互全部由框架内部完成。示例代码String answer ChatClient.create(chatModel).prompt(帮我查订单O202605070001).tools(new OrderTools()) // 注册带Tool注解的工具类.call().content();内部完整执行流程框架将用户提问 工具描述JSON Schema一起传给大模型大模型返回 FunctionCall JSON告知需要调用哪个工具、参数是什么ToolCallingAdvisor拦截到工具调用请求通过ToolCallingManager解析工具名称和入参反射调用对应的Tool方法将工具执行结果追加进对话历史再次请求大模型ToolCallingAdvisor使用callAdvisorChain.copy(this)创建子链在循环中反复调用下游 Advisor 链直到模型不再返回工具调用最终把自然语言结果返回给开发者这个循环过程是一个递归 Advisor 模式ToolCallingAdvisor自身作为递归 Advisor可以多次遍历下游 Advisor 链而链上的其他 Advisor如日志、权限校验能够观察和拦截每一次迭代。对话历史管理重要新特性ToolCallingAdvisor内置了conversationHistoryEnabled配置项。默认情况下trueAdvisor 在工具调用的每次迭代中维护完整的对话历史每次后续 LLM 调用都包含所有之前的消息用户消息、助手回复、工具回复。如果你已经有独立的ChatMemoryAdvisor管理对话历史可以通过.disableMemory()方法关闭内部历史管理避免重复维护导致 Token 浪费var toolCallingAdvisor ToolCallingAdvisor.builder().toolCallingManager(toolCallingManager).disableMemory().build();var chatMemoryAdvisor MessageChatMemoryAdvisor.builder(chatMemory).advisorOrder(BaseAdvisor.HIGHEST_PRECEDENCE 200).build();var chatClient ChatClient.builder(chatModel).defaultAdvisors(chatMemoryAdvisor, toolCallingAdvisor).build();直接返回功能当某个工具的执行结果本身就是最终答案、无需经过大语言模型再加工时可以在Tool注解上设置returnDirect true。ToolCallingAdvisor会中断工具调用循环将工具结果直接返回给调用方从而减少一次 LLM 调用降低延迟。✅ 优点极简代码开发者无感不用自己处理循环、消息拼接。✅ 适用绝大多数业务场景快速开发。3. ChatModel.call()底层原始 API手动工具执行在 Spring AI 2.x 中ChatModel是纯粹的“请求-响应”接口。调用chatModel.call(prompt)后如果模型返回 FunctionCall只会把工具调用信息放在ChatResponse中返回不会自动执行工具。开发者需要自己写循环核心步骤与 Spring AI 1.x 类似但需要显式使用ToolCallingManager来处理工具执行ChatModel chatModel ...;ToolCallingManager toolCallingManager DefaultToolCallingManager.builder().build();ChatPrompt prompt new ChatPrompt(帮我查订单O202605070001, List.of(new OrderTools()));ChatResponse response chatModel.call(prompt);while (response.hasToolCalls()) {// 1. 通过 ToolCallingManager 执行工具ToolExecutionResult result toolCallingManager.executeToolCalls(prompt, response);// 2. 获取包含工具结果的新 promptprompt result.conversationHistory();// 3. 再次请求模型response chatModel.call(prompt);}String finalContent response.getResult().getOutput().getContent();如果需要在工具调用真正执行前插入自定义逻辑如权限校验、参数审计、工具拦截可以实现自定义的ToolCallingManager或包装ToolCallback。✅ 适用场景生产环境需要自定义权限校验、审计日志、熔断限流、自定义工具调度逻辑。⚠️ 注意internalToolExecutionEnabled在 2.x 中已被移除不要再使用该配置项。4. 工具到底是谁写的非常容易踩坑框架负责自动生成工具 Schema 传给大模型、解析模型返回的 FunctionCall JSON、通过反射调用Tool方法、消息上下文组装。业务开发者负责工具内部业务逻辑。public class OrderTools {Tool(description 查询订单详情和物流)public OrderDTO queryOrder(ToolParam(description 订单编号) String orderId,ToolParam(description 是否返回物流) boolean includeLogistics){// 这里是你自己写的业务代码HTTP调用外部订单APIreturn restTemplate.getForObject(/api/orders/ orderId, OrderDTO.class);}}这里有一个极其重要的安全认知需要澄清模型从不直接执行代码。模型的角色是向服务端发起一张“调用申请单”FunctionCall JSON真正的鉴权、审批、限流与审计必须落在你自己的 JVM 代码与执行侧约束里。Tool注解只是告诉模型“有哪些工具可用”并不会替你调用第三方接口或执行数据库操作。Spring AI 2.x 还移除了toolNames()API 和SpringBeanToolCallbackResolver工具必须显式注册为 ToolCallback bean并通过.tools()传入不再支持按 bean 名称在请求时动态解析。这意味着工具的注册行为更加显式、更加可审计。5. 核心对比与版本迁移要点ChatClient vs ChatModel对比维度ChatClient推荐ChatModel底层工具自动循环✅ 默认开启由 ToolCallingAdvisor 驱动❌ 不自动执行收到 FunctionCall 即返回代码复杂度极低一次.call()需要手写 while 循环 ToolCallingManager对话历史管理Advisor 内置管理完全由开发者控制可扩展性通过 Advisor 链拦截、观测、扩展需要自定义 ToolCallingManager 或包装 ToolCallback适用场景绝大多数业务场景需要精细控制工具调度、审计、限流的生产场景Spring AI 1.x → 2.x 迁移要点工具执行循环已从 ChatModel 移除所有 ChatModel 实现OpenAI、Ollama、Anthropic 等的内置循环均已删除老代码中依赖ChatModel自动执行工具的逻辑将失效。internalToolExecutionEnabled已移除该属性不再存在无法通过它切换 ChatClient 的手动/自动模式。toolNames()和SpringBeanToolCallbackResolver已移除工具必须通过.tools()显式传入ToolCallback对象。ToolCallAdvisor重命名为ToolCallingAdvisor旧类名保留为废弃子类提供编译期的弃用警告。ChatMemory Advisor 默认置于 ToolCallingAdvisor 外层DEFAULT_CHAT_MEMORY_PRECEDENCE_ORDER被调低使记忆 Advisor 包裹工具循环而非参与每次迭代避免重复注入历史消息。6. 生产环境最佳实践6.1 安全治理是服务端责任工具描述Tool中的 description只能帮助模型选择工具不能替代安全策略。类型 Schema、业务校验、身份权限、审批、幂等、超时、审计和数据脱敏都必须在服务端落地。对于写操作类工具如提交工单、修改订单状态不应在自动循环中静默执行。正确的做法是AI 组装操作草稿 → 展示给用户确认 → 确认后才执行。这需要关闭ToolCallingAdvisor的自动注册改用手动模式控制执行时机。6.2 工具粒度与参数设计每个Tool方法应尽量保持单一职责避免一个方法承载过多业务逻辑。参数用ToolParam(description ...)标注描述要足够详细因为模型完全依赖这个描述来判断参数的含义和格式。对于可选参数使用Nullable标注框架会将其识别为可选参数。6.3 循环上限与熔断无论是自动模式还是手动模式都应设置工具调用的最大迭代次数。无限循环不仅会消耗大量 Token还可能导致业务流程失控。可以在自定义ToolCallingManager中加入计数器达到上限后返回稳定错误或转人工处理。7. 两套 Demo自动模式 手动模式Demo1ChatClient 自动工具调用日常开发首选Configurationpublic class ChatClientConfig {Beanpublic ChatClient chatClient(ChatModel chatModel) {return ChatClient.builder(chatModel).build();// ToolCallingAdvisor 由 DefaultChatClient 自动注册}}// 使用String res chatClient.prompt(帮我查订单O202605070001).tools(new OrderTools()).call().content();System.out.println(res);Demo2ChatModel 原生手动循环自主控制工具执行Servicepublic class ManualToolCallingService {private final ChatModel chatModel;private final ToolCallingManager toolCallingManager;public ManualToolCallingService(ChatModel chatModel) {this.chatModel chatModel;this.toolCallingManager DefaultToolCallingManager.builder().build();}public String queryOrder(String userMessage) {ChatPrompt prompt new ChatPrompt(userMessage, List.of(new OrderTools()));ChatResponse response chatModel.call(prompt);int maxIterations 5;int iteration 0;while (response.hasToolCalls() iteration maxIterations) {ToolExecutionResult result toolCallingManager.executeToolCalls(prompt, response);prompt result.conversationHistory();response chatModel.call(prompt);}return response.getResult().getOutput().getContent();}}8. 常见踩坑点Spring AI 1.x 的 ChatModel 自带工具循环升级到 2.x 后行为变更老代码会失效——必须将工具执行迁移到 ChatClient 或手动 ToolCallingManager。不要误以为Tool注解会自动帮你调用第三方接口——注解只是描述工具能力HTTP 请求、数据库操作等业务代码需要自己写。internalToolExecutionEnabled在 2.x 中已不存在——不要再尝试通过该属性切换模式。toolNames()已移除——工具必须通过.tools()显式传入 ToolCallback 对象不再支持按名称动态解析。手动模式适合精细化管控但代码量显著增加。普通业务优先选 ChatClient只有需要自定义权限校验、审批流程、循环上限控制等场景才建议切换到手动模式。结尾Spring AI 2.x 将工具自动循环能力从底层 ChatModel 剥离交给上层 ChatClient Advisor 体系实现。这不是简单的 API 变更而是一次架构层面的优雅分层工具调用循环从“各个模型实现各自的私有逻辑”变成了“Advisor 链中可组合、可观测、可扩展的一等公民组件”。上层 ChatClient 追求开发效率底层 ChatModel 保留完全控制权。在项目选型时根据是否需要自定义工具调度逻辑、权限校验和审计策略来选择 API 即可。
返回列表