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:
155
helpers.py
155
helpers.py
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user