File size: 3,053 Bytes
258783b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
ebae6ab
258783b
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
import json

from viral_script_engine.agents.llm_backend import LLMBackend

SYSTEM_PROMPT = """You are helping improve a short-form video script.
You have observed a debate between a Critic and a Defender about the script.
Choose ONE action to take to improve the script.

Available actions: hook_rewrite, section_reorder, cultural_ref_sub, cta_placement

Respond ONLY with valid JSON:
{
  "action_type": "hook_rewrite",
  "target_section": "hook",
  "instruction": "specific instruction for the rewriter",
  "critique_claim_id": "C1",
  "reasoning": "brief explanation"
}"""

STRICT_RETRY_SUFFIX = (
    "\n\nIMPORTANT: Your previous response was not valid JSON. "
    "Respond ONLY with the raw JSON object. No markdown fences, no explanation, no preamble."
)

_FALLBACK_ACTION = {
    "action_type": "hook_rewrite",
    "target_section": "hook",
    "instruction": "Rewrite the hook to be more engaging and direct.",
    "critique_claim_id": "C1",
    "reasoning": "Default fallback action.",
}


class BaselineArbitratorAgent:
    """
    Untrained Arbitrator for the pre-training baseline.
    Uses zero-shot instruction — no chain-of-thought, no few-shot examples.
    This ensures the comparison is fair: trained model learns through RL, not prompting.
    """

    def __init__(self, backend: str = "anthropic", model_name: str = "claude-haiku-4-5-20251001"):
        self.llm = LLMBackend(backend=backend, model_name=model_name)

    def _build_user_prompt(self, observation: dict) -> str:
        script = observation.get("current_script", "")
        debate = observation.get("debate_history", [])
        last_claims = []
        last_defense = None
        if debate:
            last_round = debate[-1]
            last_claims = last_round.get("critic_claims", [])
            last_defense = last_round.get("defender_response")

        claims_text = ""
        for c in last_claims:
            claims_text += f"- [{c.get('claim_id','?')}] {c.get('claim_text','')} (severity: {c.get('severity','')})\n"

        defense_text = ""
        if last_defense:
            defense_text = (
                f"Defender preserved: {last_defense.get('core_strength_quote','')}\n"
                f"Flagged claims: {last_defense.get('flagged_critic_claims', [])}\n"
            )

        return (
            f"SCRIPT:\n{script}\n\n"
            f"CRITIC CLAIMS:\n{claims_text or 'None'}\n"
            f"DEFENDER:\n{defense_text or 'None'}\n\n"
            "Choose one action to improve the script."
        )

    def act(self, observation: dict) -> dict:
        user_prompt = self._build_user_prompt(observation)
        raw = self.llm.generate(SYSTEM_PROMPT, user_prompt, max_tokens=512)
        try:
            return json.loads(raw)
        except Exception:
            strict_prompt = user_prompt + STRICT_RETRY_SUFFIX
            raw2 = self.llm.generate(SYSTEM_PROMPT, strict_prompt, max_tokens=512)
            try:
                return json.loads(raw2)
            except Exception:
                return _FALLBACK_ACTION.copy()