SPARK-Hackathon Challenge 2 „Da geht noch mehr!" — Prototyp. Eine neue Verwaltungs-/Parlamentsleistung jenseits von Planungs- und Genehmigungsverfahren: Aus dem Audiomitschnitt einer Gremiensitzung (Gemeinderat, Ausschuss, Bundestag, Anhörung) entsteht automatisch ein strukturiertes, durchsuchbares, rechtssicher belegbares Protokoll als Knowledge Graph mit GraphRAG — inklusive Faktencheck politischer Aussagen.
Problem. Parlaments- und Gremienprotokolle sind lange, unstrukturierte Textmassen. Wer wissen will „Welcher Beschluss mit welchem Ergebnis?", „Wer war befangen — nach welcher Norm?" oder „Stimmt diese Zahl?", sucht sich tot. Suchen allein reicht nicht — man muss Zusammenhänge sehen und jede Aussage belegen können.
Warum wir SPARK weiterdenken. Das BMDS-Referenzprojekt SPARK beschleunigt Planungs- und Genehmigungsverfahren mit KI über Dokumente + Vektorsuche. Challenge 2 fragt „Da geht noch mehr!". Wir übertragen die KI-Bausteine auf eine neue Leistung — das Protokoll selbst — und ergänzen SPARK um zwei Bausteine, die es nicht hat: Sprache/Video als Eingabe und einen Wissensgraph mit Faktencheck und lückenloser Quelle.
Warum dieser Use Case. Bundestags-Plenarprotokolle sind öffentlich/gemeinfrei, hochrelevant und mehrhop-strukturiert (Antrag→Abstimmung→Beschluss; Person→Befangenheit→Norm). Genau dort schlägt ein Knowledge Graph die reine Volltext-/Vektorsuche.
Die Lösung. Aus einem Audio-/Video-Mitschnitt oder dem amtlichen XML entsteht automatisch ein durchsuchbarer, rechtssicher belegbarer Wissensgraph mit Faktencheck — jede Aussage ist per Klick im Video an der richtigen Sekunde nachprüfbar. 81 echte Bundestagssitzungen sind geladen; KI-Faktencheck-Vorschläge sind als solche markiert und werden von Menschen freigegeben (kein Urteil per Knopfdruck).
▶ Live ausprobieren: https://ma3u.github.io/hackathon-spark/ — Sitzung wählen, Knoten anklicken, Quelle umschalten (🟢 amtlich ↔ 🎬 YouTube), 📈 Gesamt-Dashboard öffnen. Ohne Login.
Technische Details, Architektur, Datenquellen und Reproduktion: weiter unten ↓
Aktueller Stand (2026-06-11) — echte Daten. Alle 81 WP21-Plenarprotokolle + WP20/214 sind in Neo4j geladen (~136 k Knoten / ~361 k Beziehungen). Die Pages-App bietet 82 reale Sitzungen zur Auswahl mit Quellen-Umschalter 🟢 Offiziell (amtliches XML + Mediathek-Video-Deeplink je Rede) ↔ 🎬 YouTube (Gesamtmitschnitt, Zeit-Deeplinks). Faktencheck über reale Reden via In-Kontext-LLM- Extraktion → variierte Verdikte (bestätigt/teilweise/irreführend/falsch/unbelegt) als KI-Vorschlag mit Disclaimer; optional Retrieval-Grounding (DIP-API + Wikipedia) mit echter Quelle. Inkrementeller Import:
python scripts/sync_sessions.py --all --load. Fortschritt/Plan:docs/challenge-plan.md. Live: https://ma3u.github.io/hackathon-spark/
Dep-freie Demo-Szenarien (fiktiv, für CI/offline): Gemeinderat und Bundestag
(fiktive Plenardebatte) — laufen ohne pip/GPU/Netz und treiben den CI-Smoke-Test; in der
Web-Oberfläche werden nur reale Sitzungen gezeigt. SPARK-Nutzung, Echtdaten-Wege und
die Faktencheck-Grenzen: docs/spark-und-echtdaten.md.
Analyse der Bundestags-Protokollierung & des Faktenchecks:
docs/bundestag-protokollierung-analyse.md.
python3 run_demo.py # beide Szenarien
python3 run_demo.py --scenario bundestag # nur Bundestag + FaktencheckDie Demo verarbeitet zwei mitgelieferte, fiktive Mitschnitte
(Gemeinderat Musterbach; fiktive Bundestags-Plenardebatte), baut je einen Graphen
und beantwortet GraphRAG-Fragen — jede Antwort mit Audio-Zeitstempel als Beleg.
Pro Szenario landen Artefakte in output/ (SPARK-Datenformat) und in web/data/
(für GitHub Pages):
Datei (<scenario> = gemeinderat | bundestag) |
Zweck |
|---|---|
<scenario>_graph_data.json |
Graph ({metadata, nodes, relationships}) |
<scenario>_nodes.csv / _relationships.csv |
Neo4j-Bulk-Import |
<scenario>_neo4j_import.cypher |
Idempotenter MERGE-Import |
Echtes Audio:
pip install -r requirements.txt
python3 run_demo.py --audio pfad/zur/sitzung.mp3 🎙️ Audio (mp3/wav)
│
▼
┌─────────────┐ faster-whisper large-v3, language="de", word_timestamps,
│ ASR │ VAD-Filter (Stille/Applaus raus) → Wörter + Zeiten
└─────────────┘ pipeline/asr.py
│
▼
┌─────────────┐ pyannote/speaker-diarization-3.1
│ Diarisierung│ "wer spricht wann?" → Sprecher-Turns
└─────────────┘ pipeline/diarize.py
│
▼
┌─────────────┐ WhisperX forced alignment: jedes Wort dem
│ Alignment │ überlappenden Sprecher-Turn zuordnen, zu → Utterances
└─────────────┘ Redebeiträgen bündeln pipeline/align.py (Sprecher+Text+Zeit)
│ Sprecher-Label → Person via Enrollment/Rednerliste
▼
┌─────────────┐ Lokales LLM (vLLM/Ollama), JSON-Schema-Constraint:
│ Extraktion │ TOPs · Anträge · Abstimmungen · Beschlüsse · → Protocol
└─────────────┘ Befangenheiten · Aufgaben pipeline/extract.py
│ jede Aussage trägt ihre Quell-Segment-Indizes
▼
┌─────────────┐ Vier-Schichten-Ontologie + Provenienzkanten
│ Graph │ pipeline/graph_build.py → Neo4j-Graph
└─────────────┘
│
▼
┌─────────────┐ Mehrhop-Abfragen mit Audio-Beleg
│ GraphRAG │ pipeline/graphrag.py → Antworten + Zitate
└─────────────┘
Drei Audio-spezifische Knackpunkte (Hackathon-relevant):
- Diarisierung ≠ Identifikation. pyannote liefert anonyme Labels
(
SPEAKER_00…). Die Zuordnung zu echten Personen kommt aus einem Voice-Enrollment beim Namensaufruf oder aus der Rednerliste — gekapselt inspeaker_map. Stimmprofile sind biometrische Daten (Art. 9 DSGVO): Embeddings nur mit Rechtsgrundlage speichern, sonst pro Sitzung verwerfen. - Provenienz bis zur Sekunde. Jeder extrahierte Beschluss/jede Abstimmung
verweist per
BELEGT_DURCHauf das Transkriptsegment mitstart_sec/end_sec. Im Streitfall ist die Aussage per Klick im Audio nachhörbar — das macht aus einer KI-Zusammenfassung ein justiziables Protokoll. - On-prem statt Cloud. Whisper + pyannote + lokales LLM laufen vollständig im Behördennetz. Keine Sitzungsstimmen verlassen das Haus.
Protokollfragen sind mehrhop und strukturiert — genau dort versagt ein Embedding-Index über Textchunks:
| Frage | Graph-Traversierung |
|---|---|
| „Welche Beschlüsse mit welchem Ergebnis?" | Beschluss ←FUEHRT_ZU← Abstimmung (Ja/Nein/Enthaltung) |
| „Wie war die Abstimmung zum Bücherbus?" | TOP →ENTSCHIEDEN_DURCH→ Abstimmung →…→ Beschluss |
| „Wer war befangen — nach welcher Norm?" | Person →BEFANGEN_BEI→ TOP, Person →BEFANGENHEIT_NACH→ Norm §18 |
| „Welche Aufgabe, wer zuständig, welche Frist?" | Beschluss →ERZEUGT_AUFGABE→ Aufgabe |
| „Beleg im Audio?" | * →BELEGT_DURCH→ Transkriptsegment.start_sec |
Beispielausgabe (gekürzt):
❓ Welche Beschlüsse wurden gefasst?
📋 • Beschluss 2026-014: Online-Terminsystem … — Abstimmung 7:0:1 (angenommen)
↳ Audiobeleg [01:44] Dr. Petra Hoffmann: „Damit ist Beschluss 2026-014 gefasst…"
• Beschluss 2026-015: Anschaffung Bücherbus … — Abstimmung 5:2:0 (angenommen)
↳ Audiobeleg [03:56] Dr. Petra Hoffmann: „Beschluss 2026-015: Die Anschaffung…"
| Schicht | Knotentypen | Beispiel |
|---|---|---|
| L1 Normativ | Norm |
GemO §37 (Beschlussfähigkeit), §18 (Befangenheit) |
| L2 Zeitlich | Sitzung, Fristen an Aufgabe |
Sitzung 12.05.2026; Frist 09.06.2026 |
| L3 Prozedural | Tagesordnungspunkt → Antrag → Abstimmung → Beschluss → Aufgabe |
TOP 3 → Antrag → 5:2:0 → Beschluss 2026-015 |
| L4 Fallbezug | Person, Fraktion, Redebeitrag |
Klaus Brandt (GRÜN-Liste), Redebeitrag zu TOP 2 |
| L4 Fallbezug | Aussage (prüfbare Behauptung), Frage |
„über 150.000 Ladepunkte" |
| + Faktencheck | Faktencheck (Verdikt), Quelle |
irreführend · Ladesäulenregister |
| + Provenienz | Transkriptsegment |
Segment [03:56], start_sec, audio_file |
Prüfbare Aussagen aus Reden → Verdikt + Quelle + Audio-Beleg.
Konzept, Verdikt-Skala und Grenzen:
docs/bundestag-protokollierung-analyse.md.
🔎 Faktencheck der Reden:
• [irreführend] „über 150.000 öffentliche Ladepunkte" — Stefan Möller
belegt: ~121.000 · Quelle: Ladesäulenregister (Stand 2026-04) ↳ [00:18]
• [falsch] „seit 2021 nicht erhöht" — Dr. Lena Vossberg
mehr als verdoppelt (50.000 → 121.000) ↳ [00:47]
• [unbelegt] „über 40 % der Ladesäulen defekt" — keine Evidenz ↳ [01:44]
Graph: Aussage →GEPRUEFT_ALS→ Faktencheck →BELEGT_MIT→ Quelle (+ BELEGT_DURCH → Transkriptsegment).
web/index.html ist eine statische Single-File-App (3d-force-graph via CDN),
die web/data/<scenario>.json lädt — Szenario-Umschalter, Detail-Panel,
Faktencheck-Liste, Audio-Deep-Links auf Transkriptsegment.
Lokal starten (nur Python-Stdlib, kein Build-Schritt):
python3 run_demo.py --no-queries # web/data/*.json (neu) erzeugen — bei stale Daten
python3 -m http.server -d web 8000 # statischen Server starten
# → Browser öffnen: http://localhost:8000 · Stoppen: Strg-CDer Server liefert nur die statischen Dateien aus web/ aus (kein Backend). Sind die
Daten im UI veraltet oder leer, zuerst run_demo.py --no-queries laufen lassen, dann neu laden.
Deploy: Der Workflow .github/workflows/pages.yml baut die Daten und publiziert
web/ automatisch. Eigenständiges Repo + Pages mit einem Befehl:
./scripts/publish-to-github.sh hackathon-spark # braucht `gh auth login`
# → https://ma3u.github.io/hackathon-spark/Echte Bundestagsdaten brauchen kein Audio: das amtliche Plenarprotokoll-XML
(DTD dbtplenarprotokoll, ab WP19) wird direkt geparst — inklusive der
<kommentar>-Saalreaktionen (Beifall/Zwischenruf/Lachen/Widerspruch =
Jubel/Buhrufe, amtlich annotiert). Mitgeliefert ist eine echte Sitzung
(data/real/plenarprotokoll-20-214.xml, gemeinfrei) — Szenario „Bundestag (echt)".
# Echtes mitgeliefertes Protokoll (ohne Faktencheck über reale Personen):
python ingest_bundestag.py --xml data/real/plenarprotokoll-20-214.xml \
--name bundestag_real --no-factcheck
# Eigene Sitzung ziehen + verarbeiten:
# 1) Offizielle Quellen einer Sitzung ziehen (auf deiner Maschine):
./scripts/fetch-session.sh 21 81 # Open-Data-XML + DIP-API + YouTube-Audio
# 2) Parsen → Graph + Faktencheck + Dashboard, dry-run Neo4j:
python ingest_bundestag.py --xml data/incoming/21-081/21081.xml
# 3) Neo4j starten und echt laden:
docker compose -f docker-compose.neo4j.yml up -d
python ingest_bundestag.py --xml data/incoming/21-081/21081.xml --loadNeo4j-Import (pipeline/neo4j_loader.py): idempotente, parametrisierte
MERGE über den offiziellen neo4j-Driver (kein String-Interpolieren).
Neo4j-GraphRAG (pipeline/neo4j_graphrag.py): die offizielle
neo4j-graphrag-Bibliothek mit Text2Cypher — natürliche Frage →
schema-gestützter Cypher → Neo4j → Antwort (LLM on-prem via Ollama/vLLM).
Faktencheck-Fragen liefern per Few-Shot immer Verdikt + Quelle.
python -m pipeline.neo4j_graphrag "Welche Aussagen sind falsch — mit Quelle?"Dashboard pro Sitzung (pipeline/dashboard.py → web/data/*_dashboard.json,
im Pages-UI über „📊 Dashboard"): Top-Themen nach Redevolumen, Sprachanteil
pro Fraktion, positives/negatives Feedback je Thema und je Fraktion (aus
den Saalreaktionen), Faktencheck-Bilanz.
Barrierefreiheit (blinde/sehbehinderte Menschen) (pipeline/accessible.py,
Pages „♿ Vorlesefassung"): lineare, screen-reader-/TTS-taugliche Textfassung der
Sitzung — inkl. verbalisierter Saalreaktionen (Beifall = Zustimmung,
Widerspruch/Buhrufe = Ablehnung) und Faktencheck mit Quellen.
Gap-Analyse Protokoll ↔ Video (compare_protocol_video.py,
pipeline/gap_analysis.py): WER, Saalreaktions-Recall, Sprecher-/Inhalts-Lücken
gegen den amtlichen Goldstandard. Methodik: docs/test-protokoll-vs-video.md.
Doku: Quellen docs/quellen.md · Plan/Fortschritt docs/challenge-plan.md ·
Fragen an den Bundestag docs/fragen-bundestag.md.
Welche Quelle erzeugt welche Graph-Elemente — und ist sie tatsächlich geladen? Status:
✅ geladen · landed-Spalte wird von
der E2E-Test-Suite (s. u.) automatisch geprüft.
Zwei klar getrennte, quellenmarkierte Graphen pro Sitzung (metadata.herkunft,
metadata.quelle_url): herkunft=youtube (yt_<wp>_<nr>, Zeit-Deeplinks) vs.
herkunft=amtlich (amt_<wp>_<nr>). Person/Fraktion bleiben global → derselbe Mensch
in beiden Graphen. Inkrementeller Import + Status: scripts/sync_sessions.py.
| # | Quelle | Format | Loader / Modul | Erzeugte Knoten (Auswahl) | Art | landed |
|---|---|---|---|---|---|---|
| 1 | Amtliches Plenarprotokoll — ALLE WP21-Sitzungen (dserver.bundestag.de/btp/21/21NNN.xml, DTD dbtplenarprotokoll) |
XML | sync_sessions --official → bundestag_xml → graph_build → neo4j_loader (amt_-Namespace) |
Sitzung, Tagesordnungspunkt, Person, Fraktion, Redebeitrag, Aussage, AkustischesEreignis, Transkriptsegment |
echt | ✅ alle verfügbaren WP21-Sitzungen in Neo4j; Dashboards je Sitzung auf Pages |
| 2 | Inhaltsverzeichnis (im selben XML) | XML | bundestag_xml._ivz_titles |
TOP-Titel (Property) | echt | ✅ |
| 3 | Anlagen „zu Protokoll gegebene Reden" / §31-GO (im selben XML) | XML | bundestag_xml (anlagen) |
Redebeitrag (schriftlich=true) |
echt | ✅ |
| 4 | YouTube-Gesamtmitschnitte (@bundestag/streams, Auto-Untertitel) |
VTT | sync_sessions --youtube → subtitles.youtube_segments → graph_build (yt_-Namespace) |
Sitzung, Transkriptsegment (mit Startsekunde → Deeplink), Aussage, Faktencheck, Quelle |
echt | ✅ Sitzung 79 + 81 in Neo4j + Pages (Graph, Dashboard, HTML-Protokoll) |
| 5 | Diarisierte Audio-Mitschnitte | JSON | asr/align/extract → graph_build |
+ Beschluss, Abstimmung, Antrag, Befangenheit, Aufgabe |
fiktiv | ✅ Pages-Demo (gemeinderat/bundestag) |
| 6 | Sound-Event-Detection (PANNs/AudioSet) | JSON/Audio | pipeline/sound_events.py |
AkustischesEreignis (herkunft=audio-SED) |
fiktiv (Sample) | ✅ Pages-Demo |
| 7 | LLM-Faktencheck (Azure Mistral-Large-3) — reale Inhalte | REST | factcheck.factcheck_with_llm / factcheck_transcript_llm |
Faktencheck, Quelle (+ metadata.factcheck_disclaimer) |
echt | ✅ KI-Vorschlag (ungeprüft) mit Disclaimer auf YT- + amtl. Sitzungen |
| 8 | Evidenz-Korpus (Faktencheck) | JSON | pipeline/factcheck.factcheck_rule_based |
Faktencheck, Quelle |
fiktiv | ✅ nur fiktive Demo-Szenarien (dep-frei, CI) |
| 9 | Gap-Analyse YouTube ↔ amtlich | — | sync_sessions --gap → gap_analysis |
— (web/data/gap_<wp>_<nr>.json: WER, Reaktions-Recall) |
echt | ✅ Sitzung 79 + 81 |
| 10 | DIP-API (Vorgänge/Drucksachen/Metadaten) | REST/JSON | scripts/fetch-session.sh (Key) |
— (nur Fetch/Metadaten) | echt | ⬜ noch nicht in den Graph importiert |
| 11 | Embeddings (Azure text-embedding-3-large) |
Vektor | scripts/vector_search.py |
Transkriptsegment.embedding + Vektor-Index |
echt | ✅ in Neo4j |
# Inkrementell ALLE noch nicht importierten Sitzungen laden (überspringt Vorhandenes):
python scripts/sync_sessions.py --youtube --load # neue YouTube-Gesamtmitschnitte
python scripts/sync_sessions.py --official --from 1 --to 81 --load # alle amtlichen WP21-XML
python scripts/sync_sessions.py --gap # Lückenanalyse YT ↔ amtlichDas offizielle BMDS SPARK Workflow
wurde lokal geklont und untersucht. Wichtig zur Einordnung: graph-protokoll hat keine
Code-Abhängigkeit zu SPARK — wir teilen Mission, Lizenz und Bausteinkonzept, nicht Quellcode.
(Das „SPARK-Datenformat" der Schwester-Prototypen graph-insurance/-investigation/-eAkte
stammt aus dem eigenen Prototypen-Ökosystem github.com/ma3u, nicht aus SPARK selbst.)
Was wir aus SPARK übernehmen (konzeptionell):
| SPARK Workflow (BMDS) | Übernahme in graph-protokoll |
|---|---|
| Challenge 2 „Da geht noch mehr" — neue Leistung jenseits Planung/Genehmigung | Gremien-/Plenarprotokoll-Analyse als neue Verwaltungsleistung |
| Lizenz EUPL-1.2, Public Money – Public Code | gleiche Lizenz/Haltung |
| On-prem-LLM über OpenAI-kompatiblen Endpoint (LiteLLM/vLLM) | gleiches Muster (Ollama/vLLM oder Azure AI Foundry über .env) |
| Modul Inhaltsextraktion aus Antragsunterlagen | analog pipeline/extract.py / bundestag_xml.py |
| Vollständigkeits-/Plausibilitätsprüfung | analog Faktencheck/Qualitätsprüfung |
Wo wir SPARK erweitern (Bausteine, die SPARK Workflow nicht hat — SPARK nutzt Temporal + FastAPI + Qdrant/Vektor-RAG, keinen Graph):
| Fähigkeit | SPARK | graph-protokoll (Erweiterung) |
|---|---|---|
| Eingabe | Dokumente (PDF/DOCX) | Audio/ASR, amtliches Plenarprotokoll-XML, YouTube-Untertitel |
| Retrieval | Vektor-RAG (Qdrant) | Knowledge Graph (Neo4j) + GraphRAG/Text2Cypher |
| Analytik | — | Graph Data Science (PageRank, Louvain, Degree) |
| Provenienz | Dokumentbezug | Audio-Sekunde / Segment (BELEGT_DURCH) |
| Bewertung | formale/Plausibilitätsprüfung | Faktencheck mit Quellen (fiktiv; reale Personen ohne Auto-Verdikt) |
| Mehrkanal-RAG | — | Haystack/Neo4j (Volltext-RAG) + Text2Cypher nebeneinander |
Ausführliche Analyse (mit den real ausgeführten Schritten): docs/spark-und-echtdaten.md.
Für Genauigkeit & Performance auf den echten Sitzungen (siehe
docs/neo4j-echtsitzungen.md) kommen drei Neo4j-GenAI-Bausteine
zum Einsatz. LLM-Zugang on-prem-fähig über .env (OpenAI-kompatibel; hier Azure AI Foundry:
Mistral-Large-3 + Kimi-K2.6). Setup: .venv/bin/pip install -r requirements-genai.txt,
cp .env.example .env (Werte eintragen), Neo4j auf 7475/7688 starten + load_real_sessions.py.
| Baustein | Bibliothek | Skript | Zweck |
|---|---|---|---|
| GraphRAG (Text2Cypher) | neo4j-graphrag |
scripts/graphrag_compare.py |
NL-Frage → Cypher → Antwort; Modellvergleich Mistral vs. Kimi |
| Graph Data Science | graphdatascience |
scripts/gds_analysis.py |
PageRank/Louvain/Degree über den Mitsprache-Graph |
| Haystack ↔ Neo4j | neo4j-haystack |
scripts/haystack_neo4j.py |
Volltext-RAG über die Reden + Azure-Generator |
| Vektor-/Semantik-Suche | neo4j-graphrag VectorCypherRetriever |
scripts/vector_search.py |
Embeddings (Azure text-embedding-3-large, 3072d) → nativer Neo4j-Vektor-Index → Bedeutungsähnlichkeit |
Damit stehen drei komplementäre Retrieval-Wege über denselben Graphen: strukturiert
(Text2Cypher), stichwortbasiert (Haystack-Volltext) und semantisch (Vektor). Das Embedding-Modell
gab es im Azure-Resource noch nicht — es wurde per az cognitiveservices account deployment create
bereitgestellt (Mistral-Embed ist dort nicht im Katalog → text-embedding-3-large, multilingual).
.venv/bin/python scripts/graphrag_compare.py # Mistral-Large-3 vs. Kimi-K2.6, Cypher + Antwort
.venv/bin/python scripts/gds_analysis.py # zentrale Sprecher, Communities
.venv/bin/python scripts/haystack_neo4j.py "Was wurde zur Energie gesagt?"
.venv/bin/python scripts/vector_search.py "Was wurde zur Rente gesagt?" # semantisch (Embeddings)Beobachtung Modellvergleich: Beide erzeugen valides Cypher; Mistral-Large-3 ist schneller
(~2–4 s), Kimi-K2.6 ist ein Reasoning-Modell (langsamer, braucht mehr max_tokens) und war
bei der Synthese komplexer Ranglisten teils präziser. Beleg/Details: docs/neo4j-echtsitzungen.md.
tests/ enthält eine End-to-End-Suite mit ~250 realen Testfällen (pytest) gegen die echten,
in Neo4j geladenen Sitzungen — positiv (System tut das Richtige) und negativ (ungültige
Eingaben werden abgewehrt, nicht vorhandene Daten liefern leer). Sie prüft u. a. die
landed-Spalte der Mapping-Tabelle.
.venv/bin/pip install -r requirements-test.txt
NEO4J_URI=bolt://localhost:7688 .venv/bin/python -m pytest tests/ -q # → 251 passed| Gruppe | Beispiele | ~Fälle |
|---|---|---|
| ✅ positiv — Quellen gelandet | jeder Knotentyp vorhanden; je Sitzung Reden/Saalreaktionen/TOPs = Parser-Zahl | ~70 |
| ✅ positiv — Provenienz & Personen | 50 echte Redner:innen als Person-Knoten; 30× „jeder Redebeitrag ist BELEGT_DURCH" |
~80 |
| ✅ positiv — Embeddings/Index | 449 Vektoren, Dimension 3072, Vektor-Index ONLINE |
~20 |
| ❌ negativ — Sicherheit | _SAFE weist 25 unsichere Label/Injection-Strings ab; Namespacing kollisionsfrei |
~35 |
| ❌ negativ — Invarianten | 0 Faktencheck/Quelle/GEPRUEFT_ALS über reale Personen |
~6 |
| ❌ negativ — Robustheit/Absenz | malformed XML → sauberer ParseError; 25 nicht vorhandene Namen/Begriffe → leer |
~35 |
graph-protokoll/
├── run_demo.py # Audio-Demo (Gemeinderat + Bundestag)
├── ingest_bundestag.py # amtliches Plenarprotokoll-XML → Graph → Neo4j
├── pipeline/
│ ├── asr.py / diarize.py / align.py # Audio → sprecher-attribuierte Utterances
│ ├── bundestag_xml.py # Parser für amtliches dbtplenarprotokoll-XML (+ Saalreaktionen)
│ ├── extract.py # Regel-/LLM-Extraktion (TOPs, Beschlüsse, Aussagen, Fragen)
│ ├── factcheck.py # Aussagen → Verdikt + Quelle (immer)
│ ├── graph_build.py # Protokoll → 5-Schichten-Graph (SPARK-Format)
│ ├── export.py # JSON / CSV / Neo4j-Cypher
│ ├── neo4j_loader.py # idempotenter, parametrisierter Import nach Neo4j
│ ├── neo4j_graphrag.py # Neo4j-GraphRAG (Text2Cypher) — NL-Fragen über den Graphen
│ ├── dashboard.py # Sitzungs-Kennzahlen (Themen, Sprachanteil, Feedback)
│ └── graphrag.py # Offline-Demo-Router (ohne Neo4j/LLM)
├── docker-compose.neo4j.yml # lokales Neo4j 5 für Import + GraphRAG
├── scripts/fetch-session.sh # offizielle Sitzungsquellen ziehen (Open Data/DIP/YouTube)
├── data/sample/ # fiktive, diarisierte Mitschnitte
├── data/evidence/ # fiktive Evidenzbasis für den Faktencheck
├── web/ # statische GitHub-Pages-App (+ web/data/*.json)
├── docs/ # Bundestag-Protokollierung & Faktencheck-Analyse
├── scripts/publish-to-github.sh
├── .github/workflows/pages.yml
├── output/ # generierte Graph-Artefakte
├── requirements.txt # nur Produktivpfad (Demo braucht keine Deps)
├── publiccode.yml # Public-Money-Public-Code-Metadaten (EUPL-1.2)
└── LICENSE # EUPL-1.2 (Volltext via publish-Skript)
data/sample/— frei erfunden: Gemeinde Musterbach, fiktive Abgeordnete/Fraktionen, fiktive Statistiken/Quellen. Keine realen Personen, Organisationen, Sitzungen oder Zitate. Der Faktencheck läuft nur hier (gegen den fiktivendata/evidence/evidenz.json) und demonstriert den Mechanismus, nicht reale Politik.data/real/— echt: amtliches Plenarprotokoll WP20/214 (gemeinfrei, § 5 UrhG). Daraus entsteht das Szenario „Bundestag (echt)" mit Graph, Saalreaktionen, Dashboard und Vorlesefassung — ohne Faktencheck-Verdikte über reale Personen (--no-factcheck). Einordnung & weitere Echtdaten-Wege (YouTube-Untertitel, eigene Sitzungen):docs/spark-und-echtdaten.md.
Dieses Repo ist als Claude-Code-natives Agentic-AI-Projekt aufgesetzt (Agentic-Init-Prompt). Bootstrap + Selbstprüfung idempotent:
bash scripts/agentic-init.sh # fehlende Tools installieren + Stack prüfen
bash scripts/agentic-init.sh --check # nur prüfen, nichts installieren| Baustein | Inhalt |
|---|---|
CLAUDE.md |
schlank + cachebar; Details on-demand via @.claude/rules/*-Imports |
.claude/settings.json + hooks/guard.sh |
Permissions (allow/ask/deny) + PreToolUse-Guard: blockt root/home-Wipes·Force-Push, fragt bei rm -rf·reset --hard·Infra-Teardown·Secret-Reads |
.claude/rules/ (3) |
code-style · testing · api-conventions (importierte Konventionen) |
.claude/commands/ (4) |
/review · /fix-issue · /deploy-check · /plan |
.claude/agents/ (6) |
architect · implementer · reviewer · tester · security-test · compliance-reviewer (Modell + Effort je Rolle) |
.claude/skills/ (5) |
audio-transcription · bundestag-ingestion · fact-checking · graphrag-queries · knowledge-graph |
docs/adr/ (17) |
ADRs (Nygard, immutabel) + Index; Strukturentscheidungen verlinken docs/diagrams/ (Mermaid) |
docs/planning/ |
future → current → done, gespiegelt aus docs/challenge-plan.md |
docs/knowledge/ (31) |
OKF-v0.1-Wissensbasis — ein Konzept pro Datei (datamodels · services · apis · runbooks · decisions), progressive Disclosure |
Token-Disziplin (lokal, nur Dev — nie in CI). RTK (Rust Token Killer, Input-Seite,
PreToolUse-Hook) komprimiert Tool-Ausgaben vor dem Kontext — in dieser Session gemessen
88,6 % Ersparnis (≈ 589,6 k Tokens über 373 Befehle, rtk gain). caveman (Output-Seite,
Claude-Code-Skill) strippt Prosa (~65 %; Code/Commands bleiben exakt). Beide stapeln mit dem
Opus-4.8-Prompt-Caching auf der stabilen CLAUDE.md. Entscheidungen zum Design-System:
ADR-0016.
EUPL-1.2 — wie die BMDS-Referenzlösung SPARK Workflow, im Sinne von
„Public Money – Public Code". Die LICENSE enthält die offizielle EUPL-Notice;
den autoritativen Volltext zieht scripts/publish-to-github.sh beim
Veröffentlichen (oder manuell von
https://joinup.ec.europa.eu/collection/eupl/eupl-text-eupl-12).


