feat(prompt): PromptLibrary v3.1 — anti_patterns + output_priorities
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) <noreply@anthropic.com>
This commit is contained in:
@@ -197,7 +197,16 @@ def _build_system_prompt(lib: PromptLibrary, genome: HypothesisAgentGenome) -> s
|
|||||||
parts.append("")
|
parts.append("")
|
||||||
# 6. Vincoli (core scaffold)
|
# 6. Vincoli (core scaffold)
|
||||||
parts.append(_SYSTEM_CONSTRAINTS)
|
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)
|
parts.append(_SYSTEM_EXAMPLE)
|
||||||
return "\n".join(parts)
|
return "\n".join(parts)
|
||||||
|
|
||||||
|
|||||||
@@ -89,6 +89,7 @@ class PromptLibrary:
|
|||||||
v3.0: aggiunge 4 campi top-level strategy-specific iniettabili nel prompt
|
v3.0: aggiunge 4 campi top-level strategy-specific iniettabili nel prompt
|
||||||
dal compositor ``_build_system_prompt``. Tutti opzionali con default sensati
|
dal compositor ``_build_system_prompt``. Tutti opzionali con default sensati
|
||||||
per backcompat.
|
per backcompat.
|
||||||
|
v3.1: aggiunge anti_patterns e output_priorities iniettati dopo i VINCOLI core.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
styles: dict[str, str]
|
styles: dict[str, str]
|
||||||
@@ -98,6 +99,8 @@ class PromptLibrary:
|
|||||||
pattern_guidance: str = field(default="") # sezione PATTERN GUIDANCE del SYSTEM
|
pattern_guidance: str = field(default="") # sezione PATTERN GUIDANCE del SYSTEM
|
||||||
instruction: str = field(default="") # frase finale USER ("Genera una strategia...")
|
instruction: str = field(default="") # frase finale USER ("Genera una strategia...")
|
||||||
domain_warnings: str = field(default="") # opzionale: warning di dominio (es. crypto 24/7)
|
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:
|
def __post_init__(self) -> None:
|
||||||
if not self.styles:
|
if not self.styles:
|
||||||
@@ -115,6 +118,8 @@ class PromptLibrary:
|
|||||||
("pattern_guidance", self.pattern_guidance),
|
("pattern_guidance", self.pattern_guidance),
|
||||||
("instruction", self.instruction),
|
("instruction", self.instruction),
|
||||||
("domain_warnings", self.domain_warnings),
|
("domain_warnings", self.domain_warnings),
|
||||||
|
("anti_patterns", self.anti_patterns),
|
||||||
|
("output_priorities", self.output_priorities),
|
||||||
):
|
):
|
||||||
if not isinstance(value, str):
|
if not isinstance(value, str):
|
||||||
raise PromptLibraryError(
|
raise PromptLibraryError(
|
||||||
@@ -135,6 +140,8 @@ class PromptLibrary:
|
|||||||
pattern_guidance=_DEFAULT_PATTERN_GUIDANCE,
|
pattern_guidance=_DEFAULT_PATTERN_GUIDANCE,
|
||||||
instruction=_DEFAULT_INSTRUCTION,
|
instruction=_DEFAULT_INSTRUCTION,
|
||||||
domain_warnings="",
|
domain_warnings="",
|
||||||
|
anti_patterns="",
|
||||||
|
output_priorities="",
|
||||||
)
|
)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
@@ -190,6 +197,13 @@ class PromptLibrary:
|
|||||||
pattern_guidance = data.get("pattern_guidance", "")
|
pattern_guidance = data.get("pattern_guidance", "")
|
||||||
instruction = data.get("instruction", "")
|
instruction = data.get("instruction", "")
|
||||||
domain_warnings = data.get("domain_warnings", "")
|
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(
|
return cls(
|
||||||
styles=styles,
|
styles=styles,
|
||||||
@@ -198,6 +212,8 @@ class PromptLibrary:
|
|||||||
pattern_guidance=pattern_guidance,
|
pattern_guidance=pattern_guidance,
|
||||||
instruction=instruction,
|
instruction=instruction,
|
||||||
domain_warnings=domain_warnings,
|
domain_warnings=domain_warnings,
|
||||||
|
anti_patterns=anti_patterns_raw,
|
||||||
|
output_priorities=output_priorities_raw,
|
||||||
)
|
)
|
||||||
|
|
||||||
@property
|
@property
|
||||||
|
|||||||
@@ -370,3 +370,53 @@ def test_user_template_uses_instruction_from_library(mocker): # type: ignore[no
|
|||||||
user_msg = call_kwargs["user"]
|
user_msg = call_kwargs["user"]
|
||||||
assert "INSTR_X" in user_msg
|
assert "INSTR_X" in user_msg
|
||||||
assert "Genera una strategia che cerchi anomalie sfruttabili in questo regime." not 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
|
||||||
|
|||||||
@@ -76,3 +76,15 @@ def test_prompt_library_accepts_empty_string_for_optional_fields() -> None:
|
|||||||
)
|
)
|
||||||
assert lib.agent_role == ""
|
assert lib.agent_role == ""
|
||||||
assert lib.domain_warnings == ""
|
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."
|
||||||
|
|||||||
Reference in New Issue
Block a user