Add KNIEPUNKT Assistant with multi-LLM editorial workflow

Six-step weekly workflow (research → sources → storyline → draft →
quality → publication) supporting Claude, ChatGPT, Gemini, and Mistral
in parallel for creative steps. Web search via Anthropic tool for news
research. Episode index built from 34 existing KNIEPUNKT episodes for
redundancy checks. Sessions persisted as JSON for mid-workflow resume.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-04-24 23:54:23 +02:00
commit e08c484838
57 changed files with 3240 additions and 0 deletions
+114
View File
@@ -0,0 +1,114 @@
"""Draft generation across all configured providers and author selection."""
from rich.console import Console
from rich.panel import Panel
from rich.prompt import Prompt
console = Console()
_DRAFT_SYSTEM = """Du bist ein erfahrener Ghostwriter für die LinkedIn-Kolumne KNIEPUNKT von Dr. André Knie.
Tonvorgaben:
- Kolumnenhaft und glossenhaft: pointiert, amüsant, Widersprüche und Anekdoten nutzen
- Kulturell gebildet: griechische/römische Mythologie, Kanonliteratur und klassische deutsche Literatur willkommen
- Meinungsstark: die Meinung des Autors muss deutlich erkennbar sein
- Nicht zu technisch: Entscheider-Zielgruppe, kein Tech-Deep-Dive
- Lesernahe Interpretation: nicht nur Nachrichten referieren, sondern einordnen und bewerten
Format: LinkedIn Newsletter-Artikel, 600900 Wörter. Sprache: Deutsch.
Füge am Ende an: ## Quellen (nummeriert mit URL/Fundort)."""
_ENRICHMENT_SYSTEM = _DRAFT_SYSTEM + (
"\n\nDu erhältst einen bestehenden Entwurf und Überarbeitungshinweise. "
"Ändere nur, was explizit verlangt wird. Behalte Ton, Meinung und Aufhänger bei."
)
def _build_prompt(
storyline_text: str,
storyline_choice: int,
adjustments: str,
news_digest: str,
source_assessment: str,
author_input: dict,
) -> str:
adj = f"\nAnpassungen des Autors: {adjustments}" if adjustments else ""
return f"""Schreibe einen vollständigen KNIEPUNKT-Artikel.
Ausgewählte Storyline (Option {storyline_choice}):
{storyline_text}
{adj}
Verfügbare Nachrichten:
{news_digest}
Quellenbewertung (für Quellenauswahl beachten):
{source_assessment[:800]}
Persönliche Notizen des Autors:
{author_input['author_news'][:500]}
Schreibe jetzt den vollständigen Artikel im KNIEPUNKT-Stil.
Anhang: ## Quellen (nummeriert mit URL/Fundort)."""
def generate_drafts(
providers: list,
storyline_text: str,
storyline_choice: int,
adjustments: str,
news_digest: str,
source_assessment: str,
author_input: dict,
) -> dict[str, str]:
"""Run draft generation on all providers. Returns {provider_name: draft}."""
prompt = _build_prompt(storyline_text, storyline_choice, adjustments, news_digest, source_assessment, author_input)
messages = [{"role": "user", "content": prompt}]
results = {}
for provider in providers:
console.print(f"\n[yellow]Schreibe Entwurf mit {provider.name}...[/yellow]")
try:
results[provider.name] = provider.chat(messages, _DRAFT_SYSTEM, max_tokens=4096)
except Exception as e:
console.print(f"[red]{provider.name} übersprungen: {e}[/red]")
return results
def select_draft(results: dict[str, str]) -> tuple[str, str]:
"""Show all drafts, return (provider_name, selected_draft_text)."""
if not results:
raise RuntimeError("Kein Modell hat einen Entwurf geliefert. API-Keys und Verbindung prüfen.")
provider_names = list(results.keys())
for name, draft in results.items():
console.print(Panel(draft, title=f"Entwurf {name}", border_style="green"))
console.print()
if len(provider_names) > 1:
choice = Prompt.ask(
"Welchen Entwurf als Basis verwenden?",
choices=provider_names,
default=provider_names[0],
)
else:
choice = provider_names[0]
return choice, results[choice]
def enrich_draft(client, draft: str, author_feedback: str) -> str:
"""Refine the selected draft based on author feedback (Claude only)."""
from kniepunkt.llm import chat
console.print("\n[yellow]Überarbeite Entwurf...[/yellow]")
prompt = f"""Überarbeite diesen KNIEPUNKT-Entwurf basierend auf den folgenden Autorenhinweisen.
Aktueller Entwurf:
{draft}
Hinweise des Autors:
{author_feedback}"""
return chat(client, [{"role": "user", "content": prompt}], _ENRICHMENT_SYSTEM, max_tokens=4096)