feat(agent): add multi-agent delegation tool for agent-to-agent collaboration

This commit is contained in:
matevip 2026-04-04 23:26:34 +08:00
parent 9c56b9830a
commit 2c3d805521
8 changed files with 194 additions and 0 deletions

View File

@ -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 委派
* <p>
* 允许当前 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<AgentEntity>()
.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<AgentEntity> agents = agentMapper.selectList(
new LambdaQueryWrapper<AgentEntity>()
.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<AgentEntity> agents = agentMapper.selectList(
new LambdaQueryWrapper<AgentEntity>()
.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() + " 字符]";
}
}

View File

@ -0,0 +1,36 @@
package vip.mate.tool.builtin;
/**
* 跟踪 Agent 委派调用深度防止无限递归
* <p>
* 使用 ThreadLocal 存储当前线程的委派层级
* 每次 {@link DelegateAgentTool} 发起委派时 +1返回后 -1
*
* @author MateClaw Team
*/
public final class DelegationContext {
private static final ThreadLocal<Integer> 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);
}
}
}

View File

@ -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,

View File

@ -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,

View File

@ -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 ServerFilesystem参考 MateClaw 文档中的 mcpServers.filesystem
INSERT INTO mate_mcp_server (
id, name, description, transport, url, headers_json, command, args_json, env_json, cwd,

View File

@ -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 ServerFilesystem参考 MateClaw 文档中的 mcpServers.filesystem
MERGE INTO mate_mcp_server (
id, name, description, transport, url, headers_json, command, args_json, env_json, cwd,

View File

@ -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();

View File

@ -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);