Spring AI Alibaba 项目源码学习(十二)-完结:Tool

news/2025/11/18 19:56:05/文章来源:https://www.cnblogs.com/wasp520/p/19239122

Tool 系统分析

请关注微信公众号:阿呆-bot

概述

本文档分析 Spring AI Alibaba Agent Framework 中的 Tool(工具)系统,包括工具的定义、注册、调用流程、扩展机制以及 AgentTool 的实现。

入口类说明

ToolCallback - 工具回调接口

ToolCallback 是 Spring AI 提供的工具接口,定义了工具的基本能力。

核心职责

  • 定义工具名称和描述
  • 定义工具输入输出 Schema
  • 执行工具调用
  • 返回工具结果

AgentTool - Agent 作为工具

AgentTool 将 ReactAgent 封装为工具,使 Agent 可以作为工具被其他 Agent 调用。

核心职责

  • 将 Agent 转换为 ToolCallback
  • 执行 Agent 调用
  • 返回 Agent 响应

关键代码

public class AgentTool implements BiFunction<String, ToolContext, AssistantMessage> {private final ReactAgent agent;public AgentTool(ReactAgent agent) {this.agent = agent;}@Overridepublic AssistantMessage apply(String input, ToolContext toolContext) {OverAllState state = (OverAllState) toolContext.getContext().get("state");try {// Copy state to avoid affecting the original state.// The agent that calls this tool should only be aware of the ToolCallChoice and ToolResponse.OverAllState newState = agent.getAndCompileGraph().cloneState(state.data());// Build the messages list to add// Add instruction first if present, then the user input// Note: We must add all messages at once because cloneState doesn't copy keyStrategies,// so multiple updateState calls would overwrite instead of appendjava.util.List<Message> messagesToAdd = new java.util.ArrayList<>();if (StringUtils.hasLength(agent.instruction())) {messagesToAdd.add(new AgentInstructionMessage(agent.instruction()));}messagesToAdd.add(new UserMessage(input));Map<String, Object> inputs = newState.updateState(Map.of("messages", messagesToAdd));Optional<OverAllState> resultState = agent.getAndCompileGraph().invoke(inputs);Optional<List> messages = resultState.flatMap(overAllState -> overAllState.value("messages", List.class));if (messages.isPresent()) {@SuppressWarnings("unchecked")List<Message> messageList = (List<Message>) messages.get();// Use messageListAssistantMessage assistantMessage = (AssistantMessage)messageList.get(messageList.size() - 1);return assistantMessage;}}catch (Exception e) {throw new RuntimeException(e);}throw new RuntimeException("Failed to execute agent tool or failed to get agent tool result");}public static ToolCallback getFunctionToolCallback(ReactAgent agent) {// convert agent inputType to json schemaString inputSchema = StringUtils.hasLength(agent.getInputSchema())? agent.getInputSchema(): (agent.getInputType() != null )? JsonSchemaGenerator.generateForType(agent.getInputType()): null;return FunctionToolCallback.builder(agent.name(), AgentTool.create(agent)).description(agent.description()).inputType(String.class) // the inputType for ToolCallback is always String.inputSchema(inputSchema).toolCallResultConverter(CONVERTER).build();}}

关键特性

  • 状态隔离:通过 cloneState() 创建独立状态,避免影响调用 Agent 的状态
  • 指令传递:支持传递 Agent 指令
  • Schema 生成:自动生成工具输入 Schema

AgentToolNode - 工具执行节点

AgentToolNode 是执行工具调用的 Graph 节点。

核心职责

