Artikel

27. Wartung

Allgemein

Das System ist kein einmal eingerichteter Dauerläufer. Social-Media-Plattformen ändern ihre Oberflächen, Prompts altern, Sessions laufen ab, Inhalte wiederholen sich und Fehler sammeln sich an.

Wartung bedeutet hier:

Selektoren aktuell halten
Prompts kontrolliert weiterentwickeln
Dubletten entfernen
Fehler nacharbeiten
Sessions erneuern
Plattformänderungen dokumentieren
monatlich aufräumen

Die Wartung sollte nicht improvisiert erfolgen, sondern als wiederkehrende Routine.

27.1 Selektoren aktualisieren

Playwright-Adapter sind abhängig von der Weboberfläche der Plattformen.

Typische Bruchstellen:

Button-Texte ändern sich
Dialoge werden umgebaut
Upload-Felder ändern sich
Rollenbezeichnungen ändern sich
A/B-Tests verändern die Oberfläche
Sprache der Oberfläche wechselt

Selektoren dürfen deshalb nicht verstreut im Code stehen.

Schlecht:

page.get_by_role("button", name="Posten").click()

Besser:

@dataclass(frozen=True)
class LinkedInSelectorConfig:
    start_post_buttons: list[str]
    submit_buttons: list[str]
    media_buttons: list[str]


LINKEDIN_SELECTOR_CONFIG = LinkedInSelectorConfig(
    start_post_buttons=[
        "Beitrag beginnen",
        "Start a post",
    ],
    submit_buttons=[
        "Posten",
        "Post",
    ],
    media_buttons=[
        "Bild hinzufügen",
        "Medien hinzufügen",
        "Foto hinzufügen",
        "Add media",
        "Add a photo",
    ],
)

Prüfroutine bei Fehler:

Screenshot öffnen
aktuellen Buttontext prüfen
Adapter-Selektor aktualisieren
Dry-Run ausführen
Screenshot prüfen
erst danach Live-Betrieb wieder aktivieren

Nach einer Selektoränderung:

python -m src.browser_session check linkedin --headed
python -m src.main publish-next

Live erst danach:

python -m src.main publish-next --live

27.2 Prompt-Versionen pflegen

Prompts dürfen nicht stillschweigend überschrieben werden, wenn sie bereits produktiv verwendet wurden.

Empfohlene Struktur:

prompts/
├── linkedin_post.v1.txt
├── linkedin_post.v2.txt
├── workshop_note.v1.txt
├── workshop_note.v2.txt
└── quality_check.v1.txt

Prompt-Registry:

@dataclass(frozen=True)
class PromptDefinition:
    name: str
    file_name: str
    version: str
    output_type: str
    max_output_tokens: int


PROMPT_DEFINITIONS: dict[str, PromptDefinition] = {
    "linkedin_post": PromptDefinition(
        name="linkedin_post",
        file_name="linkedin_post.v2.txt",
        version="2.0.0",
        output_type="text",
        max_output_tokens=700,
    ),
}

Bei jeder Änderung festhalten:

Was wurde geändert?
Warum wurde es geändert?
Welche Beiträge waren betroffen?
Ab welchem Datum gilt die neue Version?

Beispiel:

2026-05-12
Prompt: workshop_note
Version: 2.0.0
Änderung: Verkaufsnahe Formulierungen stärker ausgeschlossen.
Grund: Mehrere Entwürfe klangen zu werblich.

Jeder PostDraft speichert weiterhin:

prompt_name
prompt_version
model
created_at

Damit bleibt nachvollziehbar, warum ältere Beiträge anders klingen.

27.3 Content-Dubletten bereinigen

Dubletten entstehen durch:

ähnliche Buchbeschreibungen
wiederholte Klassiker-Zitate
mehrere Beiträge zum selben Blogartikel
wiederkehrende Werkstatt-Themen
zu ähnliche KI-Formulierungen

Prüfung:

python -m src.main queue list --status draft
python -m src.main queue list --status published

Manuelle Dublettenbereinigung:

ähnliche Entwürfe anzeigen
schwächeren Entwurf ablehnen
besseren Entwurf freigeben
Themenliste anpassen
Prompt bei systematischer Wiederholung ändern

Beispiel für Dublettenprüfung als Wartungskommando:

class DuplicateMaintenanceService:
    def find_possible_duplicates(
        self,
        posts: list[dict[str, object]],
        max_similarity: float,
    ) -> list[tuple[str, str]]:
        result: list[tuple[str, str]] = []

        index = 0

        while index < len(posts):
            left_post = posts[index]
            left_text = left_post.get("text")
            left_id = left_post.get("id")

            if isinstance(left_text, str) is False or isinstance(left_id, str) is False:
                index += 1
                continue

            compare_index = index + 1

            while compare_index < len(posts):
                right_post = posts[compare_index]
                right_text = right_post.get("text")
                right_id = right_post.get("id")

                if isinstance(right_text, str) is True and isinstance(right_id, str) is True:
                    similarity = self._similarity(left_text, right_text)

                    if similarity >= max_similarity:
                        result.append(
                            (
                                left_id,
                                right_id,
                            )
                        )

                compare_index += 1

            index += 1

        return result

    def _similarity(self, left_text: str, right_text: str) -> float:
        left_words = set(left_text.lower().split())
        right_words = set(right_text.lower().split())

        if len(left_words) == 0 and len(right_words) == 0:
            return 1.0

        intersection = left_words.intersection(right_words)
        union = left_words.union(right_words)

        return len(intersection) / len(union)

