From 879f5f243a39ed298cca5afcae49aa30d4402ebe Mon Sep 17 00:00:00 2001 From: oudecheng <13802883547@139.com> Date: Wed, 17 Jun 2026 17:55:22 +0800 Subject: [PATCH] =?UTF-8?q?feat(task):=20=E5=A2=9E=E5=8A=A0=E5=AD=90?= =?UTF-8?q?=E4=BB=A3=E7=90=86=E6=9C=80=E5=A4=A7=E5=B5=8C=E5=A5=97=E6=B7=B1?= =?UTF-8?q?=E5=BA=A6=E6=94=AF=E6=8C=81=EF=BC=8C=E6=9B=B4=E6=96=B0=E7=9B=B8?= =?UTF-8?q?=E5=85=B3=E6=96=87=E6=A1=A3=E5=92=8C=E6=8F=90=E7=A4=BA?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/config/mod.rs | 2 +- src/gateway/runtime.rs | 16 ++++++++--- src/gateway/tool_registry_factory.rs | 19 ++++-------- src/tools/registry.rs | 43 +++++++++++++++++++++------- src/tools/task/prompt.rs | 2 +- src/tools/task/tool.rs | 2 +- 6 files changed, 53 insertions(+), 31 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index d4af3ff..03ad281 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -242,7 +242,7 @@ fn default_task_ttl_hours() -> u64 { } fn default_task_max_nesting_depth() -> u32 { - 1 + 2 } fn default_task_allowed_tools() -> Vec { diff --git a/src/gateway/runtime.rs b/src/gateway/runtime.rs index 64c895c..5be8404 100644 --- a/src/gateway/runtime.rs +++ b/src/gateway/runtime.rs @@ -17,7 +17,7 @@ use crate::storage::{ }; use crate::tools::{ DefaultSubAgentRuntime, InMemoryTaskRepository, NoopSessionMessageSender, - SessionMessageSender, SubAgentRuntimeConfig, SubagentCatalog, ToolRegistry, + SessionMessageSender, SubAgentRuntimeConfig, SubagentCatalog, TaskTool, ToolRegistry, }; use crate::tools::task::repository::TaskRepository; use crate::tools::todo_write::TodoItem; @@ -173,7 +173,7 @@ pub(crate) fn build_session_manager_with_sender( // Create SubAgentRuntime (if task tool is enabled) let (factory, task_repository): (_, Arc) = if task_config.enabled { let task_repository = Arc::new(InMemoryTaskRepository::new()); - // Build subagent tools with MCP tools + // Build subagent tools with MCP tools (task tool registered separately below) let subagent_tools = Arc::new( factory.build_subagent_tools( if mcp_tools_for_subagents.is_empty() { @@ -200,20 +200,28 @@ pub(crate) fn build_session_manager_with_sender( runtime_config, task_repository.clone(), conversations.clone(), - subagent_tools, + subagent_tools.clone(), provider_config.clone(), catalog, bus.clone(), store.clone(), )); + // 注册 task 工具到子代理工具集(需在 runtime 创建之后,打破循环依赖) + if factory.is_enabled("task") { + subagent_tools.register(TaskTool::new_with_depth( + subagent_runtime.clone(), + task_config.max_nesting_depth, + )); + } + (factory.with_subagent_runtime(subagent_runtime), task_repository) } else { (factory, Arc::new(InMemoryTaskRepository::new())) }; // Build base tools - let mut tools = factory.build(); + let tools = factory.build(); // Register MCP tools to main agent (async) // Note: MCP tools for subagents are already collected above diff --git a/src/gateway/tool_registry_factory.rs b/src/gateway/tool_registry_factory.rs index 2cf3508..182dc8e 100644 --- a/src/gateway/tool_registry_factory.rs +++ b/src/gateway/tool_registry_factory.rs @@ -88,7 +88,7 @@ impl ToolRegistryFactory { self } - fn is_enabled(&self, tool_name: &str) -> bool { + pub(crate) fn is_enabled(&self, tool_name: &str) -> bool { !self.disabled_tools.contains(tool_name) } @@ -99,7 +99,7 @@ impl ToolRegistryFactory { } pub(crate) fn build(&self) -> ToolRegistry { - let mut registry = ToolRegistry::new(); + let registry = ToolRegistry::new(); if self.is_enabled("calculator") { registry.register(CalculatorTool::new()); @@ -171,13 +171,14 @@ impl ToolRegistryFactory { registry } - /// 构建子代理专用工具集(不包含 task 工具防止递归) + /// 构建子代理专用工具集 /// 可选地包含 MCP 工具(通过 mcp_tools 参数传递) + /// 注意:task 工具由调用方在 runtime 就绪后通过 registry.register() 单独注册 pub(crate) fn build_subagent_tools( &self, mcp_tools: Option>, ) -> ToolRegistry { - let mut registry = ToolRegistry::new(); + let registry = ToolRegistry::new(); // 基础工具 if self.is_enabled("calculator") { @@ -243,16 +244,6 @@ impl ToolRegistryFactory { } } - // 注册 task 工具,允许子代理创建孙代理(深度由 TaskTool 运行时控制) - if self.is_enabled("task") && self.task_config.enabled { - if let Some(runtime) = &self.subagent_runtime { - registry.register(TaskTool::new_with_depth( - runtime.clone(), - self.task_config.max_nesting_depth, - )); - } - } - registry } } diff --git a/src/tools/registry.rs b/src/tools/registry.rs index cb72371..63ba82e 100644 --- a/src/tools/registry.rs +++ b/src/tools/registry.rs @@ -1,36 +1,51 @@ use std::collections::HashMap; +use std::sync::{Arc, RwLock}; use crate::domain::tools::{Tool, ToolFunction}; use super::traits::Tool as ToolTrait; pub struct ToolRegistry { - tools: HashMap>, + tools: RwLock>>, } impl ToolRegistry { pub fn new() -> Self { Self { - tools: HashMap::new(), + tools: RwLock::new(HashMap::new()), } } - pub fn register(&mut self, tool: T) { - self.tools.insert(tool.name().to_string(), Box::new(tool)); + pub fn register(&self, tool: T) { + self.tools + .write() + .expect("ToolRegistry lock poisoned") + .insert(tool.name().to_string(), Arc::new(tool)); } - pub fn get(&self, name: &str) -> Option<&Box> { - self.tools.get(name) + pub fn get(&self, name: &str) -> Option> { + self.tools + .read() + .expect("ToolRegistry lock poisoned") + .get(name) + .cloned() } /// Get all registered tools. /// Used for concurrent tool execution when we need to look up tools by name. - pub fn get_all(&self) -> Vec<&Box> { - self.tools.values().collect() + pub fn get_all(&self) -> Vec> { + self.tools + .read() + .expect("ToolRegistry lock poisoned") + .values() + .cloned() + .collect() } pub fn get_definitions(&self) -> Vec { self.tools + .read() + .expect("ToolRegistry lock poisoned") .values() .map(|tool| Tool { tool_type: "function".to_string(), @@ -44,11 +59,19 @@ impl ToolRegistry { } pub fn has_tools(&self) -> bool { - !self.tools.is_empty() + !self.tools + .read() + .expect("ToolRegistry lock poisoned") + .is_empty() } pub fn tool_names(&self) -> Vec { - self.tools.keys().cloned().collect() + self.tools + .read() + .expect("ToolRegistry lock poisoned") + .keys() + .cloned() + .collect() } } diff --git a/src/tools/task/prompt.rs b/src/tools/task/prompt.rs index 301d87f..cb10a5d 100644 --- a/src/tools/task/prompt.rs +++ b/src/tools/task/prompt.rs @@ -51,7 +51,7 @@ impl SubagentPromptBuilder { 1. 专注于完成任务,不要偏离目标\n\ 2. 使用可用的工具进行必要操作\n\ 3. 完成后给出简洁的总结\n\ - 4. 不要尝试创建新的子代理任务\n\n\ + 4. 当任务复杂度较高时,可以使用 `task` 工具创建子代理来处理独立子任务\n\n\ 任务追踪:\n\ 你可以使用 `todo_write` 工具追踪子任务进度。规则:同一时间只有一个 in_progress,完成后再标记下一个,3步以上才使用。\n\n\ 注意: 你没有访问主对话历史的权限,这是一个独立的执行上下文。" diff --git a/src/tools/task/tool.rs b/src/tools/task/tool.rs index 1faa6ed..3b9be66 100644 --- a/src/tools/task/tool.rs +++ b/src/tools/task/tool.rs @@ -10,7 +10,7 @@ use super::types::{TaskDefinition, TaskToolArgs}; /// Task 工具 - 创建和管理子代理 pub struct TaskTool { runtime: Arc, - /// 最大嵌套深度(0 = 主 agent 不允许创建子代理,1 = 子 agent 可创建 1 层孙 agent) + /// 最大嵌套深度(0 = 无限制用于主 agent,>0 = 限制子代理最大嵌套层级) max_nesting_depth: u32, }