feat: Add evaluation hooks, skill adaptation and team pipeline config

- Add EvaluationHook for post-execution agent evaluation
- Add SkillAdaptationHook for dynamic skill adaptation
- Add team/ directory with team coordination logic
- Add TEAM_PIPELINE.yaml for smoke_fullstack pipeline config
- Update RuntimeView, TraderView and RuntimeSettingsPanel UI
- Add runtimeApi and websocket services
- Add runtime_state.json to smoke_fullstack state

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-19 18:52:12 +08:00
parent f4a2b7f3af
commit 4b5ac86b83
87 changed files with 5042 additions and 744 deletions

View File

@@ -27,10 +27,6 @@ class PromptLoader:
else:
self.prompts_dir = Path(prompts_dir)
# Cache loaded prompts
self._prompt_cache: Dict[str, str] = {}
self._yaml_cache: Dict[str, Dict] = {}
def load_prompt(
self,
agent_type: str,
@@ -38,37 +34,20 @@ class PromptLoader:
variables: Optional[Dict[str, Any]] = None,
) -> str:
"""
Load and render Prompt
Load and render Prompt.
Args:
agent_type: Agent type (analyst, portfolio_manager, risk_manager)
prompt_name: Prompt file name (without extension)
variables: Variable dictionary for rendering Prompt
Returns:
Rendered prompt string
Examples:
loader = PromptLoader()
prompt = loader.load_prompt("analyst", "tool_selection",
{"analyst_persona": "Technical Analyst"})
No caching — always reads fresh from disk (CoPaw-style).
"""
cache_key = f"{agent_type}/{prompt_name}"
prompt_path = self.prompts_dir / agent_type / f"{prompt_name}.md"
# Try to load from cache
if cache_key not in self._prompt_cache:
prompt_path = self.prompts_dir / agent_type / f"{prompt_name}.md"
if not prompt_path.exists():
raise FileNotFoundError(
f"Prompt file not found: {prompt_path}\n"
f"Please create the prompt file or check the path.",
)
if not prompt_path.exists():
raise FileNotFoundError(
f"Prompt file not found: {prompt_path}\n"
f"Please create the prompt file or check the path.",
)
with open(prompt_path, "r", encoding="utf-8") as f:
self._prompt_cache[cache_key] = f.read()
prompt_template = self._prompt_cache[cache_key]
with open(prompt_path, "r", encoding="utf-8") as f:
prompt_template = f.read()
# If variables provided, use simple string replacement
if variables:
@@ -76,8 +55,6 @@ class PromptLoader:
else:
rendered = prompt_template
# Smart escaping: escape braces in JSON code blocks
# rendered = self._escape_json_braces(rendered)
return rendered
def _render_template(
@@ -140,45 +117,26 @@ class PromptLoader:
config_name: str,
) -> Dict[str, Any]:
"""
Load YAML configuration file
Load YAML configuration file.
Args:
agent_type: Agent type
config_name: Configuration file name (without extension)
Returns:
Configuration dictionary
Examples:
>>> loader = PromptLoader()
>>> config = loader.load_yaml_config("analyst", "personas")
No caching — always reads fresh from disk (CoPaw-style).
"""
cache_key = f"{agent_type}/{config_name}"
yaml_path = self.prompts_dir / agent_type / f"{config_name}.yaml"
if cache_key not in self._yaml_cache:
yaml_path = self.prompts_dir / agent_type / f"{config_name}.yaml"
if not yaml_path.exists():
raise FileNotFoundError(f"YAML config not found: {yaml_path}")
if not yaml_path.exists():
raise FileNotFoundError(f"YAML config not found: {yaml_path}")
with open(yaml_path, "r", encoding="utf-8") as f:
self._yaml_cache[cache_key] = yaml.safe_load(f)
return self._yaml_cache[cache_key]
with open(yaml_path, "r", encoding="utf-8") as f:
return yaml.safe_load(f) or {}
def clear_cache(self):
"""Clear cache (for hot reload)"""
self._prompt_cache.clear()
self._yaml_cache.clear()
"""No-op — caching removed (CoPaw-style, always fresh reads)."""
pass
def reload_prompt(self, agent_type: str, prompt_name: str):
"""Reload specified prompt (force cache refresh)"""
cache_key = f"{agent_type}/{prompt_name}"
if cache_key in self._prompt_cache:
del self._prompt_cache[cache_key]
"""No-op — caching removed."""
pass
def reload_config(self, agent_type: str, config_name: str):
"""Reload specified configuration (force cache refresh)"""
cache_key = f"{agent_type}/{config_name}"
if cache_key in self._yaml_cache:
del self._yaml_cache[cache_key]
"""No-op — caching removed."""
pass