Files
Brancheneinstufung2/generate_marketing_text.py

183 lines
10 KiB
Python

# generate_marketing_text.py
import os
import yaml
import logging
import time
import openai
import json
import pandas as pd
import argparse
from config import Config
# NEU: Importiere unseren GoogleSheetHandler
from google_sheet_handler import GoogleSheetHandler
# --- Konfiguration ---
KNOWLEDGE_BASE_FILE = "marketing_wissen.yaml"
# NEU: Definiere den Namen des Ziel-Tabellenblatts
OUTPUT_SHEET_NAME = "Texte_Automation"
MODEL_TO_USE = "gpt-4o"
# --- Logging einrichten ---
logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s')
def call_openai_with_retry(prompt, max_retries=3, delay=5):
"""Ruft die OpenAI API mit Retry-Logik auf und erwartet eine JSON-Antwort."""
# ... (Diese Funktion bleibt unverändert) ...
for attempt in range(max_retries):
try:
logging.info(f"Sende Prompt an OpenAI (Versuch {attempt + 1}/{max_retries})...")
response = openai.ChatCompletion.create(
model=MODEL_TO_USE,
response_format={"type": "json_object"},
messages=[{"role": "user", "content": prompt}],
temperature=0.6,
max_tokens=1024
)
content = response.choices[0].message['content'].strip()
return json.loads(content)
except json.JSONDecodeError as e:
logging.error(f"Fehler beim Parsen der JSON-Antwort von OpenAI: {e}")
logging.debug(f"Rohe Antwort: {content}")
except Exception as e:
logging.error(f"Fehler bei OpenAI-API-Aufruf: {e}")
if attempt < max_retries - 1:
logging.info(f"Warte {delay} Sekunden vor dem nächsten Versuch...")
time.sleep(delay)
else:
logging.error("Maximale Anzahl an Wiederholungen erreicht.")
return None
def build_prompt(branch_name, branch_data, position_name, position_data):
"""Baut den finalen Master-Prompt (v4.1) zusammen."""
# ... (Diese Funktion bleibt unverändert) ...
branch_pain_points = "\n".join([f"- {p}" for p in branch_data.get('pain_points', [])])
position_pain_points = "\n".join([f"- {p}" for p in position_data.get('pains_DE', [])])
return "\n".join([
"Du bist ein kompetenter Lösungsberater und brillanter Texter. Du verstehst die Herausforderungen einer Branche und einer spezifischen Management-Rolle und formulierst elegante, unaufdringliche und hochrelevante E-Mail-Texte.",
"AUFGABE: Erstelle 3 Textblöcke (Subject, Introduction_Textonly, Industry_References_Textonly) für eine E-Mail.",
"\n--- UNSERE LÖSUNG (ZUR ORIENTIERUNG FÜR DICH) ---",
"- Unsere Kernkompetenz ist eine Software zur **intelligenten, automatischen Einsatzplanung**.",
"- Wir bieten zudem eine **mobile App** für die Techniker im Außdienst.",
"\n--- KONTEXT ---",
f"ZIELBRANCHE: {branch_name}",
f"BRANCHEN-HERAUSFORDERUNGEN (PAIN POINTS):\n{branch_pain_points}",
f"\nANSPRECHPARTNER: {position_name}",
f"PERSÖNLICHE HERAUSFORDERUNGEN DES ANSPRECHPARTNERS (PAIN POINTS):\n{position_pain_points}",
f"\nREFERENZKUNDEN (Rohdaten):\n{branch_data.get('references_DE', 'Keine spezifischen Referenzen vorhanden.')}",
"\n--- DEINE AUFGABE ---",
"1. **Subject:** Formuliere eine kurze Betreffzeile (max. 5 Wörter). Richte sie **direkt an einem der persönlichen Pain Points** des Ansprechpartners (z.B. 'Kostenkontrolle im Service', 'Nahtlose Systemintegration').",
"2. **Introduction_Textonly:** Formuliere einen Einleitungstext (2 Sätze).",
" - **Satz 1 (Die Brücke):** Knüpfe an die (uns unbekannte) operative Herausforderung an. Beschreibe subtil den Nutzen einer Lösung in Form von **'optimierten Planungsprozessen'** oder einer **'digitalen Unterstützung der mobilen Teams'**.",
" - **Satz 2 (Die Relevanz):** Schaffe die Relevanz für die Zielperson, indem du das Thema mit einem ihrer persönlichen Pain Points verknüpfst.",
"3. **Industry_References_Textonly:** Formuliere einen **strategischen Referenz-Block (ca. 2-3 Sätze)** nach folgendem Muster:",
" - **Satz 1 (Social Proof):** Beginne direkt mit den Referenzkunden. Formuliere z.B. 'Ihre Marktbegleiter [Kunde A] und [Kunde B] profitieren bereits...'. Integriere **alle** genannten Referenzen und quantitative Erfolge elegant.",
" - **Satz 2 (Branchen-Expertise):** Betone unsere Erfahrung. **Vermeide das Wort 'Branche'.** Formuliere stattdessen spezifisch, z.B. 'Durch die Zusammenarbeit sind wir mit den spezifischen Anforderungen von [Zielbranche]-Unternehmen bestens vertraut.'",
" - **Satz 3 (Rollen-Relevanz):** Schaffe den direkten Nutzen für die Zielperson. Formuliere z.B. 'Dieser Wissensvorsprung hilft uns, Ihre [persönlicher Pain Point der Rolle] besonders effizient zu lösen.'",
"\n--- BEISPIEL FÜR EINEN PERFEKTEN OUTPUT (Kombination Anlagenbau & IT) ---",
'''
{
"Subject": "Nahtlose Systemintegration",
"Introduction_Textonly": "Genau hier setzt die digitale Unterstützung Ihrer Techniker an, um Serviceberichte direkt vor Ort zu erfassen und die Projektabrechnung zu beschleunigen. Für Sie als IT-Leiter ist dabei die nahtlose und sichere Integration in Ihre bestehende ERP-Landschaft von entscheidender Bedeutung.",
"Industry_References_Textonly": "Ihre Marktbegleiter wie Jungheinrich mit weltweit über 4.000 Technikern und Christ Wash Systems, wo 10 % Fahrtzeit eingespart wurde, profitieren bereits von unseren Lösungen. Durch die langjährige Zusammenarbeit sind wir mit den spezifischen Anforderungen von Anlagenbau-Unternehmen, wie der Anbindung an komplexe ERP-Systeme, bestens vertraut. Dieser Wissensvorsprung hilft uns, Ihre Integrations-Herausforderungen besonders effizient und sicher zu lösen."
}
''',
"\nErstelle jetzt das JSON-Objekt für die oben genannte Kombination aus Branche und Ansprechpartner."
])
def main(specific_branch=None):
"""Hauptfunktion zur Generierung der Marketing-Texte."""
logging.info("Starte die Generierung der Marketing-Textblöcke...")
# --- NEU: Alle Initialisierungen in einen try-Block ---
try:
Config.load_api_keys()
openai.api_key = Config.API_KEYS.get('openai')
if not openai.api_key:
raise ValueError("OpenAI API Key nicht gefunden.")
with open(KNOWLEDGE_BASE_FILE, 'r', encoding='utf-8') as f:
knowledge_base = yaml.safe_load(f)
# NEU: GoogleSheetHandler initialisieren
logging.info("Initialisiere GoogleSheetHandler...")
sheet_handler = GoogleSheetHandler()
except FileNotFoundError:
logging.critical(f"FEHLER: Die Wissensbasis '{KNOWLEDGE_BASE_FILE}' wurde nicht gefunden.")
return
except Exception as e:
logging.critical(f"FEHLER bei der Initialisierung: {e}")
return
results = []
# ... (Die Logik zur Auswahl der Branchen und Positionen bleibt unverändert) ...
target_branches = knowledge_base.get('Branchen', {})
if specific_branch:
if specific_branch in target_branches:
logging.info(f"Fokus auf einzelne Branche: {specific_branch}")
target_branches = {specific_branch: target_branches[specific_branch]}
else:
logging.error(f"FEHLER: Die angegebene Branche '{specific_branch}' wurde in der Wissensbasis nicht gefunden.")
logging.info(f"Verfügbare Branchen sind: {list(knowledge_base.get('Branchen', {}).keys())}")
return
positions = knowledge_base.get('Positionen', {})
# --- NEU: Der Generierungs-Loop bleibt gleich, aber der Output ist anders ---
for branch_name, branch_data in target_branches.items():
for position_key, position_data in positions.items():
logging.info(f"--- Generiere Texte für: Branche='{branch_name}', Position='{position_key}' ---")
prompt = build_prompt(branch_name, branch_data, position_data.get('name_DE', position_key), position_data)
generated_json = call_openai_with_retry(prompt)
if generated_json:
results.append({
'Branch Detail': branch_name,
'Department': position_key,
'Language': 'DE',
'Subject': generated_json.get('Subject', 'FEHLER BEI GENERIERUNG'),
'Introduction_Textonly': generated_json.get('Introduction_Textonly', 'FEHLER BEI GENERIERUNG'),
'Industry References (Text only)': generated_json.get('Industry_References_Textonly', 'FEHLER BEI GENERIERUNG')
})
else:
results.append({
'Branch Detail': branch_name,
'Department': position_key,
'Language': 'DE',
'Subject': 'FEHLER: KI-Antwort war ungültig',
'Introduction_Textonly': 'FEHLER: KI-Antwort war ungültig',
'Industry References (Text only)': 'FEHLER: KI-Antwort war ungültig'
})
time.sleep(2)
# --- NEU: Ergebnisse in Google Sheet schreiben statt in Excel ---
if results:
# Konvertiere die Ergebnisse in das Format, das der Handler erwartet: Liste von Listen
df = pd.DataFrame(results)
header = df.columns.tolist()
values = df.values.tolist()
data_to_write = [header] + values
# Rufe unsere neue Handler-Methode auf
success = sheet_handler.clear_and_write_data(OUTPUT_SHEET_NAME, data_to_write)
if success:
logging.info(f"\nErfolgreich! {len(results)} Textvarianten wurden in das Google Sheet '{OUTPUT_SHEET_NAME}' geschrieben.")
else:
logging.error("\nFehler! Die Textvarianten konnten nicht in das Google Sheet geschrieben werden.")
else:
logging.info("Keine Textvarianten wurden generiert.")
if __name__ == "__main__":
parser = argparse.ArgumentParser(description="Generiert Marketing-Textblöcke basierend auf der Wissensbasis.")
parser.add_argument("--branch", type=str, help="Generiert Texte nur für diese eine Branche.")
args = parser.parse_args()
main(specific_branch=args.branch)