From bbb76b613cd1923b4f28e89dcba2a66c24c19708 Mon Sep 17 00:00:00 2001 From: Lucas Orth Date: Sun, 20 Sep 2026 12:09:18 +0200 Subject: [PATCH] Workflows, Mu-Plugin, Feldnachweis und Doku --- README.md | 285 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 285 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..f47cbf3 --- /dev/null +++ b/README.md @@ -0,0 +1,285 @@ +# Skrift Auto Article + +Zwei n8n-Workflows fuer die Ratgeber-Produktion von skrift.de. + +``` +artikel-index.json Redaktionsplan, eine Zeile je Artikel + | + v Workflow 1: brief-vorbereiten +briefe/brief-.json NeuronWriter-Brief als Schreibvorlage + | + v Cowork schreibt den Artikel +artikel/artikel-.json fertiger Artikel + | + v Workflow 2: pruefen-und-importieren +WordPress-Entwurf + nachbessern/nachbessern-.json bei zu niedrigem Score +``` + +--- + +## 1. Einmalige Einrichtung + +### 1.1 Credentials in n8n + +Die Workflows referenzieren drei Credentials, die bereits im n8n-Credential-Store +angelegt sind. In den Node-Parametern steht kein einziges Geheimnis. + +| Credential | Typ | Inhalt | +|---|---|---| +| `NeuronWriter API` | HTTP Header Auth | Name `X-API-KEY`, Wert = NeuronWriter-Key | +| `Gitea Skrift Auto Article` | HTTP Header Auth | Name `Authorization`, Wert = `token ` | +| `WP skrift.de Redakteur` | HTTP Basic Auth | Benutzer `ai-agent`, Passwort = Application Password | + +Wird ein Key ausgetauscht, nur das Credential in n8n bearbeiten. +Die Workflow-Dateien bleiben unveraendert. + +**Gitea-Token:** braucht den Scope `write:repository`. +Mit `read:repository` allein scheitert jeder Schreibvorgang mit HTTP 403 und der +Meldung `token does not have at least one of required scope(s)`. + +**WordPress-Benutzer:** eigener Benutzer mit Rolle Redakteur, kein Administrator. +Die Rolle Redakteur darf technisch auch veroeffentlichen. Der Workflow setzt +`status: draft` hart und bricht ab, falls WordPress etwas anderes zurueckmeldet. +Wer das zusaetzlich absichern will, nimmt die Rolle Autor und entzieht per +Code-Snippet `publish_posts`. Dann kann der Benutzer aber keine fremden Beitraege +mehr aktualisieren, was die Idempotenz bricht. + +### 1.2 Mu-Plugin auf skrift.de + +`wordpress/skrift-ratgeber-felder.php` nach `wp-content/mu-plugins/` kopieren. + +Es registriert vier Meta-Felder REST-schreibbar: + +| Feld | Typ | Inhalt | +|---|---|---| +| `kurzantwort` | string | Ein Absatz, der die Suchfrage sofort beantwortet | +| `cta_ziel` | string (URL) | Zieladresse des Call-to-Action | +| `stand_datum` | string | Format `YYYY-MM-DD` | +| `faq` | array | `[{frage, antwort}]` | + +Ohne dieses Plugin nimmt die WordPress-REST-API die Felder stillschweigend nicht an. +Der Workflow erkennt das und schreibt eine Warnung in die Benachrichtigung. + +Ausgabe der FAQ im Beitrag ueber den Shortcode `[skrift_faq titel="Haeufige Fragen"]`, +zum Beispiel in einem Elementor-Shortcode-Widget. +Die drei einfachen Felder lassen sich in Elementor Pro direkt als Dynamic Tag +"Post Custom Field" einbinden. + +### 1.3 SMTP fuer die Benachrichtigung + +Beide Workflows enden auf einem Node `Benachrichtigung`, der **deaktiviert** ist. +Zum Aktivieren: SMTP-Credential zuweisen, Absenderadresse eintragen, +Node aktivieren. Die Meldung wird vom Node davor fertig formuliert, +am Text selbst muss nichts geaendert werden. + +--- + +## 2. Workflow 1: brief-vorbereiten + +### Start + +- Manuell in der n8n-Oberflaeche +- Zeitplan Montag 07:00 (Node ist deaktiviert, bei Bedarf aktivieren) +- Webhook: `POST https://n8n.lucas-orth.de/webhook/brief-vorbereiten` + +### Ablauf + +1. `artikel-index.json` ueber die Gitea-API lesen +2. Alle Zeilen mit `status: "offen"` herausfiltern, Pflichtfelder pruefen +3. Je Artikel `POST /new-query` an NeuronWriter +4. `POST /get-query` pollen, alle 20 Sekunden, maximal 30 Versuche +5. Antwort auf die benoetigten Felder reduzieren +6. `briefe/brief-.json` committen +7. Status auf `brief_bereit` setzen, `query_id` in die Zeile schreiben +8. Benachrichtigung + +### Pflichtfelder in artikel-index.json + +Die Datei ist ein Array. Jede Zeile braucht: + +```json +{ + "slug": "handgeschriebene-werbebriefe", + "titel": "Handgeschriebene Werbebriefe: Wann sie wirken und wann nicht", + "keyword": "handgeschriebene werbebriefe", + "kategorie": "Direktmarketing", + "cta_ziel": "https://skrift.de/muster-anfordern", + "status": "offen" +} +``` + +Fehlt eines der Felder in einer Zeile mit `status: "offen"`, bricht der Workflow +mit Angabe von Zeilennummer und fehlenden Feldern ab. +Zeilen mit anderem Status werden nicht geprueft. + +Statuswerte: `offen` -> `brief_bereit` (durch Workflow 1) -> danach frei nutzbar. + +### Was im Brief steht + +Die im Auftrag geforderten Felder liegen auf oberster Ebene: +`query_id`, `keyword`, `begriffe_pflicht`, `begriffe_erweitert`, `fragen`, `wortzahl_ziel`. + +`begriffe_pflicht` und `begriffe_erweitert` enthalten je Begriff die von +NeuronWriter empfohlene Spanne: + +```json +{"begriff": "handschriftlich", "min": 1, "max": 17, "bei_wettbewerbern_pc": 70} +``` + +`fragen` fasst vier NeuronWriter-Quellen zusammen, dedupliziert und nach +Wichtigkeit sortiert. Fragen aus der `topic_matrix` tragen ein `gewicht` von 1 bis 10. + +Darunter steht `artikel` mit slug, titel, kategorie und cta_ziel aus der Tabelle, +sowie ein Block `zusatz` mit Material, das beim Schreiben hilft: +Ueberschriften-Begriffe, Entitaeten, Suchintention, Zielwortzahl-Median und die +vollstaendigen Gliederungen der fuenf bestplatzierten Wettbewerber. + +--- + +## 3. Workflow 2: pruefen-und-importieren + +### Start + +- Manuell +- Webhook: `POST https://n8n.lucas-orth.de/webhook/pruefen-und-importieren` + +### Vertrag Cowork -> Workflow 2 + +Cowork legt je Artikel eine Datei `artikel/artikel-.json` an: + +```json +{ + "slug": "handgeschriebene-werbebriefe", + "titel": "Handgeschriebene Werbebriefe: Wann sie wirken und wann nicht", + "query_id": "565abcca58f65e52", + "keyword": "handgeschriebene werbebriefe", + "kategorie": "Direktmarketing", + "cta_ziel": "https://skrift.de/muster-anfordern", + "content_html": "