  • 解析工具调用请求
  • 执行工具调用
  • 处理工具响应
  • 支持工具拦截器

关键代码

public class AgentToolNode implements NodeActionWithConfig {private List<ToolCallback> toolCallbacks = new ArrayList<>();private List<ToolInterceptor> toolInterceptors = new ArrayList<>();private ToolCallbackResolver toolCallbackResolver;@Overridepublic Map<String, Object> apply(OverAllState state, RunnableConfig config) throws Exception {List<Message> messages = (List<Message>) state.value("messages").orElseThrow();Message lastMessage = messages.get(messages.size() - 1);Map<String, Object> updatedState = new HashMap<>();Map<String, Object> extraStateFromToolCall = new HashMap<>();if (lastMessage instanceof AssistantMessage assistantMessage) {// execute the tool functionList<ToolResponseMessage.ToolResponse> toolResponses = new ArrayList<>();for (AssistantMessage.ToolCall toolCall : assistantMessage.getToolCalls()) {// Execute tool call with interceptor chainToolCallResponse response = executeToolCallWithInterceptors(toolCall, state, config, extraStateFromToolCall);toolResponses.add(response.toToolResponse());}ToolResponseMessage toolResponseMessage = new ToolResponseMessage(toolResponses, Map.of());updatedState.put("messages", toolResponseMessage);} else if (lastMessage instanceof ToolResponseMessage toolResponseMessage) {// Handle incremental tool execution// ...} else {throw new IllegalStateException("Last message is not an AssistantMessage or ToolResponseMessage");}// Merge extra state from tool callsupdatedState.putAll(extraStateFromToolCall);return updatedState;}

AgentLlmNode - LLM 节点

AgentLlmNode 负责调用 LLM,并将工具信息传递给模型。

关键代码

public class AgentLlmNode implements NodeActionWithConfig {private List<ToolCallback> toolCallbacks = new ArrayList<>();private List<ModelInterceptor> modelInterceptors = new ArrayList<>();private ChatClient chatClient;@Overridepublic Map<String, Object> apply(OverAllState state, RunnableConfig config) throws Exception {// add streaming supportboolean stream = config.metadata("_stream_", new TypeRef<Boolean>(){}).orElse(true);if (stream) {@SuppressWarnings("unchecked")List<Message> messages = (List<Message>) state.value("messages").get();augmentUserMessage(messages, outputSchema);renderTemplatedUserMessage(messages, state.data());// Create ModelRequestModelRequest modelRequest = ModelRequest.builder().messages(messages).options(toolCallingChatOptions).context(config.metadata().orElse(new HashMap<>())).build();// Create base handler that actually calls the model with streamingModelCallHandler baseHandler = request -> {try {Flux<ChatResponse> chatResponseFlux = buildChatClientRequestSpec(request).stream().chatResponse();return ModelResponse.of(chatResponseFlux);} catch (Exception e) {return ModelResponse.of(new AssistantMessage("Exception: " + e.getMessage()));}};// Chain interceptors if anyModelCallHandler chainedHandler = InterceptorChain.chainModelInterceptors(modelInterceptors, baseHandler);// Execute the chained handlerModelResponse modelResponse = chainedHandler.call(modelRequest);return Map.of(StringUtils.hasLength(this.outputKey) ? this.outputKey : "messages", modelResponse.getMessage());} else {// Non-streaming mode// ...}}

工具注册机制

工具注册流程

工具通过以下方式注册:

  1. 直接注册:通过 ReactAgent.Builder.tools() 方法注册
  2. Interceptor 工具:通过 ModelInterceptor.getTools() 方法提供
  3. 工具解析器:通过 ToolCallbackResolver 动态解析

关键代码

// Extract regular tools from user-provided toolsif (CollectionUtils.isNotEmpty(tools)) {regularTools.addAll(tools);}// Extract interceptor toolsList<ToolCallback> interceptorTools = new ArrayList<>();if (CollectionUtils.isNotEmpty(modelInterceptors)) {interceptorTools = modelInterceptors.stream().flatMap(interceptor -> interceptor.getTools().stream()).collect(Collectors.toList());}// Combine all tools: regularTools + regularToolsList<ToolCallback> allTools = new ArrayList<>();allTools.addAll(interceptorTools);allTools.addAll(regularTools);// Set combined tools to LLM nodeif (CollectionUtils.isNotEmpty(allTools)) {llmNodeBuilder.toolCallbacks(allTools);}AgentLlmNode llmNode = llmNodeBuilder.build();// Setup tool node with all available toolsAgentToolNode toolNode = null;if (resolver != null) {toolNode = AgentToolNode.builder().toolCallbackResolver(resolver).build();}else if (CollectionUtils.isNotEmpty(allTools)) {toolNode = AgentToolNode.builder().toolCallbacks(allTools).build();}else {toolNode = AgentToolNode.builder().build();}return new ReactAgent(llmNode, toolNode, buildConfig(), this);

工具调用流程

工具调用步骤

