Add per-agent skill workspaces and TraderView management

This commit is contained in:
2026-03-17 13:55:14 +08:00
parent 1f5ee3698e
commit 2daf5717ba
35 changed files with 4774 additions and 331 deletions

View File

@@ -4,6 +4,7 @@
from pathlib import Path
from typing import Any, Optional
from .agent_workspace import load_agent_workspace_config
from backend.config.bootstrap_config import get_bootstrap_config_for_run
from .prompt_loader import PromptLoader
from .skills_manager import SkillsManager
@@ -23,6 +24,26 @@ def _append_section(parts: list[str], title: str, content: str) -> None:
parts.append(f"## {title}\n{content}")
def _build_skill_metadata_summary(skills_manager: SkillsManager, config_name: str, agent_id: str) -> str:
"""Create a compact summary of active skills for prompt routing."""
metadata_items = skills_manager.list_active_skill_metadata(config_name, agent_id)
if not metadata_items:
return ""
lines: list[str] = [
"You can use the following active skills. Prefer the most relevant one, then read its SKILL.md if needed for detailed workflow:",
]
for item in metadata_items:
parts = [f"- `{item.skill_name}`"]
if item.description:
parts.append(item.description)
if item.version:
parts.append(f"version: {item.version}")
parts.append(f"path: {item.path}")
lines.append(" | ".join(parts))
return "\n".join(lines)
def build_agent_system_prompt(
agent_id: str,
config_name: str,
@@ -31,6 +52,13 @@ def build_agent_system_prompt(
) -> str:
"""Build the final system prompt for an agent."""
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(
@@ -56,11 +84,21 @@ def build_agent_system_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}")
@@ -69,6 +107,7 @@ def build_agent_system_prompt(
skills_manager = SkillsManager()
asset_dir = skills_manager.get_agent_asset_dir(config_name, agent_id)
asset_dir.mkdir(parents=True, exist_ok=True)
agent_config = load_agent_workspace_config(asset_dir / "agent.yaml")
bootstrap_config = get_bootstrap_config_for_run(
skills_manager.project_root,
config_name,
@@ -80,26 +119,62 @@ def build_agent_system_prompt(
bootstrap_config.prompt_body,
)
_append_section(
sections,
"Role",
_read_file_if_exists(asset_dir / "ROLE.md"),
)
_append_section(
sections,
"Style",
_read_file_if_exists(asset_dir / "STYLE.md"),
)
_append_section(
sections,
"Policy",
_read_file_if_exists(asset_dir / "POLICY.md"),
)
prompt_files = agent_config.prompt_files or [
"SOUL.md",
"PROFILE.md",
"AGENTS.md",
"POLICY.md",
"MEMORY.md",
]
included_files = set(prompt_files)
title_map = {
"SOUL.md": "Soul",
"PROFILE.md": "Profile",
"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(
sections,
title_map.get(filename, filename),
_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,
"Policy",
_read_file_if_exists(asset_dir / "POLICY.md"),
)
skill_prompt = toolkit.get_agent_skill_prompt()
if skill_prompt:
_append_section(sections, "Skills", str(skill_prompt))
metadata_summary = _build_skill_metadata_summary(
skills_manager=skills_manager,
config_name=config_name,
agent_id=agent_id,
)
if metadata_summary:
_append_section(sections, "Active Skill Catalog", metadata_summary)
activated_notes = toolkit.get_activated_notes()
if activated_notes:
_append_section(sections, "Tool Usage Notes", str(activated_notes))