feat(task): 增加子代理最大嵌套深度支持,更新相关文档和提示

This commit is contained in:
oudecheng 2026-06-17 17:55:22 +08:00
parent 631c61fea2
commit 879f5f243a
6 changed files with 53 additions and 31 deletions

View File

@ -242,7 +242,7 @@ fn default_task_ttl_hours() -> u64 {
} }
fn default_task_max_nesting_depth() -> u32 { fn default_task_max_nesting_depth() -> u32 {
1 2
} }
fn default_task_allowed_tools() -> Vec<String> { fn default_task_allowed_tools() -> Vec<String> {

View File

@ -17,7 +17,7 @@ use crate::storage::{
}; };
use crate::tools::{ use crate::tools::{
DefaultSubAgentRuntime, InMemoryTaskRepository, NoopSessionMessageSender, DefaultSubAgentRuntime, InMemoryTaskRepository, NoopSessionMessageSender,
SessionMessageSender, SubAgentRuntimeConfig, SubagentCatalog, ToolRegistry, SessionMessageSender, SubAgentRuntimeConfig, SubagentCatalog, TaskTool, ToolRegistry,
}; };
use crate::tools::task::repository::TaskRepository; use crate::tools::task::repository::TaskRepository;
use crate::tools::todo_write::TodoItem; 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) // Create SubAgentRuntime (if task tool is enabled)
let (factory, task_repository): (_, Arc<dyn TaskRepository>) = if task_config.enabled { let (factory, task_repository): (_, Arc<dyn TaskRepository>) = if task_config.enabled {
let task_repository = Arc::new(InMemoryTaskRepository::new()); 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( let subagent_tools = Arc::new(
factory.build_subagent_tools( factory.build_subagent_tools(
if mcp_tools_for_subagents.is_empty() { if mcp_tools_for_subagents.is_empty() {
@ -200,20 +200,28 @@ pub(crate) fn build_session_manager_with_sender(
runtime_config, runtime_config,
task_repository.clone(), task_repository.clone(),
conversations.clone(), conversations.clone(),
subagent_tools, subagent_tools.clone(),
provider_config.clone(), provider_config.clone(),
catalog, catalog,
bus.clone(), bus.clone(),
store.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) (factory.with_subagent_runtime(subagent_runtime), task_repository)
} else { } else {
(factory, Arc::new(InMemoryTaskRepository::new())) (factory, Arc::new(InMemoryTaskRepository::new()))
}; };
// Build base tools // Build base tools
let mut tools = factory.build(); let tools = factory.build();
// Register MCP tools to main agent (async) // Register MCP tools to main agent (async)
// Note: MCP tools for subagents are already collected above // Note: MCP tools for subagents are already collected above

View File

@ -88,7 +88,7 @@ impl ToolRegistryFactory {
self 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) !self.disabled_tools.contains(tool_name)
} }
@ -99,7 +99,7 @@ impl ToolRegistryFactory {
} }
pub(crate) fn build(&self) -> ToolRegistry { pub(crate) fn build(&self) -> ToolRegistry {
let mut registry = ToolRegistry::new(); let registry = ToolRegistry::new();
if self.is_enabled("calculator") { if self.is_enabled("calculator") {
registry.register(CalculatorTool::new()); registry.register(CalculatorTool::new());
@ -171,13 +171,14 @@ impl ToolRegistryFactory {
registry registry
} }
/// 构建子代理专用工具集(不包含 task 工具防止递归) /// 构建子代理专用工具集
/// 可选地包含 MCP 工具(通过 mcp_tools 参数传递) /// 可选地包含 MCP 工具(通过 mcp_tools 参数传递)
/// 注意task 工具由调用方在 runtime 就绪后通过 registry.register() 单独注册
pub(crate) fn build_subagent_tools( pub(crate) fn build_subagent_tools(
&self, &self,
mcp_tools: Option<Vec<crate::mcp::tool_adapter::McpToolWrapper>>, mcp_tools: Option<Vec<crate::mcp::tool_adapter::McpToolWrapper>>,
) -> ToolRegistry { ) -> ToolRegistry {
let mut registry = ToolRegistry::new(); let registry = ToolRegistry::new();
// 基础工具 // 基础工具
if self.is_enabled("calculator") { 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 registry
} }
} }

View File

@ -1,36 +1,51 @@
use std::collections::HashMap; use std::collections::HashMap;
use std::sync::{Arc, RwLock};
use crate::domain::tools::{Tool, ToolFunction}; use crate::domain::tools::{Tool, ToolFunction};
use super::traits::Tool as ToolTrait; use super::traits::Tool as ToolTrait;
pub struct ToolRegistry { pub struct ToolRegistry {
tools: HashMap<String, Box<dyn ToolTrait>>, tools: RwLock<HashMap<String, Arc<dyn ToolTrait>>>,
} }
impl ToolRegistry { impl ToolRegistry {
pub fn new() -> Self { pub fn new() -> Self {
Self { Self {
tools: HashMap::new(), tools: RwLock::new(HashMap::new()),
} }
} }
pub fn register<T: ToolTrait + 'static>(&mut self, tool: T) { pub fn register<T: ToolTrait + 'static>(&self, tool: T) {
self.tools.insert(tool.name().to_string(), Box::new(tool)); self.tools
.write()
.expect("ToolRegistry lock poisoned")
.insert(tool.name().to_string(), Arc::new(tool));
} }
pub fn get(&self, name: &str) -> Option<&Box<dyn ToolTrait>> { pub fn get(&self, name: &str) -> Option<Arc<dyn ToolTrait>> {
self.tools.get(name) self.tools
.read()
.expect("ToolRegistry lock poisoned")
.get(name)
.cloned()
} }
/// Get all registered tools. /// Get all registered tools.
/// Used for concurrent tool execution when we need to look up tools by name. /// Used for concurrent tool execution when we need to look up tools by name.
pub fn get_all(&self) -> Vec<&Box<dyn ToolTrait>> { pub fn get_all(&self) -> Vec<Arc<dyn ToolTrait>> {
self.tools.values().collect() self.tools
.read()
.expect("ToolRegistry lock poisoned")
.values()
.cloned()
.collect()
} }
pub fn get_definitions(&self) -> Vec<Tool> { pub fn get_definitions(&self) -> Vec<Tool> {
self.tools self.tools
.read()
.expect("ToolRegistry lock poisoned")
.values() .values()
.map(|tool| Tool { .map(|tool| Tool {
tool_type: "function".to_string(), tool_type: "function".to_string(),
@ -44,11 +59,19 @@ impl ToolRegistry {
} }
pub fn has_tools(&self) -> bool { 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<String> { pub fn tool_names(&self) -> Vec<String> {
self.tools.keys().cloned().collect() self.tools
.read()
.expect("ToolRegistry lock poisoned")
.keys()
.cloned()
.collect()
} }
} }

View File

@ -51,7 +51,7 @@ impl SubagentPromptBuilder {
1. \n\ 1. \n\
2. 使\n\ 2. 使\n\
3. \n\ 3. \n\
4. \n\n\ 4. 使 `task` \n\n\
:\n\ :\n\
使 `todo_write` in_progress3使\n\n\ 使 `todo_write` in_progress3使\n\n\
: 访" : 访"

View File

@ -10,7 +10,7 @@ use super::types::{TaskDefinition, TaskToolArgs};
/// Task 工具 - 创建和管理子代理 /// Task 工具 - 创建和管理子代理
pub struct TaskTool { pub struct TaskTool {
runtime: Arc<dyn SubAgentRuntime>, runtime: Arc<dyn SubAgentRuntime>,
/// 最大嵌套深度0 = 主 agent 不允许创建子代理1 = 子 agent 可创建 1 层孙 agent /// 最大嵌套深度0 = 无限制用于主 agent>0 = 限制子代理最大嵌套层级
max_nesting_depth: u32, max_nesting_depth: u32,
} }