fix: Pipeline 4/4 Artikel erfolgreich
- _extract_json: Typographische Anfuehrungszeichen ersetzen - _extract_json: Detailliertes Error-Logging bei Parse-Fehlern - Writer-Prompt: Regel 5 verbietet doppelte Anfuehrungszeichen im Markdown (brechen JSON-String-Werte) - json.loads(strict=False) fuer rohe Newlines Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Dieser Commit ist enthalten in:
@@ -10,6 +10,9 @@ logger = logging.getLogger("blog.curator")
|
|||||||
def _extract_json(text):
|
def _extract_json(text):
|
||||||
"""Extrahiert JSON aus Claude-Antworten (robust)."""
|
"""Extrahiert JSON aus Claude-Antworten (robust)."""
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
|
# Typographische Anfuehrungszeichen ersetzen (brechen JSON)
|
||||||
|
text = text.replace("„", "'").replace("“", "'").replace("”", "'")
|
||||||
|
text = text.replace("«", "'").replace("»", "'")
|
||||||
# 1. Direktes Parsen versuchen
|
# 1. Direktes Parsen versuchen
|
||||||
try:
|
try:
|
||||||
return json.loads(text, strict=False)
|
return json.loads(text, strict=False)
|
||||||
@@ -20,8 +23,9 @@ def _extract_json(text):
|
|||||||
start = text.find(open_c)
|
start = text.find(open_c)
|
||||||
end = text.rfind(close_c)
|
end = text.rfind(close_c)
|
||||||
if start != -1 and end > start:
|
if start != -1 and end > start:
|
||||||
|
candidate = text[start:end+1]
|
||||||
try:
|
try:
|
||||||
return json.loads(text[start:end+1], strict=False)
|
return json.loads(candidate, strict=False)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError:
|
||||||
pass
|
pass
|
||||||
raise json.JSONDecodeError("Kein gueltiges JSON gefunden", text, 0)
|
raise json.JSONDecodeError("Kein gueltiges JSON gefunden", text, 0)
|
||||||
|
|||||||
@@ -10,6 +10,9 @@ logger = logging.getLogger("blog.writer")
|
|||||||
def _extract_json(text):
|
def _extract_json(text):
|
||||||
"""Extrahiert JSON aus Claude-Antworten (robust)."""
|
"""Extrahiert JSON aus Claude-Antworten (robust)."""
|
||||||
text = text.strip()
|
text = text.strip()
|
||||||
|
# Typographische Anfuehrungszeichen ersetzen (brechen JSON)
|
||||||
|
text = text.replace("„", "'").replace("“", "'").replace("”", "'")
|
||||||
|
text = text.replace("«", "'").replace("»", "'")
|
||||||
# 1. Direktes Parsen versuchen
|
# 1. Direktes Parsen versuchen
|
||||||
try:
|
try:
|
||||||
return json.loads(text, strict=False)
|
return json.loads(text, strict=False)
|
||||||
@@ -20,10 +23,11 @@ def _extract_json(text):
|
|||||||
start = text.find(open_c)
|
start = text.find(open_c)
|
||||||
end = text.rfind(close_c)
|
end = text.rfind(close_c)
|
||||||
if start != -1 and end > start:
|
if start != -1 and end > start:
|
||||||
|
candidate = text[start:end+1]
|
||||||
try:
|
try:
|
||||||
return json.loads(text[start:end+1], strict=False)
|
return json.loads(candidate, strict=False)
|
||||||
except json.JSONDecodeError:
|
except json.JSONDecodeError as e:
|
||||||
pass
|
import logging; logging.getLogger("blog.writer.json").warning(f"Parse at {open_c}..{close_c}: {e.msg} at pos {e.pos}, ctx: {repr(candidate[max(0,e.pos-40):e.pos+40])}")
|
||||||
raise json.JSONDecodeError("Kein gueltiges JSON gefunden", text, 0)
|
raise json.JSONDecodeError("Kein gueltiges JSON gefunden", text, 0)
|
||||||
|
|
||||||
DB_PATH = "/mnt/gitea/osint-data/osint.db"
|
DB_PATH = "/mnt/gitea/osint-data/osint.db"
|
||||||
@@ -115,10 +119,11 @@ REGELN FÜR DEN ARTIKEL:
|
|||||||
2. Erzählerischer Fließtext mit Kontext und Einordnung
|
2. Erzählerischer Fließtext mit Kontext und Einordnung
|
||||||
3. Verwende echte Umlaute (ü, ä, ö, ß)
|
3. Verwende echte Umlaute (ü, ä, ö, ß)
|
||||||
4. Markdown-Format: ## für Zwischenüberschriften, **fett** für Betonung
|
4. Markdown-Format: ## für Zwischenüberschriften, **fett** für Betonung
|
||||||
5. 800-1500 Wörter, gut strukturiert
|
5. WICHTIG: Verwende im Markdown-Text KEINE doppelten Anführungszeichen ("). Nutze stattdessen einfache Anführungszeichen (') oder *kursiv*. Doppelte Anführungszeichen brechen das JSON-Format.
|
||||||
6. Nenne und verlinke Quellen im Text wo möglich
|
6. 800-1500 Wörter, gut strukturiert
|
||||||
7. Meta-Description: 1 Satz, max 155 Zeichen, für Suchmaschinen
|
7. Nenne und verlinke Quellen im Text wo möglich
|
||||||
8. Am Ende: Einordnung/Ausblick (was bedeutet das?)
|
8. Meta-Description: 1 Satz, max 155 Zeichen, für Suchmaschinen
|
||||||
|
9. Am Ende: Einordnung/Ausblick (was bedeutet das?)
|
||||||
|
|
||||||
Antworte als JSON:
|
Antworte als JSON:
|
||||||
{{
|
{{
|
||||||
@@ -153,5 +158,5 @@ Falls das Thema einen geographischen Bezug hat, fülle geo_data:
|
|||||||
logger.info(f"Writer: Artikel '{article['title']}' geschrieben (${usage.cost_usd:.4f})")
|
logger.info(f"Writer: Artikel '{article['title']}' geschrieben (${usage.cost_usd:.4f})")
|
||||||
return article
|
return article
|
||||||
except (json.JSONDecodeError, IndexError, KeyError, TypeError) as e:
|
except (json.JSONDecodeError, IndexError, KeyError, TypeError) as e:
|
||||||
logger.error(f"Writer JSON-Parse-Fehler: {e}\nRaw: {result[:300]}")
|
logger.error(f"Writer JSON-Parse-Fehler: {e} | repr: {repr(result[:200])} | has_brace: {chr(123) in result}")
|
||||||
return None
|
return None
|
||||||
|
|||||||
In neuem Issue referenzieren
Einen Benutzer sperren