# Retrospektive — Epic 2: LangGraph Engine Backend (Phase 1) **Datum:** 2026-03-12 **Epic:** Epic 2 — LangGraph Engine Backend (Phase 1) **Status:** Abgeschlossen — 6/6 Stories erledigt **Facilitiert von:** Bob (🏃 Scrum Master) --- ## 1. Epic-Zusammenfassung **Ziel:** Funktionierender, hartcodierter LangGraph-Graph (Master→Critic→Writer) mit CouncilState, Routing-Logik und FastAPI-Endpunkten. Verifikation via Terminal/Postman. **Stories abgeschlossen:** - ✅ 2.1: CouncilState TypedDict implementieren - ✅ 2.2: Master-Agent-Node implementieren - ✅ 2.3: Critic-Agent-Node implementieren - ✅ 2.4: Writer-Agent-Node implementieren - ✅ 2.5: LangGraph-Graph bauen und ausführen - ✅ 2.6: FastAPI-Run-Endpunkte implementieren --- ## 2. Was lief gut? (Keep) **Technische Entscheidungen:** - **TypedDict + `operator.add`-Reducer** für `feedback_history` und `messages` — elegant, typensicher, und verhindert versehentliches Überschreiben akkumulativer Felder - **Numerischer Score als Single Source of Truth** in der Critic-Routing-Logik: `route_decision` wird ausschließlich vom geparsten Score abgeleitet (≥ 8.0 = approve), nicht vom LLM-generierten VERDICT-String. Das eliminiert Inkonsistenzen zwischen Score und Routing - **Safety Valve** (`MAX_ITERATIONS = 5`): Erzwingt Genehmigung nach 5 Iterationen — verhindert Endlosschleifen ohne manuellen Eingriff - **Stateless Agent-Funktionen**: Jeder Agent-Node ist eine reine Funktion (State rein → Dict raus) — perfekt testbar und leicht in den dynamischen Graph-Builder (Phase 3) übernehmbar - **`run_in_executor()`-Pattern** für die Brücke zwischen async FastAPI und synchronem `graph.invoke()` — verhindert Event-Loop-Blocking - **Differenzierte Temperaturen**: Master (0.7 kreativ), Critic (0.2 streng), Writer (0.4 ausgewogen) — jeder Agent hat ein klar definiertes Verhaltensprofil **Prozess:** - Story-Reihenfolge State → Agents → Graph → API war logisch perfekt — jede Story baut auf der vorherigen auf - 125 Backend-Tests mit vollständig gemockten LLMs — keine echten API-Aufrufe in CI, schnelle Ausführung - `_parse_critic_response()` als separate Funktion isoliert das fragile Parsing — leicht zu testen und zu verbessern --- ## 3. Was lief nicht gut? (Drop / Improve) **Technisch:** - **LLM-Instanziierung pro Aufruf**: `ChatAnthropic()` wird in jedem Agent-Node neu erstellt — Performance-Overhead bei hoher Auslastung. Ein Singleton oder Dependency-Injection wäre effizienter - **Regex-basiertes Critic-Parsing** (`_parse_critic_response`) ist fragil: Bei nicht-parsbare Antworten fällt der Score auf 0.0 zurück — funktioniert als Fallback, aber log-würdig - **`threading.Lock` in asyncem Code** (`RunStore`): Synchrone Locks auf asyncem Code bergen Deadlock-Risiko. Sollte `asyncio.Lock` verwenden - **In-Memory `RunStore`** wächst unbegrenzt und geht bei Server-Neustart verloren — für Phase 1 akzeptabel, für Produktion nicht - **ThreadPoolExecutor ohne Limit**: `run_in_executor(None, ...)` verwendet den Default-Pool ohne maximale Thread-Anzahl — bei vielen parallelen Runs könnte das System überlastet werden **Prozess:** - Kein End-to-End-Test der kompletten Graph-Ausführung mit echtem Zyklusdurchlauf — Unit-Tests decken Einzelteile ab, aber der Integrations-Test fehlt - WebSocket-Endpunkt hat keine Authentifizierung — jeder Client kann sich auf beliebige `run_id`s subscriben --- ## 4. Lernerkenntnisse | Erkenntnis | Anwendung in kommenden Epics | |------------|------------------------------| | `operator.add`-Reducer sind eleganter als manuelle Liste-Append-Logik | Für alle zukünftigen State-Felder, die akkumulieren sollen | | Numerisches Routing statt LLM-VERDICT ist deterministisch und testbar | Im dynamischen Graph-Builder (Phase 3) das gleiche Pattern verwenden | | Stateless Agent-Funktionen sind trivial in dynamische Graphen übertragbar | Phase 3 kann dieselben Node-Funktionen wiederverwenden | | `run_in_executor` ist die Standard-Brücke zwischen async/sync in FastAPI | Für alle CPU-/IO-blockierenden Operationen in Epic 4+ nutzen | --- ## 5. Aktionspunkte für kommende Epics | Priorität | Aktionspunkt | Verantwortlich | Status | |-----------|-------------|----------------|--------| | Hoch | In-Memory RunStore durch PostgreSQL-Persistenz ersetzen | Dev | Umgesetzt in Story 5.4 | | Hoch | Dynamischer Graph-Builder für Phase 3 (JSON → LangGraph) | Dev | Umgesetzt in Story 4.1 | | Mittel | LLM-Instanzen cachen oder per Dependency Injection bereitstellen | Dev | Backlog | | Mittel | `threading.Lock` → `asyncio.Lock` im RunStore | Dev | Backlog | | Niedrig | WebSocket-Authentifizierung hinzufügen | Dev | Backlog | --- ## 6. Nächstes Epic (Preview: Epic 3) **Epic 3 — Visueller Baukasten Frontend** nutzt die Backend-API aus Epic 2: - React Flow Canvas mit Custom Agent Nodes (Story 3.1 ✅) - Lineare und bedingte Edges (Story 3.2 ✅) - Blueprint-Parser konvertiert Canvas → JSON (Story 3.3 ✅) - Blueprint CRUD verbindet Frontend mit Backend-API (Story 3.4 ✅) **Wichtige Brücke:** Die REST-API aus Story 2.6 (`POST /api/councils/run`, `GET /api/councils/run/{run_id}`) wird in Epic 3 vom Frontend konsumiert. Das Blueprint-JSON aus Epic 3 wird ab Epic 4 den hartcodierten Graph aus Story 2.5 ablösen. --- *Bob (Scrum Master): "Epic 2 ist das technische Herz von CouncilOS. Die Entscheidung, den Critic-Score als numerische Single Source of Truth zu verwenden — nicht den LLM-generierten VERDICT-String — war die wichtigste Architekturentscheidung des gesamten Projekts. Zusammen mit dem Safety Valve und den stateless Agent-Funktionen haben wir ein robustes, testbares, und erweiterbares Fundament geschaffen. 125 Tests sprechen für sich."*