500 lines
18 KiB
Markdown
500 lines
18 KiB
Markdown
---
|
||
stepsCompleted:
|
||
- step-01-validate-prerequisites
|
||
- step-02-design-epics
|
||
- step-03-create-stories
|
||
- step-04-final-validation
|
||
inputDocuments:
|
||
- _bmad-output/planning-artifacts/prd.md
|
||
- _bmad-output/planning-artifacts/architecture.md
|
||
- _bmad-output/planning-artifacts/ux-design.md
|
||
bmadSkill: 'PM Agent (John) — /bmad-agent-bmm-pm → [CE] Create Epics and Stories'
|
||
bmadWorkflow: '_bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md'
|
||
---
|
||
|
||
<!-- 📋 Generated by BMAD PM Skill — Agent: John (Product Manager) -->
|
||
<!-- Skill Command: /bmad-agent-bmm-pm → [CE] Create Epics and Stories -->
|
||
<!-- Workflow: _bmad/bmm/workflows/3-solutioning/create-epics-and-stories/workflow.md -->
|
||
|
||
# CouncilOS — Epic & Story Breakdown
|
||
|
||
**Autor:** John (📋 BMAD PM Agent)
|
||
**Datum:** 2026-03-12
|
||
**Version:** 1.0.0
|
||
|
||
---
|
||
|
||
## Anforderungs-Inventar
|
||
|
||
### Funktionale Anforderungen
|
||
|
||
| ID | Anforderung |
|
||
|----|-------------|
|
||
| FR-01.1 | Agent-Nodes per Drag & Drop auf den Canvas |
|
||
| FR-01.2 | Node-Einstellungs-Panel (Name, System-Prompt, Modell, Tools) |
|
||
| FR-01.3 | Lineare Edges zwischen Nodes |
|
||
| FR-01.4 | Bedingte Edges mit Routing-Label |
|
||
| FR-01.5 | Council unter Namen speichern |
|
||
| FR-01.6 | Gespeicherten Council laden und bearbeiten |
|
||
| FR-01.7 | Blueprint-JSON-Export |
|
||
| FR-02.1 | Prompt-Eingabe als Council-Input |
|
||
| FR-02.2 | PDF-Upload als Council-Input |
|
||
| FR-02.3 | Auto-Pilot / God Mode Toggle |
|
||
| FR-02.4 | Auto-Pilot läuft autonom |
|
||
| FR-02.5 | God Mode pausiert vor jedem Agent |
|
||
| FR-02.6 | Finales Dokument anzeigen |
|
||
| FR-02.7 | Run-Verlauf einsehen |
|
||
| FR-03.1 | Aktiver Node pulsiert im Canvas |
|
||
| FR-03.2 | WebSocket-Events mit node_name + status |
|
||
| FR-03.3 | `done`-Event nach Run-Abschluss |
|
||
| FR-04.1 | God Mode Approval-Popup |
|
||
| FR-04.2 | Approve → nächster Node |
|
||
| FR-04.3 | Reject → Run abbrechen |
|
||
| FR-04.4 | Modify → Draft bearbeiten vor Fortsetzung |
|
||
| FR-05.1 | LLM-Modell pro Agent konfigurierbar |
|
||
| FR-05.2 | Web-Suche optional aktivierbar |
|
||
| FR-05.3 | PDF-Reader optional aktivierbar |
|
||
| FR-06.1 | Blueprint-CRUD REST-API |
|
||
| FR-06.2 | Blueprints in PostgreSQL JSONB |
|
||
| FR-06.3 | `version`-Feld im Blueprint |
|
||
|
||
### Nicht-Funktionale Anforderungen
|
||
|
||
| ID | Anforderung |
|
||
|----|-------------|
|
||
| NFR-01.1 | WebSocket < 500 ms |
|
||
| NFR-01.2 | Blueprint-CRUD < 200 ms P95 |
|
||
| NFR-01.3 | ≥ 10 parallele Runs |
|
||
| NFR-02.1 | API-Keys nur in Umgebungsvariablen |
|
||
| NFR-03.1 | Test-Coverage ≥ 80 % agents/, ≥ 90 % state.py |
|
||
| NFR-03.2 | Alembic für Migrationen |
|
||
| NFR-03.3 | Dynamischer Graph ab Phase 3 |
|
||
|
||
### FR-Abdeckungskarte
|
||
|
||
| Epic | Abgedeckte FRs |
|
||
|------|----------------|
|
||
| Epic 1 | NFR-01–03, Infrastruktur |
|
||
| Epic 2 | FR-02.1–02.4, FR-02.6, FR-03.1–03.3 (Phase 1 Backend) |
|
||
| Epic 3 | FR-01.1–01.7, FR-06.1–06.3 (Phase 2 Frontend) |
|
||
| Epic 4 | FR-02.1–02.7, FR-03.1–03.3 (Phase 3 Integration) |
|
||
| Epic 5 | FR-02.2, FR-04.1–04.4, FR-05.2–05.3 (Phase 4 Tools & God Mode) |
|
||
|
||
---
|
||
|
||
## Epic-Liste
|
||
|
||
1. **Epic 1:** Projekt-Setup & Infrastruktur
|
||
2. **Epic 2:** LangGraph Engine Backend (Phase 1)
|
||
3. **Epic 3:** Visueller Baukasten Frontend (Phase 2)
|
||
4. **Epic 4:** Frontend-Backend-Integration (Phase 3)
|
||
5. **Epic 5:** Tools & God Mode (Phase 4)
|
||
|
||
---
|
||
|
||
## Epic 1: Projekt-Setup & Infrastruktur
|
||
|
||
**Ziel:** Vollständig konfiguriertes, lauffähiges Entwicklungsumfeld mit Docker Compose, PostgreSQL, FastAPI-Skeleton und Next.js-Skeleton.
|
||
|
||
### Story 1.1: Docker-Compose-Umgebung aufsetzen
|
||
|
||
Als **Entwickler**
|
||
möchte ich **eine vollständige lokale Docker-Compose-Umgebung**,
|
||
damit ich **ohne lokale Python/Node-Installation entwickeln kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein frisch geclontes Repository
|
||
**Wenn** ich `docker compose up -d` ausführe
|
||
**Dann** starten drei Services: `db` (PostgreSQL 16), `api` (FastAPI Port 8000), `frontend` (Next.js Port 3000)
|
||
**Und** `GET /api/health` gibt `{"status": "ok"}` zurück
|
||
**Und** das Frontend ist unter `http://localhost:3000` erreichbar
|
||
|
||
**Gegeben** der `db`-Service läuft
|
||
**Wenn** ich `pg_isready` im Container ausführe
|
||
**Dann** antwortet PostgreSQL mit `accepting connections`
|
||
|
||
### Story 1.2: Backend-Python-Umgebung & Requirements
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **eine vollständige `requirements.txt` mit allen Abhängigkeiten**,
|
||
damit ich **reproduzierbar installieren kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein Python 3.11+ Environment
|
||
**Wenn** ich `pip install -r requirements.txt` ausführe
|
||
**Dann** sind FastAPI, LangGraph, langchain-anthropic, langchain-openai, SQLAlchemy, alembic, chromadb, tavily-python, pypdf, pytest und alle Transitivabhängigkeiten installiert
|
||
|
||
**Gegeben** die Abhängigkeiten sind installiert
|
||
**Wenn** ich `pytest backend/tests/` ausführe
|
||
**Dann** laufen alle Tests durch (auch wenn noch keine Testfälle existieren: 0 passed, 0 failed)
|
||
|
||
### Story 1.3: Datenbank-Migrationen mit Alembic
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **Alembic-Migrationen für `blueprints` und `council_runs`**,
|
||
damit ich **das Schema versioniert verwalten kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** eine laufende PostgreSQL-Instanz
|
||
**Wenn** ich `alembic upgrade head` ausführe
|
||
**Dann** werden die Tabellen `blueprints` und `council_runs` erstellt
|
||
**Und** `alembic current` zeigt die aktuelle Revision
|
||
|
||
**Gegeben** die Tabellen existieren
|
||
**Wenn** ich ein Blueprint via REST-API erstelle
|
||
**Dann** wird es in `blueprints` mit UUID, JSONB-Daten und Timestamps gespeichert
|
||
|
||
---
|
||
|
||
## Epic 2: LangGraph Engine Backend (Phase 1)
|
||
|
||
**Ziel:** Funktionierender, hartcodierter LangGraph-Graph (Master→Critic→Writer) mit CouncilState, Routing-Logik und FastAPI-Endpunkten. Verifikation via Terminal/Postman.
|
||
|
||
### Story 2.1: CouncilState TypedDict implementieren
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **den `CouncilState` TypedDict mit allen Feldern und Reducern**,
|
||
damit er **das einzige State-Objekt für alle Agents ist**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** `state.py` ist implementiert
|
||
**Wenn** ich `feedback_history` mit `operator.add` als Reducer definiere
|
||
**Dann** werden neue Feedback-Einträge angehängt — niemals überschrieben
|
||
|
||
**Wenn** ich `messages` mit `operator.add` als Reducer definiere
|
||
**Dann** akkumuliert der LLM-Nachrichtenverlauf korrekt über mehrere Nodes
|
||
|
||
**Wenn** `APPROVAL_THRESHOLD = 8.0` und `MAX_ITERATIONS = 5` definiert sind
|
||
**Dann** sind diese Konstanten importierbar und in Tests verwendbar
|
||
|
||
### Story 2.2: Master-Agent-Node implementieren
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **den `master_agent_node`**,
|
||
damit er **auf Basis von `input_topic` und `feedback_history` einen Draft erstellt**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein `CouncilState` ohne `feedback_history`
|
||
**Wenn** `master_agent_node(state)` aufgerufen wird (LLM gemockt)
|
||
**Dann** enthält der Rückgabe-Dict `current_draft` mit einem nicht-leeren String
|
||
**Und** `messages` enthält genau 3 Elemente (SystemMessage, HumanMessage, AIMessage)
|
||
**Und** `iteration_count` ist um 1 erhöht
|
||
|
||
**Gegeben** ein State mit `feedback_history = ["Too short"]`
|
||
**Wenn** `master_agent_node(state)` aufgerufen wird
|
||
**Dann** enthält der User-Prompt den Text „feedback" oder die Feedback-Einträge
|
||
|
||
### Story 2.3: Critic-Agent-Node implementieren
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **den `critic_agent_node` mit Score-Parsing und Routing**,
|
||
damit er **Drafts bewertet und `route_decision` setzt**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein Score < 8 in der LLM-Antwort
|
||
**Wenn** `critic_agent_node(state)` aufgerufen wird
|
||
**Dann** ist `route_decision = "rework"`
|
||
**Und** `feedback_history` enthält einen neuen Eintrag
|
||
|
||
**Gegeben** ein Score ≥ 8
|
||
**Dann** ist `route_decision = "approve"`
|
||
**Und** `feedback_history` bleibt unverändert
|
||
|
||
**Gegeben** `iteration_count >= MAX_ITERATIONS`
|
||
**Dann** ist `route_decision = "approve"` ohne LLM-Aufruf (Safety Valve)
|
||
|
||
**Gegeben** eine nicht-parsbare LLM-Antwort
|
||
**Dann** fällt `route_decision` auf `"rework"` zurück — kein Crash
|
||
|
||
### Story 2.4: Writer-Agent-Node implementieren
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **den `writer_agent_node`**,
|
||
damit er **den finalen Draft für die Ausgabe formatiert**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein `CouncilState` mit `current_draft`
|
||
**Wenn** `writer_agent_node(state)` aufgerufen wird (LLM gemockt)
|
||
**Dann** enthält der Rückgabe-Dict `current_draft` (finaler Output)
|
||
**Und** `active_node = "writer_agent"`
|
||
|
||
### Story 2.5: LangGraph-Graph bauen und ausführen
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **einen kompilierten LangGraph-Graphen** (`build_council_graph`),
|
||
damit er **Master→Critic→(conditional)→Writer ausführt**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** `build_council_graph()` wird aufgerufen
|
||
**Dann** ist der zurückgegebene Graph kompiliert und `invoke`-fähig
|
||
|
||
**Gegeben** ein hartcodierter Test-Input (LLMs gemockt, Score 9 bei 2. Iteration)
|
||
**Wenn** `graph.invoke(initial_state)` aufgerufen wird
|
||
**Dann** durchläuft der Graph: master→critic→(rework)→master→critic→(approve)→writer→END
|
||
**Und** der finale State hat `route_decision = "approve"` und `iteration_count = 2`
|
||
|
||
### Story 2.6: FastAPI-Run-Endpunkte implementieren
|
||
|
||
Als **API-Nutzer**
|
||
möchte ich **`POST /api/councils/run` und `GET /api/councils/run/{run_id}`**,
|
||
damit ich **einen Council-Run starten und sein Ergebnis abrufen kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** `POST /api/councils/run` mit `{"input_topic": "Test"}`
|
||
**Wenn** der Endpunkt aufgerufen wird
|
||
**Dann** gibt er `202 Accepted` mit `run_id` zurück
|
||
**Und** der Run läuft asynchron im Hintergrund
|
||
|
||
**Gegeben** ein abgeschlossener Run
|
||
**Wenn** `GET /api/councils/run/{run_id}` aufgerufen wird
|
||
**Dann** gibt er `status: "completed"` und `final_draft` zurück
|
||
|
||
**Gegeben** eine unbekannte `run_id`
|
||
**Dann** gibt er `404 Not Found` zurück
|
||
|
||
---
|
||
|
||
## Epic 3: Visueller Baukasten Frontend (Phase 2)
|
||
|
||
**Ziel:** React Flow Canvas mit Custom Nodes/Edges, Node-Einstellungs-Panel, Blueprint-Parser und PostgreSQL-Speicherung.
|
||
|
||
### Story 3.1: React-Flow-Canvas mit Custom Agent Node
|
||
|
||
Als **Nutzer**
|
||
möchte ich **Agent-Nodes per Drag & Drop auf den Canvas ziehen**,
|
||
damit ich **meinen KI-Rat visuell zusammenstellen kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** der Rat-Architekt-Tab ist geöffnet
|
||
**Wenn** ich einen Node aus der Seitenleiste auf den Canvas ziehe
|
||
**Dann** erscheint ein Custom-Agent-Node mit Default-Name, Default-Modell und Tool-Badges
|
||
|
||
**Gegeben** ein Node ist auf dem Canvas
|
||
**Wenn** ich ihn anklicke
|
||
**Dann** öffnet sich das rechte Einstellungs-Panel
|
||
|
||
**Gegeben** das Einstellungs-Panel ist offen
|
||
**Wenn** ich den Namen ändere
|
||
**Dann** aktualisiert sich der Canvas-Node sofort (Live-Bindung)
|
||
|
||
### Story 3.2: Lineare und bedingte Edges
|
||
|
||
Als **Nutzer**
|
||
möchte ich **Pfeile zwischen Nodes ziehen und als linear/bedingt markieren**,
|
||
damit ich **den Workflow meines Councils definieren kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** zwei Nodes auf dem Canvas
|
||
**Wenn** ich vom Ausgangs-Handle des ersten zum Eingangs-Handle des zweiten ziehe
|
||
**Dann** erscheint ein linearer Pfeil (grau, durchgehend)
|
||
|
||
**Gegeben** ich klicke eine Edge an
|
||
**Wenn** ich sie als „bedingt" mit Label „rework" markiere
|
||
**Dann** wird sie gestrichelt rot mit Label dargestellt
|
||
|
||
**Gegeben** ich markiere eine Edge als „approve"
|
||
**Dann** wird sie grün dargestellt
|
||
|
||
### Story 3.3: Blueprint-Parser (React Flow → JSON)
|
||
|
||
Als **System**
|
||
möchte ich **eine `parseGraphToBlueprint()`-Funktion**,
|
||
damit sie **den Canvas-State in ein valides Blueprint-JSON konvertiert**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein Canvas mit zwei Nodes und einer linearen Edge
|
||
**Wenn** `parseGraphToBlueprint(nodes, edges, name)` aufgerufen wird
|
||
**Dann** enthält das JSON `version`, `name`, `nodes[]`, `edges[]`
|
||
|
||
**Gegeben** eine bedingte Edge mit Condition-Label
|
||
**Dann** ist `type: "conditional"` und `condition: "<label>"` im Edge-JSON
|
||
|
||
**Gegeben** ein isolierter Node (keine Edges)
|
||
**Dann** erscheint eine Konsolenwarnung — der Node wird nicht aus dem JSON entfernt
|
||
|
||
### Story 3.4: Blueprint CRUD — Frontend & Backend
|
||
|
||
Als **Nutzer**
|
||
möchte ich **Councils speichern, laden und löschen können**,
|
||
damit ich **meine Configurations wiederverwenden kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein ausgefüllter Canvas
|
||
**Wenn** ich „Speichern" klicke
|
||
**Dann** wird `POST /api/councils/` aufgerufen und eine `id` zurückgegeben
|
||
**Und** eine Erfolgsmeldung erscheint
|
||
|
||
**Gegeben** bestehende Blueprints
|
||
**Wenn** ich die Blueprint-Liste abrufe (`GET /api/councils/`)
|
||
**Dann** sehe ich alle gespeicherten Councils mit Name und Datum
|
||
|
||
**Gegeben** einen Blueprint per `id`
|
||
**Wenn** ich `DELETE /api/councils/{id}` aufrufe
|
||
**Dann** gibt der Endpunkt `204 No Content` zurück
|
||
**Und** ein anschließender GET gibt `404` zurück
|
||
|
||
---
|
||
|
||
## Epic 4: Frontend-Backend-Integration (Phase 3)
|
||
|
||
**Ziel:** LangGraph liest JSON-Blueprint und baut Graph dynamisch. WebSocket-Events pulsieren Nodes im Canvas. Finaler Output erscheint im Frontend.
|
||
|
||
### Story 4.1: Dynamischer Graph-Builder aus Blueprint-JSON
|
||
|
||
Als **Backend-Entwickler**
|
||
möchte ich **`dynamic_graph_builder.py`**,
|
||
damit er **aus einem Blueprint-JSON zur Laufzeit einen LangGraph-Graphen baut**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein valides Blueprint-JSON mit 2 Nodes und 1 linearer Edge
|
||
**Wenn** `build_graph_from_blueprint(blueprint)` aufgerufen wird
|
||
**Dann** gibt er einen kompilierten `StateGraph` zurück
|
||
|
||
**Gegeben** ein Blueprint mit Zyklus (A→B→A, bedingt)
|
||
**Dann** kompiliert der Graph korrekt — kein Fehler, Zyklus bleibt erhalten
|
||
|
||
**Gegeben** ein Blueprint mit nicht-existenter Node-ID in einer Edge
|
||
**Dann** wird ein `ValueError` mit klarer Fehlermeldung geworfen
|
||
|
||
**Gegeben** ein Blueprint mit 0 Nodes
|
||
**Dann** wird ein `ValueError` geworfen
|
||
|
||
### Story 4.2: WebSocket-Agent-Events integrieren
|
||
|
||
Als **Frontend**
|
||
möchte ich **WebSocket-Events vom Backend**,
|
||
damit **der aktive Node im Canvas pulsiert**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein laufender Council-Run
|
||
**Wenn** LangGraph in `master_agent_node` eintritt
|
||
**Dann** sendet das Backend `{"node": "master_agent", "status": "running"}` über WS
|
||
|
||
**Gegeben** das Frontend verbindet sich mit `WS /ws/council/{run_id}`
|
||
**Wenn** ein Node-Event empfangen wird
|
||
**Dann** wechselt der entsprechende Node im Canvas in den Pulsier-Zustand (gelb)
|
||
|
||
**Gegeben** der Run ist abgeschlossen
|
||
**Dann** sendet das Backend `{"status": "done"}` und das Frontend bricht die WS-Verbindung
|
||
|
||
### Story 4.3: Konferenzzimmer — Live-Execution-UI
|
||
|
||
Als **Nutzer**
|
||
möchte ich **im Konferenzzimmer-Tab den laufenden Council sehen**,
|
||
damit ich **transparent nachvollziehen kann, was die KI tut**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ich starte einen Blueprint-Run
|
||
**Wenn** der Run läuft
|
||
**Dann** zeigt das Canvas (read-only) den aktuell aktiven Node pulsierend
|
||
|
||
**Wenn** der Run abgeschlossen ist
|
||
**Dann** erscheint das finale Dokument im Output-Bereich
|
||
|
||
**Gegeben** ein Run schlägt fehl
|
||
**Dann** erscheint eine klare Fehlermeldung mit dem Fehlergrund
|
||
|
||
---
|
||
|
||
## Epic 5: Tools & God Mode (Phase 4)
|
||
|
||
**Ziel:** Tavily-Web-Suche, PDF-Reader mit ChromaDB, und Human-in-the-Loop via `interrupt_before`.
|
||
|
||
### Story 5.1: Tavily Web-Suche als Agent-Tool
|
||
|
||
Als **Nutzer**
|
||
möchte ich **Web-Suche als optionales Tool für jeden Agent**,
|
||
damit **Agents aktuelle Informationen aus dem Internet nutzen können**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein Agent mit `tools.web_search = true`
|
||
**Wenn** der dynamische Graph-Builder den Agent aufbaut
|
||
**Dann** wird `web_search` als Tool an den Agent gebunden
|
||
|
||
**Gegeben** `TAVILY_API_KEY` ist gesetzt
|
||
**Wenn** `web_search("KI-Trends 2025")` aufgerufen wird
|
||
**Dann** gibt die Funktion eine formatierte Liste von Treffern zurück (gemockt in Tests)
|
||
|
||
**Gegeben** `TAVILY_API_KEY` ist nicht gesetzt
|
||
**Dann** gibt die Funktion eine klare Fehlermeldung zurück — kein Crash
|
||
|
||
### Story 5.2: PDF-Upload & ChromaDB-Ingestion
|
||
|
||
Als **Nutzer**
|
||
möchte ich **ein PDF hochladen, das als Wissensquelle für Agents dient**,
|
||
damit **Agents auf Inhalte aus langen Dokumenten zugreifen können**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** `POST /api/councils/upload-pdf` mit einer validen PDF-Datei
|
||
**Wenn** der Endpunkt aufgerufen wird
|
||
**Dann** wird das PDF in Chunks aufgeteilt, in ChromaDB gespeichert, und `chunks_ingested` zurückgegeben
|
||
|
||
**Gegeben** eine Nicht-PDF-Datei
|
||
**Dann** gibt der Endpunkt `400 Bad Request` zurück
|
||
|
||
**Gegeben** ein Agent mit `tools.pdf_reader = true`
|
||
**Wenn** `pdf_search("Schlüsselergebnisse")` aufgerufen wird
|
||
**Dann** gibt die Funktion die Top-K semantisch ähnlichsten Chunks zurück
|
||
|
||
### Story 5.3: God Mode — Human-in-the-Loop
|
||
|
||
Als **Nutzer**
|
||
möchte ich **im God Mode jeden Schritt des Councils genehmigen**,
|
||
damit ich **volle Kontrolle über den Prozess habe**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** ein Blueprint-Run mit `god_mode = true`
|
||
**Wenn** LangGraph den ersten Node abschließt
|
||
**Dann** pausiert der Graph via `interrupt_before` und `status = "paused"`
|
||
|
||
**Gegeben** ein pausierter Run
|
||
**Wenn** `POST /api/councils/run/{run_id}/approve` mit `{"action": "approve"}` aufgerufen wird
|
||
**Dann** setzt der Graph am nächsten Node fort
|
||
|
||
**Gegeben** `{"action": "reject"}`
|
||
**Dann** wird der Run mit `status = "failed"` beendet
|
||
|
||
**Gegeben** `{"action": "modify", "modified_state": {"current_draft": "..."}}`
|
||
**Dann** setzt der Graph mit dem modifizierten Draft fort
|
||
|
||
**Gegeben** das Frontend verbindet sich im God Mode
|
||
**Wenn** der Run pausiert
|
||
**Dann** erscheint das God-Mode-Overlay mit Agent-Name, Vorschlag, Grund und den drei Buttons
|
||
|
||
### Story 5.4: Run-Verlauf (History)
|
||
|
||
Als **Nutzer**
|
||
möchte ich **vergangene Council-Runs einsehen**,
|
||
damit ich **Ergebnisse nachträglich nachschlagen kann**.
|
||
|
||
**Akzeptanzkriterien:**
|
||
|
||
**Gegeben** abgeschlossene Runs
|
||
**Wenn** `GET /api/runs/` aufgerufen wird
|
||
**Dann** gibt er eine paginierte Liste aller Runs zurück
|
||
|
||
**Gegeben** eine `run_id`
|
||
**Wenn** `GET /api/runs/{run_id}` aufgerufen wird
|
||
**Dann** gibt er `status`, `final_draft`, `critic_score`, `iteration_count` zurück
|
||
|
||
**Gegeben** eine unbekannte `run_id`
|
||
**Dann** gibt er `404 Not Found` zurück
|