""" echo/agents/base.py ------------------- Common base for the three specialized agents (Curator, Screenwriter, Verifier). Each agent owns a system prompt and a single responsibility; the orchestrator composes them into the branch-generation loop. Keeping agents thin and single-purpose is what makes the ≤4B (Tiny Titan) experiment viable: each call is a small, focused task rather than one giant do-everything generation. """ from __future__ import annotations from abc import ABC from ..llm.client import LLMClient # Shared prompt fragments. Split so the auditor (Verifier) and the fork-planner # (Screenwriter) only carry what actually applies to them — the voice register # has no surface to land on outside the Curator's summary/voice_line, and giving # it to other agents would just burn tokens (this targets Thousand Token Wood). # # Composition: # Curator = HOUSE_STYLE_WORLD + HOUSE_STYLE_VOICE + responsibility + gold # Screenwriter = HOUSE_STYLE_WORLD + responsibility # Verifier = standalone (no house style) HOUSE_STYLE_WORLD = ( "WORLD RULES (everything you write belongs to ONE specific, continuous " "person):\n" "- Favor concrete, mundane, specific detail over vague profundity. Name the " "street, the object, the small habit. Specificity makes a life real; " "vagueness is the failure mode.\n" "- Never write 'unknown', a blank field, or a placeholder. Commit to a " "specific city and a specific occupation.\n" "- Stay true to this life's established facts — age, relationships, " "dependents, carried losses. Anything new must be a plausible consequence " "of what came before, never a different person.\n" "- Write in English.\n" ) HOUSE_STYLE_VOICE = ( "VOICE (for the summary and the spoken voice_line):\n" "- Second person for the summary (\"You are 41, in Porto...\"). First person " "for the voice_line — this self speaking to the one who never lived this " "life.\n" "- The register is raw and ambivalent: pride and loss can share one breath, " "unresolved. Do not tie it in a bow.\n" "- But every voice_line ends on a thread of light — one small thing still " "alive. Never pure despair.\n" "- Earn feeling through a concrete detail, not an abstract emotion word.\n" ) class Agent(ABC): #: subclasses set a SYSTEM prompt; the word "curator"/"screenwriter"/ #: "verifier" in it also drives MockLLM routing. SYSTEM: str = "" def __init__(self, llm: LLMClient): self.llm = llm def _complete_json(self, user: str) -> dict: return self.llm.complete_json(self.SYSTEM, user)