astrbbbb / astrbot /core /subagent_orchestrator.py
qa1145's picture
Upload 1245 files
8ede856 verified
from __future__ import annotations
from typing import Any
from astrbot import logger
from astrbot.core.agent.agent import Agent
from astrbot.core.agent.handoff import HandoffTool
from astrbot.core.persona_mgr import PersonaManager
from astrbot.core.provider.func_tool_manager import FunctionToolManager
class SubAgentOrchestrator:
"""Loads subagent definitions from config and registers handoff tools.
This is intentionally lightweight: it does not execute agents itself.
Execution happens via HandoffTool in FunctionToolExecutor.
"""
def __init__(
self, tool_mgr: FunctionToolManager, persona_mgr: PersonaManager
) -> None:
self._tool_mgr = tool_mgr
self._persona_mgr = persona_mgr
self.handoffs: list[HandoffTool] = []
async def reload_from_config(self, cfg: dict[str, Any]) -> None:
from astrbot.core.astr_agent_context import AstrAgentContext
agents = cfg.get("agents", [])
if not isinstance(agents, list):
logger.warning("subagent_orchestrator.agents must be a list")
return
handoffs: list[HandoffTool] = []
for item in agents:
if not isinstance(item, dict):
continue
if not item.get("enabled", True):
continue
name = str(item.get("name", "")).strip()
if not name:
continue
persona_id = item.get("persona_id")
persona_data = None
if persona_id:
try:
persona_data = await self._persona_mgr.get_persona(persona_id)
except StopIteration:
logger.warning(
"SubAgent persona %s not found, fallback to inline prompt.",
persona_id,
)
instructions = str(item.get("system_prompt", "")).strip()
public_description = str(item.get("public_description", "")).strip()
provider_id = item.get("provider_id")
if provider_id is not None:
provider_id = str(provider_id).strip() or None
tools = item.get("tools", [])
begin_dialogs = None
if persona_data:
instructions = persona_data.system_prompt or instructions
begin_dialogs = persona_data.begin_dialogs
tools = persona_data.tools
if public_description == "" and persona_data.system_prompt:
public_description = persona_data.system_prompt[:120]
if tools is None:
tools = None
elif not isinstance(tools, list):
tools = []
else:
tools = [str(t).strip() for t in tools if str(t).strip()]
agent = Agent[AstrAgentContext](
name=name,
instructions=instructions,
tools=tools, # type: ignore
)
agent.begin_dialogs = begin_dialogs
# The tool description should be a short description for the main LLM,
# while the subagent system prompt can be longer/more specific.
handoff = HandoffTool(
agent=agent,
tool_description=public_description or None,
)
# Optional per-subagent chat provider override.
handoff.provider_id = provider_id
handoffs.append(handoff)
for handoff in handoffs:
logger.info(f"Registered subagent handoff tool: {handoff.name}")
self.handoffs = handoffs