From 220c335d6f72d8e5d3ef4e4ca55e469fbe6612c1 Mon Sep 17 00:00:00 2001 From: Floke Date: Fri, 18 Jul 2025 08:23:19 +0000 Subject: [PATCH] Restore Wiki-Verify Mode, Update FSM Pitch prompt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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. --- helpers.py | 155 +++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 120 insertions(+), 35 deletions(-) diff --git a/helpers.py b/helpers.py index b29276b9..4e39446d 100644 --- a/helpers.py +++ b/helpers.py @@ -998,67 +998,152 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg logger.debug(f"Finale Branch-Evaluation: {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: + Begründung: + Vorschlag URL: + """ + + 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 # ============================================================================== + @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. - Nutzt eine "Chain-of-Thought"-Anweisung für spezifischere Ergebnisse. + Generiert einen maßgeschneiderten, nicht-werblichen Satz, der eine operative + Service-Herausforderung des Unternehmens beschreibt (v2.1). """ logger = logging.getLogger(__name__) - techniker_info = "" - if anzahl_techniker and str(anzahl_techniker).strip().lower() not in ['0', 'k.a.', '']: - techniker_info = f"bei rund {anzahl_techniker} Servicetechnikern im Außendienst" - elif anzahl_ma and str(anzahl_ma).strip().lower() not in ['0', 'k.a.', '']: - techniker_info = f"bei über {anzahl_ma} Mitarbeitern" - else: - techniker_info = "in einem Unternehmen Ihrer Größe" + # 1. Daten-Check: Genug Futter für einen guten Satz? + beschreibung_kombiniert = [] + if website_summary and 'k.a.' not in website_summary.lower(): + beschreibung_kombiniert.append(f"Website-Zusammenfassung: {website_summary}") + if wiki_absatz and 'k.a.' not in wiki_absatz.lower(): + beschreibung_kombiniert.append(f"Wikipedia-Einleitung: {wiki_absatz}") + + final_beschreibung = "\n".join(beschreibung_kombiniert) + + 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)" - # Nimm die bessere Beschreibung, wenn vorhanden - beschreibung = website_summary if website_summary and website_summary.lower() != 'k.a.' else wiki_absatz + # 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 = [ - "Du bist ein B2B-Stratege, der die verborgenen operativen Herausforderungen eines Unternehmens 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.", - "Stil-Regeln:", - "- 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.", + "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).", + "Dieser Satz muss eine spezifische, operative Service-Herausforderung des Unternehmens beschreiben, basierend auf seinen Produkten oder Dienstleistungen.", - "\n--- Denkprozess (Schritt für Schritt): ---", - "1. Analysiere die untenstehenden Unternehmensdaten.", - "2. Identifiziere die **spezifischste genannte Dienstleistung oder das spezifischste Produkt** (z.B. 'Installation von Wärmepumpen', 'Wartung von Aufzügen', 'Verlegung von Glasfaser').", - "3. Leite daraus die **konkrete operative Tätigkeit** ab, die von mobilen Teams durchgeführt wird (z.B. 'Installations-Termine', 'Störungsbehebungen', 'Wartungsarbeiten').", - "4. Formuliere den finalen Satz, der diese spezifische Tätigkeit und die Personalinfo elegant verbindet.", + "\n--- Stil-Regeln ---", + "- Formuliere als scharfsinnige Beobachtung, nicht werblich.", + "- Verwende den bereitgestellten 'Kurznamen des Unternehmens' natürlich im Satz.", + "- Verwende KEINE Anführungszeichen um den Firmennamen im finalen Satz.", + "- 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 ---", - f"Unternehmen: {company_name}", - f"Branche: {crm_branche}", - f"Beschreibung: {beschreibung}", - f"Personalinfo für den Satz: {techniker_info}", + f"Kurzname des Unternehmens: {display_name}", + f"KI-validierte Branche: {ki_branche}", + f"Beschreibung (aus Website & Wikipedia): {final_beschreibung}", + f"Personalinfo für den Satz: {personal_info}", + f"Gesamtmitarbeiterzahl (Kontext): {anzahl_ma}", "\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 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 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: 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 ---", - "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) try: - # Expliziter Aufruf eines fortschrittlicheren Modells für diese spezielle, kreative Aufgabe. - # 'gpt-4-turbo-preview' oder 'gpt-4o' sind gute, kosteneffiziente Optionen. - fsm_pitch = call_openai_chat(prompt, temperature=0.7, model="gpt-4-turbo-preview") - return fsm_pitch.strip().replace('"', '') if fsm_pitch else "k.A. (Pitch-Generierung fehlgeschlagen)" + fsm_pitch = call_openai_chat(prompt, temperature=0.6, model="gpt-4o") + 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).") + return "FEHLER (Mangelnde Daten)" + return fsm_pitch.strip().replace('"', '') except Exception as 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