feat(agent): 实现委派工具重构,支持子代理和代理团队模式

- 新增 spawn_subagent 和 spawn_agent_team 工具,替代原有的 spawn 工具
- 重构 DelegationManager 以支持单个子代理和代理团队两种委派模式
- 更新系统提示词中的委派策略说明,明确使用场景和区别
- 添加技能上下文传递功能,确保委派任务遵循指定技能
- 实现代理内部的受控下游委派机制,防止无限嵌套
- 更新工具注册和上下文设置逻辑以适配新架构
This commit is contained in:
2026-03-30 17:21:39 +08:00
parent 29dfd14aa6
commit fee9007da6
6 changed files with 490 additions and 90 deletions

View File

@ -19,10 +19,12 @@ from nanobot.agent.run_result import AgentRunResult
from nanobot.agent.tools.filesystem import EditFileTool, ListDirTool, ReadFileTool, WriteFileTool
from nanobot.agent.tools.registry import ToolRegistry
from nanobot.agent.tools.shell import ExecTool
from nanobot.agent.tools.spawn import NestedDelegateTool
from nanobot.agent.tools.web import WebFetchTool, WebSearchTool
from nanobot.providers.base import LLMProvider
if TYPE_CHECKING:
from nanobot.agent.delegation import DelegationManager
from nanobot.config.schema import ExecToolConfig
@ -51,6 +53,11 @@ class SubagentManager:
self.brave_api_key = brave_api_key
self.exec_config = exec_config or ExecToolConfig()
self.restrict_to_workspace = restrict_to_workspace
self._nested_delegate: DelegationManager | None = None
def set_nested_delegate(self, manager: "DelegationManager | None") -> None:
"""注入 delegated worker 可用的受控下游委派器。"""
self._nested_delegate = manager
async def run_local_task(
self,
@ -61,14 +68,22 @@ class SubagentManager:
system_prompt: str | None = None,
model: str | None = None,
progress_callback: Callable[..., Awaitable[None]] | None = None,
allow_nested_delegation: bool = True,
skill_context: str = "",
skill_names: list[str] | None = None,
) -> AgentRunResult:
"""执行一次本地委派任务,并返回结构化结果。"""
# 每次任务都新建一套局部工具注册表,避免不同任务之间共享临时状态。
tools = self._build_local_tools()
tools = self._build_local_tools(
allow_nested_delegation=allow_nested_delegation,
skill_names=skill_names,
)
prompt = self._build_subagent_prompt(
task,
agent_name=agent_name,
custom_system_prompt=system_prompt,
allow_nested_delegation=allow_nested_delegation,
skill_context=skill_context,
)
# 本地委派不共享主会话历史,只带“专用 system prompt + 当前任务”。
messages: list[dict[str, Any]] = [
@ -143,7 +158,12 @@ class SubagentManager:
summary=final_result,
)
def _build_local_tools(self) -> ToolRegistry:
def _build_local_tools(
self,
*,
allow_nested_delegation: bool,
skill_names: list[str] | None = None,
) -> ToolRegistry:
"""构建本地委派可用的受限工具集。"""
tools = ToolRegistry()
allowed_dir = self.workspace if self.restrict_to_workspace else None
@ -175,6 +195,8 @@ class SubagentManager:
# 网络能力保持只读:搜索和抓取,不提供消息发送/再次委派等工具。
tools.register(WebSearchTool(api_key=self.brave_api_key))
tools.register(WebFetchTool())
if allow_nested_delegation and self._nested_delegate is not None:
tools.register(NestedDelegateTool(manager=self._nested_delegate, default_skills=skill_names))
return tools
@staticmethod
@ -201,12 +223,46 @@ class SubagentManager:
task: str,
agent_name: str = "Local Subagent",
custom_system_prompt: str | None = None,
allow_nested_delegation: bool = True,
skill_context: str = "",
) -> str:
"""构建子代理专用 system prompt。"""
now = datetime.now().strftime("%Y-%m-%d %H:%M (%A)")
tz = _time.strftime("%Z") or "UTC"
# plugin agent 的自定义系统提示拼到末尾,保留通用约束,再叠加个性化指令。
extra = f"\n\n## Agent Instructions\n{custom_system_prompt.strip()}" if custom_system_prompt else ""
can_do_lines = [
"- Read and write files in the workspace",
"- Execute shell commands",
"- Search the web and fetch web pages",
"- Complete the task thoroughly",
]
cannot_do_lines = [
"- Send messages directly to users (no message tool available)",
"- Access the main agent's conversation history",
]
delegation_section = (
"\n## Downstream Delegation\n"
"- Do not delegate further. Complete the task yourself with the tools you have."
)
if allow_nested_delegation and self._nested_delegate is not None:
can_do_lines.append(
"- Use `delegate_task` for controlled downstream delegation when specialized help is required"
)
cannot_do_lines.append("- Do not start agent teams or use background delegation tools")
nested_summary = self._nested_delegate.build_nested_agents_summary()
summary_block = f"\n\n{nested_summary}" if nested_summary else ""
delegation_section = (
"\n## Downstream Delegation\n"
"- Use `delegate_task` only when a specialized downstream worker is actually needed.\n"
"- `strategy=\"a2a\"` delegates directly to an available A2A agent.\n"
"- `strategy=\"ephemeral_subagent\"` runs a temporary local worker for this task only.\n"
"- Never create, register, or persist a new local sub-agent through `subagentctl.py`, `/api/subagents`, or registry edits."
f"{summary_block}"
)
else:
cannot_do_lines.append("- Spawn other subagents or downstream workers")
skill_section = f"\n## Required Skills\n{skill_context.strip()}" if skill_context.strip() else ""
return f"""# {agent_name}
@ -220,17 +276,17 @@ You are a delegated agent spawned by the main agent to complete a specific task.
2. Your final response will be reported back to the main agent
3. Do not initiate conversations or take on side tasks
4. Be concise but informative in your findings
5. Do not create or modify persistent local sub-agents unless the task explicitly requires that workflow
## What You Can Do
- Read and write files in the workspace
- Execute shell commands
- Search the web and fetch web pages
- Complete the task thoroughly
{chr(10).join(can_do_lines)}
## What You Cannot Do
- Send messages directly to users (no message tool available)
- Spawn other subagents
- Access the main agent's conversation history
{chr(10).join(cannot_do_lines)}
{delegation_section}
{skill_section}
## Workspace
Your workspace is at: {self.workspace}