From 3f66ba877ed88f2454b4eedb8c51892791738300 Mon Sep 17 00:00:00 2001 From: Hermes Agent Date: Tue, 23 Jun 2026 22:39:31 +0200 Subject: [PATCH] =?UTF-8?q?fix:=20Pipeline=20=E2=80=94=20Dachzeile,=20Info?= =?UTF-8?q?box,=20Read-More-Position,=20Quellen-Format=20v2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- article_pipeline.py | 114 +++++++++++++++++++++++++++----------------- 1 file changed, 69 insertions(+), 45 deletions(-) diff --git a/article_pipeline.py b/article_pipeline.py index f461a20..128486c 100644 --- a/article_pipeline.py +++ b/article_pipeline.py @@ -5,30 +5,12 @@ article_pipeline.py — Stage 0: Input-Preparation für den BSN-Artikel-Workflow Liest Topic, PDFs, URLs und baut einen strukturierten Prompt für den Hermes-Agenten, der dann via delegate_task Research (Stage 1) und Write+QC (Stage 2+3) durchführt. Stage 4 (Git, Verify) macht der Agent selbst. - -Usage: - # Thema + PDF - python3 article_pipeline.py --topic "Gamegenic × TCC" --pdf pm.pdf --rubric "Nachrichten" - - # Thema + URL - python3 article_pipeline.py --topic "Neue Kennerspiel-Nominierungen" --url "https://..." - - # Nur Thema (autonome Recherche) - python3 article_pipeline.py --topic "Spiel des Jahres 2026" --rubric "Nachrichten" - - # Batch (JSON) - python3 article_pipeline.py --batch topics.json - -Output: Strukturierter Prompt nach stdout (wird vom Hermes-Cronjob als Context injiziert) -Exit-Codes: 0 = Prompt bereit, 1 = Fehler in Stage 0 """ import argparse import json import os import sys -import subprocess -import tempfile from datetime import datetime WORKSPACE = "/home/hermes/workspace" @@ -46,23 +28,6 @@ def ocr_pdf(pdf_path: str) -> str: return "\n\n".join(texts) -def extract_url_text(url: str) -> str: - """URL-Inhalt via 5-Stufen-Eskalation abrufen.""" - import urllib.request - import urllib.error - - # Stufe 1: curl direkt - try: - req = urllib.request.Request(url, headers={"User-Agent": "Mozilla/5.0"}) - with urllib.request.urlopen(req, timeout=15) as resp: - return resp.read().decode("utf-8", errors="ignore")[:5000] - except Exception: - pass - - # Stufe 2-5 wäre hier — für Stage 0 reicht Stufe 1 - return f"[URL nicht direkt abrufbar: {url}]" - - def build_prompt(topic: str, sources: list[str], rubric: str, category_id: int, image_urls: list[str], deadline: str | None, @@ -72,7 +37,48 @@ def build_prompt(topic: str, sources: list[str], rubric: str, now = datetime.now() date_str = now.strftime("%d. %B %Y") slug = topic.lower().replace(" ", "_").replace(":", "").replace("×", "x")[:40] + date_slug = now.strftime("%Y-%m-%d") + filename = f"artikel_{slug}_{date_slug}.html" + # --- WRITE+QC CONTEXT (most complex part) --- + write_context = ( + "STRUKTUR (exakt einhalten, nummeriert):\n" + "1.

TITEL

\n" + '2. DACHZEILE:
' + 'Zusammenfassung in 1-2 Sätzen
\n' + '3.

' + 'DATUM · Lesezeit: ca. N Minuten

\n' + '4. Einleitungs-Absatz

\n' + '5. INFOBOX (PFLICHT bei Unternehmen/Produkt-News): ' + '

' + '

' + 'NAME — Fakten

' + '
' + 'LabelWert
\n' + '6. ALLE Inhalts-Abschnitte (

+

)\n' + '7.


