Kritiker
Qualitätsprüfungs-System mit LLM-basierten und deterministischen Kritikern.
| Modul | generators/critic.py, generators/format_checker.py |
|---|---|
| Tabelle | content_config (type='critic') |
| Ergebnisse | content_critiques |
Kritiker-Typen
| Typ | Kritiker | Methode | Zuverlässigkeit |
|---|---|---|---|
| Inhalt | Faktenprüfer, Stilist, Strukturanalyst | LLM | Gut für semantische Prüfung |
| Format | Formatierungsprüfer | Deterministisch | 100% zuverlässig |
Wichtig: LLMs halluzinieren bei Zeichenprüfungen (Gedankenstriche, Emojis, Anführungszeichen). Der Formatierungsprüfer verwendet daher deterministischen Code statt LLM.
Architektur
Content Version
↓
┌─────────────────────────────────────┐
│ Faktenprüfer (LLM) → 8/10 │
│ Stilist (LLM) → 8/10 │
│ Strukturanalyst (LLM) → 8/10 │
│ Formatierungsprüfer (Code) → 10/10 │
└─────────────────────────────────────┘
↓
all_passed? → validate : revision
Deterministischer Format-Checker
Datei: /var/www/scripts/pipeline/generators/format_checker.py
Geprüfte Regeln
| Regel | Quelle | Prüfung |
|---|---|---|
| emojis_verboten | structure.formatierung.emojis | Unicode-Ranges + Bullet-Emojis |
| markdown_verboten | structure.ausgabe.format = "reiner Text" | **bold**, *italic*, # headers |
| fettschrift_verboten | structure.formatierung.fettschrift | **text**, __text__ |
| gedankenstriche_verboten | profile.grammatik_und_satzbau.gedankenstriche | – (U+2013), — (U+2014) |
| hashtags_verboten | structure.formatierung.hashtags = "keine" | #Hashtag Pattern |
| ausrufezeichen_sparsam | profile.formatierung.ausrufezeichen | Max. 2 Ausrufezeichen |
Post-Processing
Datei: generators/persistence.py - strip_markdown()
Für output_format == "reiner Text" werden automatisch ersetzt:
- Gedankenstriche:
–→-,—→- - Anführungszeichen:
„"→"",‚'→'' - Markdown: **bold** → bold, *italic* → italic
Aktuelle Kritiker
| ID | Name | Typ | Fokus |
|---|---|---|---|
| 30 | Faktenprüfer | LLM | Quellen, Aktualität, Genauigkeit |
| 31 | Stilist | LLM | Stil, Tonalität, Fluss |
| 32 | Strukturanalyst | LLM | Gliederung, Absätze, Aufbau |
| 33 | Formatierungsprüfer | Code | Emojis, Markdown, Gedankenstriche |
Feedback-Format
{
"rating": 8,
"score": 8,
"passed": true,
"issues": ["Issue 1", "Issue 2"],
"suggestions": ["Suggestion 1"],
"summary": "Kurze Zusammenfassung",
"deterministic": false // true bei Formatierungsprüfer
}
API-Verwendung
# Einzelnen Kritiker ausführen
from generators.critic import run_critic
result = run_critic(content, critic_id=33, model="ollama:gemma3:27b-it-qat")
# Deterministisch prüfen (ohne DB)
from generators.format_checker import check_formatting
result = check_formatting(text, structure_config, profile_config)
Kritik-Ablauf
run_critic()
def run_critic(content, critic_id, model, structure_config=None, profile_config=None):
critic = get_critic(critic_id)
# Formatierungsprüfer: Deterministisch
if critic["name"] == "Formatierungsprüfer":
return check_formatting(content, structure_config, profile_config)
# Andere Kritiker: LLM
prompt = prompt_template.format(fokus=fokus_str, content=content)
response = call_llm(prompt, model)
return parse_json(response)
run_critique_round()
- Version-Content laden
- Alle aktiven Kritiker aus content_config laden
- Kritik-Runde inkrementieren
- Jeden Kritiker ausführen (LLM oder deterministisch)
- Feedback in content_critiques speichern
- Status basierend auf Ergebnis setzen
Warum deterministisch?
LLMs halluzinieren bei Zeichenprüfung. Beispiel:
# Text ohne Gedankenstriche
text = "Vertrauen ist keine Eigenschaft, sondern eine Beziehung."
# LLM-Formatierungsprüfer: 6/10 - "2 Gedankenstriche gefunden" (FALSCH!)
# Deterministischer Checker: 10/10 - "Keine Fehler" (KORREKT)
Der Code sucht exakt nach Unicode-Zeichen:
def check_gedankenstriche(text: str) -> list[dict]:
for i, char in enumerate(text):
if char == "–": # U+2013
issues.append({"type": "en_dash", "position": i})
if char == "—": # U+2014
issues.append({"type": "em_dash", "position": i})