AI Enhancement Commands
Commands für AI-gesteuerte Profil-Anreicherung: Spracherkennung, Kategorisierung und Tag-Konsolidierung.
Alle AI-Commands nutzen die Gemini API und teilen sich die ai-detection Queue mit Rate-Limiting.
social:detect-languages
Erkennt Sprache, Land und Kategorie von Social Profiles via Gemini AI.
Verarbeitet Profile entweder synchron oder dispatcht Jobs asynchron in die ai-detection Queue.
social:detect-languages
{--limit=100 : Maximale Anzahl Profile (synchron)}
{--force : Auch bereits erkannte Profile neu erkennen}
{--profile= : Bestimmte Profile-ID}
{--queue : Jobs in Queue dispatchen statt synchron}
{--queue-limit=500 : Maximale Profile pro Queue-Run}
{--flush : Alle pending ai-detection Jobs entfernen}
Beispiele
# Synchrone Verarbeitung (Development/Debugging)
php artisan social:detect-languages --limit=50
# Asynchron in Queue dispatchen (Production)
php artisan social:detect-languages --queue --queue-limit=100
# Bestimmtes Profil force-redetecten
php artisan social:detect-languages --profile=12345 --force
# Queue komplett leeren
php artisan social:detect-languages --flush
Optionen
| Option | Beschreibung |
|---|---|
--limit=100 | Max Profile (synchroner Modus) |
--force | Re-Detection trotz vorhandener Daten |
--profile= | Einzelnes Profil via ID |
--queue | Queue-Modus (async via DetectProfileLanguage Jobs) |
--queue-limit=500 | Max Profile pro Queue-Dispatch |
--flush | Alle pending Jobs aus ai-detection Queue löschen |
Interne Schritte (Queue-Modus)
- Hard-Cap prüfen: Queue-Tiefe gegen Limit prüfen. Überspringen wenn voll
- Profile laden: Sortiert nach Follower-Anzahl (populäre Profile zuerst)
- Filter: Nur Profile mit Description, ohne Sprach-/Land-Daten, außerhalb Cooldown-Periode. Geblockte, sanitized und archivierte Profile werden ausgeschlossen (
notExcluded()Scope) - Dispatch:
DetectProfileLanguageJobs inai-detectionQueue - Rate-Limiting: Job-Middleware
RateLimited('gemini-api')begrenzt auf 15 Requests/Minute. Rate-limited Jobs werden mit Delay zurueck in die Queue gelegt (nicht geloescht).ShouldBeUnique(30-Min Lock viauniqueFor()) verhindert Duplikate
Interne Schritte (Synchron-Modus)
- Profile laden (nach denselben Kriterien wie Queue-Modus)
ChannelLanguageDetector::detect()pro Profil aufrufen- Ergebnisse (
detected_country,detected_language,detected_at) in DB speichern - 250ms Pause zwischen Requests (4 RPS max)
Cooldown-System
Profile die kürzlich erkannt wurden, werden übersprungen:
- Default Cooldown: 365 Tage (
services.gemini.cooldown_days) - Mit
--forcewird der Cooldown ignoriert
Schedule
Alle 15 Minuten mit --queue und dynamischem --queue-limit (= hourly_limit / 4).
Das Hourly-Limit kommt aus AI_ENHANCER_HOURLY_LIMIT (Default: 100).
Location: app/Console/Commands/DetectChannelLanguages.php
ai:prune-logs
Löscht fehlgeschlagene AI Detection Logs nach dem Retention-Fenster. Erfolgreiche Detections bleiben permanent erhalten, da sie wertvolle Sprach-/Land-Daten enthalten.
ai:prune-logs
{--days=7 : Aufbewahrungsdauer für fehlgeschlagene Logs}
Beispiele
# Standard: Failed Logs älter als 7 Tage löschen
php artisan ai:prune-logs
# Kürzere Retention
php artisan ai:prune-logs --days=3
# Längere Retention
php artisan ai:prune-logs --days=14
Optionen
| Option | Beschreibung |
|---|---|
--days=7 | Anzahl Tage die Failed Logs behalten werden |
Interne Schritte
AiDetectionLogRecords mitstatus = 'error'selektieren- Filter:
created_atälter als angegebene Tage - Records löschen
- Gelöschte Anzahl ausgeben
Hinweise
- Nur
error-Logs werden gelöscht -- erfolgreiche Detections bleiben permanent - Typische Fehler: API-Timeouts, Rate-Limiting, ungültige Responses
- Die
AiDetectionLogTabelle speichert Request/Response für Debugging
Schedule
2x täglich: 02:30 und 14:30 UTC mit .withoutOverlapping().
Location: app/Console/Commands/PruneAiDetectionLogs.php
tags:consolidate
Wöchentliche AI-gesteuerte Tag-Konsolidierung via Gemini. Aggregiert alle AI-generierten Tags, identifiziert Low-Use-Tags und dispatcht Chunks zur Gemini-Analyse.
tags:consolidate
{--dry-run : Vorschau ohne Dispatch}
{--force : Cooldown ignorieren, alle Tags verarbeiten}
{--chunk-size= : Chunk-Größe überschreiben}
{--max-chunks= : Maximale Chunk-Anzahl überschreiben}
Beispiele
# Standard-Lauf (nur Low-Use Tags, Cooldown beachten)
php artisan tags:consolidate
# Vorschau: welche Chunks würden dispatched
php artisan tags:consolidate --dry-run
# Alle Tags verarbeiten, Cooldown ignorieren
php artisan tags:consolidate --force
# Angepasste Chunk-Größe
php artisan tags:consolidate --chunk-size=30 --max-chunks=50
Optionen
| Option | Beschreibung |
|---|---|
--dry-run | Zeigt Chunks ohne zu dispatchen |
--force | Cooldown ignorieren, alle Kandidaten verarbeiten |
--chunk-size= | Override für postbox.tag_consolidation.chunk_size (Default: 50) |
--max-chunks= | Override für postbox.tag_consolidation.max_chunks (Default: 100) |
Interne Schritte
- Guard: Queue-Tiefe prüfen (
ai-detectionQueue, max = max_chunks * 3) - Tags aggregieren: Alle
ai_keywordsaussocial_profiles(JSONB) zählen - Filtern: Blocked Tags, Stop Words, zu kurze Tags (< 3 Zeichen)
- Split: Tags in "established" (>= 5 Profile) und "candidates" (< 5 Profile) aufteilen
- Cooldown: Kürzlich konsolidierte Tags überspringen (90 Tage Default)
- Chunking: Candidates in Chunks aufteilen (max 50 Tags/Chunk, max 100 Chunks)
- Dispatch:
ConsolidateTagChunkJobs aufai-detectionQueue mit Batch-ID
Konsolidierungs-Aktionen
Gemini analysiert jeden Chunk und schlägt Aktionen vor:
| Aktion | Beschreibung |
|---|---|
merge | Tag in einen bestehenden "established" Tag mergen |
suppress | Tag auf Blocklist setzen (irrelevant) |
keep | Tag behalten (keine Aktion) |
High-Confidence Merges werden automatisch ausgeführt; Low-Confidence gehen an Admin-Review.
Konfiguration
// config/postbox.php → tag_consolidation
'chunk_size' => env('TAG_CONSOLIDATION_CHUNK_SIZE', 50),
'max_chunks' => env('TAG_CONSOLIDATION_MAX_CHUNKS', 100),
'established_threshold' => 5,
'min_tag_length' => 3,
'consolidation_cooldown_days' => 90,
'rate_per_minute' => 10,
Schedule
Sonntags 18:00 UTC (Low-Traffic Window) mit .withoutOverlapping().
Location: app/Console/Commands/ConsolidateTags.php
ai:retry-failed
Setzt permanent fehlgeschlagene AI-Detections zurück und dispatched sie erneut.
Profile mit ai_detection_failed_at Marker werden für einen neuen Versuch freigegeben.
ai:retry-failed
{--limit=50 : Maximale Anzahl Profile pro Retry}
{--dry-run : Vorschau ohne Dispatch}
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--limit= | int | 50 | Maximale Anzahl Profile pro Lauf |
--dry-run | flag | – | Nur Vorschau, keine Jobs dispatchen |
Beispiele
# Standard: bis zu 50 fehlgeschlagene Profile retrien
php artisan ai:retry-failed
# Größerer Batch
php artisan ai:retry-failed --limit=200
# Vorschau: welche Profile wären betroffen?
php artisan ai:retry-failed --dry-run
Interne Logik
- Profile mit
ai_detection_failed_at IS NOT NULLfinden - Filter:
tracking_enabled = true,notExcluded()(nicht blocked/sanitized/archived) - Queue-Tiefe prüfen: Überspringt wenn Queue
ai-detectionbereits >max($limit, 100)Jobs hat (verhindert Job-Snowballing) ai_detection_failed_ataufnullzurücksetzenDetectProfileLanguageJob dispatchen
Schedule
Wöchentlich sonntags um 08:00 UTC.
Location: app/Console/Commands/RetryFailedAiDetection.php
keywords:update-stopwords
Analysiert Keyword-Verteilung und aktualisiert die dynamische Stopword-Liste. Keywords die in zu vielen Profilen vorkommen (z.B. "Official", "Music") werden als Stopwords markiert.
keywords:update-stopwords
{--threshold=30 : Prozent-Schwelle — Keywords in mehr als X% der Profile werden Stopwords}
{--min-profiles=100 : Mindestanzahl Profile mit Keywords bevor die Analyse startet}
{--dry-run : Vorschau ohne Speicherung}
Optionen
| Option | Typ | Default | Beschreibung |
|---|---|---|---|
--threshold= | int | 30 | Prozent-Schwelle für Stopword-Erkennung |
--min-profiles= | int | 100 | Mindestanzahl Profile bevor Analyse sinnvoll ist |
--dry-run | flag | – | Nur Vorschau, keine Cache-Aktualisierung |
Beispiele
# Standard: Stopwords für Keywords in > 30% aller Profile
php artisan keywords:update-stopwords
# Strengerer Schwellwert (> 20%)
php artisan keywords:update-stopwords --threshold=20
# Vorschau der erkannten Stopwords
php artisan keywords:update-stopwords --dry-run
Interne Logik
- Anzahl Profile mit Keywords zählen — überspringt wenn <
--min-profiles - Keywords via
LOWER(jsonb_array_elements_text(ai_keywords))extrahieren (PostgreSQL-spezifisch) - Keywords in >
threshold% der Profile als Stopwords identifizieren - Dynamische Stopword-Liste in Redis cachen (
keyword_dynamic_stopwords, TTL: 8 Tage) - Heartbeat in Cache für Health-Monitoring setzen
Hinweise
- PostgreSQL-spezifisch: Verwendet
jsonb_array_elements_text()— funktioniert nicht mit SQLite - Die dynamischen Stopwords ergänzen die statische Liste in der Keyword-Extraktion
- 8-Tage TTL stellt sicher, dass die Liste auch bei verpasstem Wochenlauf noch gültig ist
Schedule
Wöchentlich sonntags um 02:00 UTC.
Location: app/Console/Commands/UpdateKeywordStopwords.php
AI Enhancer Re-Run für gezielte Profile
Anleitung zum gezielten Re-Run der AI Enhancement auf bestimmte Profilgruppen (z.B. PRO-Profile, Favoriten). Nützlich nach Feature-Updates (neue Felder, bessere Prompts), um bestehende Profile mit der aktuellen AI-Logik neu zu verarbeiten.
Konzept
Die AI Enhancement wird über das detected_at Feld gesteuert. Profile mit detected_at innerhalb des Cooldowns (default 365 Tage) werden übersprungen. Um ein Re-Run zu erzwingen:
detected_ataufNULLsetzen → Profile erscheinen als "noch nicht erkannt"social:detect-languages --queuedispatcht sie in die Queue
Re-Run auf PRO-Profile
PRO-Profile sind via social_profiles.pro_enabled = true markiert.
-- Vorschau: Wie viele PRO-Profile betroffen?
SELECT COUNT(*) FROM social_profiles
WHERE pro_enabled = true
AND tracking_enabled = true
AND blocked_at IS NULL
AND sanitized_at IS NULL
AND archived_at IS NULL
AND detected_at IS NOT NULL;
-- Reset: detected_at auf NULL setzen
UPDATE social_profiles
SET detected_at = NULL, updated_at = NOW()
WHERE pro_enabled = true
AND tracking_enabled = true
AND blocked_at IS NULL
AND sanitized_at IS NULL
AND archived_at IS NULL
AND detected_at IS NOT NULL;
Re-Run auf Favoriten-Profile
Favoriten-Profile liegen in Workspaces mit name = 'Favoriten' (dem automatisch erstellten Favorites-Workspace pro User).
-- Vorschau: Wie viele Favoriten-Profile betroffen?
SELECT COUNT(DISTINCT sp.id) FROM social_profiles sp
JOIN watcher_sources ws ON ws.social_profile_id = sp.id
JOIN watchers w ON w.id = ws.watcher_id
JOIN workspaces wk ON wk.id = w.workspace_id
WHERE wk.name = 'Favoriten'
AND sp.tracking_enabled = true
AND sp.blocked_at IS NULL
AND sp.sanitized_at IS NULL
AND sp.archived_at IS NULL
AND sp.detected_at IS NOT NULL;
-- Reset: detected_at auf NULL setzen
UPDATE social_profiles
SET detected_at = NULL, updated_at = NOW()
WHERE id IN (
SELECT DISTINCT sp.id FROM social_profiles sp
JOIN watcher_sources ws ON ws.social_profile_id = sp.id
JOIN watchers w ON w.id = ws.watcher_id
JOIN workspaces wk ON wk.id = w.workspace_id
WHERE wk.name = 'Favoriten'
AND sp.tracking_enabled = true
AND sp.blocked_at IS NULL
AND sp.sanitized_at IS NULL
AND sp.archived_at IS NULL
AND sp.detected_at IS NOT NULL
);
Re-Run auf PRO + Favoriten kombiniert
UPDATE social_profiles
SET detected_at = NULL, updated_at = NOW()
WHERE tracking_enabled = true
AND blocked_at IS NULL
AND sanitized_at IS NULL
AND archived_at IS NULL
AND detected_at IS NOT NULL
AND (
pro_enabled = true
OR id IN (
SELECT DISTINCT ws.social_profile_id
FROM watcher_sources ws
JOIN watchers w ON w.id = ws.watcher_id
JOIN workspaces wk ON wk.id = w.workspace_id
WHERE wk.name = 'Favoriten'
)
);
Nach dem SQL-Reset
Der reguläre 15-Minuten-Scheduler (social:detect-languages --queue) pickt die Profile automatisch auf. Für schnellere Verarbeitung:
# Sofort einen größeren Batch dispatchen
php artisan social:detect-languages --queue --queue-limit=500
# Oder mit --force für alle (auch nicht-resetted) Profile
php artisan social:detect-languages --queue --queue-limit=200 --force
Monitoring
# Queue-Tiefe prüfen
php artisan queue:monitor ai-detection
# AI Detection Stats im Admin Dashboard
# → /admin/ai-enhancements
Hinweis: Das Daily Limit (AI_ENHANCER_DAILY_LIMIT, default 1400) gilt weiterhin. Bei großen Re-Runs verteilt sich die Verarbeitung über mehrere Tage.
AI Queue Worker
Alle drei AI-Commands teilen sich den ai-detection Queue Worker:
php8.4 artisan queue:work database --sleep=5 --daemon --quiet --timeout=60 --tries=3 --queue=ai-detection
Rate-Limiting erfolgt ueber Job-Middleware (RateLimited('gemini-api') mit 15 RPM). Rate-limited Jobs werden automatisch mit Delay zurueck in die Queue gelegt und bei naechster Gelegenheit verarbeitet. ShouldBeUnique mit 30-Minuten-Lock verhindert, dass der Scheduler Duplikate fuer dasselbe Profil dispatcht, waehrend ein verzoegerter Job noch aussteht.