{"config":{"lang":["de"],"separator":"[\\s\\-]+","pipeline":["stopWordFilter"],"fields":{"title":{"boost":1000.0},"text":{"boost":1.0},"tags":{"boost":1000000.0}}},"docs":[{"location":"","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Dokumentation","text":"
Diese Dokumentation folgt dem Di\u00e1taxis-Framework und ist nach Funktion (nicht nach Themen) organisiert. Drift-immun durch klare Trennung: Reference wird aus dem Code generiert, ADRs sind immutable, Tutorials/How-to sind manuell gepflegt aber knapp.
"},{"location":"#struktur","title":"Struktur","text":"docs/\n\u251c\u2500\u2500 README.md \u2190 du bist hier\n\u251c\u2500\u2500 adr/ Architecture Decision Records (immutable)\n\u2502 \u251c\u2500\u2500 README.md ADR-Index + Workflow\n\u2502 \u251c\u2500\u2500 template.md Template f\u00fcr neue ADRs\n\u2502 \u2514\u2500\u2500 NNNN-titel.md Eine Datei pro Entscheidung\n\u2514\u2500\u2500 archive/ Historische Snapshots, nicht autoritativ\n Geplant f\u00fcr sp\u00e4ter (siehe Issue #62):
docs/\n\u251c\u2500\u2500 reference/ \u2190 mkdocs autodoc-Output (nicht eingecheckt)\n\u251c\u2500\u2500 tutorials/ \u2190 Erst-Schritt-Anleitungen\n\u251c\u2500\u2500 how-to/ \u2190 Aufgaben-orientiert, \"wie deploye ich\"\n\u2514\u2500\u2500 explanation/ \u2190 Hintergr\u00fcnde, Konzepte\n"},{"location":"#nicht-hier-dokumentiert-bewusst","title":"Nicht hier dokumentiert (bewusst)","text":"Was Wo API-Reference (Endpoints, Models, Schemas) Auto-generiert aus FastAPI/Pydantic, nicht in docs/ einchecken Code-Reference (Klassen, Funktionen) mkdocstrings aus den Docstrings (geplant Phase 2) Aktueller Projekt-Stand / Issues Gitea Issues \u2014 repo.toppyr.de/tobias/gwoe-antragspruefer/issues Onboarding f\u00fcr KI-assisted Coder CLAUDE.md im Repo-Root Live-System-Status https://gwoe.toppyr.de/auswertungen (dynamisches Dashboard) Memory der KI-Sessions ~/.claude/projects/<projekt>/memory/ (privat)"},{"location":"#gegen-drift","title":"Gegen Drift","text":"docs/archive/ enth\u00e4lt historische Status-Files. Werden gelesen aber nicht aktualisiert. Wenn etwas davon noch wahr ist, geh\u00f6rt es in einen ADR oder in die generierte Reference, nicht in einen neuen Status-Snapshot.ADRs dokumentieren signifikante Architektur-Entscheidungen mit Kontext, Optionen und Konsequenzen. Format inspiriert von Michael Nygard.
"},{"location":"adr/#workflow","title":"Workflow","text":"template.md mit n\u00e4chster freier Nummer (NNNN-kebap-titel.md).proposed \u2192 diskutiert in Issue/PR \u2192 bei Akzeptanz auf accepted.accepted. Wenn eine Entscheidung sich \u00e4ndert, neuer ADR mit Supersedes: NNNN-\u2026 im Header und der alte ADR bekommt Superseded by: MMMM-\u2026.deprecated f\u00fcr Entscheidungen, die ohne Nachfolger auslaufen.Faustregel: Wenn ein neuer Kollege (oder eine neue Session) die Entscheidung sonst r\u00fcckg\u00e4ngig machen w\u00fcrde, geh\u00f6rt sie in einen ADR.
"},{"location":"adr/0001-llm-citation-binding/","title":"0001 \u2014 LLM-Citations server-seitig binden statt prompt-seitig","text":"Status accepted Datum 2026-04-10 Refs Issue #60, Commits eb045d0/ed64399/db3ada9/6ced7ae, Sub-D Tests"},{"location":"adr/0001-llm-citation-binding/#kontext","title":"Kontext","text":"Der GW\u00d6-Antragspr\u00fcfer l\u00e4sst ein LLM (Qwen Plus via DashScope) parlamentarische Antr\u00e4ge bewerten. Pro Antrag werden via Embedding-Retrieval relevante Chunks aus den Wahl- und Grundsatzprogrammen der involvierten Parteien zugespielt. Das LLM gibt im JSON-Output wahlprogramm_scores[*].wahlprogramm.zitate[*] mit jeweils text, quelle (Source-Label, z.B. \"GR\u00dcNE NRW Wahlprogramm 2022, S. 58\") und url zur\u00fcck.
Beim ersten Live-Lauf des Sub-D Citation-Property-Tests (siehe ADR 0003) gegen die Prod-DB wurden drei Halluzinations-Cases gefunden: das LLM hatte Snippets erfunden und unter realen Source-Labels zitiert. Issue #60 wurde ge\u00f6ffnet.
"},{"location":"adr/0001-llm-citation-binding/#optionen","title":"Optionen","text":"Drei strukturell unterschiedliche Wege, das LLM zu binden:
"},{"location":"adr/0001-llm-citation-binding/#option-a-scharferer-prompt","title":"Option A \u2014 Sch\u00e4rferer Prompt","text":"Die ZITATEREGEL im User-Prompt wird versch\u00e4rft. Konkret:
[Q1], [Q2], \u2026 getaggt.[Qn]-Tag an einen Chunk zu binden, den text w\u00f6rtlich aus diesem Chunk zu kopieren und das Source-Label exakt zu \u00fcbernehmen.Vorteile: prompt-only, kleiner Diff, keine Server-Pipeline-\u00c4nderung.
Nachteile: ENUM-Anker im Prompt ist ein weicher Hint. Das LLM darf zwar den Snippet aus Chunk Qn nehmen, aber es gibt nichts, was es daran hindert, die Seite aus Chunk Qm in quelle zu schreiben. Cross-Mix bleibt m\u00f6glich.
Nach dem LLM-Call werden die emittierten Zitate Server-seitig nachgearbeitet:
text per Substring oder 5-Wort-Anker gegen alle retrievten Chunks gematcht.quelle und url werden aus dem matchenden Chunk konstruiert und der LLM-Output f\u00fcr diese Felder verworfen.Vorteile: strukturell \u2014 der LLM hat keine M\u00f6glichkeit mehr, eine falsche Quelle anzugeben. Die einzigen zwei Outcomes sind \"korrekt zitiert\" oder \"verworfen\".
Nachteile: zus\u00e4tzlicher Server-seitiger Schritt nach json.loads und vor Pydantic-Validation. Match-Logik muss konsistent mit Sub-D bleiben.
quote_id-Feld","text":"Statt quelle direkt zu emittieren, soll das LLM ein neues Feld quote_id (z.B. \"Q3\") liefern, aus dem der Server quelle/url rekonstruiert.
Vorteile: explizit schema-modelliert, keine Heuristik.
Nachteile: invasiv (Pydantic-Modell, JSON-Schema, Frontend, DB-Migration); verl\u00e4sst sich darauf, dass das LLM die ID korrekt setzt \u2014 wenn es l\u00fcgt, ist die Bindung futsch.
"},{"location":"adr/0001-llm-citation-binding/#entscheidung","title":"Entscheidung","text":"A + B kombiniert. Option A liefert die Vorab-H\u00e4rtung im Prompt \u2014 sie f\u00e4ngt den Gro\u00dfteil der Cases (etwa 80%). Option B ist die strukturelle Backstop, die die restlichen 20% (echter Snippet aus Qn, falsche Seite aus Qm) verl\u00e4sslich abdeckt. Option C wurde verworfen, weil der Schema- Eingriff den Aufwand nicht rechtfertigt, solange B die LLM-Eingabe ohnehin ignoriert.
Konkret in dieser Reihenfolge implementiert:
db3ada9 \u2014 Option A: ENUM-Anker [Q1]/[Q2]/\u2026 im Prompt + Top-K 2\u21925. Live-Verifikation der drei Original-Cases aus #60: 13/13 ok.6ced7ae \u2014 Option B: embeddings.reconstruct_zitate(data, semantic_quotes) nach json.loads und vor Pydantic-Validation. Helpers find_chunk_for_text und _normalize_for_match mit identischer Logik wie Sub-D. Bei Match wird _chunk_source_label und _chunk_pdf_url angewendet, bei No-Match wird das Zitat gedroppt.reconstruct_zitate kann ein im DB gespeichertes Zitat nur noch eines von zwei Dingen sein: korrekt zitiert aus einem real retrievten Chunk, oder gar nicht da.quelle/url. K\u00fcnftige Modell- Wechsel oder Prompt-Drift k\u00f6nnen die Quellen-Korrektheit nicht mehr brechen.wahlprogramm_scores[*].zitate-Counts pro Assessment.embeddings.py hat jetzt mehr Verantwortung als nur Embedding-Retrieval \u2014 auch Match-Helpers und Source-Label-Konstruktion. Akzeptabel solange das Modul thematisch geschlossen bleibt; bei weiterem Wachstum als citations.py extrahieren.Der GW\u00d6-Antragspr\u00fcfer soll Antr\u00e4ge aus 16 Bundesl\u00e4ndern + dem Bundestag analysieren k\u00f6nnen. Jedes Parlament hat ein eigenes Doku-System mit unterschiedlichen Endpoints, Schemas, Auth-Anforderungen und Quirks:
Familie Mitglieder Charakter portala/eUI LSA, BE, BB, RP JSON-Tree-Search mitparsed-Strings, HTML-Hits in efxRecordRepeater StarWeb (eUI v2) HE browse.tt.json 2-step + Perl-Dump-Comments PARiS (Java-Servlet) HB \u00e4lterer StarWeb-Vorg\u00e4nger, eigenes Hit-Format ParlDok 8.x MV, HH, TH SPA mit Fulltext/Search + Resultpage Pagination StarFinder-CGI SH lissh.lvn.parlanet.de/cgi-bin/starfinder/0 OPAL native NRW \u00e4lteres Eigensystem PARLIS (eUI-Variante) BW polling, JSON-in-HTML-Comments Eigensysteme BY (TYPO3-Solr), SL (Umbraco/.NET), SN (ASP.NET-Webforms), BUND (DIP-API) jeweils komplett unique"},{"location":"adr/0002-adapter-architecture/#optionen","title":"Optionen","text":""},{"location":"adr/0002-adapter-architecture/#option-a-eine-mega-klasse-mit-strategy-pattern","title":"Option A \u2014 Eine Mega-Klasse mit Strategy-Pattern","text":"Eine ParlamentAdapter-Klasse mit Strategy-Object pro Doku-System, gew\u00e4hlt \u00fcber Konfiguration.
Vorteile: zentral, ein Eintrittspunkt f\u00fcr gemeinsame Logik wie Fraktion-Normalisierung.
Nachteile: kompliziert sich schnell, wenn jede Strategy ihre eigenen Quirks hat (z.B. BB hat Datum-vor-Drucksachennummer im h6, BE hat 730d-Window-Hack, HE hat Perl-Hex-Decoder). Die Trennung w\u00e4re \"Strategy mit Subclasses\" \u2014 und damit sind wir bei B.
"},{"location":"adr/0002-adapter-architecture/#option-b-abstract-base-eine-konkrete-klasse-pro-familie","title":"Option B \u2014 Abstract Base + Eine konkrete Klasse pro Familie","text":"ParlamentAdapter(ABC) mit search/get_document/download_text-Methoden. Pro Doku-System eine konkrete Klasse: PortalaAdapter, ParLDokAdapter, StarFinderCGIAdapter, StarWebHEAdapter, PARiSHBAdapter, PARLISAdapter, BayernAdapter, SaarlandAdapter, BundestagAdapter, SNEdasXmlAdapter, NRWAdapter.
Wo m\u00f6glich, wird die Klasse via Konstruktor-Parameter parametrisiert (PortalaAdapter nimmt 6 BLs auf, ParLDokAdapter 3) statt jede Wiederverwendung als Subklasse.
Registriert in einem ADAPTERS: dict[str, ParlamentAdapter] am Datei-Ende, indexiert per BL-Code (\"NRW\", \"BUND\", \u2026).
Vorteile: - Klare Verantwortlichkeit: ein Test-Lauf gegen einen LT trifft genau eine Klasse. - Reverse-Engineering-Findings k\u00f6nnen als Class-Docstring dokumentiert werden, wo sie hingeh\u00f6ren. - Parametrisierung erlaubt Wiederverwendung ohne Vererbungs-Hierarchie. - get_adapter(bundesland) ist O(1).
Nachteile: - Cross-Cutting-Concerns (Fraktion-Normalisierung) m\u00fcssen separat gel\u00f6st werden \u2014 siehe ADR-Folge zur parteien-Zentralisierung in #55.
Ein Python-File pro Adapter (z.B. app/adapters/lsa.py).
Vorteile: maximale Datei-Granularit\u00e4t.
Nachteile: Import-Overhead, der Registry-Eintrag muss in einer dritten Datei liegen, kein nennenswerter Vorteil gegen\u00fcber B solange die Adapter-Klassen unter ~500 Zeilen bleiben (PortalaAdapter ist mit ~520 Zeilen der gr\u00f6\u00dfte).
"},{"location":"adr/0002-adapter-architecture/#entscheidung","title":"Entscheidung","text":"Option B. Eine parlamente.py mit einer Klasse pro Doku-System, parametrisierbar wo sinnvoll, registriert in ADAPTERS. Cross-Cutting-Logik wie Fraktion-Normalisierung wandert in separate Module (parteien.py, bundeslaender.py) und wird per Import oder Funktions-Argument injiziert.
parlamente.py. Jeder Adapter kommt mit seinem Reverse-Engineering-Kommentar als Docstring, was bei Re-Reads nach Wochen die Wiedereinarbeit auf Minuten bringt.ADAPTERS-Registry: ein einzelner Test-Helper iteriert alle aktiven Adapter und pr\u00fcft die Akzeptanz- Kriterien (search liefert \u22653 Hits, get_document funktioniert, download_text extrahiert Text).ADAPTERS rein, ohne Ber\u00fchrung anderer Adapter.parlamente.py ist gro\u00df (~3000 Zeilen, 17 Klassen). Code-Navigation via Grep statt Auto-Imports.TEMP/ und referenziert ihn im Class-Docstring.embeddings.py:528 wurde durch ein anderes Modul verbreitet).analyze_antrag.adapters/ als Folder) w\u00fcrde den ADR superseden, wenn parlamente.py \u00fcber ~5000 Zeilen w\u00e4chst.Der LLM-Output enth\u00e4lt pro Assessment N Zitate, jedes mit text, quelle (z.B. \"GR\u00dcNE NRW Wahlprogramm 2022, S. 58\") und url. Wahrscheinlich korrekt \u2014 aber wie verifizieren wir das, ohne jedes einzeln h\u00e4ndisch nachzuschlagen?
Die naheliegenden Test-Optionen sind alle unbefriedigend:
Pydantic validiert dass jedes Zitat die Felder text, quelle, url hat und url mit /static/referenzen/ beginnt. Erkennt syntaktische Korruption, aber keine Halluzinationen.
Pro Zitat in der Prod-DB: 1. quelle per Token-Coverage-Match auf den PROGRAMME-Eintrag mappen. 2. Seitennummer aus quelle extrahieren. 3. Per fitz die PDF-Seite lesen, Whitespace + Soft-Hyphen normalisieren. 4. text muss als Substring (oder 5-Wort-Anker) in der Seite vorkommen. 5. Bug-Klasse 17 (Cross-Bundesland-Zitat): das aufgel\u00f6ste Programm muss zum Bundesland des Antrags passen, oder ein Grundsatzprogramm sein.
Vorteile: pr\u00fcft die einzige Eigenschaft die wirklich z\u00e4hlt \u2014 \"war das was zitiert wird auch wirklich da\". Findet Halluzinationen direkt.
Nachteile: braucht eine lokale Kopie der gwoe-antraege.db und der Wahlprogramm-PDFs. Test ist Pydantic-Schema-\u00fcbergreifend (Integration, nicht Unit). Skipped sauber wenn DB nicht gemounted ist.
Im analyze_antrag-Flow direkt nach LLM-Call jedes Zitat verifizieren und bei Failure abbrechen oder retry.
Vorteile: kein \"stale data in DB\"-Risiko.
Nachteile: f\u00fcgt Latenz und Komplexit\u00e4t in den Hot-Path. Die Verifikation ist O(N\u00d7M), wo N=Zitate und M=Wahlprogramm-Pages.
"},{"location":"adr/0003-citation-property-tests/#entscheidung","title":"Entscheidung","text":"Option B als pytest-Integration-Test \u2014 tests/integration/test_citations_substring.py, parametrisiert per _load_recent_assessments(limit_per_bl=5) \u00d7 _flat_zitate().
Strict substring als Default-Match (Whitespace + Soft-Hyphen normalisiert, LLM-Truncation-Marker ... toleriert), 5-Wort-Anker als Fallback f\u00fcr geringf\u00fcgige Wort-Drift wie \"LLM hat mittendrin gek\u00fcrzt\". Min-Length-Guard von 20 Zeichen verhindert false-positive Matches auf trivialen Snippets.
Marker pytestmark = pytest.mark.integration \u2014 der Test l\u00e4uft nicht in der Default-Suite, sondern explizit per pytest -m integration. Skipped wenn webapp/data/gwoe-antraege.db nicht existiert (Dev-Setup ohne DB-Kopie).
Match-Helpers (_normalize, _is_substring, _resolve_quelle_to_programm_id, _extract_page_number) sind eigene Unit-Tests in TestHelpers \u2014 die Match- Logik selbst ist nicht-trivial und braucht ihre Eigenkontrolle.
pytest -m integration gegen die DB \u2192 0 Failures = OK.reconstruct_zitate) nutzt identische Match-Heuristiken (find_chunk_for_text, _normalize_for_match). Damit kann der Test nichts fangen, was die Production nicht auch fangen w\u00fcrde, und umgekehrt \u2014 kein Test-/Prod-Drift.data/gwoe-antraege.db vom Container gepullt werden. CI-Integration steht aus.docker-compose.yml, Issue #5, project_sn_xml_export"},{"location":"adr/0004-deployment-workflow/#kontext","title":"Kontext","text":"Der GW\u00d6-Antragspr\u00fcfer l\u00e4uft als Docker-Container gwoe-antragspruefer auf einem VServer hinter Traefik (Let's Encrypt SSL, Domain gwoe.toppyr.de). Code wird via Git aus repo.toppyr.de gezogen.
Drei Subsysteme haben unterschiedliche Lebenszyklen:
app/) \u2014 wird bei jedem Deploy neu kopiert.data/gwoe-antraege.db, data/embeddings.db) \u2014 Source of Truth, MUSS persistent \u00fcber Deploys hinweg.reports/*.pdf) \u2014 sind generierte Artefakte, k\u00f6nnten theoretisch regeneriert werden, sind aber teuer (LLM-Calls + WeasyPrint), also auch persistent.Issue #5 hatte einen fr\u00fchen Schmerzpunkt: der Container-Build hat die DB \u00fcberschrieben, weil data/ nicht aus dem Build-Context exkludiert war.
Code, DB, Reports zusammen im Image. Backups via docker cp vor jedem Deploy.
Nachteile: Image wird gigantisch, jeder Deploy ist ein Risk-Event, kein automatischer Persistenz-Mechanismus.
"},{"location":"adr/0004-deployment-workflow/#option-b-docker-volumes-fur-db-und-reports","title":"Option B \u2014 Docker-Volumes f\u00fcr DB und Reports","text":"docker-compose.yml mountet ./data und ./reports vom Host als Volumes. Build kopiert nur app/ und requirements.txt.
Vorteile: Host-Volumes \u00fcberleben Container-Restart und Image-Rebuild. Backups via Standard-Linux-Tools auf dem Host.
Nachteile: Build-Context muss data/, reports/, .env exkludieren (siehe .dockerignore), sonst werden sie versehentlich ins Image kopiert und \u00fcberschreiben das gemountete Volume bei Container-Start.
Postgres als separater Container, gwoe-antragspruefer verbindet per asyncpg.
Vorteile: standard, robust, Backups via pg_dump.
Nachteile: Migration aller existierenden Queries, neue Abh\u00e4ngigkeit, mehr Operational Surface. SQLite reicht f\u00fcr die aktuelle Last (~30 Antr\u00e4ge, selten parallele Writes).
"},{"location":"adr/0004-deployment-workflow/#entscheidung","title":"Entscheidung","text":"Option B. Docker-Compose mit Host-Volumes f\u00fcr data/ und reports/. .dockerignore exkludiert beide aus dem Build-Context.
ssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n"},{"location":"adr/0004-deployment-workflow/#manueller-tar-upload-falls-git-workflow-blockiert-ist","title":"Manueller Tar-Upload (falls Git-Workflow blockiert ist)","text":"cd webapp\ntar czf /tmp/gwoe-webapp.tar.gz \\\n --exclude='venv' --exclude='__pycache__' \\\n --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n Beachte: --exclude='data' ist zwingend, sonst \u00fcberschreibt der Tar die Live-DB.
Sachsen hat keinen scrape-baren Endpoint und liest stattdessen w\u00f6chentlich manuell exportierte XML-Dumps aus EDAS. Workflow:
cp dokumente_export.xml gwoe-antragspruefer:/app/data/sn-edas/scp aus dem Container ins Host-Volume \u2014 kein Container-Restart n\u00f6tig.Details in ~/.claude/projects/<projekt>/memory/project_sn_xml_export.md.
Der Container l\u00e4uft UTC, nicht CEST. DB-Timestamps in assessments.created_at sind UTC (kein TZ-Suffix, aber UTC). Beim Korrelieren mit Commit-Zeiten (lokal CEST = UTC+2) muss konvertiert werden, sonst flie\u00dfen falsche Schlussfolgerungen ein. Detailliert in ~/.claude/projects/<projekt>/memory/reference_container_utc.md.
.dockerignore-Update).tar czf gwoe-data-$(date +%F).tar.gz data/ auf dem Host..dockerignore ist ein Foot-Gun \u2014 wenn jemand vergisst, data/ neu hinzuzuf\u00fcgen nach einem Refactor, kann es passieren dass der Build die Live-DB \u00fcberschreibt. Mitigation: ein dedicated Sub-D-style Test, der nach dem Build pr\u00fcft, dass die DB die erwarteten Tabellen hat \u2014 steht noch aus.Welches Problem hat zu der Entscheidung gef\u00fchrt? Was ist der Stand vorher, welche Constraints sind im Spiel, welche Stakeholder sind betroffen?
"},{"location":"adr/template/#optionen","title":"Optionen","text":"Welche Alternativen wurden ernsthaft erwogen? Mindestens 2, gerne mehr.
"},{"location":"adr/template/#option-a","title":"Option A \u2014 \u2026","text":"Beschreibung. Vor- und Nachteile.
"},{"location":"adr/template/#option-b","title":"Option B \u2014 \u2026","text":"Beschreibung. Vor- und Nachteile.
"},{"location":"adr/template/#entscheidung","title":"Entscheidung","text":"Welche Option wurde gew\u00e4hlt und warum? Konkret und unmissverst\u00e4ndlich, sodass ein neuer Kollege ohne Diskussion weitermachen kann.
"},{"location":"adr/template/#konsequenzen","title":"Konsequenzen","text":"Was wird leichter / schwerer durch diese Entscheidung? Welche Folge-Arbeiten fallen an? Welche Teile des Systems werden ber\u00fchrt?
"},{"location":"adr/template/#positiv","title":"Positiv","text":"Hier liegen historische Doku-Snapshots, die nicht mehr autoritativ sind. Sie beschreiben jeweils einen Zustand zu einem bestimmten Zeitpunkt; vieles davon ist heute \u00fcberholt.
"},{"location":"archive/#warum-nicht-loschen","title":"Warum nicht l\u00f6schen?","text":"../adr/) \u00fcbernommen, andere bewusst nicht \u2014 weil sie tagesaktueller Stand waren und nie ADR-w\u00fcrdig.DOKUMENTATION-2026-03-24.md 2026-03-24 Fr\u00fche Skript-basierte Architektur, vor dem Webapp-Migrate. Beschreibt scripts/process_single.sh-Workflow, der heute durch FastAPI-Backend ersetzt ist. STATUS-2026-03-28.md 2026-03-28 Tagesstand-Snapshot. Beschreibt v5-Prompt, Persistenz-Volume, Security-Headers \u2014 alle drei sind heute Standard. README-2026-03-28.md 2026-03-28 Erste Repo-README. Listet nur den NRW-Adapter (vor #2/#3/#4 und 13 weiteren BLs). Architektur-Diagramm ist veraltet."},{"location":"archive/#wenn-etwas-davon-noch-wahr-ist","title":"Wenn etwas davon noch wahr ist\u2026","text":"\u2026geh\u00f6rt es in einen ADR (../adr/) oder die generierte Reference (geplant f\u00fcr #62 Phase 2). Bitte nicht in einen neuen Status-Snapshot ziehen. Status-Snapshots sind die Drift-Falle, die wir hier vermeiden wollen.
Stand: 24. M\u00e4rz 2026, 22:15
"},{"location":"archive/DOKUMENTATION-2026-03-24/#0-datenbank","title":"0. Datenbank","text":"Alle Bewertungen werden in einer SQLite-Datenbank gespeichert: gwoe-antraege.db
# DB initialisieren (einmalig)\nsqlite3 gwoe-antraege.db < scripts/init-db.sql\n\n# \u00dcbersicht aller Antr\u00e4ge\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_antraege_uebersicht;\"\n\n# Statistik nach Fraktion\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_statistik_fraktionen;\"\n\n# Themen-Ranking\nsqlite3 -header -column gwoe-antraege.db \"SELECT * FROM v_themen_ranking;\"\n Schema: siehe scripts/init-db.sql und prompt-gwoe-antragspruefer-v4.md
# Einzelner Antrag\n./scripts/process_single.sh 18-18102\n\n# Batch aus Datei\n./scripts/gwoe-batch.sh --batch neue-antraege.txt\n\n# Alle neu rendern\n./scripts/gwoe-batch.sh --render-all\n"},{"location":"archive/DOKUMENTATION-2026-03-24/#manuell-schritt-fur-schritt","title":"Manuell (Schritt f\u00fcr Schritt)","text":"# 1. PDF herunterladen\ncurl -sL \"https://www.landtag.nrw.de/portal/WWW/dokumentenarchiv/Dokument/MMD18-18102.pdf\" \\\n -o antraege/18-18102.pdf\n\n# 2. Text extrahieren\npdftotext -layout antraege/18-18102.pdf antraege/18-18102.txt\n\n# 3. LLM analysieren (manuell oder via API)\n# \u2192 JSON in assessments.json einf\u00fcgen\n\n# 4. data.ts aktualisieren\ncat assessments.json > quarto-report/src/data.ts # vereinfacht\n\n# 5. Quarto rendern\ncd quarto-report && npm run generate\ncd reports && quarto render gwoe-18-18102.qmd --to pdf\n\n# 6. Mit Original mergen\npdftk quarto-report/reports/gwoe-18-18102.pdf antraege/18-18102.pdf \\\n cat output output/gwoe-bericht-18-18102.pdf\n"},{"location":"archive/DOKUMENTATION-2026-03-24/#api-aufruf-claude","title":"API-Aufruf (Claude)","text":"curl -sS https://api.anthropic.com/v1/messages \\\n -H \"Content-Type: application/json\" \\\n -H \"x-api-key: $ANTHROPIC_API_KEY\" \\\n -H \"anthropic-version: 2023-06-01\" \\\n -d '{\n \"model\": \"claude-sonnet-4-20250514\",\n \"max_tokens\": 4096,\n \"system\": \"<SYSTEM_PROMPT>\",\n \"messages\": [{\"role\": \"user\", \"content\": \"<USER_PROMPT>\"}]\n }'\n"},{"location":"archive/DOKUMENTATION-2026-03-24/#1-uberblick","title":"1. \u00dcberblick","text":"Der GW\u00d6-Antragspr\u00fcfer bewertet parlamentarische Antr\u00e4ge (aktuell: NRW Landtag) systematisch nach drei Dimensionen:
Das Tool generiert professionelle PDF-Berichte mit dem offiziellen ECOnGOOD-Branding.
"},{"location":"archive/DOKUMENTATION-2026-03-24/#2-gewahlte-matrix-v20-fur-gemeinden","title":"2. Gew\u00e4hlte Matrix: V2.0 f\u00fcr Gemeinden","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#warum-nicht-matrix-51","title":"Warum nicht Matrix 5.1?","text":"Die GW\u00d6-Matrix 5.1 ist f\u00fcr Unternehmen konzipiert mit Ber\u00fchrungsgruppen wie \"Kund:innen\", \"Eigent\u00fcmer:innen\", \"Mitarbeitende\". Diese passen nicht zu parlamentarischen Antr\u00e4gen.
"},{"location":"archive/DOKUMENTATION-2026-03-24/#warum-nicht-matrix-21a","title":"Warum nicht Matrix 2.1.A?","text":"Die Matrix 2.1.A ist f\u00fcr die \"\u00f6ffentliche Hand\" allgemein. Die Matrix 2.0 f\u00fcr Gemeinden bietet einen etwas weiteren Blick und ist kompakter formuliert \u2014 ideal f\u00fcr parlamentarische Analyse.
"},{"location":"archive/DOKUMENTATION-2026-03-24/#die-matrix-20-fur-gemeinden","title":"Die Matrix 2.0 f\u00fcr Gemeinden","text":"Aspekt Beschreibung Struktur 5\u00d75 (25 Felder) Fokus Kommunale Gebietsk\u00f6rperschaften Zielgruppe Kommunalpolitik, Landesantr\u00e4ge Staatsprinzipien ja (verfassungsrechtlich verankert)"},{"location":"archive/DOKUMENTATION-2026-03-24/#die-55-struktur","title":"Die 5\u00d75-Struktur","text":"Spalten (Werte des Gemeinwohls + Staatsprinzipien):
Nr Wert Staatsprinzip 1 Menschenw\u00fcrde Rechtsstaatsprinzip 2 Solidarit\u00e4t Gemeinnutz 3 \u00d6kologische Nachhaltigkeit Umwelt-Verantwortung 4 Soziale Gerechtigkeit Sozialstaatsprinzip 5 Transparenz & Mitbestimmung DemokratieZeilen (Ber\u00fchrungsgruppen):
Code Gruppe Beschreibung A Ausgelagerte Betriebe, Lieferant:innen, Dienstleister:innen Externe Beschaffung, Lieferketten B Finanzpartner:innen, Geldgeber:innen, Steuerzahler:innen Haushalt, Finanzpolitik C Politische F\u00fchrung, Verwaltung, Ehrenamtliche Mandatstr\u00e4ger:innen, Mitarbeitende D B\u00fcrger:innen und Wirtschaft Interne Wirkung, Daseinsvorsorge E Staat, Gesellschaft und Natur \u00dcberregionale/langfristige Wirkung"},{"location":"archive/DOKUMENTATION-2026-03-24/#relevanz-fur-landesantrage","title":"Relevanz f\u00fcr Landesantr\u00e4ge","text":"Die meisten Parlamentsantr\u00e4ge betreffen: - D-Zeile: Wirkung auf B\u00fcrger:innen und Wirtschaft im Land - E-Zeile: \u00dcberregionale oder langfristige Auswirkungen
Prinzip: D (intern) hat Vorrang vor E (extern).
"},{"location":"archive/DOKUMENTATION-2026-03-24/#3-programmtreue-bewertung","title":"3. Programmtreue-Bewertung","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#zwei-dimensionen","title":"Zwei Dimensionen","text":"Antr\u00e4ge werden sowohl gegen das Wahlprogramm als auch gegen das Grundsatzprogramm der Fraktionen gepr\u00fcft:
Partei Wahlprogramm Grundsatzprogramm CDU NRW 2022 \"In Freiheit leben\" (2024) SPD NRW 2022 Hamburger Programm (2007) GR\u00dcNE NRW 2022 \"...zu achten und zu sch\u00fctzen...\" (2020) FDP NRW 2022 \"Verantwortung f\u00fcr die Freiheit\" (2012) AfD NRW 2022 \"Programm f\u00fcr Deutschland\" (2016)"},{"location":"archive/DOKUMENTATION-2026-03-24/#bewertungsskala","title":"Bewertungsskala","text":"Score Bedeutung 9-10 Vollst\u00e4ndige \u00dcbereinstimmung 7-8 Hohe \u00dcbereinstimmung 5-6 Partielle \u00dcbereinstimmung 3-4 Geringe \u00dcbereinstimmung 1-2 Widerspricht Teilaspekten 0 Vollst\u00e4ndiger Widerspruch"},{"location":"archive/DOKUMENTATION-2026-03-24/#4-bewertungsskala-gwo","title":"4. Bewertungsskala GW\u00d6","text":"Punkte Stufe Beschreibung 7-10 Vorbildlich Innovative Ma\u00dfnahmen, weitreichende Verbesserungen 4-6 Erfahren Erkennbare Verbesserungen, gute Ergebnisse 2-3 Fortgeschritten Erste Ma\u00dfnahmen, erste Erfolge 1 Erste Schritte Erstes Engagement 0 Basislinie Nur gesetzliche Anforderungen negativ Widerspruch Aktiver Widerspruch zu GW\u00d6-Werten"},{"location":"archive/DOKUMENTATION-2026-03-24/#matrix-feldwertung","title":"Matrix-Feldwertung","text":"F\u00fcr jedes ber\u00fchrte Feld im Antrag: - ++ (+2/+3): Stark f\u00f6rdernd - + (+1): F\u00f6rdernd - \u25cb (0): Neutral - \u2212 (-1): Widersprechend - \u2212\u2212 (-2/-3): Stark widersprechend
Das Layout folgt strikt dem offiziellen ECOnGOOD Corporate Design Manual (Juni 2024).
Farbpalette (Prim\u00e4rfarben aus Logo):
Farbe Hex CMYK Pantone Verwendung Dunkelgrau#5a5a5a 0-0-0-80 425 U Flie\u00dftext, H1-\u00dcberschriften Gr\u00fcn #889e33 40-10-90-20 583 U Positive Werte, H3, Akzente Blau #009da5 100-10-40-00 320 U Hauptfarbe, H2, Links, Linien Hellgrau #bfbfbf 0-0-0-30 421 U Hintergr\u00fcnde, Fu\u00dfzeilen Typografie: - Prim\u00e4r: Avenir (light, roman, black) - Fallback: Arial - Web: Avenir - \u00dcberschriften: - H1: Avenir black, 22pt, Dunkelgrau - H2: Avenir black, 16pt, Blau oder Gr\u00fcn - H3: Avenir black, 12pt, Blau oder Gr\u00fcn - Flie\u00dftext: Avenir roman, 10pt, Dunkelgrau - Hervorhebungen: Avenir black
Logo: ECOnGOOD-Logo (econgood-logo.png) \u2014 zwei stilisierte Pusteblumen (Blau + Gr\u00fcn)
Boxen (CD-konform): - Fl\u00e4chig farbig (Gr\u00fcn oder Blau) mit wei\u00dfem Text - Oder mit farbiger Outline und farbigem Text - Keine abgerundeten Ecken (arc=0pt)
Tabellen: - Titelleiste in Markenfarbe (Blau) - Wei\u00dfer Text in Titelleiste, fett, Gro\u00dfbuchstaben - Innere horizontale Linien max. 1pt - 3mm Abstand zwischen Text und Linie
Layout: - Offenes, leichtes Design - Reinwei\u00dfer Hintergrund - Kr\u00e4ftige Farben (aktiv) - Genug Wei\u00dfraum zwischen Elementen
"},{"location":"archive/DOKUMENTATION-2026-03-24/#empfehlungs-symbole","title":"Empfehlungs-Symbole","text":"Empfehlung Symbol Farbe Hex Uneingeschr\u00e4nkt unterst\u00fctzen[++] Gr\u00fcn #889e33 Unterst\u00fctzen mit \u00c4nderungen [+] Blau #009da5 \u00dcberarbeiten [!] Orange #F7941D Ablehnen [X] Rot #d00000"},{"location":"archive/DOKUMENTATION-2026-03-24/#6-technische-umsetzung","title":"6. Technische Umsetzung","text":""},{"location":"archive/DOKUMENTATION-2026-03-24/#stack","title":"Stack","text":"quarto-report/\n\u251c\u2500\u2500 src/\n\u2502 \u251c\u2500\u2500 types.ts # Datentypen, Matrix-Labels\n\u2502 \u251c\u2500\u2500 data.ts # Testbewertungen\n\u2502 \u2514\u2500\u2500 generate-qmd.ts # Quarto-Generator\n\u251c\u2500\u2500 reports/ # Generierte .qmd + .pdf\n\u251c\u2500\u2500 econgood-logo.png # Logo (aus CD Manual)\n\u2514\u2500\u2500 package.json\n"},{"location":"archive/DOKUMENTATION-2026-03-24/#workflow","title":"Workflow","text":".qmd (Quarto Markdown)Im Ordner kontext/:
gwoe-matrix-2.0.md Matrix 2.0 f\u00fcr Gemeinden \u2014 vollst\u00e4ndige Dokumentation gwoe-matrix-2.0-gemeinden.pdf Offizielles Matrix-PDF gwoe-arbeitsbuch-gemeinden-2.0.pdf Arbeitsbuch zur Matrix parteiprogramme.md Kurzreferenz Wahlprogramme + Grundsatzprogramme cdu-grundsatzprogramm-2024.pdf CDU \"In Freiheit leben\" spd-hamburger-programm-2007.pdf SPD Hamburger Programm gruene-grundsatzprogramm-2020.pdf Gr\u00fcne Grundsatzprogramm fdp-grundsatzprogramm-2012.pdf FDP \"Verantwortung f\u00fcr die Freiheit\" afd-grundsatzprogramm-2016.pdf AfD \"Programm f\u00fcr Deutschland\" wahlprogramme-nrw-2022.md NRW Wahlprogramme 2022"},{"location":"archive/DOKUMENTATION-2026-03-24/#8-getestete-antrage","title":"8. Getestete Antr\u00e4ge","text":"Drucksache Titel GW\u00d6 Empfehlung 18/18081 Schlechte-Kita-Gesetz stoppen (SPD) 8/10 Uneingeschr\u00e4nkt unterst\u00fctzen 18/18088 Iran-Solidarit\u00e4t (alle) 9/10 Uneingeschr\u00e4nkt unterst\u00fctzen 18/18094 Versorgungssicherheit/Fracking (AfD) 1/10 Ablehnen 18/18099 Kultur-Schecks (CDU/Gr\u00fcne) 6/10 Unterst\u00fctzen mit \u00c4nderungen 18/18104 KI-Korrektur (FDP) 6/10 Unterst\u00fctzen mit \u00c4nderungen"},{"location":"archive/DOKUMENTATION-2026-03-24/#9-gendering","title":"9. Gendering","text":"Standard: : (Doppelpunkt) statt * (Sternchen)
Beispiele: - B\u00fcrger:innen - Lieferant:innen - Mitarbeiter:innen
"},{"location":"archive/DOKUMENTATION-2026-03-24/#10-quellen","title":"10. Quellen","text":": beim Gendern, ECOnGOOD-Logo 23.03.2026 v3 Umstellung auf Matrix 2.1.A (5\u00d75), GW\u00d6-Branding 23.03.2026 v2 Vollst\u00e4ndiger Kontext (Matrix + Wahlprogramme) 23.03.2026 v1 Initiale Version mit Matrix 5.1 Projektordner: ~/Nextcloud/dotty/projekte/2026-03-23 GW\u00d6-Antragspr\u00fcfer _WIP_/ Telegram: https://t.me/c/3823618505/4247
Neues Format:
{\n \"original\": \"Zitat aus Antrag\",\n \"vorschlag\": \"Verbesserter Text mit **Markierungen**\",\n \"begruendung\": \"GW\u00d6-Begr\u00fcndung\"\n}\n"},{"location":"archive/DOKUMENTATION-2026-03-24/#ui-erweiterungen","title":"UI-Erweiterungen","text":"Stand: 28.03.2026, 23:58 Uhr Telegram-Topic: \ud83c\udf31 GW\u00d6-Antragspr\u00fcfer (thread_id 4247)
"},{"location":"archive/README-2026-03-28/#projektziel","title":"\ud83c\udfaf Projektziel","text":"Automatische Bewertung von Parlamentsantr\u00e4gen nach der Gemeinwohl-\u00d6konomie (GW\u00d6) Matrix 2.0 f\u00fcr Gemeinden. Das Tool analysiert Antr\u00e4ge aus Landesparlamenten und bewertet sie nach GW\u00d6-Kriterien, vergleicht mit Wahl- und Parteiprogrammen und schl\u00e4gt konkrete Textverbesserungen vor.
"},{"location":"archive/README-2026-03-28/#live-system","title":"\ud83c\udf10 Live-System","text":"Was URL Webapp https://gwoe.toppyr.de Git-Repository https://repo.toppyr.de/tobias/gwoe-antragspruefer Server VServer 152.53.119.77 Container/opt/gwoe-antragspruefer/"},{"location":"archive/README-2026-03-28/#architektur","title":"\ud83c\udfd7\ufe0f Architektur","text":"\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 Frontend \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 Durchsuchen \u2502 \u2502 \ud83c\udff7\ufe0f Tags \u2502 \u2502 Pr\u00fcfen \u2502 \u2502\n\u2502 \u2502 (Liste) \u2502 \u2502 (Wolke) \u2502 \u2502 (Upload/Analyse) \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502\n \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 FastAPI Backend \u2502\n\u2502 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u2502\n\u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502 /api/ \u2502 \u2502\n\u2502 \u2502assessments\u2502 \u2502search- \u2502 \u2502analyze- \u2502 \u2502assessment/ \u2502 \u2502\n\u2502 \u2502 \u2502 \u2502landtag \u2502 \u2502drucksache\u2502 \u2502pdf \u2502 \u2502\n\u2502 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n \u2502 \u2502 \u2502\n \u25bc \u25bc \u25bc\n\u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510 \u250c\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510\n\u2502 SQLite \u2502 \u2502 OPAL API \u2502 \u2502 DashScope (Qwen) \u2502\n\u2502 gwoe-antraege\u2502 \u2502 (Landtag \u2502 \u2502 qwen-plus-latest \u2502\n\u2502 .db \u2502 \u2502 NRW) \u2502 \u2502 (LLM Analyse) \u2502\n\u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518 \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518\n"},{"location":"archive/README-2026-03-28/#projektstruktur","title":"\ud83d\udcc1 Projektstruktur","text":"webapp/\n\u251c\u2500\u2500 app/\n\u2502 \u251c\u2500\u2500 main.py # FastAPI-Endpoints + Security Middleware\n\u2502 \u251c\u2500\u2500 analyzer.py # LLM-Analyse mit v5-Prompt + Retry-Logik\n\u2502 \u251c\u2500\u2500 database.py # SQLite (aiosqlite) Persistenz\n\u2502 \u251c\u2500\u2500 models.py # Pydantic Assessment-Model\n\u2502 \u251c\u2500\u2500 parlamente.py # OPAL-Adapter f\u00fcr NRW Landtag\n\u2502 \u251c\u2500\u2500 report.py # PDF-Generierung (WeasyPrint)\n\u2502 \u251c\u2500\u2500 config.py # Settings + Environment\n\u2502 \u251c\u2500\u2500 embeddings.py # Wahlprogramm-Embeddings (optional)\n\u2502 \u251c\u2500\u2500 kontext/\n\u2502 \u2502 \u251c\u2500\u2500 gwoe-matrix-2.0.md # GW\u00d6-Matrix Referenz\n\u2502 \u2502 \u251c\u2500\u2500 wahlprogramme-nrw-2022.md # Zusammenfassungen\n\u2502 \u2502 \u251c\u2500\u2500 parteiprogramme.md # Grundsatzprogramme\n\u2502 \u2502 \u2514\u2500\u2500 *-nrw-2022-paged.txt # Volltext (paginiert)\n\u2502 \u251c\u2500\u2500 templates/\n\u2502 \u2502 \u251c\u2500\u2500 index.html # Haupt-UI (Jinja2)\n\u2502 \u2502 \u2514\u2500\u2500 quellen.html # Quellen\u00fcbersicht\n\u2502 \u2514\u2500\u2500 static/\n\u2502 \u2514\u2500\u2500 referenzen/ # Original-PDFs der Programme\n\u251c\u2500\u2500 data/ # SQLite-DBs (Docker Volume)\n\u2502 \u2514\u2500\u2500 gwoe-antraege.db # Haupt-Datenbank\n\u251c\u2500\u2500 reports/ # Generierte PDFs (Docker Volume)\n\u251c\u2500\u2500 docker-compose.yml # Traefik + Let's Encrypt\n\u251c\u2500\u2500 Dockerfile # Python 3.12 + WeasyPrint\n\u251c\u2500\u2500 requirements.txt\n\u251c\u2500\u2500 .env.example\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 .dockerignore\n\u251c\u2500\u2500 LICENSE (MIT)\n\u2514\u2500\u2500 README.md\n"},{"location":"archive/README-2026-03-28/#features-stand-28032026","title":"\u2728 Features (Stand 28.03.2026)","text":""},{"location":"archive/README-2026-03-28/#analyse","title":"Analyse","text":"DASHSCOPE_API_KEY=sk-... # Alibaba DashScope API\nKEYCLOAK_URL=https://sso.toppyr.de # Optional: SSO\nKEYCLOAK_REALM=collaboration\nKEYCLOAK_CLIENT_ID=gwoe-antragspruefer\n"},{"location":"archive/README-2026-03-28/#llm-modelle","title":"LLM-Modelle","text":"Modell Verwendung Kosten qwen-plus-latest Standard (Free Tier) Kostenlos qwen-plus Fallback (Paid) ~$0.80/MTok qwen-max Premium ~$2.40/MTok"},{"location":"archive/README-2026-03-28/#api-endpoints","title":"\ud83d\udcca API-Endpoints","text":"Methode Pfad Beschreibung GET / Web-UI GET /health Health Check GET /quellen Quellen\u00fcbersicht GET /api/assessments Alle Bewertungen (JSON) GET /api/assessment?drucksache=18/12345 Einzelne Bewertung GET /api/search?q=Klima Interne DB-Suche GET /api/search-landtag?q=Klima Landtag OPAL-Suche POST /api/analyze-drucksache Neue Analyse starten GET /api/assessment/pdf?drucksache=18/12345 PDF-Download GET /api/bundeslaender Verf\u00fcgbare Bundesl\u00e4nder GET /status/{job_id} Job-Status abfragen"},{"location":"archive/README-2026-03-28/#prompt-v5-kern-features","title":"\ud83e\udde0 Prompt v5 \u2014 Kern-Features","text":"Der Analyse-Prompt fordert:
Kurzbegr\u00fcndung
Programmtreue f\u00fcr Antragsteller UND Regierungsfraktionen:
Begr\u00fcndungen
Verbesserungsvorschl\u00e4ge (max. 3):
{\n \"original\": \"Zitat aus dem Antrag\",\n \"vorschlag\": \"Konkret **verbesserter** Text\",\n \"begruendung\": \"St\u00e4rkt GW\u00d6-Wert X durch Y\"\n}\n Themen-Tags f\u00fcr Kategorisierung
ssh vserver\ncd /opt\ngit clone https://repo.toppyr.de/tobias/gwoe-antragspruefer.git\ncd gwoe-antragspruefer\n\n# .env erstellen\ncp .env.example .env\nnano .env # DASHSCOPE_API_KEY eintragen\n\n# Starten\ndocker compose up -d\n"},{"location":"archive/README-2026-03-28/#update","title":"Update","text":"# Lokal\ncd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ngit add . && git commit -m \"Update\" && git push\n\n# Auf Server\nssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n"},{"location":"archive/README-2026-03-28/#manuelles-deploy-ohne-git-auf-server","title":"Manuelles Deploy (ohne Git auf Server)","text":"cd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ntar czf /tmp/gwoe-webapp.tar.gz --exclude='venv' --exclude='__pycache__' --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n"},{"location":"archive/README-2026-03-28/#statistiken-28032026","title":"\ud83d\udcc8 Statistiken (28.03.2026)","text":"Metrik Wert Analysierte Antr\u00e4ge 20 \u00d8 GW\u00d6-Score gesamt 4.6 \u00d8 SPD 7.7 \u00d8 GR\u00dcNE 6.0 \u00d8 CDU 6.0 \u00d8 FDP 4.8 \u00d8 AfD 1.5"},{"location":"archive/README-2026-03-28/#bekannte-issues-todos","title":"\ud83d\udc1b Bekannte Issues / TODOs","text":""},{"location":"archive/README-2026-03-28/#offen","title":"Offen","text":"Entwickelt von Tobias R\u00f6del mit Unterst\u00fctzung von Dotty \ud83d\udc7b
"},{"location":"archive/STATUS-2026-03-28/","title":"GW\u00d6-Antragspr\u00fcfer \u2014 Status 28.03.2026 (Final)","text":""},{"location":"archive/STATUS-2026-03-28/#heute-erledigt-28032026","title":"\u2705 Heute erledigt (28.03.2026)","text":""},{"location":"archive/STATUS-2026-03-28/#vormittag","title":"Vormittag","text":"/opt/gwoe-antragspruefer/)"},{"location":"archive/STATUS-2026-03-28/#aktueller-stand","title":"\ud83d\udcca Aktueller Stand","text":"Metrik Wert Analysierte Antr\u00e4ge 20 Alle mit Verbesserungsvorschl\u00e4gen \u2705 \u00d8 GW\u00d6-Score gesamt 4.6 \u00d8 SPD 7.7 \u00d8 CDU/GR\u00dcNE 6.0 \u00d8 FDP 4.8 \u00d8 AfD 1.5"},{"location":"archive/STATUS-2026-03-28/#technische-anderungen-heute","title":"\ud83d\udd27 Technische \u00c4nderungen heute","text":""},{"location":"archive/STATUS-2026-03-28/#security-mainpy","title":"Security (main.py)","text":"# Middleware hinzugef\u00fcgt:\n- X-Content-Type-Options: nosniff\n- X-Frame-Options: DENY\n- X-XSS-Protection: 1; mode=block\n- Content-Security-Policy\n- Referrer-Policy: strict-origin-when-cross-origin\n- Permissions-Policy: geolocation=(), microphone=(), camera=()\n\n# FastAPI Docs deaktiviert:\ndocs_url=None, redoc_url=None, openapi_url=None\n"},{"location":"archive/STATUS-2026-03-28/#retry-logik-analyzerpy","title":"Retry-Logik (analyzer.py)","text":"# 3 Versuche bei JSON-Parse-Fehlern\n# Temperatur steigt pro Versuch (0.3 \u2192 0.4 \u2192 0.5)\nmax_retries = 3\nfor attempt in range(max_retries):\n # ... LLM Call ...\n try:\n data = json.loads(content)\n return Assessment.model_validate(data)\n except json.JSONDecodeError:\n continue # Retry\n"},{"location":"archive/STATUS-2026-03-28/#persistente-db-dockerfile","title":"Persistente DB (Dockerfile)","text":"# VORHER (kaputt):\nCOPY data/ ./data/\nCOPY reports/ ./reports/\n\n# NACHHER (korrekt):\n# data/ und reports/ werden als Volumes gemountet\nRUN mkdir -p /app/data /app/reports\n"},{"location":"archive/STATUS-2026-03-28/#deploy-workflow","title":"Deploy-Workflow","text":"# .tarignore und --exclude verhindern \u00dcberschreiben der Server-DB\ntar czf ... --exclude='data' --exclude='reports' ...\n"},{"location":"archive/STATUS-2026-03-28/#repository-struktur","title":"\ud83d\udcc1 Repository-Struktur","text":"gwoe-antragspruefer/\n\u251c\u2500\u2500 app/\n\u2502 \u251c\u2500\u2500 main.py # FastAPI + Security\n\u2502 \u251c\u2500\u2500 analyzer.py # LLM + Retry\n\u2502 \u251c\u2500\u2500 database.py # SQLite\n\u2502 \u251c\u2500\u2500 models.py # Pydantic\n\u2502 \u251c\u2500\u2500 parlamente.py # OPAL-Adapter\n\u2502 \u251c\u2500\u2500 report.py # PDF\n\u2502 \u251c\u2500\u2500 kontext/ # GW\u00d6-Matrix, Programme\n\u2502 \u251c\u2500\u2500 templates/ # Jinja2 UI\n\u2502 \u2514\u2500\u2500 static/referenzen/ # Original-PDFs\n\u251c\u2500\u2500 docker-compose.yml\n\u251c\u2500\u2500 Dockerfile\n\u251c\u2500\u2500 requirements.txt\n\u251c\u2500\u2500 .env.example\n\u251c\u2500\u2500 .gitignore\n\u251c\u2500\u2500 LICENSE (MIT)\n\u2514\u2500\u2500 README.md\n"},{"location":"archive/STATUS-2026-03-28/#offene-todos","title":"\ud83d\udccb Offene TODOs","text":""},{"location":"archive/STATUS-2026-03-28/#prio-1","title":"Prio 1","text":"# Lokal committen\ncd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ngit add . && git commit -m \"...\" && git push\n\n# Server aktualisieren\nssh vserver 'cd /opt/gwoe-antragspruefer && git pull && docker compose up -d --build'\n"},{"location":"archive/STATUS-2026-03-28/#manuell-ohne-server-git","title":"Manuell (ohne Server-Git)","text":"cd ~/Nextcloud/dotty/projekte/2026-03-23\\ GW\u00d6-Antragspr\u00fcfer\\ _WIP_/webapp\ntar czf /tmp/gwoe-webapp.tar.gz --exclude='venv' --exclude='__pycache__' --exclude='data' --exclude='reports' --exclude='.env' .\nscp /tmp/gwoe-webapp.tar.gz vserver:/tmp/\nssh vserver 'cd /opt/gwoe-antragspruefer && tar xzf /tmp/gwoe-webapp.tar.gz && docker compose up -d --build'\n"},{"location":"archive/STATUS-2026-03-28/#logs-prufen","title":"Logs pr\u00fcfen","text":"ssh vserver 'docker logs gwoe-antragspruefer --tail 50'\n"},{"location":"archive/STATUS-2026-03-28/#db-status","title":"DB-Status","text":"ssh vserver 'docker exec gwoe-antragspruefer python -c \"\nimport sqlite3\nconn = sqlite3.connect(\\\"/app/data/gwoe-antraege.db\\\")\ncur = conn.cursor()\ncur.execute(\\\"SELECT COUNT(*) FROM assessments\\\")\nprint(f\\\"Assessments: {cur.fetchone()[0]}\\\")\n\"'\n Projekt-Status: FUNKTIONSF\u00c4HIG \u2705
Dokumentiert von Dotty, 28.03.2026, 23:58 Uhr
"}]}