Add dynamic analyst runtime updates and deployment guides

This commit is contained in:
2026-04-07 09:39:37 +08:00
parent 80ce63da5a
commit 62c7341cf6
45 changed files with 1886 additions and 159 deletions

View File

@@ -13,8 +13,11 @@ as described in the Dynamic Team Architecture.
"""
from __future__ import annotations
import json
from typing import Any, Dict, List, Optional, Callable
from dataclasses import asdict
from agentscope.message import TextBlock
from agentscope.tool import ToolResponse
from backend.agents.dynamic_team_types import (
AnalystPersona,
@@ -22,7 +25,7 @@ from backend.agents.dynamic_team_types import (
CreateAnalystResult,
AnalystTypeInfo,
)
from backend.config.constants import ANALYST_TYPES
from backend.config.constants import ANALYST_TYPES, AGENT_CONFIG
# Type alias for callbacks set by pipeline
@@ -30,6 +33,14 @@ CreateAnalystCallback = Callable[[str, str, Optional[AnalystConfig]], str]
RemoveAnalystCallback = Callable[[str], str]
def _to_tool_response(payload: Any) -> ToolResponse:
if isinstance(payload, str):
text = payload
else:
text = json.dumps(payload, ensure_ascii=False, indent=2, default=str)
return ToolResponse(content=[TextBlock(type="text", text=text)])
class DynamicTeamController:
"""Controller for dynamic analyst team management.
@@ -296,6 +307,23 @@ class DynamicTeamController:
Dict with analyst configuration and status
"""
config = self._instance_configs.get(agent_id)
current_analysts = self._get_analysts_callback() if self._get_analysts_callback else []
analyst_map = {
(getattr(agent, "name", None) or getattr(agent, "agent_id", None)): agent
for agent in current_analysts
}
if agent_id in analyst_map and not config:
builtin_meta = AGENT_CONFIG.get(agent_id, {})
return {
"found": True,
"agent_id": agent_id,
"name": builtin_meta.get("name") or agent_id,
"type": agent_id,
"is_custom": False,
"is_clone": False,
"is_builtin": True,
"message": f"Built-in analyst '{agent_id}' is active",
}
if not config:
return {
"found": False,
@@ -310,6 +338,7 @@ class DynamicTeamController:
"is_custom": config.persona is not None,
"is_clone": config.parent_id is not None,
"parent_id": config.parent_id,
"is_builtin": False,
}
def register_analyst_type(
@@ -372,13 +401,26 @@ class DynamicTeamController:
Dict with team composition information
"""
analysts = []
for agent_id, config in self._instance_configs.items():
current_analysts = self._get_analysts_callback() if self._get_analysts_callback else []
instance_configs = self._instance_configs
for agent in current_analysts:
agent_id = getattr(agent, "name", None) or getattr(agent, "agent_id", None)
if not agent_id:
continue
config = instance_configs.get(agent_id)
builtin_meta = AGENT_CONFIG.get(agent_id, {})
analysts.append({
"agent_id": agent_id,
"name": config.persona.name if config.persona else agent_id,
"type": config.analyst_type,
"is_custom": config.persona is not None,
"is_clone": config.parent_id is not None,
"name": (
config.persona.name
if config and config.persona and config.persona.name
else builtin_meta.get("name") or agent_id
),
"type": config.analyst_type if config else agent_id,
"is_custom": bool(config and config.persona is not None),
"is_clone": bool(config and config.parent_id is not None),
"is_builtin": config is None,
})
return {
@@ -418,91 +460,95 @@ def get_controller() -> Optional[DynamicTeamController]:
def create_analyst(
agent_id: str,
analyst_type: str,
name: Optional[str] = None,
focus: Optional[str] = None,
description: Optional[str] = None,
soul_md: Optional[str] = None,
agents_md: Optional[str] = None,
model_name: Optional[str] = None,
) -> Dict[str, Any]:
name: str = "",
focus: str = "",
description: str = "",
soul_md: str = "",
agents_md: str = "",
model_name: str = "",
) -> ToolResponse:
"""Tool wrapper for create_analyst.
Note: focus parameter accepts comma-separated string for tool compatibility.
"""
controller = get_controller()
if not controller:
return {"success": False, "error": "Dynamic team controller not available"}
return _to_tool_response({"success": False, "error": "Dynamic team controller not available"})
focus_list = [f.strip() for f in focus.split(",")] if focus else None
return controller.create_analyst(
agent_id=agent_id,
analyst_type=analyst_type,
name=name,
focus=focus_list,
description=description,
soul_md=soul_md,
agents_md=agents_md,
model_name=model_name,
return _to_tool_response(
controller.create_analyst(
agent_id=agent_id,
analyst_type=analyst_type,
name=name,
focus=focus_list,
description=description,
soul_md=soul_md,
agents_md=agents_md,
model_name=model_name,
)
)
def clone_analyst(
source_id: str,
new_id: str,
name: Optional[str] = None,
focus_additions: Optional[str] = None,
description_override: Optional[str] = None,
model_name: Optional[str] = None,
) -> Dict[str, Any]:
name: str = "",
focus_additions: str = "",
description_override: str = "",
model_name: str = "",
) -> ToolResponse:
"""Tool wrapper for clone_analyst.
Note: focus_additions accepts comma-separated string.
"""
controller = get_controller()
if not controller:
return {"success": False, "error": "Dynamic team controller not available"}
return _to_tool_response({"success": False, "error": "Dynamic team controller not available"})
additions_list = [f.strip() for f in focus_additions.split(",")] if focus_additions else None
return controller.clone_analyst(
source_id=source_id,
new_id=new_id,
name=name,
focus_additions=additions_list,
description_override=description_override,
model_name=model_name,
return _to_tool_response(
controller.clone_analyst(
source_id=source_id,
new_id=new_id,
name=name,
focus_additions=additions_list,
description_override=description_override,
model_name=model_name,
)
)
def remove_analyst(agent_id: str) -> Dict[str, Any]:
def remove_analyst(agent_id: str) -> ToolResponse:
"""Tool wrapper for remove_analyst."""
controller = get_controller()
if not controller:
return {"success": False, "error": "Dynamic team controller not available"}
return controller.remove_analyst(agent_id)
return _to_tool_response({"success": False, "error": "Dynamic team controller not available"})
return _to_tool_response(controller.remove_analyst(agent_id))
def list_analyst_types() -> List[Dict[str, Any]]:
def list_analyst_types() -> ToolResponse:
"""Tool wrapper for list_analyst_types."""
controller = get_controller()
if not controller:
return []
return controller.list_analyst_types()
return _to_tool_response([])
return _to_tool_response(controller.list_analyst_types())
def get_analyst_info(agent_id: str) -> Dict[str, Any]:
def get_analyst_info(agent_id: str) -> ToolResponse:
"""Tool wrapper for get_analyst_info."""
controller = get_controller()
if not controller:
return {"found": False, "error": "Controller not available"}
return controller.get_analyst_info(agent_id)
return _to_tool_response({"found": False, "error": "Controller not available"})
return _to_tool_response(controller.get_analyst_info(agent_id))
def get_team_summary() -> Dict[str, Any]:
def get_team_summary() -> ToolResponse:
"""Tool wrapper for get_team_summary."""
controller = get_controller()
if not controller:
return {"error": "Controller not available"}
return controller.get_team_summary()
return _to_tool_response({"error": "Controller not available"})
return _to_tool_response(controller.get_team_summary())
__all__ = [