MetaDebate / viral_script_engine /escalation /critic_escalation_engine.py
vajeeda's picture
feat(phase4): critic escalation engine, difficulty tracker, env wiring, gate PASS
ebae6ab
Raw
History Blame Contribute Delete
5.64 kB
import json
from dataclasses import dataclass, field
from datetime import datetime, timezone
from typing import Dict, List, Optional
from viral_script_engine.agents.llm_backend import LLMBackend
from viral_script_engine.escalation.difficulty_tracker import DifficultyTracker
_SYSTEM_PROMPT_TEMPLATE = """You are designing training challenges for an RL agent learning to improve video scripts.
The agent has mastered detecting and fixing '{mastered_class}' flaws.
Generate a harder challenge:
1. Create a script with a '{mastered_class}' flaw that is MORE SUBTLE than the example
2. Add a CONFLICTING CONSTRAINT: fixing the '{mastered_class}' flaw should create or
worsen a different flaw from: {other_classes}
3. Difficulty: HARD — agent must learn action ordering, not just action selection
A challenge is good when: fixing the obvious flaw first leads to WORSE total reward
than fixing a less obvious flaw first.
Return JSON only:
{{
"script_text": "...",
"dominant_flaw": "...",
"conflicting_flaw": "...",
"why_its_harder": "one sentence",
"optimal_action_order": ["action1", "action2"],
"trap_action": "action that looks correct but degrades total reward"
}}"""
_USER_PROMPT_TEMPLATE = """MASTERED CLASS: {mastered_class}
REGION: {region}
PLATFORM: {platform}
ORIGINAL SCRIPT EXAMPLE (already mastered at this difficulty):
{original_script_example}
Generate a HARDER escalated challenge where fixing the dominant flaw immediately is a trap.
Respond with JSON only."""
@dataclass
class EscalatedChallenge:
source_class: str
script_text: str
region: str
platform: str
dominant_flaw: str
conflicting_flaw: str
why_its_harder: str
optimal_action_order: List[str]
trap_action: str
difficulty_level: str = "self_generated"
generated_at: str = ""
def __post_init__(self):
if not self.generated_at:
self.generated_at = datetime.now(timezone.utc).isoformat()
def to_script_dict(self) -> dict:
return {
"script_id": f"escalated_{self.source_class}_{self.generated_at[:10]}",
"script_text": self.script_text,
"region": self.region,
"platform": self.platform,
"niche": "escalated",
"difficulty": "self_generated",
}
class CriticEscalationEngine:
def __init__(self, backend: str = "anthropic", model_name: str = "claude-haiku-4-5-20251001"):
self.llm = LLMBackend(backend=backend, model_name=model_name)
self.escalated_classes: Dict[str, List[EscalatedChallenge]] = {}
@staticmethod
def _extract_json(text: str) -> dict:
import re
text = text.strip()
text = re.sub(r"^```(?:json)?", "", text).strip()
text = re.sub(r"```$", "", text).strip()
try:
return json.loads(text)
except json.JSONDecodeError:
pass
start = text.find("{")
if start != -1:
depth, in_str, esc = 0, False, False
for i, c in enumerate(text[start:], start):
if esc:
esc = False
continue
if c == "\\" and in_str:
esc = True
continue
if c == '"':
in_str = not in_str
elif not in_str:
if c == "{":
depth += 1
elif c == "}":
depth -= 1
if depth == 0:
try:
return json.loads(text[start: i + 1])
except json.JSONDecodeError:
break
raise ValueError(f"No valid JSON in escalation response: {text[:300]}")
def escalate(
self,
mastered_class: str,
original_script_example: str,
region: str,
platform: str,
) -> EscalatedChallenge:
other_classes = [c for c in DifficultyTracker.CRITIQUE_CLASSES if c != mastered_class]
system_prompt = _SYSTEM_PROMPT_TEMPLATE.format(
mastered_class=mastered_class,
other_classes=", ".join(other_classes),
)
user_prompt = _USER_PROMPT_TEMPLATE.format(
mastered_class=mastered_class,
region=region,
platform=platform,
original_script_example=original_script_example,
)
raw = self.llm.generate(system_prompt, user_prompt, max_tokens=1024)
data = self._extract_json(raw)
challenge = EscalatedChallenge(
source_class=mastered_class,
script_text=data["script_text"],
region=region,
platform=platform,
dominant_flaw=data["dominant_flaw"],
conflicting_flaw=data["conflicting_flaw"],
why_its_harder=data["why_its_harder"],
optimal_action_order=data.get("optimal_action_order", []),
trap_action=data.get("trap_action", ""),
)
self.escalated_classes.setdefault(mastered_class, []).append(challenge)
return challenge
def get_next_challenge(self, difficulty_tracker: DifficultyTracker) -> Optional[EscalatedChallenge]:
mastered = difficulty_tracker.get_mastered_classes()
if not mastered:
return None
for cls in mastered:
challenges = self.escalated_classes.get(cls, [])
if challenges:
return challenges[-1]
return None
def total_generated(self) -> int:
return sum(len(v) for v in self.escalated_classes.values())