...

...

", + "kurzantwort": "Ein Absatz, der die Hauptfrage direkt beantwortet.", + "faq": [{"frage": "...", "antwort": "..."}], + "seo_titel": "max. 60 Zeichen", + "seo_beschreibung": "max. 155 Zeichen", + "stand_datum": "2026-09-20" +} +``` + +Pflicht sind `slug`, `titel`, `query_id`, `kategorie`, `content_html`. +Der Slug im Dateinamen muss mit dem Slug im JSON uebereinstimmen, sonst bricht +der Workflow fuer diesen Artikel ab. +`query_id` stammt aus dem Brief, Workflow 1 schreibt sie zusaetzlich in +`artikel-index.json`. + +Fehlt `stand_datum`, setzt der Workflow das heutige Datum. +Fehlt `seo_titel`, wird `titel` verwendet. + +### Ablauf + +1. Ordner `artikel/` auflisten, alle `artikel-*.json` einsammeln +2. Je Artikel: Datei laden, zugehoerigen Brief laden +3. `POST /evaluate-content` an NeuronWriter, `content_score` auslesen +4. Score unter Zielwert: + - fehlende Begriffe gegen den Brief berechnen + - `nachbessern/nachbessern-.json` committen + - naechster Artikel +5. Score ab Zielwert: + - `POST /import-content` speichert die Revision in NeuronWriter + - `GET /wp-json/wp/v2/posts?slug=` prueft auf vorhandenen Beitrag + - Kategorie suchen, bei Bedarf anlegen + - Beitrag anlegen oder aktualisieren, immer `status: draft` + - Meta-Felder und SEOPress-Felder im selben Request + - Cache leeren + - Zeile in `status-importe.json` +6. Benachrichtigung mit Vorschaulinks + +### Zielwert anpassen + +Im Node **Konfiguration**, Feld `score_ziel`. Standard ist 72. +Der Wert gilt fuer alle Artikel eines Laufs. +Alle anderen Stellschrauben liegen im selben Node: Basis-URLs, Ordnernamen, +Pause zwischen Artikeln, Empfaengeradresse. + +### Nachbesserungsbericht + +`/evaluate-content` liefert ausschliesslich `{status, content_score}`, +also keine Liste fehlender Begriffe. Der Workflow berechnet sie deshalb selbst, +indem er den Artikeltext gegen `begriffe_pflicht` und `begriffe_erweitert` +aus dem Brief zaehlt. + +```json +{ + "score": 44, + "score_ziel": 72, + "wortzahl": {"ist": 96, "ziel": 1178, "differenz": -1082}, + "fehlende_pflichtbegriffe": [{"begriff": "empfänger", "soll_min": 1, "soll_max": 9, "ist": 0, "luecke": 1}], + "fehlende_zusatzbegriffe": [], + "unbeantwortete_fragen": [{"frage": "...", "gewicht": 10}] +} +``` + +Die Zaehlung arbeitet als Teilstring auf kleingeschriebenem Text. +Komposita zaehlen mit: `brief` wird auch in `Werbebriefe` gefunden. +Umlaute werden **nicht** normalisiert. Steht im Artikel `Empfaenger` statt +`Empfänger`, gilt der Begriff als fehlend. Das ist gewollt, weil NeuronWriter +genauso zaehlt. + +--- + +## 4. Fehlerverhalten + +Jeder HTTP-Node hat einen eigenen Fehlerausgang mit einer Meldung, die die +wahrscheinliche Ursache benennt statt nur den Statuscode. + +Ein Artikel darf uebersprungen werden bei: +Analyse-Start, Status-Abfrage, Poll-Timeout, Artikel-Datei laden, +Score-Ermittlung, Nachbesserungs-Commit. +Der Lauf geht dann mit dem naechsten Artikel weiter, der Fehler landet in der +Benachrichtigung. + +Ein Artikel darf **nicht** uebersprungen werden beim Schreiben nach WordPress. +Der Node `Beitrag anlegen oder aktualisieren` hat bewusst kein Continue On Fail. +Schlaegt er fehl, stoppt der gesamte Lauf, damit kein halb importierter Zustand +unbemerkt bleibt. Dasselbe gilt fuer das Zurueckschreiben des Status in +Workflow 1: bleibt die Tabelle auf `offen`, wuerden beim naechsten Lauf +Analysen doppelt erzeugt und Kontingent verbraucht. + +HTTP-Nodes wiederholen bei technischen Fehlern dreimal mit drei Sekunden Abstand. +Zwischen zwei Artikeln liegt eine Pause von fuenf Sekunden. + +--- + +## 5. Aufbau des Repos + +``` +artikel-index.json Redaktionsplan +artikel/ fertige Artikel aus Cowork +briefe/ NeuronWriter-Briefe aus Workflow 1 +nachbessern/ Berichte zu Artikeln unter Zielwert +status-importe.json was wann als Entwurf nach WordPress ging +workflows/ die beiden importfertigen n8n-Dateien +build/ Generatorskripte, die die Workflow-JSONs erzeugen +wordpress/ Mu-Plugin fuer die Meta-Felder +docs/ Feldnachweis und Rohbelege der API-Antworten +.env Zugangsdaten, nicht versioniert +.env.example Vorlage ohne Werte +``` + +Die Workflow-JSONs werden nicht von Hand bearbeitet, sondern aus +`build/gen_wf1.py` und `build/gen_wf2.py` erzeugt: + +``` +python3 build/gen_wf1.py +python3 build/gen_wf2.py +``` + +Danach in n8n importieren oder per API aktualisieren. +Wer lieber direkt in der n8n-Oberflaeche arbeitet, exportiert den Workflow +anschliessend wieder nach `workflows/` und laesst die Generatoren liegen. + +--- + +## 6. Belege + +`docs/feldnachweis.md` listet jede Stelle, an der ein Feldname aus einer echten +API-Antwort ermittelt wurde, mit Beispielantwort. +Die Rohantworten liegen als `docs/beleg-*.json` daneben.