27.4 Fehlgeschlagene Posts prüfen

Fehlgeschlagene Posts dürfen nicht liegen bleiben.

Prüfen:

python -m src.main queue list --status failed
python -m src.main queue list --status blocked
python -m src.main logs errors --limit 50

Für jeden Fehler:

Screenshot öffnen
Fehlermeldung lesen
Ursache klassifizieren
Status manuell entscheiden
Selektor korrigieren oder Beitrag ablehnen
Dry-Run wiederholen

Fehlerarten:

failed: technischer Fehler, eventuell wiederholbar
blocked: kritischer Plattformzustand, nicht automatisch wiederholen

Beispiele:

Post button not found → Selektor prüfen
Image file does not exist → Bildpfad korrigieren
Login required → Session erneuern
CAPTCHA detected → manuell prüfen, Plattform pausieren

Empfohlenes Vorgehen bei blocked:

Plattform-Kill-Switch aktivieren
Session sichtbar prüfen
Screenshot prüfen
keine Live-Aktion ausführen
nach manueller Klärung Dry-Run
Kill-Switch erst danach deaktivieren

27.5 Session erneuern

Sessions laufen ab oder werden durch Plattformen ungültig.

Symptome:

Login required
checkpoint
security verification
leerer Feed
unerwartete Login-Seite

Session erneuern:

python -m src.browser_session login linkedin
python -m src.browser_session check linkedin --headed

Für Instagram:

python -m src.browser_session login instagram
python -m src.browser_session check instagram --headed

Für Facebook:

python -m src.browser_session login facebook
python -m src.browser_session check facebook --headed

Nach Session-Erneuerung:

python -m src.main publish-next

Erst wenn der Dry-Run sauber ist:

python -m src.main publish-next --live

Session-Dateien bleiben sensibel:

storage/sessions/linkedin.json
storage/sessions/instagram.json
storage/sessions/facebook.json

Rechte prüfen:

chmod 700 storage/sessions
chmod 600 storage/sessions/*.json

27.6 Plattformänderungen dokumentieren

Jede relevante Plattformänderung wird dokumentiert.

Datei:

docs/platform_changes.md

Beispiel:

# Plattformänderungen

## 2026-05-12 LinkedIn

Problem:
Der Button „Beitrag beginnen“ wurde in der deutschen Oberfläche zeitweise nicht gefunden.

Beobachtung:
Screenshot: storage/screenshots/linkedin/draft_20260512_001_failed.png

Änderung:
Zusätzlicher Buttontext ergänzt:
- Start a post

Betroffene Datei:
src/platforms/linkedin_publisher.py

Prüfung:
Dry-Run erfolgreich am 2026-05-12.

Minimal festhalten:

Datum
Plattform
Symptom
Screenshot
geänderte Datei
neuer Selektor
Dry-Run-Ergebnis

Das verhindert, dass dieselben Fehler später erneut analysiert werden müssen.

27.7 Monatsroutine

Einmal im Monat sollte das System systematisch geprüft werden.

Checkliste:

1. Queue prüfen
2. Fehler prüfen
3. blockierte Beiträge prüfen
4. Logs rotieren oder archivieren
5. Screenshots archivieren
6. Sessions testen
7. Prompts prüfen
8. Dubletten prüfen
9. Reports auswerten
10. Plattformänderungen dokumentieren

Kommandos:

python -m src.main queue status
python -m src.main queue list --status failed
python -m src.main queue list --status blocked
python -m src.main logs errors --limit 100
python -m src.main report weekly --start 2026-05-01 --end 2026-05-31

Screenshots archivieren:

mkdir -p storage/archive/screenshots/2026-05
find storage/screenshots -type f -name "*.png" -mtime +30 -exec mv {} storage/archive/screenshots/2026-05/ \;

Logs archivieren:

mkdir -p storage/archive/logs/2026-05
cp storage/logs/events.jsonl storage/archive/logs/2026-05/events.jsonl
cp storage/logs/errors.jsonl storage/archive/logs/2026-05/errors.jsonl
cp storage/logs/openai_usage.jsonl storage/archive/logs/2026-05/openai_usage.jsonl

Danach neue Logdateien anlegen:

: > storage/logs/events.jsonl
: > storage/logs/errors.jsonl
: > storage/logs/openai_usage.jsonl

Vor dem Leeren der Logs muss das Archiv geprüft sein.

Monatsnotiz:

# Monatswartung 2026-05

Queue:
- draft: 4
- approved: 1
- published: 18
- failed: 2
- blocked: 0

Änderungen:
- LinkedIn-Selektor ergänzt
- Instagram-Caption-Prompt verschärft
- 3 Dubletten abgelehnt

Offen:
- Facebook-Bildupload erneut prüfen

Ergebnis dieses Kapitels

Die Wartung umfasst jetzt:

Selektorpflege
Prompt-Versionierung
Dublettenbereinigung
Fehlernacharbeit
Session-Erneuerung
Dokumentation von Plattformänderungen
monatliche Betriebsprüfung

Damit bleibt das System kontrollierbar, auch wenn Plattformen ihre Oberflächen ändern oder Inhalte sich wiederholen.

0 Kommentare