Kritiker

Erstellt: 2025-12-20 | Aktualisiert: 2025-12-31

Qualitätsprüfungs-System mit LLM-basierten und deterministischen Kritikern.

Modulgenerators/critic.py, generators/format_checker.py
Tabellecontent_config (type='critic')
Ergebnissecontent_critiques

Kritiker-Typen

TypKritikerMethodeZuverlässigkeit
InhaltFaktenprüfer, Stilist, StrukturanalystLLMGut für semantische Prüfung
FormatFormatierungsprüferDeterministisch100% 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

RegelQuellePrüfung
emojis_verbotenstructure.formatierung.emojisUnicode-Ranges + Bullet-Emojis
markdown_verbotenstructure.ausgabe.format = "reiner Text"**bold**, *italic*, # headers
fettschrift_verbotenstructure.formatierung.fettschrift**text**, __text__
gedankenstriche_verbotenprofile.grammatik_und_satzbau.gedankenstriche– (U+2013), — (U+2014)
hashtags_verbotenstructure.formatierung.hashtags = "keine"#Hashtag Pattern
ausrufezeichen_sparsamprofile.formatierung.ausrufezeichenMax. 2 Ausrufezeichen

Post-Processing

Datei: generators/persistence.py - strip_markdown()

Für output_format == "reiner Text" werden automatisch ersetzt:

Aktuelle Kritiker

IDNameTypFokus
30FaktenprüferLLMQuellen, Aktualität, Genauigkeit
31StilistLLMStil, Tonalität, Fluss
32StrukturanalystLLMGliederung, Absätze, Aufbau
33FormatierungsprüferCodeEmojis, 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()

  1. Version-Content laden
  2. Alle aktiven Kritiker aus content_config laden
  3. Kritik-Runde inkrementieren
  4. Jeden Kritiker ausführen (LLM oder deterministisch)
  5. Feedback in content_critiques speichern
  6. 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})