From 2c3d8055210f2bfd572ce1671f8f51977a8d927d Mon Sep 17 00:00:00 2001 From: matevip Date: Sat, 4 Apr 2026 23:26:34 +0800 Subject: [PATCH] feat(agent): add multi-agent delegation tool for agent-to-agent collaboration --- .../mate/tool/builtin/DelegateAgentTool.java | 130 ++++++++++++++++++ .../mate/tool/builtin/DelegationContext.java | 36 +++++ .../src/main/resources/db/data-en.sql | 5 + .../src/main/resources/db/data-mysql-en.sql | 5 + .../src/main/resources/db/data-mysql-zh.sql | 5 + .../src/main/resources/db/data-zh.sql | 5 + .../main/resources/db/tools-sync-mysql.sql | 4 + .../src/main/resources/db/tools-sync.sql | 4 + 8 files changed, 194 insertions(+) create mode 100644 mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegateAgentTool.java create mode 100644 mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegationContext.java diff --git a/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegateAgentTool.java b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegateAgentTool.java new file mode 100644 index 00000000..9f76f3e2 --- /dev/null +++ b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegateAgentTool.java @@ -0,0 +1,130 @@ +package vip.mate.tool.builtin; + +import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.ai.tool.annotation.Tool; +import org.springframework.ai.tool.annotation.ToolParam; +import org.springframework.stereotype.Component; +import vip.mate.agent.AgentService; +import vip.mate.agent.model.AgentEntity; +import vip.mate.agent.repository.AgentMapper; + +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; + +/** + * 内置工具:Agent 委派 + *

+ * 允许当前 Agent 将子任务委派给另一个 Agent 执行,实现多 Agent 协作。 + * 被委派的 Agent 在独立会话中运行,结果作为工具观察返回给调用方。 + * + * @author MateClaw Team + */ +@Slf4j +@Component +@RequiredArgsConstructor +public class DelegateAgentTool { + + private static final int MAX_DELEGATION_DEPTH = 3; + private static final int MAX_RESULT_LENGTH = 4000; + + private final AgentService agentService; + private final AgentMapper agentMapper; + + @Tool(description = """ + 委派任务给另一个 Agent 执行,实现多 Agent 协作。 + 当前任务需要特定领域的 Agent 协助时使用此工具。 + 目标 Agent 将在独立会话中执行任务,返回其最终回复。 + 注意:需提供完整的任务上下文,目标 Agent 无法看到当前对话历史。""") + public String delegateToAgent( + @ToolParam(description = "目标 Agent 的名称(精确匹配)") String agentName, + @ToolParam(description = "要委派的任务描述,必须包含完整上下文信息") String task) { + + // 1. 参数校验 + if (agentName == null || agentName.isBlank()) { + return "[错误] 请指定目标 Agent 名称。" + availableAgentsHint(); + } + if (task == null || task.isBlank()) { + return "[错误] 请提供任务描述。"; + } + + // 2. 递归深度检查 + int depth = DelegationContext.currentDepth(); + if (depth >= MAX_DELEGATION_DEPTH) { + return "[错误] 委派层级已达上限(" + MAX_DELEGATION_DEPTH + " 层),无法继续委派,请直接处理任务。"; + } + + // 3. 按名称查找目标 Agent + AgentEntity target = agentMapper.selectOne( + new LambdaQueryWrapper() + .eq(AgentEntity::getName, agentName.trim()) + .eq(AgentEntity::getEnabled, true)); + + if (target == null) { + return "[错误] 未找到名为「" + agentName + "」的已启用 Agent。" + availableAgentsHint(); + } + + // 4. 在独立会话中执行 + String tempConversationId = "delegate-" + UUID.randomUUID(); + log.info("Agent 委派: depth={}, target={}({}), task={}", + depth + 1, target.getName(), target.getId(), + task.length() > 100 ? task.substring(0, 100) + "..." : task); + + DelegationContext.enter(); + try { + String result = agentService.chat(target.getId(), task, tempConversationId); + String truncated = truncate(result, MAX_RESULT_LENGTH); + return "[Agent「" + target.getName() + "」的回复]\n\n" + truncated; + } catch (Exception e) { + log.error("Agent 委派执行失败: target={}, error={}", target.getName(), e.getMessage(), e); + return "[错误] Agent「" + target.getName() + "」执行失败: " + e.getMessage(); + } finally { + DelegationContext.exit(); + } + } + + @Tool(description = "列出所有可用的 Agent(已启用),包括名称、类型和描述。用于在委派前了解有哪些 Agent 可以协助。") + public String listAvailableAgents() { + List agents = agentMapper.selectList( + new LambdaQueryWrapper() + .eq(AgentEntity::getEnabled, true) + .orderByAsc(AgentEntity::getName)); + + if (agents.isEmpty()) { + return "当前没有可用的 Agent。"; + } + + StringBuilder sb = new StringBuilder("可用 Agent 列表:\n\n"); + for (AgentEntity agent : agents) { + sb.append("- **").append(agent.getName()).append("**"); + sb.append(" (").append(agent.getAgentType()).append(")"); + if (agent.getDescription() != null && !agent.getDescription().isBlank()) { + sb.append(" — ").append(agent.getDescription()); + } + sb.append("\n"); + } + return sb.toString(); + } + + private String availableAgentsHint() { + List agents = agentMapper.selectList( + new LambdaQueryWrapper() + .eq(AgentEntity::getEnabled, true) + .select(AgentEntity::getName)); + if (agents.isEmpty()) { + return ""; + } + String names = agents.stream() + .map(AgentEntity::getName) + .collect(Collectors.joining("、")); + return "\n可用 Agent: " + names; + } + + private static String truncate(String text, int maxLength) { + if (text == null) return ""; + if (text.length() <= maxLength) return text; + return text.substring(0, maxLength) + "\n\n... [结果已截断,原文共 " + text.length() + " 字符]"; + } +} diff --git a/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegationContext.java b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegationContext.java new file mode 100644 index 00000000..f466e3fb --- /dev/null +++ b/mateclaw-server/src/main/java/vip/mate/tool/builtin/DelegationContext.java @@ -0,0 +1,36 @@ +package vip.mate.tool.builtin; + +/** + * 跟踪 Agent 委派调用深度,防止无限递归。 + *

+ * 使用 ThreadLocal 存储当前线程的委派层级。 + * 每次 {@link DelegateAgentTool} 发起委派时 +1,返回后 -1。 + * + * @author MateClaw Team + */ +public final class DelegationContext { + + private static final ThreadLocal DEPTH = ThreadLocal.withInitial(() -> 0); + + private DelegationContext() {} + + /** 获取当前委派深度(0 = 顶层调用) */ + public static int currentDepth() { + return DEPTH.get(); + } + + /** 进入下一层委派 */ + public static void enter() { + DEPTH.set(DEPTH.get() + 1); + } + + /** 退出当前委派层 */ + public static void exit() { + int current = DEPTH.get(); + if (current <= 1) { + DEPTH.remove(); + } else { + DEPTH.set(current - 1); + } + } +} diff --git a/mateclaw-server/src/main/resources/db/data-en.sql b/mateclaw-server/src/main/resources/db/data-en.sql index 8532607b..04395dfd 100644 --- a/mateclaw-server/src/main/resources/db/data-en.sql +++ b/mateclaw-server/src/main/resources/db/data-en.sql @@ -329,6 +329,11 @@ MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, KEY (id) VALUES (1000000013, 'MateClawDocTool', 'MateClaw Docs', 'Read built-in MateClaw project documentation. action=list to list docs, action=read to read specific doc.', 'builtin', 'mateClawDocTool', '📚', TRUE, TRUE, NOW(), NOW(), 0); +-- Built-in tool: Agent Delegation (Multi-Agent Collaboration) +MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +KEY (id) +VALUES (1000000014, 'DelegateAgentTool', 'Agent Delegation', 'Delegate tasks to other Agents for multi-agent collaboration. Call target Agent by name, run in isolated session and return result.', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0); + -- Example MCP Server: Filesystem (see MateClaw docs mcpServers.filesystem) MERGE INTO mate_mcp_server ( id, name, description, transport, url, headers_json, command, args_json, env_json, cwd, diff --git a/mateclaw-server/src/main/resources/db/data-mysql-en.sql b/mateclaw-server/src/main/resources/db/data-mysql-en.sql index 1484c1e8..ffde81f0 100644 --- a/mateclaw-server/src/main/resources/db/data-mysql-en.sql +++ b/mateclaw-server/src/main/resources/db/data-mysql-en.sql @@ -331,6 +331,11 @@ INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name VALUES (1000000013, 'MateClawDocTool', 'MateClaw Docs', 'Read built-in MateClaw project documentation. action=list to list docs, action=read to read specific doc.', 'builtin', 'mateClawDocTool', '📚', TRUE, TRUE, NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), enabled=VALUES(enabled), builtin=VALUES(builtin), update_time=VALUES(update_time), deleted=VALUES(deleted); +-- Built-in tool: Agent Delegation (Multi-Agent Collaboration) +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000014, 'DelegateAgentTool', 'Agent Delegation', 'Delegate tasks to other Agents for multi-agent collaboration. Call target Agent by name, run in isolated session and return result.', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0) +ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), enabled=VALUES(enabled), builtin=VALUES(builtin), update_time=VALUES(update_time), deleted=VALUES(deleted); + -- Example MCP Server: Filesystem (see MateClaw docs mcpServers.filesystem) INSERT INTO mate_mcp_server (id, name, description, transport, url, headers_json, command, args_json, env_json, cwd, enabled, connect_timeout_seconds, read_timeout_seconds, last_status, last_error, diff --git a/mateclaw-server/src/main/resources/db/data-mysql-zh.sql b/mateclaw-server/src/main/resources/db/data-mysql-zh.sql index 224c1d4b..f3118276 100644 --- a/mateclaw-server/src/main/resources/db/data-mysql-zh.sql +++ b/mateclaw-server/src/main/resources/db/data-mysql-zh.sql @@ -331,6 +331,11 @@ INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name VALUES (1000000013, 'MateClawDocTool', 'MateClaw 文档', '读取 MateClaw 内置项目文档。action=list 列出所有文档,action=read 读取指定文档内容(如 zh/config.md)。', 'builtin', 'mateClawDocTool', '📚', TRUE, TRUE, NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), enabled=VALUES(enabled), builtin=VALUES(builtin), update_time=VALUES(update_time), deleted=VALUES(deleted); +-- 内置工具:Agent 委派(多 Agent 协作) +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000014, 'DelegateAgentTool', 'Agent 委派', '委派任务给其他 Agent 执行,实现多 Agent 协作。支持按名称调用目标 Agent,在独立会话中运行并返回结果。', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0) +ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), enabled=VALUES(enabled), builtin=VALUES(builtin), update_time=VALUES(update_time), deleted=VALUES(deleted); + -- 示例 MCP Server:Filesystem(参考 MateClaw 文档中的 mcpServers.filesystem) INSERT INTO mate_mcp_server ( id, name, description, transport, url, headers_json, command, args_json, env_json, cwd, diff --git a/mateclaw-server/src/main/resources/db/data-zh.sql b/mateclaw-server/src/main/resources/db/data-zh.sql index 90a39ad6..c38ef42e 100644 --- a/mateclaw-server/src/main/resources/db/data-zh.sql +++ b/mateclaw-server/src/main/resources/db/data-zh.sql @@ -329,6 +329,11 @@ MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, KEY (id) VALUES (1000000013, 'MateClawDocTool', 'MateClaw 文档', '读取 MateClaw 内置项目文档。action=list 列出所有文档,action=read 读取指定文档内容(如 zh/config.md)。', 'builtin', 'mateClawDocTool', '📚', TRUE, TRUE, NOW(), NOW(), 0); +-- 内置工具:Agent 委派(多 Agent 协作) +MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +KEY (id) +VALUES (1000000014, 'DelegateAgentTool', 'Agent 委派', '委派任务给其他 Agent 执行,实现多 Agent 协作。支持按名称调用目标 Agent,在独立会话中运行并返回结果。', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0); + -- 示例 MCP Server:Filesystem(参考 MateClaw 文档中的 mcpServers.filesystem) MERGE INTO mate_mcp_server ( id, name, description, transport, url, headers_json, command, args_json, env_json, cwd, diff --git a/mateclaw-server/src/main/resources/db/tools-sync-mysql.sql b/mateclaw-server/src/main/resources/db/tools-sync-mysql.sql index 818bf888..1a134090 100644 --- a/mateclaw-server/src/main/resources/db/tools-sync-mysql.sql +++ b/mateclaw-server/src/main/resources/db/tools-sync-mysql.sql @@ -45,3 +45,7 @@ ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), de INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) VALUES (1000000011, 'WorkspaceMemoryTool', '工作区记忆', '读写数据库中的工作区 Markdown 文档,用于维护 PROFILE.md、MEMORY.md 和 memory/YYYY-MM-DD.md 等持久记忆。', 'builtin', 'workspaceMemoryTool', '🧠', TRUE, TRUE, NOW(), NOW(), 0) ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), builtin=VALUES(builtin), update_time=NOW(); + +INSERT INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +VALUES (1000000014, 'DelegateAgentTool', 'Agent 委派', '委派任务给其他 Agent 执行,实现多 Agent 协作。支持按名称调用目标 Agent,在独立会话中运行并返回结果。', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0) +ON DUPLICATE KEY UPDATE name=VALUES(name), display_name=VALUES(display_name), description=VALUES(description), tool_type=VALUES(tool_type), bean_name=VALUES(bean_name), icon=VALUES(icon), builtin=VALUES(builtin), update_time=NOW(); diff --git a/mateclaw-server/src/main/resources/db/tools-sync.sql b/mateclaw-server/src/main/resources/db/tools-sync.sql index 605d2608..f39e28e8 100644 --- a/mateclaw-server/src/main/resources/db/tools-sync.sql +++ b/mateclaw-server/src/main/resources/db/tools-sync.sql @@ -48,3 +48,7 @@ VALUES (1000000010, 'DocumentExtractTool', '文档文本提取', '从 PDF、Word MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) KEY (id) VALUES (1000000011, 'WorkspaceMemoryTool', '工作区记忆', '读写数据库中的工作区 Markdown 文档,用于维护 PROFILE.md、MEMORY.md 和 memory/YYYY-MM-DD.md 等持久记忆。', 'builtin', 'workspaceMemoryTool', '🧠', TRUE, TRUE, NOW(), NOW(), 0); + +MERGE INTO mate_tool (id, name, display_name, description, tool_type, bean_name, icon, enabled, builtin, create_time, update_time, deleted) +KEY (id) +VALUES (1000000014, 'DelegateAgentTool', 'Agent 委派', '委派任务给其他 Agent 执行,实现多 Agent 协作。支持按名称调用目标 Agent,在独立会话中运行并返回结果。', 'builtin', 'delegateAgentTool', '🤝', TRUE, TRUE, NOW(), NOW(), 0);