presseportale/app/Jobs/ScorePressRelease.php
Kevin Adametz 3ae57d9dba Admin-PM-Liste: KI-Pruefungs-Modal + geschaerfte KI-Prompts
- Spalten umsortiert (Aktionen, Datum, KI-Pruefung zuerst); Pruefen-Button
  nur noch Icon, oeffnet Detail-Modal mit Klassifikation, Begruendung,
  Content-Score inkl. Faktor-Breakdown und Re-Check (wire:poll bis Queue durch)
- Content-Score: Bild-Punkte (10) systemseitig statt KI (nur ein Upload-Bild
  moeglich), Quellen 10->5, Pressestil 20->25; Breakdown wandert ins Audit-Log
- Klassifikations-Prompt prueft gegen aktive Ablehnungskategorien (K1-K6,
  rejection_categories): klarer Verstoss => direkt rot mit Kategorie-Code

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-07-10 16:37:47 +00:00

85 lines
2.8 KiB
PHP
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<?php
namespace App\Jobs;
use App\Enums\PressReleaseContentTier;
use App\Models\KiAudit;
use App\Models\PressRelease;
use App\Services\PressRelease\ContentScore\ContentScoreManager;
use App\Services\PressRelease\ContentScore\ContentScoreResult;
use Illuminate\Contracts\Queue\ShouldQueue;
use Illuminate\Foundation\Queue\Queueable;
use Illuminate\Support\Facades\Log;
/**
* Berechnet den Content-Score einer Pressemitteilung asynchron (Konzept §15.2).
*
* Läuft auf der Queue „classification" (gemeinsam mit der Klassifikation, ein
* Drain-Befehl). Nutzt den konfigurierten Treiber; bei Ausfall greift der
* deterministische Fallback. Ergebnis: `content_score` + abgeleitete
* `content_tier` + `scored_at`, plus `ki_audits`-Eintrag (type=content_score).
*
* Reine Qualitätsbewertung – ändert nie den Status (das macht die
* Klassifikation).
*/
class ScorePressRelease implements ShouldQueue
{
use Queueable;
public int $tries = 3;
public function __construct(
public readonly int $pressReleaseId,
public readonly ?string $providerOverride = null,
) {}
public function handle(ContentScoreManager $manager): void
{
$pressRelease = PressRelease::withoutGlobalScopes()->find($this->pressReleaseId);
if ($pressRelease === null) {
return;
}
$result = $this->score($manager, $pressRelease);
$tier = PressReleaseContentTier::fromScore($result->score);
$pressRelease->forceFill([
'content_score' => $result->score,
'content_tier' => $tier->value,
'scored_at' => now(),
])->save();
KiAudit::query()->create([
'press_release_id' => $pressRelease->id,
'type' => KiAudit::TYPE_CONTENT_SCORE,
'provider' => $result->provider,
'model' => $result->model,
'result' => (string) $result->score,
'reason' => $tier->label(),
'raw_response' => ['breakdown' => $result->breakdown] + $result->rawResponse,
'created_at' => now(),
]);
}
/**
* Bewertet über den aktiven (oder explizit gewählten) Treiber; bei Fehler
* greift der deterministische Fallback.
*/
private function score(ContentScoreManager $manager, PressRelease $pressRelease): ContentScoreResult
{
$provider = $this->providerOverride ?: $manager->getDefaultDriver();
try {
return $manager->driver($provider)->score($pressRelease);
} catch (\Throwable $exception) {
Log::warning('Content-Score fiel auf den deterministischen Treiber zurück.', [
'press_release_id' => $pressRelease->id,
'provider' => $provider,
'error' => $exception->getMessage(),
]);
return $manager->driver('deterministic')->score($pressRelease);
}
}
}