Spaces:
Running
Running
| # CLAUDE.md | |
| Guidance for working on **The Echo** — an agentic tree of the lives you didn't live. | |
| ## What this project is | |
| The Echo takes one fork in a person's life ("I left Brazil instead of staying") | |
| and grows a **tree of alternate selves**. Each node is a coherent alternate life; | |
| each branch is a dramatic turning point. The user navigates the tree, and each | |
| echo speaks back in a subtly altered version of their own voice. | |
| It is built for the **Thousand Token Wood** track of a small-model hackathon | |
| (all models ≤32B, Gradio + HF Spaces, demo video + social post). The award | |
| surface it targets: Thousand Token Wood (delight), Best Agent (true multi-agent + | |
| tools), Off-Brand (the living tree UI), Best Demo (voice + emotional reveal), and | |
| Tiny Titan (if a ≤4B model holds coherence). | |
| **This is an agentic inference system, not an RL project.** There is no training | |
| loop, no reward, no policy gradient. The "intelligence" is three coordinated | |
| agents using a frozen LLM zero-shot. Do not add RL unless explicitly asked. | |
| ## The core idea that must never break | |
| The single hardest, most important property: **"the same you" stays recognizable | |
| across every branch.** A child life is always generated as a *causal consequence* | |
| of its parent's `WorldState` — never invented from scratch. Age math must add up, | |
| relationships and dependents must persist or change for a reason, carried losses | |
| must echo forward. The `Verifier` exists to enforce this. If a change would let | |
| branches drift into unrelated people, it is wrong. | |
| ## Architecture (dependency order — leaves first) | |
| ``` | |
| core/world_state.py data: WorldState, LifeFacts, EmotionalTone (no deps) | |
| core/tree.py LifeTree: branching graph + history (-> world_state) | |
| llm/client.py LLMClient; MockLLM (offline) + LocalLLM (HF model) | |
| tools/research.py world-grounding (real place/era detail) (-> world_state) | |
| tools/voice.py TTS; pitch shifts by emotional valence (-> world_state) | |
| agents/base.py thin Agent base (-> llm) | |
| agents/curator.py grows child life from parent (causal) (-> base, world_state) | |
| agents/screenwriter.py plans the next two dramatic forks (-> base, world_state) | |
| agents/verifier.py audits branch coherence; triggers regen (-> base, world_state) | |
| core/orchestrator.py conductor: curator→verifier loop, voice, plan (-> everything) | |
| smoke_test.py full pipeline with mocks, no GPU (-> everything) | |
| ``` | |
| When rewriting files, follow this same order: leaves before roots, so the | |
| project never sits in a broken state. | |
| ## The agentic loop (one user choice → one new node) | |
| 1. **Curator** reads parent + full branch history (+ research grounding) → child `WorldState` | |
| 2. **Verifier** audits child vs branch; orchestrator regenerates up to `max_regen` times | |
| 3. **Voice tool** synthesizes the child's spoken line (pitch nudged by valence) | |
| 4. **Screenwriter** plans the child's next two forks | |
| 5. node added to `LifeTree` | |
| The orchestrator is UI-agnostic. The (future) Gradio app calls `seed()` once, | |
| then `choose_fork(node_id, i)` per click, and renders `graph()`. | |
| ## Hard rules | |
| - **Keep `MockLLM` and the mock tools working at all times.** `python -m | |
| echo.smoke_test` must pass with zero ML dependencies installed. This is the | |
| project's correctness gate. Every change is validated here first. | |
| - **Lazy-import heavy deps.** `torch`, `transformers`, `peft`, `bitsandbytes`, | |
| TTS backends are imported *inside* the methods that need them, never at module | |
| top level. Importing any `echo` module must stay cheap and dependency-free. | |
| - **Never import `orchestrator` from `core/__init__.py`.** It pulls in agents + | |
| tools, and `tools/voice.py` imports `core.world_state` — importing the | |
| orchestrator at package load creates a circular import. Import it explicitly: | |
| `from echo.core.orchestrator import Orchestrator`. | |
| - **Agents stay single-purpose and thin.** One responsibility each. This is what | |
| makes the ≤4B (Tiny Titan) path viable — small focused calls instead of one | |
| giant generation. Don't merge agents. | |
| - **All model access goes through `LLMClient`.** Agents never touch | |
| `transformers` directly. Swapping 14B ↔ 3B must remain a one-line config | |
| change. | |
| - **Favor concrete, mundane, specific detail over vague profundity** in any | |
| prompt or generated content. Specificity is what makes a life feel real; | |
| vagueness is the main failure mode. | |
| ## Quantization & LoRA (planned, on the `LocalLLM`) | |
| These are the next technical additions and belong only in `llm/client.py`: | |
| - **4-bit quantization** via `BitsAndBytesConfig` (bitsandbytes): `load_in_4bit`, | |
| NF4, double quantization. This is what lets a 14B fit on a free-tier GPU and | |
| makes the honest small-model fit real. | |
| - **Optional LoRA adapters** for light *supervised* fine-tuning of an agent (e.g. | |
| teaching the Curator format + tone). This is SFT, not RL. Per-agent adapters | |
| are loadable but training is out of scope unless asked. | |
| The `MockLLM` must remain untouched by either — it stays the offline test path. | |
| ## Tiny Titan experiment | |
| The orchestrator tracks `GrowthStats.regen_rate` (regenerations / nodes grown). | |
| This is the metric that decides 14B vs ≤4B: run the same pipeline on both and | |
| compare how often each model breaks branch coherence. A ≤4B model with a low | |
| regeneration rate wins the honest-fit argument and the Tiny Titan award. | |
| ## Tone & safety of the experience | |
| Dramatic forks touch real emotion (loss, regret). The framing must be **wonder, | |
| not regret** — "look how many vibrant lives lived inside one choice," never "look | |
| what you lost." The closing artifact (`final_map_summary`) deliberately ends with | |
| gentleness ("none of them are more real than the one you're in"). Keep that | |
| spirit in any new generated content. This is a toy that should leave people | |
| lighter, not heavier. | |
| ## How to verify any change | |
| ```bash | |
| python -m echo.smoke_test # must print "ALL STAGES PASSED ✓" | |
| ``` | |
| If that passes, the agentic loop, tree, tools, and serialization are all intact. | |
| Only after that should you test with a real model. | |
| ## Commands | |
| ```bash | |
| # offline correctness gate (no GPU, no ML deps) | |
| python -m echo.smoke_test | |
| # real model run (requires: pip install -r requirements.txt) | |
| python -c " | |
| from echo.llm.client import LocalLLM, LLMConfig | |
| from echo.tools.research import MockResearch | |
| from echo.tools.voice import MockVoice | |
| from echo.core.orchestrator import Orchestrator | |
| llm = LocalLLM(LLMConfig(model_name='Qwen/Qwen2.5-3B-Instruct')); llm.load() | |
| orch = Orchestrator(llm, MockResearch(), MockVoice()) | |
| root = orch.seed('I stayed instead of moving abroad', base_age=24) | |
| print(root.facts.constraints_text()) | |
| " | |
| ``` |