From 19a3592a2074660bf3dbd436fa89dcebe81ef98b Mon Sep 17 00:00:00 2001 From: Adriano Dal Pastro Date: Fri, 15 May 2026 21:38:35 +0000 Subject: [PATCH] =?UTF-8?q?feat(prompt):=20PromptLibrary=20v3.1=20?= =?UTF-8?q?=E2=80=94=20anti=5Fpatterns=20+=20output=5Fpriorities?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Estende il compositor del SYSTEM con 2 sezioni opzionali iniettate DOPO i VINCOLI core e PRIMA dell'EXAMPLE: ANTI-PATTERN DA EVITARE: lista esplicita di cose da evitare (overfitting, correlazione=causalita, > 4 AND, singolo evento estremo, ecc.) PRIORITA' DI OUTPUT: trade-off come "robustezza > ottimalita su singolo regime", "semplicita > complessita raffinata", "selettivita > attivita" Razionale: ridurre la varianza non-utile nelle strategie generate quando il LLM affronta trade-off, e prevenire overfitting nel design. Entrambi i campi sono opzionali (skip se "") -> backward-compatible con prompts.json v3.0. PromptLibrary v3.1: +2 fields top-level (default ""). _build_system_prompt: 2 sezioni condizionali post-VINCOLI. Test: +3 unit (compositor inject/skip + from_json parsing). Tot: 235 test pass. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../multi_swarm_core/agents/hypothesis.py | 11 +++- .../multi_swarm_core/genome/prompt_library.py | 16 ++++++ .../tests/unit/test_hypothesis_agent.py | 50 +++++++++++++++++++ .../tests/unit/test_prompt_library.py | 12 +++++ 4 files changed, 88 insertions(+), 1 deletion(-) diff --git a/src/multi_swarm_core/multi_swarm_core/agents/hypothesis.py b/src/multi_swarm_core/multi_swarm_core/agents/hypothesis.py index 0c3a31f..aab649f 100644 --- a/src/multi_swarm_core/multi_swarm_core/agents/hypothesis.py +++ b/src/multi_swarm_core/multi_swarm_core/agents/hypothesis.py @@ -197,7 +197,16 @@ def _build_system_prompt(lib: PromptLibrary, genome: HypothesisAgentGenome) -> s parts.append("") # 6. Vincoli (core scaffold) parts.append(_SYSTEM_CONSTRAINTS) - # 7. Esempio (core scaffold) + # 7. NEW v3.1: anti-pattern e output priorities (da prompts.json, opzionali) + if lib.anti_patterns: + parts.append("\nANTI-PATTERN DA EVITARE:\n") + parts.append(lib.anti_patterns) + parts.append("") + if lib.output_priorities: + parts.append("\nPRIORITA' DI OUTPUT (trade-off):\n") + parts.append(lib.output_priorities) + parts.append("") + # 8. Esempio (core scaffold) parts.append(_SYSTEM_EXAMPLE) return "\n".join(parts) diff --git a/src/multi_swarm_core/multi_swarm_core/genome/prompt_library.py b/src/multi_swarm_core/multi_swarm_core/genome/prompt_library.py index 46fcfcc..dc654ab 100644 --- a/src/multi_swarm_core/multi_swarm_core/genome/prompt_library.py +++ b/src/multi_swarm_core/multi_swarm_core/genome/prompt_library.py @@ -89,6 +89,7 @@ class PromptLibrary: v3.0: aggiunge 4 campi top-level strategy-specific iniettabili nel prompt dal compositor ``_build_system_prompt``. Tutti opzionali con default sensati per backcompat. + v3.1: aggiunge anti_patterns e output_priorities iniettati dopo i VINCOLI core. """ styles: dict[str, str] @@ -98,6 +99,8 @@ class PromptLibrary: pattern_guidance: str = field(default="") # sezione PATTERN GUIDANCE del SYSTEM instruction: str = field(default="") # frase finale USER ("Genera una strategia...") domain_warnings: str = field(default="") # opzionale: warning di dominio (es. crypto 24/7) + anti_patterns: str = field(default="") # NEW v3.1: lista esplicita di pattern da evitare + output_priorities: str = field(default="") # NEW v3.1: trade-off espliciti di output def __post_init__(self) -> None: if not self.styles: @@ -115,6 +118,8 @@ class PromptLibrary: ("pattern_guidance", self.pattern_guidance), ("instruction", self.instruction), ("domain_warnings", self.domain_warnings), + ("anti_patterns", self.anti_patterns), + ("output_priorities", self.output_priorities), ): if not isinstance(value, str): raise PromptLibraryError( @@ -135,6 +140,8 @@ class PromptLibrary: pattern_guidance=_DEFAULT_PATTERN_GUIDANCE, instruction=_DEFAULT_INSTRUCTION, domain_warnings="", + anti_patterns="", + output_priorities="", ) @classmethod @@ -190,6 +197,13 @@ class PromptLibrary: pattern_guidance = data.get("pattern_guidance", "") instruction = data.get("instruction", "") domain_warnings = data.get("domain_warnings", "") + # Parse new optional top-level fields (v3.1) + anti_patterns_raw = data.get("anti_patterns", "") + output_priorities_raw = data.get("output_priorities", "") + if not isinstance(anti_patterns_raw, str): + raise PromptLibraryError(f"anti_patterns deve essere stringa, non {type(anti_patterns_raw)}") + if not isinstance(output_priorities_raw, str): + raise PromptLibraryError(f"output_priorities deve essere stringa, non {type(output_priorities_raw)}") return cls( styles=styles, @@ -198,6 +212,8 @@ class PromptLibrary: pattern_guidance=pattern_guidance, instruction=instruction, domain_warnings=domain_warnings, + anti_patterns=anti_patterns_raw, + output_priorities=output_priorities_raw, ) @property diff --git a/src/multi_swarm_core/tests/unit/test_hypothesis_agent.py b/src/multi_swarm_core/tests/unit/test_hypothesis_agent.py index bc3f88c..6100984 100644 --- a/src/multi_swarm_core/tests/unit/test_hypothesis_agent.py +++ b/src/multi_swarm_core/tests/unit/test_hypothesis_agent.py @@ -370,3 +370,53 @@ def test_user_template_uses_instruction_from_library(mocker): # type: ignore[no user_msg = call_kwargs["user"] assert "INSTR_X" in user_msg assert "Genera una strategia che cerchi anomalie sfruttabili in questo regime." not in user_msg + + +def test_build_system_prompt_includes_anti_patterns_and_priorities(mocker): # type: ignore[no-untyped-def] + """anti_patterns e output_priorities da PromptLibrary appaiono nel SYSTEM prompt.""" + fake_llm = mocker.MagicMock() + fake_llm.complete.return_value = CompletionResult( + text=VALID_STRATEGY_JSON, + input_tokens=200, + output_tokens=80, + tier=ModelTier.C, + model="qwen", + ) + lib = PromptLibrary( + styles={"physicist": "Pensa come un fisico."}, + focus={}, + anti_patterns="EVITA_X", + output_priorities="PRIORI_X", + ) + agent = HypothesisAgent(llm=fake_llm, prompt_library=lib) + agent.propose(make_genome(), make_summary()) + call_kwargs = fake_llm.complete.call_args.kwargs + system_msg = call_kwargs["system"] + assert "ANTI-PATTERN DA EVITARE" in system_msg + assert "EVITA_X" in system_msg + assert "PRIORITA' DI OUTPUT" in system_msg + assert "PRIORI_X" in system_msg + + +def test_build_system_prompt_skips_anti_patterns_and_priorities_when_empty(mocker): # type: ignore[no-untyped-def] + """anti_patterns='' e output_priorities='' -> sezioni opzionali assenti nel SYSTEM.""" + fake_llm = mocker.MagicMock() + fake_llm.complete.return_value = CompletionResult( + text=VALID_STRATEGY_JSON, + input_tokens=200, + output_tokens=80, + tier=ModelTier.C, + model="qwen", + ) + lib = PromptLibrary( + styles={"physicist": "Pensa come un fisico."}, + focus={}, + anti_patterns="", + output_priorities="", + ) + agent = HypothesisAgent(llm=fake_llm, prompt_library=lib) + agent.propose(make_genome(), make_summary()) + call_kwargs = fake_llm.complete.call_args.kwargs + system_msg = call_kwargs["system"] + assert "ANTI-PATTERN" not in system_msg + assert "PRIORITA' DI OUTPUT" not in system_msg diff --git a/src/multi_swarm_core/tests/unit/test_prompt_library.py b/src/multi_swarm_core/tests/unit/test_prompt_library.py index 86485af..887ea55 100644 --- a/src/multi_swarm_core/tests/unit/test_prompt_library.py +++ b/src/multi_swarm_core/tests/unit/test_prompt_library.py @@ -76,3 +76,15 @@ def test_prompt_library_accepts_empty_string_for_optional_fields() -> None: ) assert lib.agent_role == "" assert lib.domain_warnings == "" + + +def test_from_json_loads_anti_patterns_and_output_priorities(tmp_path: Path) -> None: + """from_json() legge anti_patterns e output_priorities (v3.1).""" + data = { + "styles": {"physicist": {"directive": "Cerca leggi conservative."}}, + "anti_patterns": "Evita overfitting.", + "output_priorities": "Robustezza > ottimalita.", + } + lib = PromptLibrary.from_json(_write_json(data, tmp_path)) + assert lib.anti_patterns == "Evita overfitting." + assert lib.output_priorities == "Robustezza > ottimalita."