  1. LLM 生成工具调用:AgentLlmNode 调用 LLM,LLM 返回包含工具调用的 AssistantMessage
  2. 工具调用解析:AgentToolNode 解析 AssistantMessage 中的工具调用
  3. 工具执行:通过工具拦截器链执行工具调用
  4. 响应生成:将工具执行结果封装为 ToolResponseMessage
  5. 状态更新:更新状态中的消息列表

关键代码

/*** Execute a tool call with interceptor chain support.*/private ToolCallResponse executeToolCallWithInterceptors(AssistantMessage.ToolCall toolCall,OverAllState state,RunnableConfig config,Map<String, Object> extraStateFromToolCall) {// Create ToolCallRequestToolCallRequest request = ToolCallRequest.builder().toolCall(toolCall).context(config.metadata().orElse(new HashMap<>())).build();// Create base handler that actually executes the toolToolCallHandler baseHandler = req -> {ToolCallback toolCallback = resolve(req.getToolName());String result = toolCallback.call(req.getArguments(),new ToolContext(Map.of("state", state, "config", config, "extraState", extraStateFromToolCall)));return ToolCallResponse.of(req.getToolCallId(), req.getToolName(), result);};// Chain interceptors if anyToolCallHandler chainedHandler = InterceptorChain.chainToolInterceptors(toolInterceptors, baseHandler);// Execute the chained handlerreturn chainedHandler.call(request);}private ToolCallback resolve(String toolName) {return toolCallbacks.stream().filter(callback -> callback.getToolDefinition().name().equals(toolName)).findFirst().orElseGet(() -> toolCallbackResolver.resolve(toolName));}

工具扩展机制

自定义工具实现

开发者可以通过以下方式扩展工具:

  1. 实现 ToolCallback 接口:创建自定义工具
  2. 使用 FunctionToolCallback:将函数转换为工具
  3. AgentTool:将 Agent 转换为工具

工具类型

  • 函数工具:通过 FunctionToolCallback 将 Java 函数转换为工具
  • Agent 工具:通过 AgentTool 将 Agent 转换为工具
  • Interceptor 工具:通过 ModelInterceptor.getTools() 提供工具

关键类关系

以下 PlantUML 类图展示了 Tool 系统的类关系:

image.png

关键流程

以下 PlantUML 时序图展示了工具调用的完整流程:
image.png

实现关键点说明

1. 工具注册机制

工具通过多种方式注册:

  • 直接注册:通过 Builder 的 tools() 方法
  • Interceptor 工具:通过 ModelInterceptor.getTools()
  • 动态解析:通过 ToolCallbackResolver

2. 工具调用流程

工具调用经过以下步骤:

  1. LLM 生成工具调用请求
  2. AgentToolNode 解析工具调用
  3. 通过拦截器链执行工具
  4. 生成工具响应
  5. 更新状态

3. 拦截器支持

工具调用支持拦截器:

  • ToolInterceptor 可以拦截工具调用
  • 支持请求修改和响应处理
  • 支持重试、错误处理等功能

4. Agent 作为工具

Agent 可以作为工具被调用:

  • 通过 AgentTool 封装
  • 状态隔离,不影响调用 Agent
  • 支持指令传递

5. 工具解析器

支持动态工具解析:

  • ToolCallbackResolver 接口
  • 可以按需加载工具
  • 支持工具发现机制

6. 状态管理

工具调用可以更新状态:

  • 通过 extraStateFromToolCall 传递额外状态
  • 工具响应添加到消息列表
  • 支持状态合并

工具扩展示例

创建自定义工具

// 1. 使用 FunctionToolCallback
FunctionToolCallback tool = FunctionToolCallback.builder("myTool", (input, context) -> {// 工具逻辑return "result";
})
.description("My custom tool")
.build();// 2. 实现 ToolCallback 接口
public class MyTool implements ToolCallback {@Overridepublic String call(String arguments, ToolContext context) {// 工具逻辑return "result";}@Overridepublic ToolDefinition getToolDefinition() {return ToolDefinition.builder().name("myTool").description("My custom tool").build();}
}// 3. 将 Agent 转换为工具
ReactAgent subAgent = ReactAgent.builder().name("subAgent").description("Sub agent").model(model).build();ToolCallback agentTool = AgentTool.getFunctionToolCallback(subAgent);

总结说明

核心设计理念

  1. 统一接口:所有工具实现 ToolCallback 接口
  2. 灵活注册:支持多种工具注册方式
  3. 拦截器支持:工具调用支持拦截器链
  4. Agent 工具化:Agent 可以作为工具被调用

关键优势

  • 灵活性:支持多种工具类型和注册方式
  • 可扩展性:易于添加新的工具实现
  • 可组合性:Agent 可以作为工具,实现 Agent 嵌套
  • 拦截器支持:支持工具调用的拦截和修改

解决的问题

  • 工具集成:统一工具接口,简化工具集成
  • Agent 嵌套:通过 AgentTool 实现 Agent 嵌套调用
  • 动态工具:通过 ToolCallbackResolver 支持动态工具加载
  • 工具拦截:通过拦截器支持工具调用的增强

使用场景

  • 函数工具:将业务函数封装为工具
  • Agent 工具:将 Agent 封装为工具,实现多 Agent 协作
  • Interceptor 工具:通过拦截器提供内置工具
  • 动态工具:通过解析器动态加载工具

Tool 系统为 Agent Framework 提供了强大的工具能力,使开发者能够灵活地扩展 Agent 的功能,实现复杂的业务需求。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/969290.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

ftp,sftp,scp,tftp几种简单对比,以及python实现ftp功能

ftp,sftp,scp,tftp几种简单对比,以及python实现ftp功能对比如下:特性维度FTPSFTPSCPTFTP安全性 明文传输 基于SSH加密 基于SSH加密 无加密默认端口 21 22 22 69协议基础 TCP SSH SSH UDP认证方式 用户名/密码 多种(…

实用指南:深入解析音频编解码器(Audio CODEC):硬件、接口与驱动开发

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

福利MegaLLM–175刀免费额度建教程

0.简介 MegaLLM 是一个 API 中转服务,支持主流模型 OpenAI、Anthropic、Google、Meta 等,以及包括国产千问、DeepSeek、GLM、K2 等。可以在 Claude Code、 Codex、OpenCode、Kilocode、RooCode... 1. 注册就送 75 刀…

C# 常用控件(学习笔记8)

1. TreeView 树形控件/// <summary> /// 添加 /// </summary> /// <param name="sender"></param> /// <param name="e"></param> private void BtnTreeAdd_…

模拟赛记录 11/18

显然不应该把别人的模拟赛指认成自己的。

代码随想录Day14_

代码随想录Day14_226. 翻转二叉树 - 力扣(LeetCode)class Solution { public:TreeNode* invertTree(TreeNode* root) {if(root==NULL) return root;swap(root->left,root->right);invertTree(root->left);i…

白嫖MegaLLM–175刀免费额度建教程

0.简介 MegaLLM 是一个 API 中转服务,支持主流模型 OpenAI、Anthropic、Google、Meta 等,以及包括国产千问、DeepSeek、GLM、K2 等。可以在 Claude Code、 Codex、OpenCode、Kilocode、RooCode... 1. 注册就送 75 刀…

如何找到适合好用的 AI 数据分析工具?Aloudata Agent 值得一试!

AI 数据分析软件则通过自然语言交互、智能问数、自动化建模查询等技术,让业务人员无需写复杂的 SQL 即可自主获取数据洞察,快速定位问题根因,并生成结构化决策建议。AI 数据分析软件显著提升企业决策精准性与敏捷性…

linux burpsuite

Burp Suite 是一个用于 Web 应用程序安全测试的工具,主要用于拦截和修改 HTTP 请求/响应,进行安全测试,如漏洞扫描、渗透测试等。它不是 Linux 系统的一部分,而是独立的软件,通常通过下载安装包进行部署。 如果你…

linux bug

您提到的“Linux bug”可能是指Linux系统中出现的bug或问题。Linux是一个开源操作系统,其稳定性、性能和安全性在社区的持续维护下不断提升。如果您遇到Linux系统中的问题,比如崩溃、性能下降、功能异常等,可以具体…

linux broadcom

您提到的 + #引号 + Linux Broadcom + #引号 + 可能是指与 Broadcom 公司相关的 Linux 系统或驱动,特别是在网络设备、无线网卡(如 RTL8812AE、RTL8814AE 等)的驱动支持方面。一、Broadcom 无线网卡驱动支持 …

Duan.ai - 将长视频变成适合社交的短视频AI工具

将长视频变成适合社交的短视频AI工具 现在的视频平台越来越“快节奏”。 抖音、快手、小红书、B站、YouTube Shorts…… 用户只愿意给内容 3 秒钟的耐心。 可现实是:我们手里大量素材都是 几分钟甚至几十分钟的长视频…

DS trick record 1

考虑一类经典的问题,形如有置换 \(x\leftarrow F(x)\),满足在一个值 \(B\) 次过后有 \(x=F(x)\)。 比较常见的是对序列维护区间置换,区间半群(或更弱)和。 例如 P4145 花神游历各国,其中 \(F(x)=\lfloor\sqrt x\…

2025年11月成都房产律师,成都合同纠纷律师,成都刑事律师事务所推荐,实力律所解析委托无忧之选!

《2025年11月成都房产律师、合同纠纷律师、刑事律师事务所推荐:四川颂贤律师事务所》在2025年11月的成都,如果您正在寻找专业可靠的房产律师、合同纠纷律师或者刑事律师事务所,四川颂贤律师事务所会是一个值得考虑的…

2025年11月成都建设工程律师,成都执业律师,成都经济纠纷律师事务所推荐:聚焦办案实力与胜诉口碑!

2025年11月成都建设工程律师、执业律师、经济纠纷律师事务所推荐:聚焦办案实力与胜诉口碑!在2025年11月的成都,当人们面临建设工程、经济纠纷等各类法律问题时,寻找一家靠谱的律师事务所至关重要。四川颂贤律师事务…

2025年11月成都合同律师,成都律师,成都婚姻律师事务所推荐,资深经验与品牌保障口碑之选!

2025年11月,成都合同与婚姻法律服务优选——四川颂贤律师事务所在2025年11月的成都,当人们遇到合同纠纷或者婚姻相关法律问题时,一家值得信赖的律师事务所显得尤为重要。四川颂贤律师事务所便是众多寻求法律帮助者的…

(CF2166) Codeforces Round 1064 (Div. 2)

CF2166A. Same Difference 显然最后只会变成原串的最后一个字符,考虑其在串中出现次数即可。 #include <bits/stdc++.h> using namespace std; string s; int cnt[26], len;void prepare() {cin >> len &…

详细介绍:【C++庖丁解牛】哈希表/散列表的设计原理 | 哈希函数

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

Balatro GBA - 在Game Boy Advance上体验扑克 Roguelike

Balatro GBA是一个非官方的粉丝项目,旨在Game Boy Advance平台上重现Balatro游戏的核心玩法。该项目包含完整的扑克手牌分析、小丑卡牌系统、盲注机制等核心功能,采用GBA硬件优化技术实现流畅的游戏体验。Balatro GB…

在线离线

在线&离线在线和离线可以简单的理解为对于所有的操作是否需要读入完毕。在线: 询问还没有结束就输出回答,即边问边运行,问一句答一句 如树套树,且带有“可持久化”的算法(主席树(可持久化线段树)) 离线: 在…