Restore Wiki-Verify Mode, Update FSM Pitch prompt

- FEATURE: Der `wiki_verify`-Modus wurde wiederhergestellt und ist nun über die CLI/das Menü aufrufbar.
- FIX: `data_processor.py` enthält nun die `process_wiki_verify`-Methode, die gezielt Wikipedia-Artikel mittels ChatGPT verifiziert.
- FIX: Dispatcher in `brancheneinstufung.py` erkennt und startet den `wiki_verify`-Modus korrekt.
This commit is contained in:
2025-07-18 08:23:19 +00:00
parent 31560d32a7
commit b48fcc5b56

View File

@@ -998,67 +998,152 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
logger.debug(f"Finale Branch-Evaluation: {result}") logger.debug(f"Finale Branch-Evaluation: {result}")
return result return result
@retry_on_failure
def verify_wiki_article_chatgpt(company_name, website, wiki_url):
"""
Überprüft mittels ChatGPT, ob ein gegebener Wikipedia-Artikel zum Unternehmen passt.
"""
logger = logging.getLogger(__name__)
logger.info(f"Starte ChatGPT-Verifizierung für '{company_name}' und URL '{wiki_url[:50]}...'")
prompt = f"""
Aufgabe: Prüfe, ob der folgende Wikipedia-Artikel tatsächlich das angegebene Unternehmen beschreibt.
Unternehmen:
- Name: {company_name}
- Website: {website}
Vorgegebener Wikipedia-Artikel:
- URL: {wiki_url}
Antworte ausschließlich im folgenden Format (keine Einleitung, kein Schlusssatz):
Konsistenz: <OK oder X>
Begründung: <Sehr kurze Begründung für deine Entscheidung. Gib an, welche Namen oder Fakten übereinstimmen oder abweichen.>
Vorschlag URL: <Gib hier die korrekte URL an, falls der Artikel falsch ist und du eine bessere findest. Sonst leer lassen.>
"""
try:
chat_response = call_openai_chat(prompt, temperature=0.0)
if not chat_response:
raise APIError("Keine Antwort von OpenAI für Wiki-Verifizierung erhalten.")
# Parsing der Antwort
result = {"consistency": "FEHLER", "justification": "", "suggested_url": ""}
lines = chat_response.strip().split("\n")
for line in lines:
if ":" in line:
key, value = line.split(":", 1)
key = key.strip().lower()
value = value.strip()
if key == "konsistenz":
result["consistency"] = value.upper()
elif key == "begründung":
result["justification"] = value
elif key == "vorschlag url":
result["suggested_url"] = value
return result
except Exception as e:
logger.error(f"Fehler bei der ChatGPT Wiki-Verifizierung: {e}")
return {"consistency": "FEHLER API", "justification": str(e), "suggested_url": ""}
# ============================================================================== # ==============================================================================
# Chat GPT FSM Pitch # Chat GPT FSM Pitch
# ============================================================================== # ==============================================================================
@retry_on_failure @retry_on_failure
def generate_fsm_argument(company_name, crm_branche, website_summary, wiki_absatz, anzahl_ma, anzahl_techniker): 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, warum das Unternehmen FSM einsetzen sollte. Generiert einen maßgeschneiderten, nicht-werblichen Satz, der eine operative
Nutzt eine "Chain-of-Thought"-Anweisung für spezifischere Ergebnisse. Service-Herausforderung des Unternehmens beschreibt (v2.1).
""" """
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
techniker_info = "" # 1. Daten-Check: Genug Futter für einen guten Satz?
if anzahl_techniker and str(anzahl_techniker).strip().lower() not in ['0', 'k.a.', '']: beschreibung_kombiniert = []
techniker_info = f"bei rund {anzahl_techniker} Servicetechnikern im Außendienst" if website_summary and 'k.a.' not in website_summary.lower():
elif anzahl_ma and str(anzahl_ma).strip().lower() not in ['0', 'k.a.', '']: beschreibung_kombiniert.append(f"Website-Zusammenfassung: {website_summary}")
techniker_info = f"bei über {anzahl_ma} Mitarbeitern" if wiki_absatz and 'k.a.' not in wiki_absatz.lower():
else: beschreibung_kombiniert.append(f"Wikipedia-Einleitung: {wiki_absatz}")
techniker_info = "in einem Unternehmen Ihrer Größe"
# Nimm die bessere Beschreibung, wenn vorhanden final_beschreibung = "\n".join(beschreibung_kombiniert)
beschreibung = website_summary if website_summary and website_summary.lower() != 'k.a.' else wiki_absatz
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.")
return "FEHLER (Mangelnde Daten)"
# 2. Firmennamen priorisieren: Kurzform > Langform
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
def round_number(n):
if n < 100: return n
if n < 1000: return int(round(n / 50.0)) * 50
return int(round(n / 100.0)) * 100
personal_info = "in einem Unternehmen Ihrer Größe" # Default
try:
# Priorität 1: Explizite Technikerzahl
if anzahl_techniker and int(anzahl_techniker) > 0:
anzahl_techniker = round_number(int(anzahl_techniker))
personal_info = f"bei rund {anzahl_techniker} Servicetechnikern"
# 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:
personal_info = f"bei schätzungsweise {techniker_bucket_ml} Servicetechnikern"
# Priorität 3: Gesamtmitarbeiterzahl
elif anzahl_ma and int(anzahl_ma) > 0:
anzahl_ma = round_number(int(anzahl_ma))
personal_info = f"bei über {anzahl_ma} Mitarbeitern"
except (ValueError, TypeError):
logger.debug("Keine validen MA/Techniker-Zahlen für Pitch.")
# 4. Der finale, verbesserte Prompt
prompt_parts = [ prompt_parts = [
"Du bist ein B2B-Stratege, der die verborgenen operativen Herausforderungen eines Unternehmens 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), der als hochpersonalisierter Einstieg in einer E-Mail dient.", "Aufgabe: Formuliere EINEN EINZIGEN, flüssig lesbaren Satz (ca. 20-35 Wörter).",
"Stil-Regeln:", "Dieser Satz muss eine spezifische, operative Service-Herausforderung des Unternehmens beschreiben, basierend auf seinen Produkten oder Dienstleistungen.",
"- Absolut NICHT werblich klingen. Keine Produktnamen, keine direkten Lösungsangebote.",
"- Formuliere es als eine scharfsinnige Beobachtung über die operative Tätigkeit des Unternehmens.",
"- Der Satz muss spezifische Keywords aus der Unternehmensbeschreibung aufgreifen.",
"\n--- Denkprozess (Schritt für Schritt): ---", "\n--- Stil-Regeln ---",
"1. Analysiere die untenstehenden Unternehmensdaten.", "- Formuliere als scharfsinnige Beobachtung, nicht werblich.",
"2. Identifiziere die **spezifischste genannte Dienstleistung oder das spezifischste Produkt** (z.B. 'Installation von Wärmepumpen', 'Wartung von Aufzügen', 'Verlegung von Glasfaser').", "- Verwende den bereitgestellten 'Kurznamen des Unternehmens' natürlich im Satz.",
"3. Leite daraus die **konkrete operative Tätigkeit** ab, die von mobilen Teams durchgeführt wird (z.B. 'Installations-Termine', 'Störungsbehebungen', 'Wartungsarbeiten').", "- Verwende KEINE Anführungszeichen um den Firmennamen im finalen Satz.",
"4. Formuliere den finalen Satz, der diese spezifische Tätigkeit und die Personalinfo elegant verbindet.", "- 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').",
"- Wenn die Beschreibung keine klare Service-Tätigkeit enthält, antworte NUR mit dem Wort 'FEHLER_DATEN'.",
"\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).",
"2. Formuliere einen Satz, der diese Tätigkeit mit der Personalinfo verbindet und als Herausforderung darstellt.",
"3. Wenn keine klare Service-Tätigkeit erkennbar ist, antworte mit 'FEHLER_DATEN'.",
"\n--- Unternehmenskontext ---", "\n--- Unternehmenskontext ---",
f"Unternehmen: {company_name}", f"Kurzname des Unternehmens: {display_name}",
f"Branche: {crm_branche}", f"KI-validierte Branche: {ki_branche}",
f"Beschreibung: {beschreibung}", f"Beschreibung (aus Website & Wikipedia): {final_beschreibung}",
f"Personalinfo für den Satz: {techniker_info}", f"Personalinfo für den Satz: {personal_info}",
f"Gesamtmitarbeiterzahl (Kontext): {anzahl_ma}",
"\n--- Beispiele für den gewünschten Output-Stil ---", "\n--- Beispiele für den gewünschten Output-Stil ---",
"Beispiel 1 (Kontext: Branche 'Energie', Beschreibung '...baut Ladeinfrastruktur aus...'): Angesichts des beschleunigten Ausbaus der Ladeinfrastruktur ist die reibungslose Koordination der Installationstermine Ihrer mobilen Teams entscheidend für den Projekterfolg.", "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 2 (Kontext: Branche 'Anlagenbau', Beschreibung '...Service für Produktionsanlagen...'): Bei der Wartung komplexer Produktionsanlagen hängt die Kundenzufriedenheit 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.",
"\n--- Deine Aufgabe ---", "\n--- Deine Aufgabe ---",
"Führe den Denkprozess durch und gib NUR den finalen, perfekten Satz für das oben genannte Unternehmen aus:", "Führe den Denkprozess durch und gib NUR den finalen Satz aus ODER das Wort 'FEHLER_DATEN'.",
] ]
prompt = "\n".join(prompt_parts) prompt = "\n".join(prompt_parts)
try: try:
# Expliziter Aufruf eines fortschrittlicheren Modells für diese spezielle, kreative Aufgabe. fsm_pitch = call_openai_chat(prompt, temperature=0.6, model="gpt-4o")
# 'gpt-4-turbo-preview' oder 'gpt-4o' sind gute, kosteneffiziente Optionen. if not fsm_pitch or "FEHLER_DATEN" in fsm_pitch:
fsm_pitch = call_openai_chat(prompt, temperature=0.7, model="gpt-4-turbo-preview") logger.warning(f"KI konnte keinen validen FSM-Pitch für {company_name} generieren (Grund: Mangelnde Daten).")
return fsm_pitch.strip().replace('"', '') if fsm_pitch else "k.A. (Pitch-Generierung fehlgeschlagen)" return "FEHLER (Mangelnde Daten)"
return fsm_pitch.strip().replace('"', '')
except Exception as e: except Exception as e:
logger.error(f"Fehler bei der Generierung des FSM-Pitches für {company_name}: {e}") logger.error(f"Fehler bei der Generierung des FSM-Pitches für {company_name}: {e}")
return "k.A. (Fehler bei Pitch-Generierung)" return "FEHLER (API-Fehler)"
@retry_on_failure @retry_on_failure