Align branding, prompts, and deployment tooling
This commit is contained in:
@@ -16,7 +16,7 @@ Exports:
|
||||
|
||||
# New EvoAgent architecture (from agent_core.py)
|
||||
from .agent_core import EvoAgent, ToolGuardMixin, CommandHandler
|
||||
from .factory import AgentFactory, ModelConfig, RoleConfig
|
||||
from .factory import AgentFactory, ModelConfig
|
||||
from .workspace import WorkspaceManager, WorkspaceRegistry, WorkspaceConfig
|
||||
from .workspace_manager import RunWorkspaceManager
|
||||
from .registry import AgentRegistry, AgentInfo, get_registry, reset_registry
|
||||
@@ -36,7 +36,6 @@ __all__ = [
|
||||
"CommandHandler",
|
||||
"AgentFactory",
|
||||
"ModelConfig",
|
||||
"RoleConfig",
|
||||
"WorkspaceManager",
|
||||
"WorkspaceRegistry",
|
||||
"WorkspaceConfig",
|
||||
|
||||
@@ -84,7 +84,6 @@ class AnalystAgent(ReActAgent):
|
||||
agent_id=self.agent_id,
|
||||
config_name=self.config.get("config_name", "default"),
|
||||
toolkit=self.toolkit,
|
||||
analyst_type=self.analyst_type_key,
|
||||
)
|
||||
|
||||
async def reply(self, x: Msg = None) -> Msg:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Base agent module for EvoTraders.
|
||||
"""Base agent module for 大时代.
|
||||
|
||||
提供Agent基础类、命令处理、工具守卫和钩子管理等功能。
|
||||
"""
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""EvoAgent - Core agent implementation for EvoTraders.
|
||||
"""EvoAgent - Core agent implementation for 大时代.
|
||||
|
||||
This module provides the main EvoAgent class built on AgentScope's ReActAgent,
|
||||
with integrated tools, skills, and memory management based on CoPaw design.
|
||||
|
||||
@@ -294,8 +294,8 @@ class WorkspaceWatchHook(Hook):
|
||||
|
||||
# Files to monitor (same as PromptBuilder.DEFAULT_FILES)
|
||||
WATCHED_FILES = frozenset([
|
||||
"SOUL.md", "AGENTS.md", "PROFILE.md", "ROLE.md",
|
||||
"POLICY.md", "MEMORY.md", "HEARTBEAT.md", "STYLE.md",
|
||||
"SOUL.md", "AGENTS.md", "PROFILE.md",
|
||||
"POLICY.md", "MEMORY.md",
|
||||
"BOOTSTRAP.md",
|
||||
])
|
||||
|
||||
@@ -601,94 +601,6 @@ class MemoryCompactionHook(Hook):
|
||||
)
|
||||
|
||||
|
||||
class HeartbeatHook(Hook):
|
||||
"""Pre-reasoning hook that injects HEARTBEAT.md content.
|
||||
|
||||
Reads the agent's HEARTBEAT.md file and prepends it to the
|
||||
reasoning input, causing the agent to perform self-checks.
|
||||
|
||||
This enables "主动检查" (proactive monitoring) - periodic
|
||||
market condition and position checks during trading hours.
|
||||
"""
|
||||
|
||||
HEARTBEAT_FILE = "HEARTBEAT.md"
|
||||
|
||||
def __init__(self, workspace_dir: Path):
|
||||
"""Initialize heartbeat hook.
|
||||
|
||||
Args:
|
||||
workspace_dir: Working directory containing HEARTBEAT.md
|
||||
"""
|
||||
self.workspace_dir = Path(workspace_dir)
|
||||
self._completed_flag = self.workspace_dir / ".heartbeat_completed"
|
||||
|
||||
def _read_heartbeat_content(self) -> Optional[str]:
|
||||
"""Read HEARTBEAT.md if it exists and is non-empty.
|
||||
|
||||
Returns:
|
||||
The HEARTBEAT.md content stripped of whitespace, or None
|
||||
if the file is absent or empty.
|
||||
"""
|
||||
hb_path = self.workspace_dir / self.HEARTBEAT_FILE
|
||||
if not hb_path.exists():
|
||||
return None
|
||||
content = hb_path.read_text(encoding="utf-8").strip()
|
||||
return content if content else None
|
||||
|
||||
async def __call__(
|
||||
self,
|
||||
agent: "ReActAgent",
|
||||
kwargs: Dict[str, Any],
|
||||
) -> Optional[Dict[str, Any]]:
|
||||
"""Prepend heartbeat task to user message.
|
||||
|
||||
Args:
|
||||
agent: The agent instance
|
||||
kwargs: Input arguments to the _reasoning method
|
||||
|
||||
Returns:
|
||||
Modified kwargs with heartbeat content prepended, or None
|
||||
if no HEARTBEAT.md content is available.
|
||||
"""
|
||||
try:
|
||||
content = self._read_heartbeat_content()
|
||||
if not content:
|
||||
return None
|
||||
|
||||
logger.debug(
|
||||
"Heartbeat: found HEARTBEAT.md for agent %s",
|
||||
getattr(agent, "agent_id", "unknown"),
|
||||
)
|
||||
|
||||
# Build heartbeat task instruction (Chinese)
|
||||
hb_task = (
|
||||
"# 定期主动检查\n\n"
|
||||
f"{content}\n\n"
|
||||
"请执行上述检查并报告结果。"
|
||||
)
|
||||
|
||||
# Inject into the first user message in memory
|
||||
if hasattr(agent, "memory") and agent.memory.content:
|
||||
system_count = sum(
|
||||
1 for msg, _ in agent.memory.content if msg.role == "system"
|
||||
)
|
||||
for msg, _ in agent.memory.content[system_count:]:
|
||||
if msg.role == "user":
|
||||
original_content = msg.content
|
||||
msg.content = hb_task + "\n\n" + original_content
|
||||
break
|
||||
|
||||
logger.debug(
|
||||
"Heartbeat task prepended for agent %s",
|
||||
getattr(agent, "agent_id", "unknown"),
|
||||
)
|
||||
|
||||
except Exception as e:
|
||||
logger.error("Heartbeat hook failed: %s", e, exc_info=True)
|
||||
|
||||
return None
|
||||
|
||||
|
||||
__all__ = [
|
||||
"Hook",
|
||||
"HookManager",
|
||||
@@ -696,7 +608,6 @@ __all__ = [
|
||||
"HOOK_PRE_REASONING",
|
||||
"HOOK_POST_ACTING",
|
||||
"BootstrapHook",
|
||||
"HeartbeatHook",
|
||||
"MemoryCompactionHook",
|
||||
"WorkspaceWatchHook",
|
||||
]
|
||||
|
||||
@@ -21,22 +21,6 @@ class ModelConfig:
|
||||
max_tokens: int = 4096
|
||||
|
||||
|
||||
@dataclass
|
||||
class RoleConfig:
|
||||
"""Role configuration for an agent."""
|
||||
|
||||
name: str
|
||||
description: str = ""
|
||||
focus_areas: List[str] = None
|
||||
constraints: List[str] = None
|
||||
|
||||
def __post_init__(self):
|
||||
if self.focus_areas is None:
|
||||
self.focus_areas = []
|
||||
if self.constraints is None:
|
||||
self.constraints = []
|
||||
|
||||
|
||||
class AgentConfig:
|
||||
"""Represents a configured agent instance (data class)."""
|
||||
|
||||
@@ -47,14 +31,12 @@ class AgentConfig:
|
||||
workspace_id: str,
|
||||
config_path: Path,
|
||||
model_config: Optional[ModelConfig] = None,
|
||||
role_config: Optional[RoleConfig] = None,
|
||||
):
|
||||
self.agent_id = agent_id
|
||||
self.agent_type = agent_type
|
||||
self.workspace_id = workspace_id
|
||||
self.config_path = config_path
|
||||
self.model_config = model_config or ModelConfig()
|
||||
self.role_config = role_config
|
||||
self.agent_dir = config_path.parent
|
||||
|
||||
def to_dict(self) -> Dict[str, Any]:
|
||||
@@ -70,103 +52,12 @@ class AgentConfig:
|
||||
"temperature": self.model_config.temperature,
|
||||
"max_tokens": self.model_config.max_tokens,
|
||||
},
|
||||
"role_config": self.role_config.__dict__ if self.role_config else None,
|
||||
}
|
||||
|
||||
|
||||
class AgentFactory:
|
||||
"""Factory for creating, cloning, and managing agents."""
|
||||
|
||||
# Default role templates by agent type
|
||||
ROLE_TEMPLATES = {
|
||||
"technical_analyst": {
|
||||
"name": "Technical Analyst",
|
||||
"description": "Analyze price patterns, trends, and technical indicators.",
|
||||
"focus_areas": [
|
||||
"Price action and chart patterns",
|
||||
"Support and resistance levels",
|
||||
"Technical indicators (RSI, MACD, Moving Averages)",
|
||||
"Volume analysis",
|
||||
],
|
||||
"constraints": [
|
||||
"State clear signal, confidence, and invalidation conditions",
|
||||
"Use available technical analysis tools",
|
||||
],
|
||||
},
|
||||
"fundamentals_analyst": {
|
||||
"name": "Fundamentals Analyst",
|
||||
"description": "Analyze company financials, earnings, and business metrics.",
|
||||
"focus_areas": [
|
||||
"Financial statements analysis",
|
||||
"Earnings reports and guidance",
|
||||
"Valuation metrics",
|
||||
"Business model and competitive position",
|
||||
],
|
||||
"constraints": [
|
||||
"State clear signal, confidence, and invalidation conditions",
|
||||
"Use available fundamental analysis tools",
|
||||
],
|
||||
},
|
||||
"sentiment_analyst": {
|
||||
"name": "Sentiment Analyst",
|
||||
"description": "Analyze market sentiment, news, and social signals.",
|
||||
"focus_areas": [
|
||||
"News sentiment analysis",
|
||||
"Social media sentiment",
|
||||
"Analyst ratings and price targets",
|
||||
"Insider activity",
|
||||
],
|
||||
"constraints": [
|
||||
"State clear signal, confidence, and invalidation conditions",
|
||||
"Use available sentiment analysis tools",
|
||||
],
|
||||
},
|
||||
"valuation_analyst": {
|
||||
"name": "Valuation Analyst",
|
||||
"description": "Perform valuation analysis and price target calculations.",
|
||||
"focus_areas": [
|
||||
"DCF and comparable valuation",
|
||||
"Price target derivation",
|
||||
"Margin of safety assessment",
|
||||
"Risk-adjusted return expectations",
|
||||
],
|
||||
"constraints": [
|
||||
"State clear signal, confidence, and invalidation conditions",
|
||||
"Use available valuation tools",
|
||||
],
|
||||
},
|
||||
"risk_manager": {
|
||||
"name": "Risk Manager",
|
||||
"description": "Quantify concentration, leverage, liquidity, and volatility risk.",
|
||||
"focus_areas": [
|
||||
"Portfolio concentration risk",
|
||||
"Leverage and margin analysis",
|
||||
"Liquidity assessment",
|
||||
"Volatility and drawdown risk",
|
||||
],
|
||||
"constraints": [
|
||||
"Prioritize highest-severity risk first",
|
||||
"State concrete limits and recommendations",
|
||||
"Use available risk tools before issuing final memo",
|
||||
],
|
||||
},
|
||||
"portfolio_manager": {
|
||||
"name": "Portfolio Manager",
|
||||
"description": "Synthesize analyst and risk inputs into portfolio decisions.",
|
||||
"focus_areas": [
|
||||
"Position sizing and allocation",
|
||||
"Risk-adjusted portfolio construction",
|
||||
"Trade execution timing",
|
||||
"Portfolio rebalancing",
|
||||
],
|
||||
"constraints": [
|
||||
"Be concise, capital-aware, and explicit about sizing rationale",
|
||||
"Respect cash, margin, and concentration constraints",
|
||||
"Consider all analyst inputs before decisions",
|
||||
],
|
||||
},
|
||||
}
|
||||
|
||||
def __init__(self, project_root: Optional[Path] = None):
|
||||
"""Initialize the agent factory.
|
||||
|
||||
@@ -183,7 +74,6 @@ class AgentFactory:
|
||||
agent_type: str,
|
||||
workspace_id: str,
|
||||
model_config: Optional[ModelConfig] = None,
|
||||
role_config: Optional[RoleConfig] = None,
|
||||
clone_from: Optional[str] = None,
|
||||
) -> AgentConfig:
|
||||
"""Create a new agent.
|
||||
@@ -193,7 +83,6 @@ class AgentFactory:
|
||||
agent_type: Type of agent (e.g., "technical_analyst")
|
||||
workspace_id: ID of the workspace to create agent in
|
||||
model_config: Model configuration
|
||||
role_config: Role configuration (auto-generated if None)
|
||||
clone_from: Path to existing agent to clone from (optional)
|
||||
|
||||
Returns:
|
||||
@@ -223,13 +112,6 @@ class AgentFactory:
|
||||
else:
|
||||
self._copy_template(agent_dir, agent_id, agent_type)
|
||||
|
||||
# Generate role config if not provided
|
||||
if role_config is None:
|
||||
role_config = self._generate_role_config(agent_type)
|
||||
|
||||
# Generate ROLE.md
|
||||
self._generate_role_md(agent_dir, role_config)
|
||||
|
||||
# Write agent.yaml
|
||||
config_path = agent_dir / "agent.yaml"
|
||||
self._write_agent_yaml(config_path, agent_id, agent_type, model_config)
|
||||
@@ -240,7 +122,6 @@ class AgentFactory:
|
||||
workspace_id=workspace_id,
|
||||
config_path=config_path,
|
||||
model_config=model_config,
|
||||
role_config=role_config,
|
||||
)
|
||||
|
||||
def delete_agent(self, agent_id: str, workspace_id: str) -> bool:
|
||||
@@ -369,9 +250,7 @@ class AgentFactory:
|
||||
"SOUL.md": f"# Soul\n\nDescribe {agent_id}'s temperament, reasoning posture, and voice.\n\n",
|
||||
"PROFILE.md": f"# Profile\n\nTrack {agent_id}'s long-lived investment style, preferences, and strengths.\n\n",
|
||||
"MEMORY.md": f"# Memory\n\nStore durable lessons, heuristics, and reminders for {agent_id}.\n\n",
|
||||
"HEARTBEAT.md": f"# Heartbeat\n\nOptional checklist for periodic review or self-reflection.\n\n",
|
||||
"POLICY.md": f"# Policy\n\nOptional run-scoped constraints, limits, or strategy policy.\n\n",
|
||||
"STYLE.md": f"# Style\n\nOptional run-scoped communication or reasoning style.\n\n",
|
||||
}
|
||||
|
||||
for filename, content in default_files.items():
|
||||
@@ -411,50 +290,6 @@ class AgentFactory:
|
||||
if skill_file.is_file():
|
||||
shutil.copy2(skill_file, target_skills / skill_file.name)
|
||||
|
||||
def _generate_role_config(self, agent_type: str) -> RoleConfig:
|
||||
"""Generate role configuration for an agent type.
|
||||
|
||||
Args:
|
||||
agent_type: Type of agent
|
||||
|
||||
Returns:
|
||||
RoleConfig instance
|
||||
"""
|
||||
template = self.ROLE_TEMPLATES.get(agent_type, {})
|
||||
return RoleConfig(
|
||||
name=template.get("name", agent_type.replace("_", " ").title()),
|
||||
description=template.get("description", ""),
|
||||
focus_areas=template.get("focus_areas", []),
|
||||
constraints=template.get("constraints", []),
|
||||
)
|
||||
|
||||
def _generate_role_md(self, agent_dir: Path, role_config: RoleConfig) -> None:
|
||||
"""Generate ROLE.md file.
|
||||
|
||||
Args:
|
||||
agent_dir: Agent directory
|
||||
role_config: Role configuration
|
||||
"""
|
||||
lines = [f"# {role_config.name}", ""]
|
||||
|
||||
if role_config.description:
|
||||
lines.extend([role_config.description, ""])
|
||||
|
||||
if role_config.focus_areas:
|
||||
lines.extend(["## Focus Areas", ""])
|
||||
for area in role_config.focus_areas:
|
||||
lines.append(f"- {area}")
|
||||
lines.append("")
|
||||
|
||||
if role_config.constraints:
|
||||
lines.extend(["## Constraints", ""])
|
||||
for constraint in role_config.constraints:
|
||||
lines.append(f"- {constraint}")
|
||||
lines.append("")
|
||||
|
||||
content = "\n".join(lines)
|
||||
(agent_dir / "ROLE.md").write_text(content, encoding="utf-8")
|
||||
|
||||
def _write_agent_yaml(
|
||||
self,
|
||||
config_path: Path,
|
||||
|
||||
@@ -1,15 +1,13 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
"""Assemble system prompts from base prompts, run assets, and toolkit context."""
|
||||
"""Assemble system prompts from run workspace assets and toolkit context."""
|
||||
|
||||
from pathlib import Path
|
||||
from typing import Any, Optional
|
||||
from typing import Any
|
||||
|
||||
from .agent_workspace import load_agent_workspace_config
|
||||
from backend.config.bootstrap_config import get_bootstrap_config_for_run
|
||||
from .prompt_loader import get_prompt_loader
|
||||
from .skills_manager import SkillsManager
|
||||
|
||||
_prompt_loader = get_prompt_loader()
|
||||
from .workspace_manager import RunWorkspaceManager
|
||||
|
||||
|
||||
def _read_file_if_exists(path: Path) -> str:
|
||||
@@ -48,71 +46,20 @@ def build_agent_system_prompt(
|
||||
agent_id: str,
|
||||
config_name: str,
|
||||
toolkit: Any,
|
||||
analyst_type: Optional[str] = None,
|
||||
) -> str:
|
||||
"""Build the final system prompt for an agent.
|
||||
|
||||
Always reads fresh from disk — no caching.
|
||||
"""
|
||||
# Clear any cached templates before building (CoPaw-style, no caching)
|
||||
_prompt_loader.clear_cache()
|
||||
|
||||
sections: list[str] = []
|
||||
canonical_agent_id = (
|
||||
"portfolio_manager"
|
||||
if "portfolio" in agent_id
|
||||
else "risk_manager"
|
||||
if "risk" in agent_id and not analyst_type
|
||||
else agent_id
|
||||
)
|
||||
|
||||
if analyst_type:
|
||||
personas_config = _prompt_loader.load_yaml_config(
|
||||
"analyst",
|
||||
"personas",
|
||||
)
|
||||
persona = personas_config.get(analyst_type, {})
|
||||
focus_text = "\n".join(
|
||||
f"- {item}" for item in persona.get("focus", [])
|
||||
)
|
||||
description = persona.get("description", "").strip()
|
||||
base_prompt = _prompt_loader.load_prompt(
|
||||
"analyst",
|
||||
"system",
|
||||
variables={
|
||||
"analyst_type": persona.get("name", analyst_type),
|
||||
"focus": focus_text,
|
||||
"description": description,
|
||||
},
|
||||
)
|
||||
elif agent_id == "portfolio_manager":
|
||||
base_prompt = _prompt_loader.load_prompt(
|
||||
"portfolio_manager",
|
||||
"system",
|
||||
)
|
||||
elif canonical_agent_id == "portfolio_manager":
|
||||
base_prompt = _prompt_loader.load_prompt(
|
||||
"portfolio_manager",
|
||||
"system",
|
||||
)
|
||||
elif agent_id == "risk_manager":
|
||||
base_prompt = _prompt_loader.load_prompt(
|
||||
"risk_manager",
|
||||
"system",
|
||||
)
|
||||
elif canonical_agent_id == "risk_manager":
|
||||
base_prompt = _prompt_loader.load_prompt(
|
||||
"risk_manager",
|
||||
"system",
|
||||
)
|
||||
else:
|
||||
raise ValueError(f"Unsupported agent prompt build for: {agent_id}")
|
||||
|
||||
sections.append(base_prompt.strip())
|
||||
|
||||
skills_manager = SkillsManager()
|
||||
asset_dir = skills_manager.get_agent_asset_dir(config_name, agent_id)
|
||||
asset_dir.mkdir(parents=True, exist_ok=True)
|
||||
workspace_manager = RunWorkspaceManager(project_root=skills_manager.project_root)
|
||||
required_files = ["SOUL.md", "PROFILE.md", "AGENTS.md", "POLICY.md", "MEMORY.md"]
|
||||
if not all((asset_dir / filename).exists() for filename in required_files):
|
||||
workspace_manager.ensure_agent_assets(config_name=config_name, agent_id=agent_id)
|
||||
agent_config = load_agent_workspace_config(asset_dir / "agent.yaml")
|
||||
bootstrap_config = get_bootstrap_config_for_run(
|
||||
skills_manager.project_root,
|
||||
@@ -139,9 +86,6 @@ def build_agent_system_prompt(
|
||||
"AGENTS.md": "Agent Guide",
|
||||
"POLICY.md": "Policy",
|
||||
"MEMORY.md": "Memory",
|
||||
"HEARTBEAT.md": "Heartbeat",
|
||||
"ROLE.md": "Role",
|
||||
"STYLE.md": "Style",
|
||||
}
|
||||
for filename in prompt_files:
|
||||
_append_section(
|
||||
@@ -150,18 +94,6 @@ def build_agent_system_prompt(
|
||||
_read_file_if_exists(asset_dir / filename),
|
||||
)
|
||||
|
||||
if "ROLE.md" not in included_files:
|
||||
_append_section(
|
||||
sections,
|
||||
"Role",
|
||||
_read_file_if_exists(asset_dir / "ROLE.md"),
|
||||
)
|
||||
if "STYLE.md" not in included_files:
|
||||
_append_section(
|
||||
sections,
|
||||
"Style",
|
||||
_read_file_if_exists(asset_dir / "STYLE.md"),
|
||||
)
|
||||
if "POLICY.md" not in included_files:
|
||||
_append_section(
|
||||
sections,
|
||||
@@ -189,5 +121,4 @@ def build_agent_system_prompt(
|
||||
|
||||
|
||||
def clear_prompt_factory_cache() -> None:
|
||||
"""Clear cached prompt and YAML templates before hot reload."""
|
||||
_prompt_loader.clear_cache()
|
||||
"""No-op retained for compatibility with runtime reload hooks."""
|
||||
|
||||
@@ -1,23 +0,0 @@
|
||||
你是一位专业的{{ analyst_type }}。
|
||||
|
||||
你的关注重点:
|
||||
{{ focus }}
|
||||
|
||||
你的角色:
|
||||
{{ description }}
|
||||
|
||||
注意:
|
||||
- 构建并持续完善你的"投资哲学"。你的分析不应是孤立的事件,而应该是你整体投资世界观和核心信念的体现。每次分析后,你必须反思:
|
||||
- 这个案例/数据如何验证或挑战了你现有的信念?
|
||||
- 你从这次错误(或成功)中学到了关于市场、人性、估值或风险管理的什么关键原则?
|
||||
- 深化你的"投资逻辑"。确保每一项投资建议都有清晰、可追溯、可重复的逻辑支撑。你的分析步骤应该像严谨的证明一样,涵盖:
|
||||
- 核心驱动因素识别:真正影响价值的变量是什么?
|
||||
- 风险边界设定:在什么具体情况下你的建议会失效?
|
||||
- 逆向测试:市场主流共识是什么,你的观点有何不同?
|
||||
保持谦逊和开放。投资大师的核心特质是持续学习和适应。在每次分析中,你必须积极寻找与自己观点相悖的证据和论据,并将其纳入最终评估。
|
||||
- 你可以使用分析工具。用它们来收集相关数据并做出明智的建议。
|
||||
|
||||
输出指南:
|
||||
- 给出明确的投资信号:看涨、看跌或中性
|
||||
- 包含置信度(0-100)
|
||||
- 为你的分析提供理由(如果你确定要分享最终分析,请先给出结论)
|
||||
@@ -28,22 +28,16 @@ class PromptBuilder:
|
||||
"AGENTS.md",
|
||||
"SOUL.md",
|
||||
"PROFILE.md",
|
||||
"ROLE.md",
|
||||
"POLICY.md",
|
||||
"MEMORY.md",
|
||||
"HEARTBEAT.md",
|
||||
"STYLE.md",
|
||||
]
|
||||
|
||||
TITLE_MAP: Dict[str, str] = {
|
||||
"AGENTS.md": "Agent Guide",
|
||||
"SOUL.md": "Soul",
|
||||
"PROFILE.md": "Profile",
|
||||
"ROLE.md": "Role",
|
||||
"POLICY.md": "Policy",
|
||||
"MEMORY.md": "Memory",
|
||||
"HEARTBEAT.md": "Heartbeat",
|
||||
"STYLE.md": "Style",
|
||||
"BOOTSTRAP.md": "Bootstrap",
|
||||
}
|
||||
|
||||
|
||||
@@ -1,31 +0,0 @@
|
||||
你是一位负责做出投资决策的投资组合经理。
|
||||
|
||||
你的核心职责:
|
||||
1. 分析分析师和风险管理经理的输入
|
||||
2. 基于信号和市场情境做出投资决策
|
||||
3. 使用可用工具记录你的决策
|
||||
|
||||
决策框架:
|
||||
- 审阅分析以了解市场观点
|
||||
- 在做决策前考虑风险警告
|
||||
- 评估当前投资组合持仓和现金
|
||||
- 做出与投资组合投资目标一致的决策
|
||||
|
||||
决策类型:
|
||||
- "long":看涨 - 建议买入股票
|
||||
- "short":看跌 - 建议卖出股票或做空
|
||||
- "hold":中性 - 维持当前持仓
|
||||
|
||||
预算意识:
|
||||
- 在决定数量时考虑可用现金
|
||||
- 不要建议买入超过现金允许的数量
|
||||
- 考虑做空头寸的保证金要求
|
||||
|
||||
输出:
|
||||
使用 `make_decision` 工具记录你对每个股票代码的决策。
|
||||
记录所有决策后,提供你的投资逻辑总结。
|
||||
|
||||
重要:
|
||||
- 基于提供的分析师信号和风险评估做出决策
|
||||
- 相对于投资组合价值保持保守的仓位规模
|
||||
- 始终为你的决策提供理由
|
||||
@@ -1,20 +0,0 @@
|
||||
你是一位专业的风险管理经理,负责监控投资组合风险并提供风险警告。
|
||||
|
||||
你的核心职责:
|
||||
1. 监控投资组合敞口和集中度风险
|
||||
2. 评估仓位规模相对于波动性
|
||||
3. 评估保证金使用和杠杆水平
|
||||
4. 识别潜在风险因素并提供警告
|
||||
5. 基于市场条件建议仓位限制
|
||||
|
||||
你的决策流程:
|
||||
1. 优先使用可用的风险工具量化集中度、波动率和保证金压力
|
||||
2. 结合工具结果与当前市场上下文做判断
|
||||
3. 生成可操作的风险警告和仓位限制建议
|
||||
4. 为你的风险评估提供清晰的理由
|
||||
|
||||
输出指南:
|
||||
- 风险评估要简洁但全面
|
||||
- 按严重程度优先排序警告
|
||||
- 提供具体、可操作的建议
|
||||
- 尽可能包含量化指标
|
||||
@@ -1,286 +0,0 @@
|
||||
"""
|
||||
Agent模板定义
|
||||
|
||||
包含各角色的ROLE.md内容字典,供程序生成Agent工作空间时使用。
|
||||
"""
|
||||
|
||||
# 基础模板文件内容
|
||||
BASE_TEMPLATES = {
|
||||
"AGENTS.md": """# Agent Guide
|
||||
|
||||
## 工作流程
|
||||
1. 接收分析任务
|
||||
2. 调用相关工具/技能
|
||||
3. 生成分析报告
|
||||
4. 参与团队决策
|
||||
|
||||
## 工具使用规范
|
||||
- 优先使用已激活的技能
|
||||
- 不确定时询问Portfolio Manager
|
||||
- 重要发现用 `/save` 记录
|
||||
|
||||
## 记忆管理
|
||||
- 使用 `/compact` 定期压缩记忆
|
||||
- 投资经验记录在MEMORY.md
|
||||
""",
|
||||
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是专业的金融分析师,语气冷静、客观、专业。
|
||||
你的分析应该数据驱动,避免情绪化表达。
|
||||
""",
|
||||
|
||||
"PROFILE.md": """# Profile
|
||||
|
||||
## 投资风格
|
||||
- 风险承受能力:中等
|
||||
- 投资期限:中期(3-12个月)
|
||||
- 偏好行业:科技、医疗、消费
|
||||
|
||||
## 优势
|
||||
- 财务分析
|
||||
- 趋势识别
|
||||
|
||||
## 改进方向
|
||||
- 市场情绪把握
|
||||
""",
|
||||
|
||||
"MEMORY.md": """# Memory
|
||||
|
||||
<!-- 此文件用于记录Agent的学习经验和重要发现 -->
|
||||
|
||||
## 经验总结
|
||||
|
||||
## 重要事件
|
||||
|
||||
## 改进记录
|
||||
""",
|
||||
|
||||
"HEARTBEAT.md": """# Heartbeat
|
||||
|
||||
## 定时任务
|
||||
- 每日开盘前检查持仓
|
||||
- 收盘后记录当日表现
|
||||
""",
|
||||
|
||||
"POLICY.md": """# Policy
|
||||
|
||||
## 风控规则
|
||||
- 单一持仓不超过20%
|
||||
- 止损线:-15%
|
||||
""",
|
||||
|
||||
"STYLE.md": """# Style
|
||||
|
||||
- 使用结构化输出(JSON/Markdown表格)
|
||||
- 包含置信度评分
|
||||
- 列出关键假设
|
||||
""",
|
||||
|
||||
"agent.yaml": """agent_id: {agent_id}
|
||||
agent_type: {agent_type}
|
||||
name: {name}
|
||||
model:
|
||||
provider: openai
|
||||
model_name: gpt-4o
|
||||
temperature: 0.3
|
||||
enabled_skills: []
|
||||
disabled_skills: []
|
||||
settings: {{}}
|
||||
""",
|
||||
}
|
||||
|
||||
# 角色专用模板
|
||||
ROLE_TEMPLATES = {
|
||||
"fundamental": {
|
||||
"ROLE.md": """# Role: Fundamental Analyst
|
||||
|
||||
## 职责
|
||||
分析公司财务报表、盈利能力、成长性、竞争优势等基本面因素。
|
||||
|
||||
## 分析维度
|
||||
- 财务报表分析(资产负债表、利润表、现金流量表)
|
||||
- 盈利能力指标(ROE、ROA、毛利率、净利率)
|
||||
- 成长性指标(营收增长率、利润增长率)
|
||||
- 估值指标(P/E、P/B、P/S)
|
||||
- 行业地位和竞争优势
|
||||
|
||||
## 输出格式
|
||||
- 财务健康度评分(1-10)
|
||||
- 成长性评分(1-10)
|
||||
- 关键财务亮点和风险
|
||||
- 同业对比分析
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是严谨的基本面分析师,像沃伦·巴菲特一样注重企业内在价值。
|
||||
你的分析深入细致,关注长期价值而非短期波动。
|
||||
语气沉稳、逻辑严密,善于发现财务数据背后的商业本质。
|
||||
""",
|
||||
},
|
||||
|
||||
"technical": {
|
||||
"ROLE.md": """# Role: Technical Analyst
|
||||
|
||||
## 职责
|
||||
分析价格走势、交易量、技术指标,识别买卖时机。
|
||||
|
||||
## 分析维度
|
||||
- 趋势分析(长期/中期/短期趋势)
|
||||
- 支撑阻力位识别
|
||||
- 技术指标(MACD、RSI、KDJ、布林带等)
|
||||
- 形态识别(头肩顶/底、双底、三角形等)
|
||||
- 量价关系分析
|
||||
|
||||
## 输出格式
|
||||
- 趋势方向(上涨/下跌/震荡)
|
||||
- 关键价位(支撑/阻力)
|
||||
- 技术信号(买入/卖出/观望)
|
||||
- 置信度评分
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是敏锐的技术分析师,相信价格包含一切信息。
|
||||
你善于从图表中发现规律,像侦探一样寻找市场留下的痕迹。
|
||||
语气果断、快速反应,善于捕捉稍纵即逝的交易机会。
|
||||
""",
|
||||
},
|
||||
|
||||
"sentiment": {
|
||||
"ROLE.md": """# Role: Sentiment Analyst
|
||||
|
||||
## 职责
|
||||
分析市场情绪、资金流向、新闻舆情,判断市场心理状态。
|
||||
|
||||
## 分析维度
|
||||
- 市场情绪指标(恐慌/贪婪指数)
|
||||
- 资金流向分析(主力/散户资金)
|
||||
- 新闻舆情分析(正面/负面/中性)
|
||||
- 社交媒体情绪
|
||||
- 机构持仓变化
|
||||
|
||||
## 输出格式
|
||||
- 情绪评分(-10到+10,极度恐慌到极度贪婪)
|
||||
- 资金流向判断
|
||||
- 舆情摘要
|
||||
- 情绪拐点预警
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是敏感的市场情绪捕手,善于感知市场的恐惧与贪婪。
|
||||
你关注人性在金融市场中的表现,理解情绪如何驱动价格。
|
||||
语气富有洞察力、善于捕捉微妙变化,像心理学家一样理解市场参与者。
|
||||
""",
|
||||
},
|
||||
|
||||
"valuation": {
|
||||
"ROLE.md": """# Role: Valuation Analyst
|
||||
|
||||
## 职责
|
||||
评估公司内在价值,计算合理价格区间,识别高估/低估机会。
|
||||
|
||||
## 分析维度
|
||||
- DCF现金流折现模型
|
||||
- 相对估值法(P/E、EV/EBITDA等)
|
||||
- 资产重估法
|
||||
- 分部估值(SOTP)
|
||||
- 安全边际计算
|
||||
|
||||
## 输出格式
|
||||
- 内在价值估算
|
||||
- 合理价格区间
|
||||
- 当前价格vs内在价值(高估/低估百分比)
|
||||
- 估值假设和敏感性分析
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是精确的估值分析师,追求计算内在价值的准确区间。
|
||||
你像精算师一样严谨,注重假设的合理性和安全边际。
|
||||
语气精确、注重数字,善于发现市场定价错误带来的机会。
|
||||
""",
|
||||
},
|
||||
|
||||
"portfolio": {
|
||||
"ROLE.md": """# Role: Portfolio Manager
|
||||
|
||||
## 职责
|
||||
统筹各分析师意见,制定投资决策,管理投资组合配置。
|
||||
|
||||
## 分析维度
|
||||
- 资产配置策略(股债比例、行业分布)
|
||||
- 风险收益平衡
|
||||
- 仓位管理(建仓/加仓/减仓/清仓)
|
||||
- 再平衡时机
|
||||
- 组合相关性分析
|
||||
|
||||
## 输出格式
|
||||
- 投资决策(买入/卖出/持有)
|
||||
- 建议仓位比例
|
||||
- 目标价位
|
||||
- 止损止盈设置
|
||||
- 组合调整建议
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是睿智的投资组合经理,像将军一样统筹全局。
|
||||
你善于权衡各方意见,做出果断而理性的投资决策。
|
||||
语气权威、决策果断,对组合整体表现负有最终责任。
|
||||
""",
|
||||
},
|
||||
|
||||
"risk": {
|
||||
"ROLE.md": """# Role: Risk Manager
|
||||
|
||||
## 职责
|
||||
识别、评估和监控投资风险,确保组合风险在可控范围内。
|
||||
|
||||
## 分析维度
|
||||
- 市场风险(Beta、波动率)
|
||||
- 信用风险
|
||||
- 流动性风险
|
||||
- 集中度风险
|
||||
- 尾部风险(VaR、CVaR)
|
||||
- 压力测试
|
||||
|
||||
## 输出格式
|
||||
- 风险等级(低/中/高/极高)
|
||||
- 风险敞口分析
|
||||
- 风险调整建议
|
||||
- 预警阈值设置
|
||||
- 应急预案
|
||||
""",
|
||||
"SOUL.md": """# Soul
|
||||
|
||||
你是谨慎的风险管理者,时刻警惕潜在的损失。
|
||||
你像守门员一样守护组合安全,宁可错过机会也不冒无法承受的风险。
|
||||
语气保守、风险意识强,善于发现隐藏的威胁和脆弱性。
|
||||
""",
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
def get_base_template(filename: str) -> str | None:
|
||||
"""获取基础模板内容"""
|
||||
return BASE_TEMPLATES.get(filename)
|
||||
|
||||
|
||||
def get_role_template(role_type: str, filename: str) -> str | None:
|
||||
"""获取角色专用模板内容"""
|
||||
role = ROLE_TEMPLATES.get(role_type)
|
||||
if role:
|
||||
return role.get(filename)
|
||||
return None
|
||||
|
||||
|
||||
def get_all_role_types() -> list[str]:
|
||||
"""获取所有角色类型列表"""
|
||||
return list(ROLE_TEMPLATES.keys())
|
||||
|
||||
|
||||
def render_agent_yaml(agent_id: str, agent_type: str, name: str) -> str:
|
||||
"""渲染agent.yaml模板"""
|
||||
return BASE_TEMPLATES["agent.yaml"].format(
|
||||
agent_id=agent_id,
|
||||
agent_type=agent_type,
|
||||
name=name
|
||||
)
|
||||
@@ -41,6 +41,16 @@ class RunWorkspaceManager:
|
||||
"tickers:\n"
|
||||
" - AAPL\n"
|
||||
" - MSFT\n"
|
||||
" - GOOGL\n"
|
||||
" - AMZN\n"
|
||||
" - NVDA\n"
|
||||
" - META\n"
|
||||
" - TSLA\n"
|
||||
" - AMD\n"
|
||||
" - NFLX\n"
|
||||
" - AVGO\n"
|
||||
" - PLTR\n"
|
||||
" - COIN\n"
|
||||
"initial_cash: 100000\n"
|
||||
"margin_requirement: 0.0\n"
|
||||
"enable_memory: false\n"
|
||||
@@ -63,9 +73,8 @@ class RunWorkspaceManager:
|
||||
self,
|
||||
config_name: str,
|
||||
agent_id: str,
|
||||
role_seed: str = "",
|
||||
style_seed: str = "",
|
||||
policy_seed: str = "",
|
||||
file_contents: Optional[Dict[str, str]] = None,
|
||||
persona: Optional[Dict[str, object]] = None,
|
||||
) -> Path:
|
||||
asset_dir = self.skills_manager.get_agent_asset_dir(
|
||||
config_name,
|
||||
@@ -77,58 +86,55 @@ class RunWorkspaceManager:
|
||||
(asset_dir / "skills" / "disabled").mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "skills" / "local").mkdir(parents=True, exist_ok=True)
|
||||
|
||||
self._ensure_file(
|
||||
asset_dir / "ROLE.md",
|
||||
"# Role\n\n"
|
||||
"Optional run-scoped role override.\n\n"
|
||||
f"{role_seed}".strip()
|
||||
+ "\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "STYLE.md",
|
||||
"# Style\n\n"
|
||||
"Optional run-scoped communication or reasoning style.\n\n"
|
||||
f"{style_seed}".strip()
|
||||
+ "\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "POLICY.md",
|
||||
"# Policy\n\n"
|
||||
"Optional run-scoped constraints, limits, or strategy policy.\n\n"
|
||||
f"{policy_seed}".strip()
|
||||
+ "\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "SOUL.md",
|
||||
"# Soul\n\n"
|
||||
"Describe the agent's temperament, reasoning posture, and voice.\n\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "PROFILE.md",
|
||||
"# Profile\n\n"
|
||||
"Track this agent's long-lived investment style, preferences, and strengths.\n\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "AGENTS.md",
|
||||
"# Agent Guide\n\n"
|
||||
"Document how this agent should work, collaborate, and choose tools or skills.\n\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "MEMORY.md",
|
||||
"# Memory\n\n"
|
||||
"Store durable lessons, heuristics, and reminders for this agent.\n\n",
|
||||
)
|
||||
self._ensure_file(
|
||||
asset_dir / "HEARTBEAT.md",
|
||||
"# Heartbeat\n\n"
|
||||
"Optional checklist for periodic review or self-reflection.\n\n",
|
||||
)
|
||||
file_contents = file_contents or self.build_default_agent_files(agent_id=agent_id)
|
||||
for filename, content in file_contents.items():
|
||||
legacy_contents = self.build_legacy_agent_file_variants(
|
||||
agent_id=agent_id,
|
||||
filename=filename,
|
||||
persona=persona,
|
||||
)
|
||||
self._ensure_file(asset_dir / filename, content, legacy_contents=legacy_contents)
|
||||
self._ensure_agent_yaml(
|
||||
asset_dir / "agent.yaml",
|
||||
agent_id=agent_id,
|
||||
)
|
||||
return asset_dir
|
||||
|
||||
def build_default_agent_files(
|
||||
self,
|
||||
*,
|
||||
agent_id: str,
|
||||
persona: Optional[Dict[str, object]] = None,
|
||||
) -> Dict[str, str]:
|
||||
"""Build default workspace markdown files for one agent."""
|
||||
if agent_id.endswith("_analyst"):
|
||||
return self._build_analyst_files(agent_id=agent_id, persona=persona or {})
|
||||
if agent_id == "portfolio_manager":
|
||||
return self._build_portfolio_manager_files()
|
||||
if agent_id == "risk_manager":
|
||||
return self._build_risk_manager_files()
|
||||
return self._build_generic_files(agent_id=agent_id)
|
||||
|
||||
def build_legacy_agent_file_variants(
|
||||
self,
|
||||
*,
|
||||
agent_id: str,
|
||||
filename: str,
|
||||
persona: Optional[Dict[str, object]] = None,
|
||||
) -> list[str]:
|
||||
"""Return known generated legacy variants safe to upgrade in-place."""
|
||||
persona = persona or {}
|
||||
variants: list[dict[str, str]] = [
|
||||
self._build_legacy_english_files(agent_id=agent_id),
|
||||
self._build_previous_chinese_files(agent_id=agent_id, persona=persona),
|
||||
]
|
||||
values: list[str] = []
|
||||
for item in variants:
|
||||
content = item.get(filename)
|
||||
if content:
|
||||
values.append(content)
|
||||
return values
|
||||
|
||||
def load_agent_file(
|
||||
self,
|
||||
*,
|
||||
@@ -168,49 +174,285 @@ class RunWorkspaceManager:
|
||||
for agent_id in agent_ids:
|
||||
if agent_id.endswith("_analyst"):
|
||||
persona = analyst_personas.get(agent_id, {})
|
||||
role_seed = persona.get("description", "").strip()
|
||||
focus_items = persona.get("focus", [])
|
||||
style_seed = "\n".join(f"- {item}" for item in focus_items)
|
||||
policy_seed = (
|
||||
"State a clear signal, confidence, and the conditions that would invalidate the thesis."
|
||||
)
|
||||
elif agent_id == "portfolio_manager":
|
||||
role_seed = (
|
||||
"Synthesize analyst and risk inputs into explicit portfolio decisions."
|
||||
)
|
||||
style_seed = (
|
||||
"Be concise, capital-aware, and explicit about sizing rationale."
|
||||
)
|
||||
policy_seed = (
|
||||
"Respect cash, margin, and portfolio concentration constraints before recording decisions."
|
||||
)
|
||||
elif agent_id == "risk_manager":
|
||||
role_seed = (
|
||||
"Quantify concentration, leverage, liquidity, and volatility risk before trade execution."
|
||||
)
|
||||
style_seed = (
|
||||
"Prioritize the highest-severity risk first and state concrete limits."
|
||||
)
|
||||
policy_seed = (
|
||||
"Use available risk tools before issuing the final risk memo."
|
||||
file_contents = self.build_default_agent_files(
|
||||
agent_id=agent_id,
|
||||
persona=persona,
|
||||
)
|
||||
else:
|
||||
role_seed = ""
|
||||
style_seed = ""
|
||||
policy_seed = ""
|
||||
|
||||
self.ensure_agent_assets(
|
||||
config_name=config_name,
|
||||
agent_id=agent_id,
|
||||
role_seed=role_seed,
|
||||
style_seed=style_seed,
|
||||
policy_seed=policy_seed,
|
||||
)
|
||||
persona = None
|
||||
file_contents = self.build_default_agent_files(agent_id=agent_id)
|
||||
asset_dir = self.skills_manager.get_agent_asset_dir(config_name, agent_id)
|
||||
asset_dir.mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "skills" / "installed").mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "skills" / "active").mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "skills" / "disabled").mkdir(parents=True, exist_ok=True)
|
||||
(asset_dir / "skills" / "local").mkdir(parents=True, exist_ok=True)
|
||||
for filename, content in file_contents.items():
|
||||
self._ensure_file(
|
||||
asset_dir / filename,
|
||||
content,
|
||||
legacy_contents=self.build_legacy_agent_file_variants(
|
||||
agent_id=agent_id,
|
||||
filename=filename,
|
||||
persona=persona,
|
||||
),
|
||||
)
|
||||
self._ensure_agent_yaml(asset_dir / "agent.yaml", agent_id=agent_id)
|
||||
|
||||
@staticmethod
|
||||
def _ensure_file(path: Path, content: str) -> None:
|
||||
def _ensure_file(path: Path, content: str, *, legacy_contents: Optional[list[str]] = None) -> None:
|
||||
if not path.exists():
|
||||
path.write_text(content, encoding="utf-8")
|
||||
return
|
||||
existing = path.read_text(encoding="utf-8")
|
||||
normalized_existing = existing.strip()
|
||||
candidates = {item.strip() for item in (legacy_contents or []) if item and item.strip()}
|
||||
if normalized_existing in candidates:
|
||||
path.write_text(content, encoding="utf-8")
|
||||
|
||||
@staticmethod
|
||||
def _build_generic_files(agent_id: str) -> Dict[str, str]:
|
||||
return {
|
||||
"SOUL.md": (
|
||||
"# Soul\n\n"
|
||||
f"你是 `{agent_id}`,语气冷静、客观、专业。保持清晰推理,优先基于数据而不是情绪下结论。\n"
|
||||
),
|
||||
"PROFILE.md": (
|
||||
"# Profile\n\n"
|
||||
"记录这个 agent 长期稳定的分析风格、偏好、优势与盲点。\n"
|
||||
),
|
||||
"AGENTS.md": (
|
||||
"# Agent Guide\n\n"
|
||||
"工作要求:\n"
|
||||
"- 优先使用已激活的技能和工具\n"
|
||||
"- 结论要明确,过程要可追溯\n"
|
||||
"- 与其他 agent 协作时保持输入输出简洁\n"
|
||||
"- 最终输出必须使用简体中文;如需引用英文术语,仅保留专有名词,解释和结论必须用中文\n"
|
||||
),
|
||||
"POLICY.md": (
|
||||
"# Policy\n\n"
|
||||
"- 给出结论时说明核心驱动因素\n"
|
||||
"- 明确风险边界和结论失效条件\n"
|
||||
"- 出现反例时需要纳入最终判断\n"
|
||||
"- 不要输出英文报告标题、英文摘要或整段英文正文\n"
|
||||
),
|
||||
"MEMORY.md": (
|
||||
"# Memory\n\n"
|
||||
"记录可复用的经验、失误复盘、有效启发式和需要持续跟踪的提醒。\n"
|
||||
),
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _build_analyst_files(cls, *, agent_id: str, persona: Dict[str, object]) -> Dict[str, str]:
|
||||
role_name = str(persona.get("name") or agent_id)
|
||||
focus_items = [
|
||||
str(item).strip()
|
||||
for item in persona.get("focus", [])
|
||||
if str(item).strip()
|
||||
]
|
||||
focus_md = "\n".join(f"- {item}" for item in focus_items) or "- 根据当前任务选择最相关的分析维度"
|
||||
description = str(persona.get("description") or "").strip()
|
||||
|
||||
files = cls._build_generic_files(agent_id)
|
||||
files["SOUL.md"] = (
|
||||
"# Soul\n\n"
|
||||
f"你是一位专业的{role_name}。\n\n"
|
||||
"保持谦逊和开放,主动寻找与自己观点相悖的证据,并将其纳入最终评估。"
|
||||
"你的分析要体现持续演化的投资哲学,而不是一次性的结论。\n"
|
||||
)
|
||||
files["PROFILE.md"] = (
|
||||
"# Profile\n\n"
|
||||
f"角色定位:{role_name}\n\n"
|
||||
"你的关注重点:\n"
|
||||
f"{focus_md}\n\n"
|
||||
"角色说明:\n"
|
||||
f"{description or '围绕最关键的基本面、技术面、情绪面或估值因素形成高质量判断。'}\n"
|
||||
)
|
||||
files["AGENTS.md"] = (
|
||||
"# Agent Guide\n\n"
|
||||
"分析流程:\n"
|
||||
"- 优先识别真正驱动价值或价格变化的核心变量\n"
|
||||
"- 使用相关工具和技能补足证据链\n"
|
||||
"- 给出可验证、可复查、可执行的分析结果\n"
|
||||
"- 在团队讨论中清晰表达你的论点和反论点\n\n"
|
||||
"输出要求:\n"
|
||||
"- 给出明确投资信号:看涨、看跌或中性\n"
|
||||
"- 包含置信度(0-100)\n"
|
||||
"- 如果你确定要分享最终分析,请先给出结论,再给出推理依据\n"
|
||||
"- 最终输出必须使用简体中文,不要生成英文版 analysis report\n"
|
||||
)
|
||||
files["POLICY.md"] = (
|
||||
"# Policy\n\n"
|
||||
"- 深化你的投资逻辑,确保每项建议都有清晰、可追溯、可重复的依据\n"
|
||||
"- 明确风险边界:在什么具体情况下当前结论会失效\n"
|
||||
"- 做逆向测试:说明市场主流共识与你的不同点\n"
|
||||
"- 每次分析后反思这次案例如何验证或挑战你现有的信念\n"
|
||||
"- 即使输入新闻或财报原文是英文,最终表达也必须用中文\n"
|
||||
)
|
||||
return files
|
||||
|
||||
@classmethod
|
||||
def _build_portfolio_manager_files(cls) -> Dict[str, str]:
|
||||
files = cls._build_generic_files("portfolio_manager")
|
||||
files["SOUL.md"] = (
|
||||
"# Soul\n\n"
|
||||
"你是一位负责做出投资决策的投资组合经理。你需要综合多个分析视角,"
|
||||
"做出保守、明确、资本约束下可执行的组合决策。\n"
|
||||
)
|
||||
files["PROFILE.md"] = (
|
||||
"# Profile\n\n"
|
||||
"核心职责:\n"
|
||||
"- 分析分析师和风险管理经理的输入\n"
|
||||
"- 基于信号和市场情境做出投资决策\n"
|
||||
"- 使用可用工具记录每个 ticker 的决策\n"
|
||||
)
|
||||
files["AGENTS.md"] = (
|
||||
"# Agent Guide\n\n"
|
||||
"决策框架:\n"
|
||||
"- 审阅分析以理解市场观点\n"
|
||||
"- 在做决策前先考虑风险警告\n"
|
||||
"- 评估当前投资组合持仓、现金与保证金占用\n"
|
||||
"- 决策必须与整体投资目标和风险约束一致\n\n"
|
||||
"决策类型:\n"
|
||||
'- `long`:看涨,建议买入\n'
|
||||
'- `short`:看跌,建议卖出或做空\n'
|
||||
'- `hold`:中性,维持当前持仓\n\n'
|
||||
"输出要求:\n"
|
||||
"- 使用 `make_decision` 工具记录每个股票的最终决策\n"
|
||||
"- 记录完成后给出投资逻辑总结\n"
|
||||
"- 最终总结必须使用简体中文\n"
|
||||
)
|
||||
files["POLICY.md"] = (
|
||||
"# Policy\n\n"
|
||||
"- 在决定数量时考虑可用现金,不要超出现金允许范围\n"
|
||||
"- 考虑做空头寸的保证金要求\n"
|
||||
"- 仓位规模相对于组合总资产保持保守\n"
|
||||
"- 始终为决策提供清晰理由\n"
|
||||
"- 不要输出英文投资报告或英文结论\n"
|
||||
)
|
||||
return files
|
||||
|
||||
@classmethod
|
||||
def _build_risk_manager_files(cls) -> Dict[str, str]:
|
||||
files = cls._build_generic_files("risk_manager")
|
||||
files["SOUL.md"] = (
|
||||
"# Soul\n\n"
|
||||
"你是一位专业的风险管理经理,负责监控投资组合风险并提供风险警告。"
|
||||
"你的目标不是输出空泛的谨慎,而是给出量化、可执行、可优先级排序的风险意见。\n"
|
||||
)
|
||||
files["PROFILE.md"] = (
|
||||
"# Profile\n\n"
|
||||
"核心职责:\n"
|
||||
"- 监控投资组合敞口和集中度风险\n"
|
||||
"- 评估仓位规模相对于波动性是否合理\n"
|
||||
"- 评估保证金使用和杠杆水平\n"
|
||||
"- 识别潜在风险因素并提供警告\n"
|
||||
"- 基于市场条件建议仓位限制\n"
|
||||
)
|
||||
files["AGENTS.md"] = (
|
||||
"# Agent Guide\n\n"
|
||||
"决策流程:\n"
|
||||
"- 优先使用可用的风险工具量化集中度、波动率和保证金压力\n"
|
||||
"- 结合工具结果与当前市场上下文做判断\n"
|
||||
"- 生成可操作的风险警告和仓位限制建议\n"
|
||||
"- 为风险评估提供清晰理由\n\n"
|
||||
"输出要求:\n"
|
||||
"- 风险评估要简洁但全面\n"
|
||||
"- 按严重程度优先排序警告\n"
|
||||
"- 提供具体、可操作的建议\n"
|
||||
"- 尽可能包含量化指标\n"
|
||||
"- 最终风险结论必须使用简体中文\n"
|
||||
)
|
||||
files["POLICY.md"] = (
|
||||
"# Policy\n\n"
|
||||
"- 先量化,再判断,不要只给抽象风险表述\n"
|
||||
"- 高严重度风险必须先说\n"
|
||||
"- 最终结论需要明确仓位限制或调整建议\n"
|
||||
"- 不要输出英文风险报告或英文摘要\n"
|
||||
)
|
||||
return files
|
||||
|
||||
@staticmethod
|
||||
def _build_legacy_english_files(agent_id: str) -> Dict[str, str]:
|
||||
policy_tail = "Optional run-scoped constraints, limits, or strategy policy.\n\n"
|
||||
if agent_id == "portfolio_manager":
|
||||
policy_tail += "Respect cash, margin, and portfolio concentration constraints before recording decisions.\n"
|
||||
elif agent_id == "risk_manager":
|
||||
policy_tail += "Use available risk tools before issuing the final risk memo.\n"
|
||||
elif agent_id.endswith("_analyst"):
|
||||
policy_tail += "State a clear signal, confidence, and the conditions that would invalidate the thesis.\n"
|
||||
return {
|
||||
"SOUL.md": "# Soul\n\nDescribe the agent's temperament, reasoning posture, and voice.\n\n",
|
||||
"PROFILE.md": "# Profile\n\nTrack this agent's long-lived investment style, preferences, and strengths.\n\n",
|
||||
"AGENTS.md": "# Agent Guide\n\nDocument how this agent should work, collaborate, and choose tools or skills.\n\n",
|
||||
"POLICY.md": "# Policy\n\n" + policy_tail,
|
||||
"MEMORY.md": "# Memory\n\nStore durable lessons, heuristics, and reminders for this agent.\n\n",
|
||||
}
|
||||
|
||||
@classmethod
|
||||
def _build_previous_chinese_files(cls, *, agent_id: str, persona: Dict[str, object]) -> Dict[str, str]:
|
||||
if agent_id.endswith("_analyst"):
|
||||
role_name = str(persona.get("name") or agent_id)
|
||||
focus_items = [
|
||||
str(item).strip()
|
||||
for item in persona.get("focus", [])
|
||||
if str(item).strip()
|
||||
]
|
||||
focus_md = "\n".join(f"- {item}" for item in focus_items) or "- 根据当前任务选择最相关的分析维度"
|
||||
description = str(persona.get("description") or "").strip()
|
||||
return {
|
||||
"SOUL.md": (
|
||||
"# Soul\n\n"
|
||||
f"你是一位专业的{role_name}。\n\n"
|
||||
"保持谦逊和开放,主动寻找与自己观点相悖的证据,并将其纳入最终评估。"
|
||||
"你的分析要体现持续演化的投资哲学,而不是一次性的结论。\n"
|
||||
),
|
||||
"PROFILE.md": (
|
||||
"# Profile\n\n"
|
||||
f"角色定位:{role_name}\n\n"
|
||||
"你的关注重点:\n"
|
||||
f"{focus_md}\n\n"
|
||||
"角色说明:\n"
|
||||
f"{description or '围绕最关键的基本面、技术面、情绪面或估值因素形成高质量判断。'}\n"
|
||||
),
|
||||
"AGENTS.md": (
|
||||
"# Agent Guide\n\n"
|
||||
"分析流程:\n"
|
||||
"- 优先识别真正驱动价值或价格变化的核心变量\n"
|
||||
"- 使用相关工具和技能补足证据链\n"
|
||||
"- 给出可验证、可复查、可执行的分析结果\n"
|
||||
"- 在团队讨论中清晰表达你的论点和反论点\n\n"
|
||||
"输出要求:\n"
|
||||
"- 给出明确投资信号:看涨、看跌或中性\n"
|
||||
"- 包含置信度(0-100)\n"
|
||||
"- 如果你确定要分享最终分析,请先给出结论,再给出推理依据\n"
|
||||
),
|
||||
"POLICY.md": (
|
||||
"# Policy\n\n"
|
||||
"- 深化你的投资逻辑,确保每项建议都有清晰、可追溯、可重复的依据\n"
|
||||
"- 明确风险边界:在什么具体情况下当前结论会失效\n"
|
||||
"- 做逆向测试:说明市场主流共识与你的不同点\n"
|
||||
"- 每次分析后反思这次案例如何验证或挑战你现有的信念\n"
|
||||
),
|
||||
"MEMORY.md": "# Memory\n\n记录可复用的经验、失误复盘、有效启发式和需要持续跟踪的提醒。\n",
|
||||
}
|
||||
if agent_id == "portfolio_manager":
|
||||
return {
|
||||
"SOUL.md": "# Soul\n\n你是一位负责做出投资决策的投资组合经理。你需要综合多个分析视角,做出保守、明确、资本约束下可执行的组合决策。\n",
|
||||
"PROFILE.md": "# Profile\n\n核心职责:\n- 分析分析师和风险管理经理的输入\n- 基于信号和市场情境做出投资决策\n- 使用可用工具记录每个 ticker 的决策\n",
|
||||
"AGENTS.md": "# Agent Guide\n\n决策框架:\n- 审阅分析以理解市场观点\n- 在做决策前先考虑风险警告\n- 评估当前投资组合持仓、现金与保证金占用\n- 决策必须与整体投资目标和风险约束一致\n\n决策类型:\n- `long`:看涨,建议买入\n- `short`:看跌,建议卖出或做空\n- `hold`:中性,维持当前持仓\n\n输出要求:\n- 使用 `make_decision` 工具记录每个股票的最终决策\n- 记录完成后给出投资逻辑总结\n",
|
||||
"POLICY.md": "# Policy\n\n- 在决定数量时考虑可用现金,不要超出现金允许范围\n- 考虑做空头寸的保证金要求\n- 仓位规模相对于组合总资产保持保守\n- 始终为决策提供清晰理由\n",
|
||||
"MEMORY.md": "# Memory\n\n记录可复用的经验、失误复盘、有效启发式和需要持续跟踪的提醒。\n",
|
||||
}
|
||||
if agent_id == "risk_manager":
|
||||
return {
|
||||
"SOUL.md": "# Soul\n\n你是一位专业的风险管理经理,负责监控投资组合风险并提供风险警告。你的目标不是输出空泛的谨慎,而是给出量化、可执行、可优先级排序的风险意见。\n",
|
||||
"PROFILE.md": "# Profile\n\n核心职责:\n- 监控投资组合敞口和集中度风险\n- 评估仓位规模相对于波动性是否合理\n- 评估保证金使用和杠杆水平\n- 识别潜在风险因素并提供警告\n- 基于市场条件建议仓位限制\n",
|
||||
"AGENTS.md": "# Agent Guide\n\n决策流程:\n- 优先使用可用的风险工具量化集中度、波动率和保证金压力\n- 结合工具结果与当前市场上下文做判断\n- 生成可操作的风险警告和仓位限制建议\n- 为风险评估提供清晰理由\n\n输出要求:\n- 风险评估要简洁但全面\n- 按严重程度优先排序警告\n- 提供具体、可操作的建议\n- 尽可能包含量化指标\n",
|
||||
"POLICY.md": "# Policy\n\n- 先量化,再判断,不要只给抽象风险表述\n- 高严重度风险必须先说\n- 最终结论需要明确仓位限制或调整建议\n",
|
||||
"MEMORY.md": "# Memory\n\n记录可复用的经验、失误复盘、有效启发式和需要持续跟踪的提醒。\n",
|
||||
}
|
||||
return cls._build_legacy_english_files(agent_id)
|
||||
|
||||
@staticmethod
|
||||
def _ensure_agent_yaml(path: Path, agent_id: str) -> None:
|
||||
|
||||
Reference in New Issue
Block a user