presseportale/docs/user-admin/Entwicklungsplan KI-Pruefung und Veroeffentlichung.md
Kevin Adametz 8d8d957884 Doku: Status-Sync 11./12.06., Decision-Update Preisstruktur und Phase-9-Plan
- Decision-Update Preisstruktur & Veroeffentlichungs-Flow aufgenommen
  (Launch-Tarife, Slot-Verbrauch bei Veroeffentlichung, Submit-Gate,
  Launch-Credits) inkl. Klarstellung 12.06.: Gelb geht direkt live,
  keine manuelle Pruef-Queue, nur Rot wird abgelehnt
- Alle Status-Dokumente auf den Code-Stand gezogen: README-Index,
  STATUS-ABGLEICH (KI-Pipeline, Bilder/Lizenzen, Pricing), Checkliste
  (KI- und Titelbild-Bloecke, Launch-To-dos), Admin-User,
  user-zusammenhaenge (Datenmodell-Delta), Entwicklungsplan KI-Pruefung
  (Phase 0 abgehakt, Decision-Abgleich)
- Ueberschriebene Tarif-Abschnitte in Konzept-Update 1/2 und
  Relaunch-Konzept mit Superseded-/IST-Hinweisen markiert
- Neues Plan-Dokument PHASE-9-FLOW-UND-TARIFE-PLAN.md (9A-9J)
- Phase-8-Roadmap-Doku (20-PHASE-8-USER-PANEL.md) + PROGRESS-Eintraege

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
2026-06-12 09:20:22 +00:00

26 KiB
Raw Permalink Blame History

Entwicklungsplan: KI-Prüfung & Veröffentlichungs-Pipeline

Stand: 11.06.2026 — Phasen 05 abgeschlossen, Phase 6 (Trust-Score) offen.

Dieser Plan definiert die schrittweise Umsetzung der automatisierten Prüfung und Veröffentlichung von Pressemitteilungen (PM). Er ist so geschnitten, dass jede Phase einzeln umgesetzt, getestet und ausgeliefert werden kann.

Abgleich mit dem Decision-Update (11.06.2026): Das Decision-Update Preisstruktur & Veröffentlichungs-Flow setzt auf dieser Pipeline auf und ergänzt zum Launch drei noch offene Flow-Regeln, die nicht Teil dieses Plans waren:

  1. Submit-Gate: „Zur Prüfung einreichen" wird hinter eine aktive Buchung gelegt (das Modal zeigt ohne Buchung einen Buchungs-Hinweis).
  2. Slot-Verbrauch bei Veröffentlichung statt bei Einreichung — rot abgelehnte PMs verbrauchen keinen Slot. Der aktuelle Quota-Stub zählt noch beim Einreichen (submitForReview) und muss umgestellt werden.
  3. Kein Re-Check zum Launch: eine Einreichung = eine Prüfung; Nachbessern + erneut prüfen kommt erst in Phase 2.
  4. Gelb-Routing geändert (Entscheidung 12.06.2026): Gelb geht zum Launch direkt live wie Grün — keine manuelle Review-Queue mehr. Nur Rot wird abgelehnt (mit Begründung an den Autor). Phase 4 unten beschreibt das ursprünglich gebaute Verhalten (Gelb → manuelle Queue); die Umstellung erfolgt im Phase-9-Plan (docs/PHASE-9-FLOW-UND-TARIFE-PLAN.md, Päckchen 9A).

Ziel & Leitprinzip

Jede eingehende PM wird automatisch von einer KI geprüft. Nur in äußersten Fällen erfolgt eine manuelle redaktionelle Prüfung. Zwei Einreichungsstellen müssen denselben Prüf-Pfad durchlaufen:

  1. Web-Formular (Customer- und Admin-Editor)
  2. API (/api/v1/press-releases)

