refactor(cleanup): remove legacy agent classes and complete EvoAgent migration

Remove deprecated AnalystAgent, PMAgent, and RiskAgent classes.
All agent creation now goes through UnifiedAgentFactory creating EvoAgent instances.

- Delete backend/agents/analyst.py (169 lines)
- Delete backend/agents/portfolio_manager.py (420 lines)
- Delete backend/agents/risk_manager.py (139 lines)
- Update all imports to use EvoAgent exclusively
- Clean up unused imports across 25 files
- Update tests to work with simplified agent structure

Constraint: EvoAgent is now the single source of truth for all agent roles
Constraint: UnifiedAgentFactory handles runtime agent creation
Rejected: Keep legacy aliases | creates maintenance burden
Confidence: high
Scope-risk: moderate (affects agent instantiation paths)
Directive: All new agent features must be added to EvoAgent, not legacy classes
Not-tested: Kubernetes sandbox executor (marked with TODO)
This commit is contained in:
2026-04-02 10:51:14 +08:00
parent 49d704c363
commit 45c3996434
34 changed files with 201 additions and 1534 deletions

View File

@@ -2,31 +2,23 @@
"""Unified Agent Factory - Centralized agent creation for 大时代.
This module provides a unified factory for creating all agent types (analysts,
risk manager, portfolio manager) with consistent configuration. It replaces
the scattered agent creation logic in main.py, pipeline.py, and pipeline_runner.py.
risk manager, portfolio manager) as EvoAgent instances with consistent
configuration. It replaces the scattered agent creation logic in main.py,
pipeline.py, and pipeline_runner.py.
Key features:
- Single entry point for all agent creation
- Automatic EvoAgent vs Legacy Agent selection based on _resolve_evo_agent_ids()
- Creates EvoAgent instances for all agent roles
- Consistent parameter handling across all agent types
- Support for workspace-driven configuration
- Long-term memory integration
"""
from __future__ import annotations
import os
from pathlib import Path
from typing import TYPE_CHECKING, Any, Optional, Protocol, TypeVar, Union
from typing import Any, Optional, Protocol
if TYPE_CHECKING:
from backend.agents.base.evo_agent import EvoAgent
from backend.agents.analyst import AnalystAgent
from backend.agents.risk_manager import RiskAgent
from backend.agents.portfolio_manager import PMAgent
# Type aliases for agent types
AgentType = Union["EvoAgent", "AnalystAgent", "RiskAgent", "PMAgent"]
T = TypeVar("T")
from backend.agents.base.evo_agent import EvoAgent
class AgentFactoryProtocol(Protocol):
@@ -39,7 +31,7 @@ class AgentFactoryProtocol(Protocol):
formatter: Any,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> AnalystAgent | EvoAgent: ...
) -> EvoAgent: ...
def create_risk_manager(
self,
@@ -47,7 +39,7 @@ class AgentFactoryProtocol(Protocol):
formatter: Any,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> RiskAgent | EvoAgent: ...
) -> EvoAgent: ...
def create_portfolio_manager(
self,
@@ -57,18 +49,14 @@ class AgentFactoryProtocol(Protocol):
margin_requirement: float,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> PMAgent | EvoAgent: ...
) -> EvoAgent: ...
class UnifiedAgentFactory:
"""Unified factory for creating agents with consistent configuration.
"""Unified factory for creating EvoAgent instances with consistent configuration.
This factory centralizes agent creation logic and automatically selects
between EvoAgent (new) and Legacy Agent based on the EVO_AGENT_IDS
environment variable configuration.
By default, all supported roles use EvoAgent. Set EVO_AGENT_IDS=legacy
to disable EvoAgent entirely.
This factory centralizes agent creation logic and creates EvoAgent instances
for all agent roles (analysts, risk manager, portfolio manager).
Example:
factory = UnifiedAgentFactory(
@@ -103,7 +91,6 @@ class UnifiedAgentFactory:
config_name: str,
skills_manager: Any,
toolkit_factory: Optional[Any] = None,
evo_agent_ids: Optional[set[str]] = None,
):
"""Initialize the agent factory.
@@ -111,49 +98,11 @@ class UnifiedAgentFactory:
config_name: Run configuration name (e.g., "smoke_fullstack")
skills_manager: SkillsManager instance for skill/asset management
toolkit_factory: Optional factory function for creating toolkits
evo_agent_ids: Optional set of agent IDs to use EvoAgent.
If None, uses _resolve_evo_agent_ids() default.
"""
self.config_name = config_name
self.skills_manager = skills_manager
self.toolkit_factory = toolkit_factory
# Determine which agents should use EvoAgent
if evo_agent_ids is not None:
self._evo_agent_ids = evo_agent_ids
else:
self._evo_agent_ids = self._resolve_evo_agent_ids()
def _resolve_evo_agent_ids(self) -> set[str]:
"""Return agent ids selected to use EvoAgent.
By default, all supported roles use EvoAgent.
EVO_AGENT_IDS can be used to limit to specific roles.
"""
from backend.config.constants import ANALYST_TYPES
all_supported = set(ANALYST_TYPES) | {"risk_manager", "portfolio_manager"}
raw = os.getenv("EVO_AGENT_IDS", "")
if not raw.strip():
# Default: all supported roles use EvoAgent
return all_supported
if raw.strip().lower() in ("legacy", "old", "none"):
return set()
requested = {item.strip() for item in raw.split(",") if item.strip()}
return {
agent_id
for agent_id in requested
if agent_id in ANALYST_TYPES
or agent_id in {"risk_manager", "portfolio_manager"}
}
def _should_use_evo_agent(self, agent_id: str) -> bool:
"""Check if an agent should use EvoAgent."""
return agent_id in self._evo_agent_ids
def _create_toolkit(
self,
agent_type: str,
@@ -202,10 +151,8 @@ class UnifiedAgentFactory:
agent_config: Any,
long_term_memory: Optional[Any] = None,
extra_kwargs: Optional[dict[str, Any]] = None,
) -> "EvoAgent":
) -> EvoAgent:
"""Create an EvoAgent instance."""
from backend.agents.base.evo_agent import EvoAgent
workspace_dir = self.skills_manager.get_agent_asset_dir(
self.config_name, agent_id
)
@@ -239,7 +186,7 @@ class UnifiedAgentFactory:
formatter: Any,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> "AnalystAgent | EvoAgent":
) -> EvoAgent:
"""Create an analyst agent.
Args:
@@ -250,31 +197,16 @@ class UnifiedAgentFactory:
long_term_memory: Optional long-term memory instance
Returns:
AnalystAgent or EvoAgent instance
EvoAgent instance
"""
toolkit = self._create_toolkit(analyst_type, active_skill_dirs)
if self._should_use_evo_agent(analyst_type):
agent_config = self._load_agent_config(analyst_type)
return self._create_evo_agent(
agent_id=analyst_type,
model=model,
formatter=formatter,
toolkit=toolkit,
agent_config=agent_config,
long_term_memory=long_term_memory,
)
# Legacy path
from backend.agents.analyst import AnalystAgent
return AnalystAgent(
analyst_type=analyst_type,
toolkit=toolkit,
agent_config = self._load_agent_config(analyst_type)
return self._create_evo_agent(
agent_id=analyst_type,
model=model,
formatter=formatter,
agent_id=analyst_type,
config={"config_name": self.config_name},
toolkit=toolkit,
agent_config=agent_config,
long_term_memory=long_term_memory,
)
@@ -284,7 +216,7 @@ class UnifiedAgentFactory:
formatter: Any,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> "RiskAgent | EvoAgent":
) -> EvoAgent:
"""Create a risk manager agent.
Args:
@@ -294,31 +226,17 @@ class UnifiedAgentFactory:
long_term_memory: Optional long-term memory instance
Returns:
RiskAgent or EvoAgent instance
EvoAgent instance
"""
toolkit = self._create_toolkit("risk_manager", active_skill_dirs)
if self._should_use_evo_agent("risk_manager"):
agent_config = self._load_agent_config("risk_manager")
return self._create_evo_agent(
agent_id="risk_manager",
model=model,
formatter=formatter,
toolkit=toolkit,
agent_config=agent_config,
long_term_memory=long_term_memory,
)
# Legacy path
from backend.agents.risk_manager import RiskAgent
return RiskAgent(
agent_config = self._load_agent_config("risk_manager")
return self._create_evo_agent(
agent_id="risk_manager",
model=model,
formatter=formatter,
name="risk_manager",
config={"config_name": self.config_name},
long_term_memory=long_term_memory,
toolkit=toolkit,
agent_config=agent_config,
long_term_memory=long_term_memory,
)
def create_portfolio_manager(
@@ -329,7 +247,7 @@ class UnifiedAgentFactory:
margin_requirement: float,
active_skill_dirs: Optional[list[Path]] = None,
long_term_memory: Optional[Any] = None,
) -> "PMAgent | EvoAgent":
) -> EvoAgent:
"""Create a portfolio manager agent.
Args:
@@ -341,52 +259,34 @@ class UnifiedAgentFactory:
long_term_memory: Optional long-term memory instance
Returns:
PMAgent or EvoAgent instance
EvoAgent instance
"""
if self._should_use_evo_agent("portfolio_manager"):
agent_config = self._load_agent_config("portfolio_manager")
agent_config = self._load_agent_config("portfolio_manager")
# For PM, toolkit is created after agent (needs owner reference)
from backend.agents.base.evo_agent import EvoAgent
# For PM, toolkit is created after agent (needs owner reference)
workspace_dir = self.skills_manager.get_agent_asset_dir(
self.config_name, "portfolio_manager"
)
workspace_dir = self.skills_manager.get_agent_asset_dir(
self.config_name, "portfolio_manager"
)
agent = EvoAgent(
agent_id="portfolio_manager",
config_name=self.config_name,
workspace_dir=workspace_dir,
model=model,
formatter=formatter,
skills_manager=self.skills_manager,
prompt_files=getattr(agent_config, "prompt_files", ["SOUL.md"]),
initial_cash=initial_cash,
margin_requirement=margin_requirement,
long_term_memory=long_term_memory,
)
agent.toolkit = self._create_toolkit(
"portfolio_manager", active_skill_dirs, owner=agent
)
setattr(agent, "run_id", self.config_name)
# Keep workspace_id for backward compatibility
setattr(agent, "workspace_id", self.config_name)
return agent
# Legacy path
from backend.agents.portfolio_manager import PMAgent
return PMAgent(
name="portfolio_manager",
agent = EvoAgent(
agent_id="portfolio_manager",
config_name=self.config_name,
workspace_dir=workspace_dir,
model=model,
formatter=formatter,
skills_manager=self.skills_manager,
prompt_files=getattr(agent_config, "prompt_files", ["SOUL.md"]),
initial_cash=initial_cash,
margin_requirement=margin_requirement,
config={"config_name": self.config_name},
long_term_memory=long_term_memory,
toolkit_factory=self.toolkit_factory,
toolkit_factory_kwargs={"active_skill_dirs": active_skill_dirs or []},
)
agent.toolkit = self._create_toolkit(
"portfolio_manager", active_skill_dirs, owner=agent
)
setattr(agent, "run_id", self.config_name)
# Keep workspace_id for backward compatibility
setattr(agent, "workspace_id", self.config_name)
return agent
# Singleton factory instance cache