KI-Konzil/_bmad-output/planning-artifacts/epics.md
copilot-swe-agent[bot] 3be3cb73b6 Add all BMAD skill artifacts: epics, stories, sprint-status, QA tests, project-context, readiness report
Co-authored-by: Kenearos <86194771+Kenearos@users.noreply.github.com>
2026-03-12 14:26:40 +00:00

500 lines
18 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

---
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-0103, Infrastruktur |
| Epic 2 | FR-02.102.4, FR-02.6, FR-03.103.3 (Phase 1 Backend) |
| Epic 3 | FR-01.101.7, FR-06.106.3 (Phase 2 Frontend) |
| Epic 4 | FR-02.102.7, FR-03.103.3 (Phase 3 Integration) |
| Epic 5 | FR-02.2, FR-04.104.4, FR-05.205.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 (MasterCriticWriter) 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: mastercritic→(rework)→mastercritic→(approve)→writerEND
**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 (ABA, 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