Anpassung FSM Pitch

This commit is contained in:
2025-07-18 08:58:39 +00:00
parent 9bfe5785e6
commit 9d227ae8da

View File

@@ -1058,76 +1058,73 @@ def verify_wiki_article_chatgpt(company_name, website, wiki_url):
def generate_fsm_pitch(company_name, company_short_name, ki_branche, website_summary, wiki_absatz, anzahl_ma, anzahl_techniker, techniker_bucket_ml): def generate_fsm_pitch(company_name, company_short_name, ki_branche, website_summary, wiki_absatz, anzahl_ma, anzahl_techniker, techniker_bucket_ml):
""" """
Generiert einen maßgeschneiderten, nicht-werblichen Satz, der eine operative Generiert einen maßgeschneiderten, nicht-werblichen Satz, der eine operative
Service-Herausforderung des Unternehmens beschreibt (v2.1). Service-Herausforderung des Unternehmens beschreibt (v2.2 - Final).
""" """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
# 1. Daten-Check: Genug Futter für einen guten Satz? # 1. Daten-Check
beschreibung_kombiniert = [] beschreibung_kombiniert = []
if website_summary and 'k.a.' not in website_summary.lower(): if website_summary and 'k.a.' not in website_summary.lower():
beschreibung_kombiniert.append(f"Website-Zusammenfassung: {website_summary}") beschreibung_kombiniert.append(f"Website-Zusammenfassung: {website_summary}")
if wiki_absatz and 'k.a.' not in wiki_absatz.lower(): if wiki_absatz and 'k.a.' not in wiki_absatz.lower():
beschreibung_kombiniert.append(f"Wikipedia-Einleitung: {wiki_absatz}") beschreibung_kombiniert.append(f"Wikipedia-Einleitung: {wiki_absatz}")
final_beschreibung = "\n".join(beschreibung_kombiniert) final_beschreibung = "\n".join(beschreibung_kombiniert)
if not final_beschreibung or len(final_beschreibung.split()) < 10: if not final_beschreibung or len(final_beschreibung.split()) < 10:
logger.warning(f"Zu wenige Informationen für FSM-Pitch für {company_name}. Breche ab.") logger.warning(f"Zu wenige Informationen für FSM-Pitch für {company_name}. Breche ab.")
return "FEHLER (Mangelnde Daten)" return "FEHLER (Mangelnde Daten)"
# 2. Firmennamen priorisieren: Kurzform > Langform # 2. Firmennamen priorisieren
display_name = company_short_name if company_short_name and company_short_name.lower() != 'k.a.' else company_name display_name = company_short_name if company_short_name and company_short_name.lower() != 'k.a.' else company_name
# 3. Mitarbeiter-/Techniker-Info aufbereiten und priorisieren # 3. Personal-Info aufbereiten
def round_number(n): def round_number(n):
if n < 100: return n if n < 100: return n
if n < 1000: return int(round(n / 50.0)) * 50 if n < 1000: return int(round(n / 50.0)) * 50
return int(round(n / 100.0)) * 100 return int(round(n / 100.0)) * 100
personal_info = "Ihrer mobilen Teams" # Neuer, neutralerer Default
personal_info = "in einem Unternehmen Ihrer Größe" # Default
try: try:
# Priorität 1: Explizite Technikerzahl
if anzahl_techniker and int(anzahl_techniker) > 0: if anzahl_techniker and int(anzahl_techniker) > 0:
anzahl_techniker = round_number(int(anzahl_techniker)) anzahl_techniker = round_number(int(anzahl_techniker))
personal_info = f"bei rund {anzahl_techniker} Servicetechnikern" personal_info = f"Ihrer {anzahl_techniker} Servicetechniker im Außendienst"
# Priorität 2: ML-Techniker-Bucket (wenn keine exakte Zahl vorhanden)
elif techniker_bucket_ml and 'k.a.' not in techniker_bucket_ml.lower() and anzahl_techniker == 0: elif techniker_bucket_ml and 'k.a.' not in techniker_bucket_ml.lower() and anzahl_techniker == 0:
personal_info = f"bei schätzungsweise {techniker_bucket_ml} Servicetechnikern" personal_info = f"Ihrer schätzungsweise {techniker_bucket_ml} Servicetechniker"
# Priorität 3: Gesamtmitarbeiterzahl
elif anzahl_ma and int(anzahl_ma) > 0: elif anzahl_ma and int(anzahl_ma) > 0:
anzahl_ma = round_number(int(anzahl_ma)) anzahl_ma = round_number(int(anzahl_ma))
personal_info = f"bei über {anzahl_ma} Mitarbeitern" personal_info = f"Ihrer Teams bei über {anzahl_ma} Mitarbeitern"
except (ValueError, TypeError): except (ValueError, TypeError):
logger.debug("Keine validen MA/Techniker-Zahlen für Pitch.") logger.debug("Keine validen MA/Techniker-Zahlen für Pitch.")
# 4. Der finale, verbesserte Prompt # 4. Der finale, stark verbesserte Prompt
prompt_parts = [ prompt_parts = [
"Du bist ein B2B-Stratege, der die operativen Herausforderungen eines Unternehmens im technischen Außendienst erkennt und präzise auf den Punkt bringt.", "Du bist ein B2B-Stratege, der die operativen Herausforderungen eines Unternehmens im technischen Außendienst erkennt und präzise auf den Punkt bringt.",
"Aufgabe: Formuliere EINEN EINZIGEN, flüssig lesbaren Satz (ca. 20-35 Wörter).", "Aufgabe: Formuliere EINEN EINZIGEN, flüssig lesbaren Satz (ca. 20-35 Wörter), der eine spezifische, operative Service-Herausforderung beschreibt.",
"Dieser Satz muss eine spezifische, operative Service-Herausforderung des Unternehmens beschreiben, basierend auf seinen Produkten oder Dienstleistungen.",
"\n--- Stil-Regeln ---", "\n--- ABSOLUTE TOP-PRIORITÄT: STRIKTE REGELN ---",
"- Formuliere als scharfsinnige Beobachtung, nicht werblich.", "1. **Fokus auf Field Service:** Der Satz MUSS eine Herausforderung beschreiben, die von mobilen Teams (Techniker, Installateure, Gutachter, etc.) gelöst wird. Beschreibe NICHT die allgemeine Unternehmensstrategie, Produktion oder Produktentwicklung.",
"- Verwende den bereitgestellten 'Kurznamen des Unternehmens' natürlich im Satz.", "2. **Kein externes Wissen:** Verwende AUSSCHLIESSLICH die Informationen aus dem 'Unternehmenskontext'. Erfinde keine Fakten (wie z.B. 'jüngste Cyberangriffe').",
"- Verwende KEINE Anführungszeichen um den Firmennamen im finalen Satz.", "3. **Vermeide Lösungs-Wörter:** Benutze NICHT unsere Lösungs-Wörter wie 'Planung', 'Disposition', 'Koordination', 'Organisation'. Fokussiere auf das Problem des Kunden (z.B. 'pünktliche Ausführung', 'schnelle Störungsbehebung').",
"- Vermeide generische Phrasen wie 'Schlüssel zum Erfolg'. Fokussiere auf operative Konsequenzen (z.B. 'entscheidend für die Servicequalität', 'unerlässlich für die Anlagenverfügbarkeit').", "4. **Keine Business-Floskeln:** Vermeide generische Phrasen. Stattdessen nenne greifbare, operative Konsequenzen.",
"- Wenn die Beschreibung keine klare Service-Tätigkeit enthält, antworte NUR mit dem Wort 'FEHLER_DATEN'.", "5. **Daten-Check:** Wenn die Beschreibung keine klare Tätigkeit für mobile Teams enthält, antworte NUR mit dem Wort 'FEHLER_DATEN'.",
"\n--- Denkprozess (Schritt für Schritt) ---", "\n--- Denkprozess (Schritt für Schritt) ---",
"1. Analysiere die Unternehmensdaten. Leite aus der Beschreibung die konkrete Tätigkeit von mobilen Teams ab (Installation, Wartung, Reparatur, Logistik, Begutachtung).", "1. Analysiere die Beschreibung: Welche **konkrete Tätigkeit** wird von mobilen Mitarbeitern ausgeführt (z.B. 'Wartung von Klimaanlagen', 'Installation von Sicherheitssystemen', 'Begutachtung von Bauschäden')?",
"2. Formuliere einen Satz, der diese Tätigkeit mit der Personalinfo verbindet und als Herausforderung darstellt.", "2. Leite daraus die **operative Konsequenz** ab, wenn diese Tätigkeit gut oder schlecht ausgeführt wird (z.B. 'Anlagenverfügbarkeit', 'Kundenzufriedenheit', 'Einhaltung von Fristen', 'Betriebssicherheit').",
"3. Wenn keine klare Service-Tätigkeit erkennbar ist, antworte mit 'FEHLER_DATEN'.", "3. Formuliere den finalen Satz, der die Tätigkeit, die Personalinfo und die operative Konsequenz elegant verbindet.",
"\n--- Unternehmenskontext ---", "\n--- Unternehmenskontext ---",
f"Kurzname des Unternehmens: {display_name}", f"Kurzname des Unternehmens: {display_name}",
f"KI-validierte Branche: {ki_branche}", f"KI-validierte Branche: {ki_branche}",
f"Beschreibung (aus Website & Wikipedia): {final_beschreibung}", f"Beschreibung (aus Website & Wikipedia): {final_beschreibung}",
f"Personalinfo für den Satz: {personal_info}", f"Personalinfo für den Satz: {personal_info}",
f"Gesamtmitarbeiterzahl (Kontext): {anzahl_ma}",
"\n--- Beispiele für ZU VERMEIDENDEN Output-Stil ---",
"- FALSCH: '...ist die nahtlose Zusammenführung von Entwicklung und Herstellung der Schlüssel zum Erfolg.' (Fokus-Drift)",
"- FALSCH: '...ist die effiziente Einsatzplanung entscheidend.' (Lösungs-Kontamination)",
"- FALSCH: '...spielt eine entscheidende Rolle für den Markterfolg.' (Business-Floskel)",
"\n--- Beispiele für den gewünschten Output-Stil ---", "\n--- Beispiele für den gewünschten Output-Stil ---",
"Beispiel 1: Angesichts des beschleunigten Ausbaus der Ladeinfrastruktur bei EnBW ist die reibungslose Koordination der Installationstermine Ihrer mobilen Teams entscheidend für den Projekterfolg.", "Beispiel 1 (Kontext: Wartung von Produktionsanlagen): Bei der Wartung komplexer Produktionsanlagen für Siemens hängt die Anlagenverfügbarkeit direkt von der pünktlichen und effizienten Durchführung der Serviceeinsätze ab.",
"Beispiel 2: Bei der Wartung komplexer Produktionsanlagen für Siemens hängt die Kundenzufriedenheit direkt von der pünktlichen und effizienten Durchführung der Serviceeinsätze ab.", "Beispiel 2 (Kontext: Installation von Sicherheitssystemen): Angesichts der hohen Sicherheitsanforderungen im Bankensektor ist die fehlerfreie Installation von Alarmsystemen durch Ihre Servicetechniker entscheidend für die Betriebssicherheit.",
"\n--- Deine Aufgabe ---", "\n--- Deine Aufgabe ---",
"Führe den Denkprozess durch und gib NUR den finalen Satz aus ODER das Wort 'FEHLER_DATEN'.", "Führe den Denkprozess durch und gib NUR den finalen Satz aus ODER das Wort 'FEHLER_DATEN'.",
@@ -1136,7 +1133,7 @@ def generate_fsm_pitch(company_name, company_short_name, ki_branche, website_sum
prompt = "\n".join(prompt_parts) prompt = "\n".join(prompt_parts)
try: try:
fsm_pitch = call_openai_chat(prompt, temperature=0.6, model="gpt-4o") fsm_pitch = call_openai_chat(prompt, temperature=0.7, model="gpt-4o")
if not fsm_pitch or "FEHLER_DATEN" in fsm_pitch: if not fsm_pitch or "FEHLER_DATEN" in fsm_pitch:
logger.warning(f"KI konnte keinen validen FSM-Pitch für {company_name} generieren (Grund: Mangelnde Daten).") logger.warning(f"KI konnte keinen validen FSM-Pitch für {company_name} generieren (Grund: Mangelnde Daten).")
return "FEHLER (Mangelnde Daten)" return "FEHLER (Mangelnde Daten)"