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 {
1
2
}
fn default_task_allowed_tools() -> Vec<String> {

View File

@ -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<dyn TaskRepository>) = 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

View File

@ -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<Vec<crate::mcp::tool_adapter::McpToolWrapper>>,
) -> 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
}
}

View File

@ -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<String, Box<dyn ToolTrait>>,
tools: RwLock<HashMap<String, Arc<dyn ToolTrait>>>,
}
impl ToolRegistry {
pub fn new() -> Self {
Self {
tools: HashMap::new(),
tools: RwLock::new(HashMap::new()),
}
}
pub fn register<T: ToolTrait + 'static>(&mut self, tool: T) {
self.tools.insert(tool.name().to_string(), Box::new(tool));
pub fn register<T: ToolTrait + 'static>(&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<dyn ToolTrait>> {
self.tools.get(name)
pub fn get(&self, name: &str) -> Option<Arc<dyn ToolTrait>> {
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<dyn ToolTrait>> {
self.tools.values().collect()
pub fn get_all(&self) -> Vec<Arc<dyn ToolTrait>> {
self.tools
.read()
.expect("ToolRegistry lock poisoned")
.values()
.cloned()
.collect()
}
pub fn get_definitions(&self) -> Vec<Tool> {
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<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\
2. 使\n\
3. \n\
4. \n\n\
4. 使 `task` \n\n\
:\n\
使 `todo_write` in_progress3使\n\n\
: 访"

View File

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