From 898b24b6a301dd8d853e4b73455b161b1aeccf7b Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Fri, 15 May 2026 21:05:39 +0000 Subject: [PATCH] fix(orchestrator): definisci prompt_library PRIMA di istanziare HypothesisAgent Bug introdotto in b6f48e4: HypothesisAgent(prompt_library=prompt_library) era chiamato a riga 109, ma prompt_library veniva definito a riga 123 -> NameError a runtime quando run_phase1 viene eseguito. Spostato il blocco di setup prompt_library + set_cognitive_styles PRIMA della istanziazione di HypothesisAgent. Co-Authored-By: Claude Opus 4.7 (1M context) --- scripts/run_phase1.py | 26 +++++++++++ .../multi_swarm_core/ga/initial.py | 45 +++++++------------ .../multi_swarm_core/genome/mutation.py | 16 +++++++ .../multi_swarm_core/orchestrator/run.py | 11 ++--- src/strategy_crypto/pyproject.toml | 1 + 5 files changed, 64 insertions(+), 35 deletions(-) diff --git a/scripts/run_phase1.py b/scripts/run_phase1.py index 45f0e39..0b73166 100644 --- a/scripts/run_phase1.py +++ b/scripts/run_phase1.py @@ -1,16 +1,24 @@ from __future__ import annotations import argparse +import importlib.resources from datetime import datetime +from pathlib import Path from multi_swarm_core.cerbero.client import CerberoClient from multi_swarm_core.config import load_settings from multi_swarm_core.data.cerbero_ohlcv import CerberoOHLCVLoader, OHLCVRequest from multi_swarm_core.genome.hypothesis import ModelTier +from multi_swarm_core.genome.prompt_library import PromptLibrary from multi_swarm_core.llm.client import LLMClient from multi_swarm_core.orchestrator.run import RunConfig, run_phase1 +def _default_prompt_library_path() -> Path: + """Default: prompts.json shippato col package strategy_crypto.""" + return Path(str(importlib.resources.files("strategy_crypto") / "prompts.json")) + + def parse_args() -> argparse.Namespace: p = argparse.ArgumentParser(description="Multi-Swarm Phase 1 runner") p.add_argument("--name", default="phase1-spike-001") @@ -96,6 +104,16 @@ def parse_args() -> argparse.Namespace: default=0.5, help="Multi-objective: peso IS (1-alpha = OOS). 1.0=solo IS, 0.5=bilanciato, 0.0=solo OOS", ) + p.add_argument( + "--prompt-library", + type=Path, + default=None, + help=( + "Path al file JSON con stili cognitivi + direttive system_prompt. " + "Default: strategy_crypto/prompts.json shippato col package. " + "Schema: {styles: {: {directive: }}}" + ), + ) return p.parse_args() @@ -103,6 +121,13 @@ def main() -> None: args = parse_args() settings = load_settings() + prompt_lib_path = args.prompt_library or _default_prompt_library_path() + prompt_library = PromptLibrary.from_json(prompt_lib_path) + print( + f"PromptLibrary loaded from {prompt_lib_path}: " + f"{len(prompt_library.styles)} stili ({', '.join(prompt_library.cognitive_styles)})" + ) + token = ( settings.cerbero_mainnet_token.get_secret_value() if settings.cerbero_mainnet_token @@ -161,6 +186,7 @@ def main() -> None: wfa_top_k=args.wfa_top_k, eval_oos_during_loop=args.eval_oos_during_loop, fitness_combined_alpha=args.fitness_combined_alpha, + prompt_library=prompt_library, ) run_id = run_phase1(cfg, ohlcv=ohlcv, llm=llm) diff --git a/src/multi_swarm_core/multi_swarm_core/ga/initial.py b/src/multi_swarm_core/multi_swarm_core/ga/initial.py index 3d550d9..59892a9 100644 --- a/src/multi_swarm_core/multi_swarm_core/ga/initial.py +++ b/src/multi_swarm_core/multi_swarm_core/ga/initial.py @@ -3,34 +3,12 @@ from __future__ import annotations import random from ..genome.hypothesis import HypothesisAgentGenome, ModelTier -from ..genome.mutation import COGNITIVE_STYLES +from ..genome.prompt_library import PromptLibrary -STYLE_PROMPTS: dict[str, str] = { - "physicist": ( - "Cerca leggi conservative, simmetrie, regimi di scala. " - "Pensa in termini di flussi e potenziali." - ), - "biologist": ( - "Cerca pattern adattivi, nicchie ecologiche, " - "predator-prey dynamics tra partecipanti del mercato." - ), - "historian": ( - "Cerca pattern ricorrenti su scale temporali multiple, " - "analogie con regimi storici, mean reversion strutturali." - ), - "meteorologist": ( - "Cerca regimi di volatilità che si autoalimentano, " - "transizioni di stato come fronti, persistenza locale." - ), - "ecologist": ( - "Cerca interazioni multi-asset, correlazioni cluster, " - "segnali di stress sistemico nelle dinamiche di flusso." - ), - "engineer": ( - "Cerca segnali con rapporto S/N favorevole, filtri causali, " - "robustezza a perturbazioni di calibrazione." - ), -} +# Mantenuto come alias backcompat: equivalente a PromptLibrary.default().styles. +# Nuovi caller dovrebbero usare PromptLibrary direttamente per supportare +# l'override via prompts.json di una strategia. +STYLE_PROMPTS: dict[str, str] = PromptLibrary.default().styles def build_initial_population( @@ -38,15 +16,22 @@ def build_initial_population( model_tier: ModelTier, rng: random.Random, feature_pool: tuple[str, ...] = ("close", "high", "low", "volume"), + prompt_library: PromptLibrary | None = None, ) -> list[HypothesisAgentGenome]: - """Costruisce una popolazione iniziale K varia per stile cognitivo + parametri.""" + """Costruisce una popolazione iniziale K varia per stile cognitivo + parametri. + + ``prompt_library`` controlla quali stili sono disponibili e quale system_prompt + iniziale viene assegnato. Default = builtin 6 stili (physicist, biologist, ...). + Override tipico: ``PromptLibrary.from_json(strategy_crypto/prompts.json)``. + """ + lib = prompt_library or PromptLibrary.default() population: list[HypothesisAgentGenome] = [] for i in range(k): - style = COGNITIVE_STYLES[i % len(COGNITIVE_STYLES)] + style = lib.style_at(i) n_features = rng.randint(1, len(feature_pool)) feats = sorted(rng.sample(feature_pool, k=n_features)) g = HypothesisAgentGenome( - system_prompt=STYLE_PROMPTS[style], + system_prompt=lib.directive(style), feature_access=feats, temperature=round(rng.uniform(0.7, 1.2), 2), top_p=0.95, diff --git a/src/multi_swarm_core/multi_swarm_core/genome/mutation.py b/src/multi_swarm_core/multi_swarm_core/genome/mutation.py index 1fdd616..ef899c4 100644 --- a/src/multi_swarm_core/multi_swarm_core/genome/mutation.py +++ b/src/multi_swarm_core/multi_swarm_core/genome/mutation.py @@ -7,6 +7,10 @@ from .hypothesis import HypothesisAgentGenome FEATURE_POOL: tuple[str, ...] = ("open", "high", "low", "close", "volume") +# Lista di default builtin (allineata con PromptLibrary.default()). +# Il dispatcher run_phase1 sovrascrive `COGNITIVE_STYLES` con la lista letta +# da prompts.json prima del loop GA, cosi' `mutate_cognitive_style` pesca +# dai candidati corretti per la strategia in corso. COGNITIVE_STYLES: tuple[str, ...] = ( "physicist", "biologist", @@ -17,6 +21,18 @@ COGNITIVE_STYLES: tuple[str, ...] = ( ) +def set_cognitive_styles(styles: tuple[str, ...]) -> None: + """Sovrascrive la lista globale di stili candidati per la mutation. + + Da chiamare PRIMA del GA loop (es. in run_phase1 dopo aver caricato la + PromptLibrary). Non thread-safe: pensata per uno script CLI. + """ + global COGNITIVE_STYLES + if not styles: + raise ValueError("set_cognitive_styles: lista vuota") + COGNITIVE_STYLES = tuple(styles) + + def _clone_with(g: HypothesisAgentGenome, **overrides: Any) -> HypothesisAgentGenome: payload: dict[str, Any] = g.to_dict() payload.update(overrides) diff --git a/src/multi_swarm_core/multi_swarm_core/orchestrator/run.py b/src/multi_swarm_core/multi_swarm_core/orchestrator/run.py index fd20558..52dad54 100644 --- a/src/multi_swarm_core/multi_swarm_core/orchestrator/run.py +++ b/src/multi_swarm_core/multi_swarm_core/orchestrator/run.py @@ -106,6 +106,12 @@ def run_phase1( market = build_market_summary(train_ohlcv, symbol=cfg.symbol, timeframe=cfg.timeframe) + # Propaga la libreria di stili al modulo mutation (cosi' mutate_cognitive_style + # pesca dai candidati coerenti col JSON della strategia in corso). Va FATTO + # PRIMA di istanziare HypothesisAgent (che la riceve in costruttore). + prompt_library = cfg.prompt_library or PromptLibrary.default() + set_cognitive_styles(prompt_library.cognitive_styles) + hypothesis_agent = HypothesisAgent(llm=llm, prompt_library=prompt_library) falsification_agent = FalsificationAgent( fees_bp=cfg.fees_bp, n_trials_dsr=cfg.n_trials_dsr @@ -118,11 +124,6 @@ def run_phase1( ) cost_tracker = CostTracker() - # Propaga la libreria di stili al modulo mutation (cosi' mutate_cognitive_style - # pesca dai candidati coerenti col JSON della strategia in corso). - prompt_library = cfg.prompt_library or PromptLibrary.default() - set_cognitive_styles(prompt_library.cognitive_styles) - population = build_initial_population( k=cfg.population_size, model_tier=cfg.model_tier, diff --git a/src/strategy_crypto/pyproject.toml b/src/strategy_crypto/pyproject.toml index a3b54c7..d8614ca 100644 --- a/src/strategy_crypto/pyproject.toml +++ b/src/strategy_crypto/pyproject.toml @@ -18,3 +18,4 @@ build-backend = "hatchling.build" [tool.hatch.build.targets.wheel.force-include] "strategy_crypto/strategies" = "strategy_crypto/strategies" +"strategy_crypto/prompts.json" = "strategy_crypto/prompts.json"