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:
@@ -1,48 +1,46 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Agents package for the current mixed runtime.
|
||||
Agents package for the EvoAgent-based runtime.
|
||||
|
||||
Exports:
|
||||
- EvoAgent: Next-generation agent with workspace support
|
||||
- EvoAgent: Core agent with workspace support
|
||||
- ToolGuardMixin: Tool call approval/denial flow
|
||||
- CommandHandler: System command handling
|
||||
- AgentFactory: Design-time agent creation under `workspaces/`
|
||||
- WorkspaceManager: Legacy alias for the persistent `workspaces/` registry
|
||||
- WorkspaceManager: Alias for the persistent `workspaces/` registry
|
||||
- WorkspaceRegistry: Explicit design-time `workspaces/` registry
|
||||
- RunWorkspaceManager: Run-scoped workspace asset manager
|
||||
- AgentRegistry: Central agent registry
|
||||
- Legacy compatibility: AnalystAgent, PMAgent, RiskAgent
|
||||
- UnifiedAgentFactory: Runtime agent factory for creating EvoAgent instances
|
||||
"""
|
||||
|
||||
# New EvoAgent architecture (from agent_core.py)
|
||||
# EvoAgent architecture
|
||||
from .agent_core import EvoAgent, ToolGuardMixin, CommandHandler
|
||||
from .factory import AgentFactory, ModelConfig
|
||||
from .unified_factory import UnifiedAgentFactory, get_agent_factory, clear_factory_cache
|
||||
from .workspace import WorkspaceManager, WorkspaceRegistry, WorkspaceConfig
|
||||
from .workspace_manager import RunWorkspaceManager
|
||||
from .registry import AgentRegistry, AgentInfo, get_registry, reset_registry
|
||||
|
||||
# Legacy agents (backward compatibility)
|
||||
from .analyst import AnalystAgent
|
||||
from .portfolio_manager import PMAgent
|
||||
from .risk_manager import RiskAgent
|
||||
|
||||
__all__ = [
|
||||
# New architecture
|
||||
# Core EvoAgent
|
||||
"EvoAgent",
|
||||
"ToolGuardMixin",
|
||||
"CommandHandler",
|
||||
# Factories
|
||||
"AgentFactory",
|
||||
"ModelConfig",
|
||||
"UnifiedAgentFactory",
|
||||
"get_agent_factory",
|
||||
"clear_factory_cache",
|
||||
# Workspace
|
||||
"WorkspaceManager",
|
||||
"WorkspaceRegistry",
|
||||
"WorkspaceConfig",
|
||||
"RunWorkspaceManager",
|
||||
# Registry
|
||||
"AgentRegistry",
|
||||
"AgentInfo",
|
||||
"get_registry",
|
||||
"reset_registry",
|
||||
# Legacy compatibility
|
||||
"AnalystAgent",
|
||||
"PMAgent",
|
||||
"RiskAgent",
|
||||
]
|
||||
|
||||
@@ -1,169 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Analyst Agent - Based on AgentScope ReActAgent
|
||||
Performs analysis using tools and LLM
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
AnalystAgent is deprecated and will be removed in a future version.
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` instead.
|
||||
See docs/CRITICAL_FIXES.md for migration guide.
|
||||
"""
|
||||
import warnings
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from agentscope.agent import ReActAgent
|
||||
from agentscope.memory import InMemoryMemory, LongTermMemoryBase
|
||||
from agentscope.message import Msg
|
||||
|
||||
from ..config.constants import ANALYST_TYPES
|
||||
from ..utils.progress import progress
|
||||
from .prompt_factory import build_agent_system_prompt, clear_prompt_factory_cache
|
||||
|
||||
# Emit deprecation warning on module import
|
||||
warnings.warn(
|
||||
"AnalystAgent is deprecated. Use EvoAgent instead. "
|
||||
"See docs/CRITICAL_FIXES.md for migration guide.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
class AnalystAgent(ReActAgent):
|
||||
"""
|
||||
Analyst Agent - Uses LLM for tool selection and analysis
|
||||
Inherits from AgentScope's ReActAgent
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` with
|
||||
workspace-driven configuration instead.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
analyst_type: str,
|
||||
toolkit: Any,
|
||||
model: Any,
|
||||
formatter: Any,
|
||||
agent_id: Optional[str] = None,
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
long_term_memory: Optional[LongTermMemoryBase] = None,
|
||||
):
|
||||
"""
|
||||
Initialize Analyst Agent
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
Use :class:`backend.agents.unified_factory.UnifiedAgentFactory`
|
||||
or :class:`backend.agents.base.evo_agent.EvoAgent` instead.
|
||||
|
||||
Args:
|
||||
analyst_type: Type of analyst (e.g., "fundamentals", etc.)
|
||||
toolkit: AgentScope Toolkit instance
|
||||
model: LLM model instance
|
||||
formatter: Message formatter instance
|
||||
agent_id: Agent ID (defaults to "{analyst_type}_analyst")
|
||||
config: Configuration dictionary
|
||||
long_term_memory: Optional ReMeTaskLongTermMemory instance
|
||||
"""
|
||||
# Emit runtime deprecation warning
|
||||
warnings.warn(
|
||||
f"AnalystAgent('{analyst_type}') is deprecated. "
|
||||
"Use EvoAgent via UnifiedAgentFactory instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
if analyst_type not in ANALYST_TYPES:
|
||||
raise ValueError(
|
||||
f"Unknown analyst type: {analyst_type}. "
|
||||
f"Must be one of: {list(ANALYST_TYPES.keys())}",
|
||||
)
|
||||
|
||||
object.__setattr__(self, "analyst_type_key", analyst_type)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"analyst_persona",
|
||||
ANALYST_TYPES[analyst_type]["display_name"],
|
||||
)
|
||||
|
||||
if agent_id is None:
|
||||
agent_id = analyst_type
|
||||
object.__setattr__(self, "agent_id", agent_id)
|
||||
|
||||
object.__setattr__(self, "config", config or {})
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
sys_prompt = self._load_system_prompt()
|
||||
|
||||
kwargs = {
|
||||
"name": agent_id,
|
||||
"sys_prompt": sys_prompt,
|
||||
"model": model,
|
||||
"formatter": formatter,
|
||||
"toolkit": toolkit,
|
||||
"memory": InMemoryMemory(),
|
||||
"max_iters": 10,
|
||||
}
|
||||
if long_term_memory:
|
||||
kwargs["long_term_memory"] = long_term_memory
|
||||
kwargs["long_term_memory_mode"] = "static_control"
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def _load_system_prompt(self) -> str:
|
||||
"""Load system prompt for analyst"""
|
||||
return build_agent_system_prompt(
|
||||
agent_id=self.agent_id,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
)
|
||||
|
||||
async def reply(self, x: Msg = None) -> Msg:
|
||||
"""
|
||||
Override reply method to add progress tracking
|
||||
|
||||
Args:
|
||||
x: Input message (content must be str)
|
||||
|
||||
Returns:
|
||||
Response message (content is str)
|
||||
"""
|
||||
ticker = None
|
||||
if x and hasattr(x, "metadata") and x.metadata:
|
||||
ticker = x.metadata.get("tickers")
|
||||
|
||||
if ticker:
|
||||
progress.update_status(
|
||||
self.name,
|
||||
ticker,
|
||||
f"Starting {self.analyst_persona} analysis",
|
||||
)
|
||||
|
||||
result = await super().reply(x)
|
||||
|
||||
if ticker:
|
||||
progress.update_status(
|
||||
self.name,
|
||||
ticker,
|
||||
"Analysis completed",
|
||||
)
|
||||
|
||||
return result
|
||||
|
||||
def reload_runtime_assets(self, active_skill_dirs: Optional[list] = None) -> None:
|
||||
"""Reload toolkit and system prompt from current run assets."""
|
||||
from .toolkit_factory import create_agent_toolkit
|
||||
|
||||
clear_prompt_factory_cache()
|
||||
self.toolkit = create_agent_toolkit(
|
||||
self.agent_id,
|
||||
self.config.get("config_name", "default"),
|
||||
active_skill_dirs=active_skill_dirs,
|
||||
)
|
||||
self._apply_runtime_sys_prompt(self._load_system_prompt())
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
@@ -8,7 +8,7 @@ import logging
|
||||
from abc import ABC, abstractmethod
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING, Any, Callable, Dict, List, Optional, Protocol
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from .agent import EvoAgent
|
||||
|
||||
@@ -8,11 +8,11 @@ from __future__ import annotations
|
||||
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field, asdict
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -31,7 +31,6 @@ from .hooks import (
|
||||
HOOK_PRE_REASONING,
|
||||
)
|
||||
from ..prompts.builder import (
|
||||
PromptBuilder,
|
||||
build_system_prompt_from_workspace,
|
||||
)
|
||||
from ..agent_workspace import load_agent_workspace_config
|
||||
|
||||
@@ -12,11 +12,10 @@ from dataclasses import dataclass, field
|
||||
from datetime import datetime
|
||||
from enum import Enum
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, List, Optional, Set
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from .evaluation_hook import (
|
||||
EvaluationCollector,
|
||||
EvaluationResult,
|
||||
MetricType,
|
||||
)
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import json
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import UTC, datetime
|
||||
from enum import Enum
|
||||
|
||||
|
||||
@@ -1,420 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Portfolio Manager Agent - Based on AgentScope ReActAgent
|
||||
Responsible for decision-making (NOT trade execution)
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
PMAgent is deprecated and will be removed in a future version.
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` instead.
|
||||
See docs/CRITICAL_FIXES.md for migration guide.
|
||||
"""
|
||||
import warnings
|
||||
from pathlib import Path
|
||||
from typing import Any, Dict, Optional, Callable
|
||||
|
||||
from agentscope.agent import ReActAgent
|
||||
from agentscope.memory import InMemoryMemory, LongTermMemoryBase
|
||||
from agentscope.message import Msg, TextBlock
|
||||
from agentscope.tool import Toolkit, ToolResponse
|
||||
|
||||
from ..utils.progress import progress
|
||||
from .prompt_factory import build_agent_system_prompt, clear_prompt_factory_cache
|
||||
from .team_pipeline_config import update_active_analysts
|
||||
from ..config.constants import ANALYST_TYPES
|
||||
|
||||
# Emit deprecation warning on module import
|
||||
warnings.warn(
|
||||
"PMAgent is deprecated. Use EvoAgent instead. "
|
||||
"See docs/CRITICAL_FIXES.md for migration guide.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
class PMAgent(ReActAgent):
|
||||
"""
|
||||
Portfolio Manager Agent - Makes investment decisions
|
||||
|
||||
Key features:
|
||||
1. PM outputs decisions only (action + quantity per ticker)
|
||||
2. Trade execution happens externally (in pipeline/executor)
|
||||
3. Supports both backtest and live modes
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` with
|
||||
workspace-driven configuration instead.
|
||||
"""
|
||||
"""
|
||||
Portfolio Manager Agent - Makes investment decisions
|
||||
|
||||
Key features:
|
||||
1. PM outputs decisions only (action + quantity per ticker)
|
||||
2. Trade execution happens externally (in pipeline/executor)
|
||||
3. Supports both backtest and live modes
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
name: str = "portfolio_manager",
|
||||
model: Any = None,
|
||||
formatter: Any = None,
|
||||
initial_cash: float = 100000.0,
|
||||
margin_requirement: float = 0.25,
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
long_term_memory: Optional[LongTermMemoryBase] = None,
|
||||
toolkit_factory: Any = None,
|
||||
toolkit_factory_kwargs: Optional[Dict[str, Any]] = None,
|
||||
toolkit: Optional[Toolkit] = None,
|
||||
):
|
||||
# Emit runtime deprecation warning
|
||||
warnings.warn(
|
||||
"PMAgent is deprecated. Use EvoAgent via UnifiedAgentFactory instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
object.__setattr__(self, "config", config or {})
|
||||
|
||||
# Portfolio state
|
||||
object.__setattr__(
|
||||
self,
|
||||
"portfolio",
|
||||
{
|
||||
"cash": initial_cash,
|
||||
"positions": {},
|
||||
"margin_used": 0.0,
|
||||
"margin_requirement": margin_requirement,
|
||||
},
|
||||
)
|
||||
|
||||
# Decisions made in current cycle
|
||||
object.__setattr__(self, "_decisions", {})
|
||||
toolkit_factory_kwargs = toolkit_factory_kwargs or {}
|
||||
object.__setattr__(self, "_toolkit_factory", toolkit_factory)
|
||||
object.__setattr__(
|
||||
self,
|
||||
"_toolkit_factory_kwargs",
|
||||
toolkit_factory_kwargs,
|
||||
)
|
||||
object.__setattr__(self, "_create_team_agent_cb", None)
|
||||
object.__setattr__(self, "_remove_team_agent_cb", None)
|
||||
|
||||
# Create toolkit after local state is ready so bound tool methods can be registered.
|
||||
if toolkit is None:
|
||||
if toolkit_factory is not None:
|
||||
toolkit = toolkit_factory(
|
||||
name,
|
||||
self.config.get("config_name", "default"),
|
||||
owner=self,
|
||||
**toolkit_factory_kwargs,
|
||||
)
|
||||
else:
|
||||
toolkit = self._create_toolkit()
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
|
||||
sys_prompt = build_agent_system_prompt(
|
||||
agent_id=name,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
)
|
||||
|
||||
kwargs = {
|
||||
"name": name,
|
||||
"sys_prompt": sys_prompt,
|
||||
"model": model,
|
||||
"formatter": formatter,
|
||||
"toolkit": toolkit,
|
||||
"memory": InMemoryMemory(),
|
||||
"max_iters": 10,
|
||||
}
|
||||
if long_term_memory:
|
||||
kwargs["long_term_memory"] = long_term_memory
|
||||
kwargs["long_term_memory_mode"] = "both"
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def _create_toolkit(self) -> Toolkit:
|
||||
"""Create toolkit with decision recording tool"""
|
||||
toolkit = Toolkit()
|
||||
toolkit.register_tool_function(self._make_decision)
|
||||
return toolkit
|
||||
|
||||
def _make_decision(
|
||||
self,
|
||||
ticker: str,
|
||||
action: str,
|
||||
quantity: int,
|
||||
confidence: int = 50,
|
||||
reasoning: str = "",
|
||||
) -> ToolResponse:
|
||||
"""
|
||||
Record a trading decision for a ticker.
|
||||
|
||||
Args:
|
||||
ticker: Stock ticker symbol (e.g., "AAPL")
|
||||
action: Decision - "long", "short" or "hold"
|
||||
quantity: Number of shares to trade (0 for hold)
|
||||
confidence: Confidence level 0-100
|
||||
reasoning: Explanation for this decision
|
||||
|
||||
Returns:
|
||||
ToolResponse confirming decision recorded
|
||||
"""
|
||||
if action not in ["long", "short", "hold"]:
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=f"Invalid action: {action}. "
|
||||
"Must be 'long', 'short', or 'hold'.",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
self._decisions[ticker] = {
|
||||
"action": action,
|
||||
"quantity": quantity if action != "hold" else 0,
|
||||
"confidence": confidence,
|
||||
"reasoning": reasoning,
|
||||
}
|
||||
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=f"Decision recorded: {action} "
|
||||
f"{quantity} shares of {ticker}"
|
||||
f" (confidence: {confidence}%)",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def _add_team_analyst(self, agent_id: str) -> ToolResponse:
|
||||
"""Add one analyst to active discussion team."""
|
||||
config_name = self.config.get("config_name", "default")
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
active = update_active_analysts(
|
||||
project_root=project_root,
|
||||
config_name=config_name,
|
||||
available_analysts=list(ANALYST_TYPES.keys()),
|
||||
add=[agent_id],
|
||||
)
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=(
|
||||
f"Active analyst team updated. Added: {agent_id}. "
|
||||
f"Current active analysts: {', '.join(active)}"
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def _remove_team_analyst(self, agent_id: str) -> ToolResponse:
|
||||
"""Remove one analyst from active discussion team."""
|
||||
callback_msg = ""
|
||||
callback = self._remove_team_agent_cb
|
||||
if callback is not None:
|
||||
callback_msg = callback(agent_id=agent_id)
|
||||
|
||||
config_name = self.config.get("config_name", "default")
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
active = update_active_analysts(
|
||||
project_root=project_root,
|
||||
config_name=config_name,
|
||||
available_analysts=list(ANALYST_TYPES.keys()),
|
||||
remove=[agent_id],
|
||||
)
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=(
|
||||
f"Active analyst team updated. Removed: {agent_id}. "
|
||||
f"Current active analysts: {', '.join(active)}"
|
||||
+ (f" | {callback_msg}" if callback_msg else "")
|
||||
),
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def _set_active_analysts(self, agent_ids: str) -> ToolResponse:
|
||||
"""Set active analysts from comma-separated agent ids."""
|
||||
requested = [
|
||||
item.strip() for item in str(agent_ids or "").split(",") if item.strip()
|
||||
]
|
||||
config_name = self.config.get("config_name", "default")
|
||||
project_root = Path(__file__).resolve().parents[2]
|
||||
active = update_active_analysts(
|
||||
project_root=project_root,
|
||||
config_name=config_name,
|
||||
available_analysts=list(ANALYST_TYPES.keys()),
|
||||
set_to=requested,
|
||||
)
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text=f"Active analyst team set to: {', '.join(active)}",
|
||||
),
|
||||
],
|
||||
)
|
||||
|
||||
def _create_team_analyst(self, agent_id: str, analyst_type: str) -> ToolResponse:
|
||||
"""Create a runtime analyst instance and activate it."""
|
||||
callback = self._create_team_agent_cb
|
||||
if callback is None:
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(
|
||||
type="text",
|
||||
text="Runtime agent creation is not available in current pipeline.",
|
||||
),
|
||||
],
|
||||
)
|
||||
result = callback(agent_id=agent_id, analyst_type=analyst_type)
|
||||
return ToolResponse(
|
||||
content=[
|
||||
TextBlock(type="text", text=result),
|
||||
],
|
||||
)
|
||||
|
||||
def set_team_controller(
|
||||
self,
|
||||
*,
|
||||
create_agent_callback: Optional[Callable[..., str]] = None,
|
||||
remove_agent_callback: Optional[Callable[..., str]] = None,
|
||||
) -> None:
|
||||
"""Inject runtime team lifecycle callbacks from pipeline."""
|
||||
object.__setattr__(self, "_create_team_agent_cb", create_agent_callback)
|
||||
object.__setattr__(self, "_remove_team_agent_cb", remove_agent_callback)
|
||||
|
||||
async def reply(self, x: Msg = None) -> Msg:
|
||||
"""
|
||||
Make investment decisions
|
||||
|
||||
Returns:
|
||||
Msg with decisions in metadata
|
||||
"""
|
||||
if x is None:
|
||||
return Msg(
|
||||
name=self.name,
|
||||
content="No input provided",
|
||||
role="assistant",
|
||||
)
|
||||
|
||||
# Clear previous decisions
|
||||
self._decisions = {}
|
||||
|
||||
progress.update_status(
|
||||
self.name,
|
||||
None,
|
||||
"Analyzing and making decisions",
|
||||
)
|
||||
|
||||
result = await super().reply(x)
|
||||
|
||||
progress.update_status(self.name, None, "Completed")
|
||||
|
||||
# Attach decisions to metadata
|
||||
if result.metadata is None:
|
||||
result.metadata = {}
|
||||
result.metadata["decisions"] = self._decisions.copy()
|
||||
result.metadata["portfolio"] = self.portfolio.copy()
|
||||
|
||||
return result
|
||||
|
||||
def get_decisions(self) -> Dict[str, Dict]:
|
||||
"""Get decisions from current cycle"""
|
||||
return self._decisions.copy()
|
||||
|
||||
def get_portfolio_state(self) -> Dict[str, Any]:
|
||||
"""Get current portfolio state"""
|
||||
return self.portfolio.copy()
|
||||
|
||||
def load_portfolio_state(self, portfolio: Dict[str, Any]):
|
||||
"""Load portfolio state"""
|
||||
if not portfolio:
|
||||
return
|
||||
self.portfolio = {
|
||||
"cash": portfolio.get("cash", self.portfolio["cash"]),
|
||||
"positions": portfolio.get("positions", {}).copy(),
|
||||
"margin_used": portfolio.get("margin_used", 0.0),
|
||||
"margin_requirement": portfolio.get(
|
||||
"margin_requirement",
|
||||
self.portfolio["margin_requirement"],
|
||||
),
|
||||
}
|
||||
|
||||
def update_portfolio(self, portfolio: Dict[str, Any]):
|
||||
"""Update portfolio after external execution"""
|
||||
self.portfolio.update(portfolio)
|
||||
|
||||
def _has_open_positions(self) -> bool:
|
||||
"""Return whether the current portfolio still has non-zero positions."""
|
||||
for position in self.portfolio.get("positions", {}).values():
|
||||
if position.get("long", 0) or position.get("short", 0):
|
||||
return True
|
||||
return False
|
||||
|
||||
def can_apply_initial_cash(self) -> bool:
|
||||
"""Only allow cash rebasing before any positions or margin exist."""
|
||||
return (
|
||||
not self._has_open_positions()
|
||||
and float(self.portfolio.get("margin_used", 0.0) or 0.0) == 0.0
|
||||
)
|
||||
|
||||
def apply_runtime_portfolio_config(
|
||||
self,
|
||||
*,
|
||||
margin_requirement: Optional[float] = None,
|
||||
initial_cash: Optional[float] = None,
|
||||
) -> Dict[str, bool]:
|
||||
"""Apply safe run-time portfolio config updates."""
|
||||
result = {
|
||||
"margin_requirement": False,
|
||||
"initial_cash": False,
|
||||
}
|
||||
|
||||
if margin_requirement is not None:
|
||||
self.portfolio["margin_requirement"] = float(margin_requirement)
|
||||
result["margin_requirement"] = True
|
||||
|
||||
if initial_cash is not None and self.can_apply_initial_cash():
|
||||
self.portfolio["cash"] = float(initial_cash)
|
||||
result["initial_cash"] = True
|
||||
|
||||
return result
|
||||
|
||||
def reload_runtime_assets(self, active_skill_dirs: Optional[list] = None) -> None:
|
||||
"""Reload toolkit and system prompt from current run assets."""
|
||||
from .toolkit_factory import create_agent_toolkit
|
||||
|
||||
clear_prompt_factory_cache()
|
||||
toolkit_factory = self._toolkit_factory or create_agent_toolkit
|
||||
toolkit_kwargs = dict(self._toolkit_factory_kwargs)
|
||||
if active_skill_dirs is not None:
|
||||
toolkit_kwargs["active_skill_dirs"] = active_skill_dirs
|
||||
|
||||
self.toolkit = toolkit_factory(
|
||||
self.name,
|
||||
self.config.get("config_name", "default"),
|
||||
owner=self,
|
||||
**toolkit_kwargs,
|
||||
)
|
||||
self._apply_runtime_sys_prompt(
|
||||
build_agent_system_prompt(
|
||||
agent_id=self.name,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
),
|
||||
)
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
@@ -1,139 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
Risk Manager Agent - Based on AgentScope ReActAgent
|
||||
Uses LLM for risk assessment
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
RiskAgent is deprecated and will be removed in a future version.
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` instead.
|
||||
See docs/CRITICAL_FIXES.md for migration guide.
|
||||
"""
|
||||
import warnings
|
||||
from typing import Any, Dict, Optional
|
||||
|
||||
from agentscope.agent import ReActAgent
|
||||
from agentscope.memory import InMemoryMemory, LongTermMemoryBase
|
||||
from agentscope.message import Msg
|
||||
from agentscope.tool import Toolkit
|
||||
|
||||
from ..utils.progress import progress
|
||||
from .prompt_factory import build_agent_system_prompt, clear_prompt_factory_cache
|
||||
|
||||
# Emit deprecation warning on module import
|
||||
warnings.warn(
|
||||
"RiskAgent is deprecated. Use EvoAgent instead. "
|
||||
"See docs/CRITICAL_FIXES.md for migration guide.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
|
||||
class RiskAgent(ReActAgent):
|
||||
"""
|
||||
Risk Manager Agent - Uses LLM for risk assessment
|
||||
Inherits from AgentScope's ReActAgent
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
Use :class:`backend.agents.base.evo_agent.EvoAgent` with
|
||||
workspace-driven configuration instead.
|
||||
"""
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
model: Any,
|
||||
formatter: Any,
|
||||
name: str = "risk_manager",
|
||||
config: Optional[Dict[str, Any]] = None,
|
||||
long_term_memory: Optional[LongTermMemoryBase] = None,
|
||||
toolkit: Optional[Toolkit] = None,
|
||||
):
|
||||
"""
|
||||
Initialize Risk Manager Agent
|
||||
|
||||
.. deprecated:: 0.2.0
|
||||
Use :class:`backend.agents.unified_factory.UnifiedAgentFactory`
|
||||
or :class:`backend.agents.base.evo_agent.EvoAgent` instead.
|
||||
|
||||
Args:
|
||||
model: LLM model instance
|
||||
formatter: Message formatter instance
|
||||
name: Agent name
|
||||
config: Configuration dictionary
|
||||
long_term_memory: Optional ReMeTaskLongTermMemory instance
|
||||
"""
|
||||
# Emit runtime deprecation warning
|
||||
warnings.warn(
|
||||
"RiskAgent is deprecated. Use EvoAgent via UnifiedAgentFactory instead.",
|
||||
DeprecationWarning,
|
||||
stacklevel=2,
|
||||
)
|
||||
|
||||
object.__setattr__(self, "config", config or {})
|
||||
object.__setattr__(self, "agent_id", name)
|
||||
|
||||
if toolkit is None:
|
||||
toolkit = Toolkit()
|
||||
object.__setattr__(self, "toolkit", toolkit)
|
||||
|
||||
sys_prompt = self._load_system_prompt()
|
||||
|
||||
kwargs = {
|
||||
"name": name,
|
||||
"sys_prompt": sys_prompt,
|
||||
"model": model,
|
||||
"formatter": formatter,
|
||||
"toolkit": toolkit,
|
||||
"memory": InMemoryMemory(),
|
||||
"max_iters": 10,
|
||||
}
|
||||
if long_term_memory:
|
||||
kwargs["long_term_memory"] = long_term_memory
|
||||
kwargs["long_term_memory_mode"] = "static_control"
|
||||
|
||||
super().__init__(**kwargs)
|
||||
|
||||
def _load_system_prompt(self) -> str:
|
||||
"""Load system prompt for risk manager"""
|
||||
return build_agent_system_prompt(
|
||||
agent_id=self.agent_id,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
)
|
||||
|
||||
async def reply(self, x: Msg = None) -> Msg:
|
||||
"""
|
||||
Provide risk assessment
|
||||
|
||||
Args:
|
||||
x: Input message (content must be str)
|
||||
|
||||
Returns:
|
||||
Msg with risk warnings (content is str)
|
||||
"""
|
||||
progress.update_status(self.name, None, "Assessing risk")
|
||||
|
||||
result = await super().reply(x)
|
||||
|
||||
progress.update_status(self.name, None, "Risk assessment completed")
|
||||
|
||||
return result
|
||||
|
||||
def reload_runtime_assets(self, active_skill_dirs: Optional[list] = None) -> None:
|
||||
"""Reload toolkit and system prompt from current run assets."""
|
||||
from .toolkit_factory import create_agent_toolkit
|
||||
|
||||
clear_prompt_factory_cache()
|
||||
self.toolkit = create_agent_toolkit(
|
||||
self.agent_id,
|
||||
self.config.get("config_name", "default"),
|
||||
active_skill_dirs=active_skill_dirs,
|
||||
)
|
||||
self._apply_runtime_sys_prompt(self._load_system_prompt())
|
||||
|
||||
def _apply_runtime_sys_prompt(self, sys_prompt: str) -> None:
|
||||
"""Update the prompt used by future turns and the cached system msg."""
|
||||
self._sys_prompt = sys_prompt
|
||||
for msg, _marks in self.memory.content:
|
||||
if getattr(msg, "role", None) == "system":
|
||||
msg.content = sys_prompt
|
||||
break
|
||||
@@ -6,7 +6,7 @@ import shutil
|
||||
import tempfile
|
||||
import zipfile
|
||||
from threading import Lock
|
||||
from typing import Any, Dict, Iterable, Iterator, List, Optional, Set
|
||||
from typing import Any, Dict, Iterable, List, Optional, Set
|
||||
from urllib.parse import urlparse
|
||||
from urllib.request import urlretrieve
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Callable, Dict, List, Optional, Set
|
||||
from typing import Callable, Dict, List, Set
|
||||
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
@@ -10,7 +10,6 @@ from __future__ import annotations
|
||||
import logging
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from agentscope.message import Msg
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ from __future__ import annotations
|
||||
import asyncio
|
||||
import logging
|
||||
import uuid
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Union
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional
|
||||
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from typing import Any, Awaitable, Callable, Dict, List, Optional, Type
|
||||
from typing import Any, Dict, List, Optional
|
||||
|
||||
from agentscope.message import Msg
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ import yaml
|
||||
|
||||
from backend.agents.agent_workspace import load_agent_workspace_config
|
||||
from backend.agents.skills_manager import SkillsManager
|
||||
from backend.agents.skill_loader import load_skill_from_dir, get_skill_tools
|
||||
from backend.agents.skill_metadata import parse_skill_metadata
|
||||
from backend.config.bootstrap_config import get_bootstrap_config_for_run
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
Reference in New Issue
Block a user