<-- NACH allen Inhalten, VOR Quellen!\n' + '8. Quellen-Überschrift: Quellen\n' + '9. JEDE Quelle als:
  • Name — Beschreibung
  • \n' + '10.

    \n' + '11.

    Recherchestand: DATUM. ' + 'Basierend auf QUELLEN.

    \n' + '\n' + 'KRITISCHE REGELN (QC-Fail bei Verstoß):\n' + '- Read-More NACH allen Inhalten, VOR Quellen (NICHT nach Absatz 1!)\n' + '- JEDE Quelle als mit target="_blank" — KEINE unverlinkten Quellen!\n' + '- Dachzeile + Infobox sind PFLICHT\n' + '- Quellen-Überschrift: font-size:1.17em (NICHT 0.9rem)\n' + '- Kein "XML API v2", kein "Betriebsgeheimnis", keine Emojis\n' + '- Datei endet mit \n' + f'- Rubrik: {rubric}' + ) + + # --- BUILD OUTPUT --- lines = [] lines.append(f"Führe den BSN-Artikel-Workflow für folgendes Thema aus:") lines.append(f"**Thema:** {topic}") @@ -80,7 +86,7 @@ def build_prompt(topic: str, sources: list[str], rubric: str, lines.append(f"**Rubrik:** {rubric} (Joomla catid={category_id})") if deadline: lines.append(f"**Deadline:** {deadline}") - lines.append(f"**Dateiname:** artikel_{slug}_{now.strftime('%Y-%m-%d')}.html") + lines.append(f"**Dateiname:** {filename}") lines.append("") # PDF-OCR-Ergebnisse @@ -106,11 +112,24 @@ def build_prompt(topic: str, sources: list[str], rubric: str, lines.append("") lines.append("Nutze **delegate_task** um einen Research-Subagent zu starten:") lines.append("") - lines.append(f"- goal: \"Recherchiere zum Thema: {topic}. Sammle Fakten aus allen Quellen, bewerte sie (AAA/AA/A), ergänze BGG-Daten (Designer, Verlag, Jahr). Erstelle Research-Brief in /tmp/research_brief.md. Bei unvollständigen Kern-Fakten → Exit 1.\"") - lines.append("- context: Enthält PDF-Texte und URLs (siehe oben). Nutze 5-Stufen-Eskalation für unerreichbare URLs. web_search site:boardgamegeek.com für BGG-Daten.") + lines.append( + f'- goal: "Recherchiere zum Thema: {topic}. ' + f'Sammle Fakten aus allen Quellen, bewerte sie (AAA/AA/A), ' + f'ergänze BGG-Daten (Designer, Verlag, Jahr). ' + f'Erstelle Research-Brief in /tmp/research_brief.md. ' + f'Bei unvollständigen Kern-Fakten → Exit 1."' + ) + lines.append( + '- context: "Enthält PDF-Texte und URLs (siehe oben). ' + 'Nutze 5-Stufen-Eskalation für unerreichbare URLs. ' + 'web_search site:boardgamegeek.com für BGG-Daten."' + ) lines.append('- toolsets: ["web", "terminal", "file", "search", "skills"]') lines.append("") - lines.append("**WICHTIG:** Warte bis Sub-Agent #1 fertig ist. Prüfe /tmp/research_brief.md. Bei Exit-Code 1 → MELDUNG an Basti, NICHT fortfahren.") + lines.append( + "**WICHTIG:** Warte bis Sub-Agent #1 fertig ist. " + "Prüfe /tmp/research_brief.md. Bei Exit-Code 1 → MELDUNG an Basti, NICHT fortfahren." + ) lines.append("") # STAGE 2+3: Write+QC @@ -118,8 +137,15 @@ def build_prompt(topic: str, sources: list[str], rubric: str, lines.append("") lines.append("Nutze **delegate_task** für einen Write+QC-Subagent:") lines.append("") - lines.append(f'- goal: "Schreibe HTML-Artikel zum Thema: {topic}. Nutze Research-Brief aus /tmp/research_brief.md. Datei: artikel_{slug}_{now.strftime("%Y-%m-%d")}.html in /home/hermes/workspace/. Danach: bash /home/hermes/workspace/verify_article.sh artikel_{slug}_{now.strftime("%Y-%m-%d")}.html. Bei Fehlern korrigieren, max 3 Versuche."') - lines.append(f'- context: "HTML-Regeln: KEIN XML API v2, KEIN Betriebsgeheimnis, KEINE Emojis.

    mit Datum+Lesezeit. Genau 1×


    . Quellen als Quellen +
      mit .

      mit Quellen-Ratings. Datei endet mit . Rubrik: {rubric}."') + lines.append( + f'- goal: "Schreibe HTML-Artikel zum Thema: {topic}. ' + f'Nutze Research-Brief aus /tmp/research_brief.md. ' + f'Datei: /home/hermes/workspace/{filename}. ' + f'Danach: bash /home/hermes/workspace/verify_article.sh ' + f'/home/hermes/workspace/{filename}. ' + f'Bei Fehlern korrigieren, max 3 Versuche."' + ) + lines.append(f'- context: """\n{write_context}\n"""') lines.append('- toolsets: ["web", "terminal", "file", "search", "skills"]') lines.append("") lines.append("**WICHTIG:** Warte bis Sub-Agent #2 fertig. Prüfe Exit-Code und Datei.") @@ -129,15 +155,13 @@ def build_prompt(topic: str, sources: list[str], rubric: str, lines.append("## STAGE 4: Deliver") lines.append("") lines.append("1. Git-Commit:") - lines.append(f" ```bash") - lines.append(f" cd /home/hermes/workspace && git add artikel_{slug}_{now.strftime('%Y-%m-%d')}.html") + lines.append(f" cd /home/hermes/workspace && git add {filename}") lines.append(f' git commit -m "artikel: {topic[:50]}"') - lines.append(f" ```") lines.append("2. Prüfen ob Article-Server erreichbar:") lines.append(" curl -s 'http://185.162.249.159:8890/?key=br3ttsp1el-n3ws-2026' | head -3") lines.append("3. Basti benachrichtigen per send_message (Telegram):") lines.append(f" ✅ Artikel fertig: {topic}") - lines.append(f" 📄 /home/hermes/workspace/artikel_{slug}_{now.strftime('%Y-%m-%d')}.html") + lines.append(f" 📄 /home/hermes/workspace/{filename}") lines.append(" 📊 verify_article.sh: 0 Fehler") return "\n".join(lines)