Es gibt zwei voneinander unabhängige Bewertungen (Konzept-Update 1, Abschnitt 15):

  • Klassifikations-Score (Grün/Gelb/Rot) — der „Red Flag": entscheidet, ob überhaupt veröffentlicht wird. Jetzt umzusetzen.
  • Content-Score (0100) → Stufe (Standard/Geprüft/Hochwertig) — die Qualitätsbewertung („Scoring"). Spätere Phase.

Konzept-Grundlage

  • docs/konzept/Konzept-Update 1 Überarbeitete Abschnitte.md, §15.1 (Klassifikations-Score) und §15.2 (Content-Score).
  • docs/konzept/Konzept-Update 2 Score-Stufen-System.md (Stufen-Mapping, content_tier, Außenkommunikation).

Klassifikations-Score laut Konzept §15.1:

Klassifikation Bedeutung Auswirkung
Grün unauffällig direkte Veröffentlichung (optional 510 Min. Verzögerung)
Gelb unklar/grenzwertig manuelle Review-Queue (nicht boostbar)
Rot unzulässig zurück an Autor mit Begründung, keine Veröffentlichung

Faktoren (Red Flags): Werbung statt PM, beleidigend/diskriminierend, rechtlich heikel, Spam-Muster, unseriöse Versprechen. Speicherung laut Konzept: press_releases.classification plus Audit-Log ki_audits.

Ist-Zustand (Bestandsaufnahme)

  • Statuswerte (App\Enums\PressReleaseStatus): draft, review, published, rejected, archived.
  • Web-Einreichung: App\Services\PressRelease\PressReleaseService::submitForReview() prüft nur eine wortbasierte Blacklist (config/blacklist.php via BlacklistService), setzt sonst Status review, erhöht das Quota und schreibt ein PressReleaseStatusLog.
  • Veröffentlichung: PressReleaseService::publish() (Admin-Aktion) und der Cron App\Console\Commands\PublishScheduledPressReleases (publiziert review-PMs mit fälligem scheduled_at). Beide prüfen erneut nur die Blacklist.
  • API: App\Http\Controllers\Api\V1\PressReleaseController::store() / update() schreiben status direkt aus dem Request (erlaubt: draft, review) und rufen submitForReview nicht auf. Eine API-PM mit status=review landet damit ohne Blacklist-/Quota-/Log-Prüfung in der Queue.
  • UI-Einreichung (Prozess-Start):
    • Detailansicht ([show.blade.php]): vollständiges Modal confirm-submit-review mit rechtlichen Hinweisen, Quota-Anzeige und Bestätigungs-Checkboxen → ruft submitForReview.
    • Bearbeiten ([edit.blade.php]): Button „Speichern & zur Prüfung" mit nur wire:confirm (Browser-Dialog), kein Modal.
    • Erstellen ([create.blade.php]): Button „Zur Prüfung senden" ohne Modal.
  • Kein Datenmodell für Klassifikation/Score: keine Spalten classification, content_score, content_tier, keine Tabelle ki_audits.

Lücken & Risiken

  • L1 — API-Bypass: Einreichung über die API umgeht jede Prüfung.
  • L2 — Keine echte Inhaltsprüfung: nur eine triviale Wort-Blacklist; keine Erkennung von Werbung, Spam, rechtlich heiklen oder unseriösen Inhalten.
  • L3 — Auto-Publish ohne Klassifikation: geplante PMs werden vom Cron veröffentlicht, ohne dass eine inhaltliche Bewertung stattgefunden hat.
  • L4 — Uneinheitlicher Prozess-Start: das Bestätigungs-Modal existiert nur in der Detailansicht, nicht beim Bearbeiten/Erstellen.
  • L5 — Kein Audit: KI-Entscheidungen wären ohne ki_audits nicht nachvollziehbar (DSGVO / Nachweispflicht).

Zielarchitektur

Einreichung (Formular ODER API)
        │
        ▼
  SubmissionService.submit()                ← ein einziger Funnel
        │
        ├─ Hard-Filter: Blacklist (synchron, deterministisch)
        ▼
  ClassificationService.classify()          ← KI (Claude), mit Fallback
        │
        ├─ Rot   → status=rejected, Begründung an Autor
        ├─ Gelb  → status=review   (manuelle Queue, „äußerste Fälle")
        └─ Grün  → Veröffentlichungspfad (sofort / geplant)
        │
        ▼
  ki_audits (vollständiges Audit-Log jeder KI-Entscheidung)
        +
  press_releases.classification / classified_at
        +
  (später) content_score / content_tier

Kernregeln:

  • Formular und API rufen ausschließlich SubmissionService.submit() auf. Die API darf status nicht mehr frei setzen; published ist über die API nie erreichbar.
  • Re-Klassifikation bei jeder Änderung einer PM (Konzept §15.1: „Bei Änderung der PM wird neu klassifiziert").
  • Schwellen/Verhalten sind konfigurierbar (config/scoring.php), damit sie ohne Code-Änderung kalibriert werden können.

Entwicklungsschritte

Phase 0 — Prozess-Start im UI vereinheitlichen — erledigt (11.06.2026)

Ziel: Das bestehende Einreichungs-Modal erscheint überall dort, wo eine PM eingereicht wird — auch beim Bearbeiten (Button „Speichern & zur Prüfung") und beim Erstellen (Button „Zur Prüfung senden"). Reiner UI-Schritt, kein Backend.

Umsetzung: Das Modal confirm-submit-review (rechtliche Hinweise, Quota, Bestätigungs-Checkboxen) wird in Customer-Show, -Create und -Edit über flux:modal.trigger geöffnet; bestätigt ruft es wie geplant submitForReview bzw. saveAndSubmit/save('review').

Umfang:

  • Modal confirm-submit-review aus show.blade.php in eine wiederverwendbare Blade-/Volt-Komponente extrahieren (z. B. resources/views/livewire/components/press-release-submit-modal.blade.php).
  • In edit.blade.php den wire:confirm-Button durch einen flux:modal.trigger ersetzen; bei Bestätigung wird wie bisher saveAndSubmit ausgeführt (erst speichern, dann einreichen).
  • In create.blade.php denselben Modal-Trigger vor save('review') schalten.
  • Texte/Checkboxen identisch zur Detailansicht halten (rechtliche Hinweise, Quota, Bestätigungen).

Betroffene Dateien: resources/views/livewire/customer/press-releases/{show,edit,create}.blade.php, neue Komponente unter resources/views/livewire/components/.

Done: In allen drei Ansichten (Customer: show/edit/create) öffnet derselbe Bestätigungsdialog; Tests für Edit/Create-Submit grün.

Admin-Editor (/admin/press-releases/) — bewusst ausgenommen: Der Admin-Editor behält sein bisheriges Verhalten (wire:confirm). Begründung: Wenn eine PM beim Admin landet, hat die vorgelagerte User-Prüfung (Einreichungs-Modal im Customer-Flow) bereits stattgefunden. Der Admin braucht hier keinen erneuten Bestätigungsdialog. Stattdessen erhält der Admin-Editor in einer späteren Phase einen zusätzlichen „Prüfung"-Button (siehe Phase 4: On-Demand-KI-Prüfung).

Tests: Volt-Tests, die das Öffnen des Modals und den Submit-Pfad (saveAndSubmit / save('review')) abdecken.

Phase 1 — Einreichungs-Funnel & API-Absicherung — erledigt (11.06.2026)

Ziel: Beide Einreichungsstellen laufen durch einen Pfad; die API-Lücke (L1) wird geschlossen. Noch ohne KI — nur Vereinheitlichung.

Umsetzung:

  • PressReleaseService::submitForReview() ist der alleinige Einreichungs-Einstieg (Web-Formular und API rufen dieselbe Methode). Auf eine separate SubmissionService-Fassade wurde bewusst verzichtet — submitForReview ist bereits die stabile Schnittstelle, in die Phase 3 die KI-Klassifikation einhängt.
  • API: status aus den Validierungsregeln von StorePressReleaseRequest und UpdatePressReleaseRequest entfernt (inkl. ungenutzter Imports). store() erzeugt jetzt immer PressReleaseStatus::Draft; ein übergebenes status wird ignoriert. update() kann den Status nicht mehr setzen.
  • Neue explizite Route POST /api/v1/press-releases/{pressRelease}/submit (press-releases.submit) → PressReleaseController::submit(). Diese prüft press-releases:write, Ownership und erlaubt nur draft/rejected (sonst 409); ruft submitForReview(); eine BlacklistViolationException wird als 422 mit Begründung zurückgegeben. Damit greifen Blacklist-, Quota- und Status-Log-Behandlung auch für API-Einreichungen.
  • published ist über die API weiterhin nie erreichbar (nur Admin-Aktion/Cron).

Betroffene Dateien: app/Http/Controllers/Api/V1/PressReleaseController.php, app/Http/Requests/Api/V1/{Store,Update}PressReleaseRequest.php, routes/api.php. PressReleaseService blieb unverändert (Schnittstelle ausreichend).

Done: API kann keine PM mehr ungeprüft in review heben; eine PM-Einreichung verhält sich über API und Formular identisch.

Tests: tests/Feature/Api/V1/PressReleaseSubmitApiTest.php (Create erzeugt immer Draft & ignoriert status; Submit-Route hebt nach review, zählt Quota, schreibt Log; Blacklist → 422 + rejected; fehlende Schreibrechte → 403; bereits in review → 409; fremde PM → 403). Alle grün.

Phase 2 — Datenmodell & Audit — erledigt (11.06.2026)

Ziel: Persistenz für Klassifikation und vollständiges KI-Audit. Noch ohne Verhaltensänderung (alle Felder nullable).

Umsetzung:

  • Migration add_classification_to_press_releases: Spalten classification (string(16), nullable, nach status) und classified_at (timestamp, nullable) plus Index auf classification. content_score/content_tier bewusst erst in Phase 5 (siehe Datenmodell-Anhang).
  • Migration create_ki_audits_table: press_release_id (FK, cascade), type, provider (nullable), model (nullable), result (nullable), reason (text, nullable), raw_response (json, nullable), created_at (useCurrent), Index (press_release_id, type). Kein updated_at (append-only Log).
  • Model App\Models\KiAudit ($timestamps = false, Cast raw_response → array, Konstanten TYPE_CLASSIFICATION/TYPE_CONTENT_SCORE, Relation pressRelease()), Relation PressRelease::kiAudits() (neueste zuerst).
  • Enum App\Enums\PressReleaseClassification (Green/Yellow/Red + label()), in PressRelease::casts() für classification registriert.
  • config/scoring.php: Anbieter/Modell-Auswahl (CLASSIFICATION_PROVIDER, Default deterministic, CLASSIFICATION_MODEL), Timeout, Grün-Verzögerung (Minuten), Gelb→manuelle-Queue-Flag sowie Content-Score-Stufen-Schwellen (Phase 5).
  • KiAuditFactory mit States classification() / contentScore().

Betroffene Dateien: zwei neue Migrationen unter database/migrations/, app/Models/PressRelease.php, app/Models/KiAudit.php, app/Enums/PressReleaseClassification.php, config/scoring.php, database/factories/KiAuditFactory.php.

Done: Migrationen laufen; Modelle/Casts/Relation vorhanden; keine bestehende Funktionalität verändert (alle Felder nullable).

Tests: tests/Feature/PressReleaseClassificationModelTest.php (Enum-/ Datetime-Cast, Default null, kiAudits()-Reihenfolge, raw_response-Array-Cast

  • Relation, Cascade-Delete). Alle grün.

Phase 3 — KI-Klassifikation (Red Flag) — erledigt (11.06.2026)

Ziel: Echte inhaltliche Prüfung jeder Einreichung; Ergebnis asynchron als Klassifikation gespeichert und auditiert.

Entscheidungen (11.06.2026): Erster aktiver Anbieter ist OpenAI (Key/ Budget vorhanden); Anthropic/Gemini folgen über dieselbe Treiber-Schnittstelle. Klassifikation läuft asynchron über die Queue (synchron wäre später nicht handelbar). Zum Testen ohne Dauer-Worker gibt es einen Drain-Befehl.

Umsetzung:

  • Provider-agnostische Treiber-Architektur unter app/Services/PressRelease/Classification/:
    • Interface Contracts\ClassificationDriver::classify(PressRelease): ClassificationResult.
    • ClassificationResult (Value Object: Enum-Klassifikation, reasons[], provider, model, rawResponse, reasonText()).
    • Drivers\OpenAiClassificationDriver — OpenAI Chat-Completions via Http-Client, liest config/services.openai (Key/URL/Modell/Timeout), erzwingt response_format: json_object und parst {classification, reasons[]}. Wirft bei fehlendem Key / HTTP-Fehler / ungültigem JSON.
    • Drivers\DeterministicClassificationDriver — Blacklist → Rot/Grün (nie Gelb), als Fallback ohne externe API.
    • ClassificationManager (Laravel-Manager) löst den Treiber aus config('scoring.classification.provider') auf (createOpenaiDriver/createDeterministicDriver).
  • Asynchroner Job app/Jobs/ClassifyPressRelease (Queue classification, tries=3): klassifiziert über den aktiven Treiber, bei Ausfall Fallback auf den deterministischen Treiber (mit Log::warning), schreibt press_releases.classification/classified_at und einen ki_audits-Eintrag (inkl. provider/model/reason/raw_response).
  • Einbindung in den Funnel: PressReleaseService::submitForReview() stößt nach dem synchronen Blacklist-Hard-Filter und dem Statuswechsel den Job an (ClassifyPressRelease::dispatch(...)->onQueue('classification')). Greift für Formular und API (gemeinsamer Einstieg aus Phase 1).
  • Drain-Befehl php artisan classification:work (Option --once): arbeitet die Queue einmalig ab und beendet sich (queue:work --stop-when-empty) — zum Testen ohne permanenten Worker.
  • Konfig: config/scoring.php Default-Provider auf openai gesetzt (CLASSIFICATION_PROVIDER); Modell leer ⇒ config('services.openai.model').
  • Test-Isolation: phpunit.xml erzwingt CLASSIFICATION_PROVIDER=deterministic und leeren OPENAI_API_KEY, damit die Suite keine echten OpenAI-Calls macht; der OpenAI-Pfad wird gezielt mit Http::fake() getestet.

Noch offen (bewusst): Re-Klassifikation bei jeder PM-Änderung (Update über Formular/API) ist noch nicht verdrahtet — Phase 3 klassifiziert beim Einreichen. Nachzuziehen, wenn das Status-Routing (Phase 4) steht. Anthropic-/ Gemini-Treiber + SDK folgen separat.

Betroffene Dateien: app/Services/PressRelease/Classification/*, app/Jobs/ClassifyPressRelease.php, app/Console/Commands/RunClassificationQueue.php, app/Services/PressRelease/PressReleaseService.php, config/scoring.php, phpunit.xml.

Done: Jede Einreichung (Formular + API) stößt asynchron eine Klassifikation an, erzeugt einen ki_audits-Eintrag; bei KI-Ausfall greift der deterministische Fallback nachvollziehbar. Status-Routing folgt in Phase 4.

Tests: tests/Feature/PressReleaseClassificationJobTest.php (OpenAI grün/gelb mit Http::fake, Fallback bei HTTP-500, deterministisch Rot bei Blacklist, Dispatch auf Queue classification via Queue::fake). Alle grün; volle Suite 416 grün (2 vorbestehende WIP-Failures unverändert).

Phase 4 — Routing, Auto-Publish & Review-Queue — erledigt (11.06.2026)

Ziel: Die Klassifikation steuert den Status. Manuelle Prüfung nur noch bei Gelb.

Umsetzung:

  • Routing im Job über PressReleaseService::routeByClassification() (vom ClassifyPressRelease-Job nach dem Klassifizieren aufgerufen):
    • Rotreject(..., source: 'ki'): status=rejected, KI-Begründung per Mail an den Autor (PressReleaseRejected, wie bei Blacklist).
    • Gelb → keine Aktion, bleibt review (manuelle Admin-Queue).
    • GrünautoPublishGreen(): ohne Termin sofort veröffentlichen, optional mit Sicherheitsfenster scoring.classification.green_delay_minutes (über published_at-Override); mit zukünftigem scheduled_at bleibt die PM in review und der Scheduler publiziert zum Termin.
    • Greift nur, solange die PM noch review ist (manuelle Admin-Eingriffe haben Vorrang). publish() erhielt einen ?Carbon $publishedAtOverride-Parameter, reject() einen string $source-Parameter.
  • Scheduler PublishScheduledPressReleases: Kandidaten-Query um where('classification', 'green') erweitert — nur grüne fällige PMs werden automatisch publiziert; gelbe warten immer auf den Admin. Geplante Termine werden weiterhin respektiert.
  • Admin-Review-Queue: Index- und Show-Ansicht zeigen ein KI-Klassifikations- Badge (grün/gelb/rot); der Index hat einen Klassifikations-Filter (classificationFilter, inkl. URL-Param, Active-Chip, Reset) — damit „nur Gelb" filterbar. Die Show-Ansicht blendet im Review-Block den KI-Hinweis (Begründung aus dem jüngsten ki_audits-Eintrag) ein.

Test-Isolation (wichtig): Da Tests mit sync-Queue den Job inline ausführen, wurde der Klassifikations-Job in den „submit→review"-Tests via Queue::fake() entkoppelt (Workflow, PublishModal, API-Submit). Die Scheduler-Tests setzen jetzt classification = green für Publish-Kandidaten; neuer Test: fällige gelbe PM bleibt review.

Betroffene Dateien: app/Services/PressRelease/PressReleaseService.php, app/Jobs/ClassifyPressRelease.php, app/Console/Commands/PublishScheduledPressReleases.php, Admin-Views resources/views/livewire/admin/press-releases/{index,show}.blade.php.

Done: Grüne PMs gehen automatisch live (sofort/zum Termin), rote werden abgelehnt + Autor benachrichtigt, nur gelbe landen in der manuellen Queue; Admin sieht Klassifikation + KI-Begründung und kann nach Gelb filtern.

Tests: Routing in PressReleaseClassificationJobTest (Rot→rejected+Mail, Grün-sofort→published+Mail, Grün-geplant→bleibt review, Gelb→bleibt review); Scheduler in PressReleaseSchedulingTest (grün fällig→published, gelb fällig→review); Admin-UI in PressReleaseIndexPhase8bTest (KI-Badge, Klassifikations-Filter). Volle Suite 423 grün (2 vorbestehende WIP-Failures).

Admin „Prüfung"-Button (On-Demand-KI-Prüfung) erledigt (11.06.2026):

  • Im Admin-Editor gibt es oben den Button „Prüfung", der ein Modal admin-ki-check öffnet: auswählbare Klassifikation (Content-Score als „in Vorbereitung" deaktiviert) und ein Anbieter-Override (Konfiguriert / OpenAI / Deterministisch).
  • runKiCheck() dispatcht ClassifyPressRelease auf der Queue classification mit route: false und optionalem providerOverride. Das ist eine nachgelagerte Re-Check-Prüfung: sie aktualisiert nur classification + ki_audits, ohne den Status zu ändern (kein Auto-Publish/Reject) — die Entscheidung bleibt beim Admin (Ergebnis sichtbar in der Detailansicht).
  • Dafür erhielt ClassifyPressRelease die Parameter bool $route = true und ?string $providerOverride = null.

Tests: tests/Feature/Admin/AdminKiCheckTest.php (Button/Modal sichtbar; Dispatch mit route=false + Provider-Override; Abbruch ohne Auswahl; Re-Check-Job aktualisiert Bewertung, lässt Status unverändert).

Re-Klassifikation bei Änderung (Konzept §15.1) — erledigt (11.06.2026):

  • Neue Service-Methode reclassifyIfClassified(): dispatcht nur wenn die PM bereits klassifiziert ist ClassifyPressRelease mit route: false (Re-Check ohne Statusänderung).
  • Eingehängt überall dort, wo Inhalt geändert wird, und nur bei tatsächlicher Änderung von Titel/Text (wasChanged(['title', 'text'])): Customer-Editor save(), Admin-Editor save(), API update(). Beim Einreichen übernimmt weiterhin submitForReview die (routende) Klassifikation.

Tests: tests/Feature/PressReleaseReclassifyTest.php (Service dispatcht nur bei vorhandener Klassifikation; API-Update klassifiziert neu bei Text-Änderung, nicht bei reiner Keyword-Änderung).

Noch offen / Folgearbeiten:

  • Live-Aktualisierung der Ansicht nach Abschluss des Hintergrund-Jobs (Polling/Event) wäre ein optionales UX-Upgrade; aktuell erscheint das Ergebnis nach Reload/Navigation.
  • Content-Score-Option im Prüfungs-Modal — mit Phase 5 aktiviert (s. u.).

Phase 5 — Content-Score & Stufen — erledigt (11.06.2026)

Ziel: Qualitätsbewertung 0100 → Stufe Standard/Geprüft/Hochwertig (Konzept-Update 2).

Umsetzung:

  • Datenmodell: Migration add_content_score_to_press_releasescontent_score (tinyint, nullable), content_tier (string, nullable, Index), scored_at. Enum App\Enums\PressReleaseContentTier (Standard/Geprueft/Hochwertig) mit fromScore() (Schwellen aus config/scoring.php), label() und isPubliclyBadged() (Standard wird laut Update 2 nicht beworben). In PressRelease als Cast registriert.
  • Schwellen (config/scoring.php): Geprüft ≥ 60, Hochwertig ≥ 80 (Update 2), kalibrierbar; plus Anbieter/Modell/Timeout für den Score.
  • Treiber-Architektur unter app/Services/PressRelease/ContentScore/ analog zur Klassifikation: Contracts\ContentScoreDriver, ContentScoreResult, Drivers\OpenAiContentScoreDriver (gewichtete Faktoren §15.2 als JSON {score, breakdown}), Drivers\DeterministicContentScoreDriver (regelbasierte Heuristik: Länge, Bild, Quelle, Headline, Vollständigkeit), ContentScoreManager.
  • Job app/Jobs/ScorePressRelease (Queue classification, Fallback auf deterministisch): schreibt content_score + abgeleitete content_tier + scored_at und ki_audits (type=content_score). Optionaler providerOverride.
  • Berechnung bei Einreichung (submitForReview dispatcht Klassifikation und Score) und bei Inhaltsänderung (rescoreIfScored() in Customer-/ Admin-Editor und API-update(), analog zur Re-Klassifikation).
  • Anzeige:
    • Customer-Editor: Score-Panel (Punktzahl, Stufe, „noch X Punkte bis zur nächsten Stufe") — der produktive Editor-Score laut Update 2.
    • Admin-Index & -Show: Stufen-/Score-Badge (intern inkl. Punktzahl).
    • Customer-Detailansicht: öffentliches Stufen-Badge (✓ Geprüft / ★ Hochwertig; Standard ohne Badge).
  • Admin-Prüfungs-Modal: Content-Score-Option aktiviert; runKiCheck() dispatcht zusätzlich ScorePressRelease.

Done: Score wird bei Einreichung/Änderung berechnet, Stufe abgeleitet, auditiert und überall sichtbar.

Tests: tests/Feature/PressReleaseContentScoreTest.php (Tier-Mapping, öffentliche Badges, OpenAI-Score→Tier+Audit, Fallback, Dispatch bei Submit, Re-Score nur wenn bereits bewertet); Editor-Panel in CustomerPressReleaseEditPhase7Test; Stufen-Badge in PressReleaseIndexPhase8bTest; Content-Score-Dispatch in AdminKiCheckTest. Volle Suite 440 grün.

Noch offen / Folgearbeiten:

  • Public Web-Frontend (presseecho/businessportal24): Stufen-Badges in den öffentlichen Listen/Detailseiten gemäß Update 2 ergänzen (bisher nur im Portal/Backend und der Customer-Ansicht).
  • Score-History & Breakdown-Ansicht (Publisher-Dashboard) und Boost- Eligibilität (Abschnitt 16) sind eigene spätere Ausbaustufen.

Phase 6 — Trust-Score (später)

Account-/Firmen-Ebene (Konzept §15.3): lockert die KI-Freigabe-Schwelle für zuverlässige Publisher. Eigene spätere Ausbaustufe; hier nur als Ausblick vermerkt.


Datenmodell-Anhang (Zielzustand)

press_releases (Ergänzungen)
  + classification        enum(green,yellow,red) NULL
  + classified_at         timestamp NULL
  + content_score         tinyint NULL        (Phase 5)
  + content_tier          enum(standard,gepruft,hochwertig) NULL  (Phase 5)

ki_audits (neu)
  - id
  - press_release_id      FK
  - type                  enum(classification,content_score)
  - provider              string   (z. B. anthropic)
  - model                 string   (z. B. claude-opus-4-8)
  - result                string/json
  - reason                text NULL
  - raw_response          json/longtext
  - created_at            timestamp

Offene Entscheidungen

  • Anbieter & Modell entschieden (11.06.2026): Erster aktiver Anbieter ist OpenAI (CLASSIFICATION_PROVIDER=openai, Modell aus config/services.openai). Architektur provider-agnostisch; Anthropic/Gemini folgen. Offen bleibt, ob später mehrere Anbieter parallel (Primär + Fallback jenseits des deterministischen) laufen sollen.
  • Synchron vs. Queue entschieden (11.06.2026): Queue (asynchron, Queue-Name classification). Drain zum Testen: php artisan classification:work.
  • Dependency: OpenAI-Treiber nutzt den nativen Http-Client (kein neues Composer-Paket). Anthropic PHP-SDK (anthropic-ai/sdk) ist freigegeben; für Gemini je ein offizielles/etabliertes SDK oder HTTP-Client bei Umsetzung des Treibers.
  • Grün-Verzögerung: 0 Min. (sofort) oder 510 Min. (Konzept-Option) als Sicherheitsfenster — konfigurierbar über scoring.classification.green_delay_minutes, Default noch festzulegen.
  • Gelb-Verhalten: ausschließlich manuelle Queue, oder zusätzlich automatische Benachrichtigung des Autors.
  • DSGVO: Aufbewahrung/Anonymisierung der raw_response in ki_audits.

Nächste Schritte

Phasen 05 sind umgesetzt (Suite grün). Es folgen:

  1. Launch-Block aus dem Decision-Update (siehe Abgleich-Box oben): Submit-Gate hinter Buchung, Slot-Verbrauch bei Veröffentlichung, Tarif-/Zahlungs-Modul.
  2. Betrieb: Queue-Worker für classification im Produktions-Setup (Test-Drain: php artisan classification:work).
  3. Folgearbeiten: Live-Aktualisierung des KI-Ergebnisses in der UI, Stufen-Badges im öffentlichen Web-Frontend, Anthropic-/Gemini-Treiber.
  4. Phase 6: Trust-Score (eigene Ausbaustufe).