QC: Umlaut-Normalisierung + Prompt-Ergaenzung
Drei unabhaengige Schutzschichten gegen falsche Umschreibungen
(ae/oe/ue/ss statt ä/ö/ü/ß) im Lagebild:
1. Prompt-Ergaenzung in INCREMENTAL_ANALYSIS_PROMPT_TEMPLATE und
INCREMENTAL_BRIEFING_PROMPT_TEMPLATE (analyzer.py): explizite
Priorisierung, dass die Regel "echte UTF-8-Umlaute" Vorrang vor
"bestehende Formulierungen beibehalten" hat. Adressiert den Fall,
dass Claude beim inkrementellen Update Altlasten weitertraegt.
2. Deterministische Normalisierung in post_refresh_qc.py:
- normalize_german_umlauts(text) - Regex mit Wortgrenzen, case-
preserving, Whitelist-tauglich, ~140 Eintraege im Woerterbuch
abgeleitet aus den 140 Hard-Hits in Lage #6
- normalize_umlaut_fields(db, incident_id) - laedt summary und
latest_developments, normalisiert, schreibt nur bei Aenderungen
zurueck (idempotent)
- Eingehaengt in run_post_refresh_qc() nach dem Location-Check,
Fehler stoppen die Pipeline nicht (identisches Muster wie
bestehende Checks)
3. scripts/bootstrap_umlaut_repair.py - Einmal-Skript zur
Bestandsbereinigung der bereits gespeicherten summary-Felder.
Idempotent. Beim initialen Lauf auf Produktiv-DB: 14 Lagen
aktualisiert, 431 Ersetzungen insgesamt, Lage #6 von 140 auf
15 Rest-Treffer reduziert.
Whitelist (leer): aktuell kein Konflikt zwischen deutschen Ziel-
Woertern und englischen Fremdwoertern. Kann bei Bedarf erweitert
werden ohne Schema-Aenderung.
Verifikation:
- py_compile OK fuer alle drei Dateien
- Service-Restart ohne Errors
- Unit-Tests: positive Faelle ("Oeffnung der Strasse" -> 4 Ersetzungen),
Whitelist ("Boeing liefert Business-Access" -> 0 Ersetzungen),
Komposita ("Wasserstrasse", "Parlamentspraesident") korrekt
- Bootstrap 2x ausgefuehrt (erster Lauf 288 Ersetzungen, zweiter 143
nach Dict-Erweiterung), kumulativ 431
Architektur bleibt dormant ohne Daten-Altlasten: wenn keine Lage
Umschreibungen enthaelt, arbeitet normalize_umlaut_fields in <1ms
und schreibt nichts. Kein Overhead im Refresh-Pfad.
Dieser Commit ist enthalten in:
78
scripts/bootstrap_umlaut_repair.py
Normale Datei
78
scripts/bootstrap_umlaut_repair.py
Normale Datei
@@ -0,0 +1,78 @@
|
||||
"""Einmal-Repair: normalisiert Umlaute in summary und latest_developments
|
||||
aller aktiven Lagen deterministisch (deutsche Umschreibungs-Form -> echte Umlaute).
|
||||
|
||||
Idempotent: mehrfaches Ausfuehren hat keinen zusaetzlichen Effekt, wenn
|
||||
bereits normalisierte Texte vorliegen.
|
||||
|
||||
Aufruf (auf dem Monitor-Server):
|
||||
cd /home/claude-dev/AegisSight-Monitor/src
|
||||
python3 ../scripts/bootstrap_umlaut_repair.py
|
||||
"""
|
||||
import sqlite3
|
||||
import sys
|
||||
import os
|
||||
|
||||
# Sicherstellen, dass src/ im PYTHONPATH ist, damit services/post_refresh_qc importiert werden kann
|
||||
_here = os.path.dirname(os.path.abspath(__file__))
|
||||
_src = os.path.abspath(os.path.join(_here, "..", "src"))
|
||||
if _src not in sys.path:
|
||||
sys.path.insert(0, _src)
|
||||
|
||||
from services.post_refresh_qc import normalize_german_umlauts # noqa: E402
|
||||
|
||||
DB_PATH = "/home/claude-dev/osint-data/osint.db"
|
||||
|
||||
|
||||
def main():
|
||||
conn = sqlite3.connect(DB_PATH)
|
||||
conn.row_factory = sqlite3.Row
|
||||
try:
|
||||
c = conn.cursor()
|
||||
rows = c.execute(
|
||||
"SELECT id, title, summary, latest_developments FROM incidents "
|
||||
"WHERE status IN ('active', 'archived') ORDER BY id"
|
||||
).fetchall()
|
||||
|
||||
total_summary = 0
|
||||
total_dev = 0
|
||||
updated = 0
|
||||
|
||||
for r in rows:
|
||||
iid = r["id"]
|
||||
title = r["title"] or ""
|
||||
summary_orig = r["summary"] or ""
|
||||
dev_orig = r["latest_developments"] or ""
|
||||
|
||||
new_summary, n_s = normalize_german_umlauts(summary_orig)
|
||||
new_dev, n_d = normalize_german_umlauts(dev_orig)
|
||||
|
||||
if n_s == 0 and n_d == 0:
|
||||
continue
|
||||
|
||||
c.execute(
|
||||
"UPDATE incidents SET summary = ?, latest_developments = ? WHERE id = ?",
|
||||
(
|
||||
new_summary if n_s > 0 else summary_orig,
|
||||
new_dev if n_d > 0 else dev_orig,
|
||||
iid,
|
||||
),
|
||||
)
|
||||
updated += 1
|
||||
total_summary += n_s
|
||||
total_dev += n_d
|
||||
print(
|
||||
f" Lage #{iid:>3} {title[:50]:50} "
|
||||
f"summary: {n_s:>4} | latest_developments: {n_d:>3}"
|
||||
)
|
||||
|
||||
conn.commit()
|
||||
print()
|
||||
print(f"Ergebnis: {updated} Lagen aktualisiert. "
|
||||
f"{total_summary} Ersetzungen in summary, {total_dev} in latest_developments "
|
||||
f"(gesamt {total_summary + total_dev}).")
|
||||
finally:
|
||||
conn.close()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
In neuem Issue referenzieren
Einen Benutzer sperren