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 97676f4d6f
commit 220c335d6f

View File

@@ -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: <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
# ==============================================================================
@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