#!/usr/bin/env python3 """#18 Cross-Podcast-Debatte: Kuratiere Gegenüberstellungen zu gemeinsamen Themen. Nimmt die stärksten Cross-Podcast-Paare und lässt Qwen Übereinstimmungen/Divergenzen zusammenfassen. Nutzung: DASHSCOPE_API_KEY=... python3 curate_debates.py [db-pfad] [limit] """ import json import os import sys import time import sqlite3 from openai import OpenAI DB_PATH = sys.argv[1] if len(sys.argv) > 1 else "data/db.sqlite" LIMIT = int(sys.argv[2]) if len(sys.argv) > 2 else 100 API_KEY = os.environ.get("DASHSCOPE_API_KEY", "") BASE_URL = "https://dashscope-intl.aliyuncs.com/compatible-mode/v1" MODEL = "qwen-plus" SYSTEM_PROMPT = """Du bist ein Diskursanalyst. Du erhältst zwei Textabschnitte aus VERSCHIEDENEN Podcasts, die dasselbe Thema behandeln. Erstelle eine kurze Gegenüberstellung. Antworte NUR mit JSON: { "topic": "Das gemeinsame Thema in 3-5 Wörtern", "agreement": "Worin stimmen beide überein? (1-2 Sätze)", "divergence": "Worin unterscheiden sie sich? (1-2 Sätze, oder 'keine wesentliche Divergenz')", "insight": "Was lernt man durch die Gegenüberstellung, das man aus keinem der beiden allein lernen würde? (1 Satz)" }""" def curate_pair(client, text_a, meta_a, text_b, meta_b): user_msg = f"""Podcast A — {meta_a}: "{text_a}" Podcast B — {meta_b}: "{text_b}" Erstelle die Gegenüberstellung.""" try: resp = client.chat.completions.create( model=MODEL, messages=[ {"role": "system", "content": SYSTEM_PROMPT}, {"role": "user", "content": user_msg}, ], temperature=0.2, max_tokens=300, ) content = resp.choices[0].message.content.strip() if content.startswith("```"): content = content.split("```")[1].strip() if content.startswith("json"): content = content[4:].strip() return json.loads(content) except Exception as e: return {"topic": "error", "agreement": "", "divergence": "", "insight": str(e)} def main(): if not API_KEY: print("DASHSCOPE_API_KEY nicht gesetzt.") sys.exit(1) client = OpenAI(api_key=API_KEY, base_url=BASE_URL) db = sqlite3.connect(DB_PATH) db.row_factory = sqlite3.Row db.executescript(""" CREATE TABLE IF NOT EXISTS debates ( id INTEGER PRIMARY KEY AUTOINCREMENT, topic TEXT, source_podcast TEXT, source_episode TEXT, source_idx INTEGER, target_podcast TEXT, target_episode TEXT, target_idx INTEGER, agreement TEXT, divergence TEXT, insight TEXT, score REAL ); CREATE INDEX IF NOT EXISTS idx_debates_topic ON debates(topic); """) # Get strongest cross-podcast links rows = db.execute(""" SELECT sl.podcast_id, sl.source_episode, sl.source_idx, sl.target_podcast, sl.target_episode, sl.target_idx, sl.score, p1.text as source_text, p2.text as target_text, pc1.name as source_podcast_name, pc2.name as target_podcast_name, e1.title as source_title, e1.guest as source_guest, e2.title as target_title, e2.guest as target_guest FROM semantic_links sl JOIN paragraphs p1 ON sl.podcast_id = p1.podcast_id AND sl.source_episode = p1.episode_id AND sl.source_idx = p1.idx JOIN paragraphs p2 ON sl.target_podcast = p2.podcast_id AND sl.target_episode = p2.episode_id AND sl.target_idx = p2.idx JOIN episodes e1 ON sl.podcast_id = e1.podcast_id AND sl.source_episode = e1.id JOIN episodes e2 ON sl.target_podcast = e2.podcast_id AND sl.target_episode = e2.id JOIN podcasts pc1 ON sl.podcast_id = pc1.id JOIN podcasts pc2 ON sl.target_podcast = pc2.id WHERE sl.podcast_id != sl.target_podcast ORDER BY sl.score DESC LIMIT ? """, (LIMIT,)).fetchall() print(f"Kuratiere {len(rows)} Cross-Podcast-Debatten mit {MODEL}…") existing = set() try: for r in db.execute("SELECT source_podcast||source_episode||source_idx||target_podcast||target_episode||target_idx as k FROM debates").fetchall(): existing.add(r["k"]) except Exception: pass processed = 0 for i, row in enumerate(rows): key = f"{row['podcast_id']}{row['source_episode']}{row['source_idx']}{row['target_podcast']}{row['target_episode']}{row['target_idx']}" if key in existing: continue meta_a = f"{row['source_podcast_name']} / {row['source_episode']}: {row['source_title']} ({row['source_guest']})" meta_b = f"{row['target_podcast_name']} / {row['target_episode']}: {row['target_title']} ({row['target_guest']})" result = curate_pair(client, row["source_text"][:800], meta_a, row["target_text"][:800], meta_b) db.execute( "INSERT INTO debates (topic, source_podcast, source_episode, source_idx, " "target_podcast, target_episode, target_idx, agreement, divergence, insight, score) " "VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)", (result.get("topic", ""), row["podcast_id"], row["source_episode"], row["source_idx"], row["target_podcast"], row["target_episode"], row["target_idx"], result.get("agreement", ""), result.get("divergence", ""), result.get("insight", ""), row["score"]) ) processed += 1 if processed % 10 == 0: db.commit() print(f" {processed} kuratiert…") time.sleep(0.3) db.commit() topics = db.execute("SELECT topic, COUNT(*) as c FROM debates GROUP BY topic ORDER BY c DESC LIMIT 20").fetchall() print(f"\nFertig: {processed} Debatten kuratiert.") print("Top-Themen:") for t in topics: print(f" {t['topic']}: {t['c']}") db.close() if __name__ == "__main__": main()