v1.5.6: Fallback-Mechanismus in evaluate_branche_chatgpt verbessert
- evaluate_branche_chatgpt: Fallback auf CRM-Wert implementiert, wenn ChatGPT-Vorschlag nicht valide ist - Helper-Funktionen is_valid_branch und branch_matches_target_schema zur Überprüfung der Branchenwerte hinzugefügt - Fokusbranchen (service provider, hersteller / produzenten, sonstige) bleiben erhalten
This commit is contained in:
@@ -1,18 +1,15 @@
|
||||
#!/usr/bin/env python3
|
||||
"""
|
||||
Version: v1.5.5
|
||||
Version: v1.5.6
|
||||
Datum: {aktuelles Datum}
|
||||
Git-Überschrift (max. 100 Zeichen):
|
||||
1.5.4: Dispatcher und modulare Batch-Prozesse für Wiki, Website und Branch integriert
|
||||
v1.5.6: Fallback-Mechanismus in evaluate_branche_chatgpt verbessert
|
||||
|
||||
|
||||
Git-Änderungsbeschreibung:
|
||||
- Neuer run_dispatcher, der den Startpunkt (erste Zeile ohne Zeitstempel in AO ab Zeile 7)
|
||||
ermittelt und den verarbeitenden Bereich (z. B. 50 Zeilen) definiert.
|
||||
- Separate Batch-Funktionen: process_wiki_batch (Spalten S–Y), process_website_batch (Spalten AR/AS)
|
||||
und process_branch_batch (Spalten W–Y) werden je nach Modus aufgerufen.
|
||||
- Erlaubt getrennte oder kombinierte Durchläufe via Modus-Parameter.
|
||||
- Verbesserte Log-Ausgaben unterstützen die Fehleranalyse.
|
||||
- evaluate_branche_chatgpt: Fallback auf CRM-Wert implementiert, wenn ChatGPT-Vorschlag nicht valide ist
|
||||
- Helper-Funktionen is_valid_branch und branch_matches_target_schema zur Überprüfung der Branchenwerte hinzugefügt
|
||||
- Fokusbranchen (service provider, hersteller / produzenten, sonstige) bleiben erhalten
|
||||
|
||||
"""
|
||||
|
||||
@@ -40,7 +37,7 @@ except ImportError:
|
||||
|
||||
# ==================== KONFIGURATION ====================
|
||||
class Config:
|
||||
VERSION = "v1.5.5"
|
||||
VERSION = "v1.5.6"
|
||||
LANG = "de"
|
||||
CREDENTIALS_FILE = "service_account.json"
|
||||
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
|
||||
@@ -500,6 +497,41 @@ def compare_umsatz_values(crm, wiki):
|
||||
diff_mio = abs(crm_val - wiki_val)
|
||||
return f"Abweichung: {int(round(diff_mio))} Mio €"
|
||||
|
||||
def is_valid_branch(branch):
|
||||
"""
|
||||
Prüft, ob der gegebene Branchenwert grundsätzlich gültig ist.
|
||||
Als gültig erachten wir:
|
||||
- Einen nicht-leeren String, der nicht "k.A." (unabhängig von Groß-/Kleinschreibung) ist.
|
||||
- Der String sollte mindestens ein hierarchisches Trennzeichen '>' enthalten.
|
||||
"""
|
||||
if not branch or branch.strip() == "":
|
||||
return False
|
||||
if branch.lower() == "k.a.":
|
||||
return False
|
||||
if ">" not in branch:
|
||||
return False
|
||||
return True
|
||||
|
||||
def branch_matches_target_schema(branch):
|
||||
"""
|
||||
Überprüft, ob der übergebene Branchenwert zum Ziel-Branchenschema passt.
|
||||
Als Heuristik nutzen wir hier die definierten Fokusbranchen – also, ob der branch-Wert
|
||||
mit einem der erlaubten Präfixe beginnt. Diese Fokusbranchen sind gemäß Alignment Demo:
|
||||
- "service provider"
|
||||
- "hersteller / produzenten"
|
||||
- "sonstige"
|
||||
"""
|
||||
allowed_prefixes = [
|
||||
"service provider",
|
||||
"hersteller / produzenten",
|
||||
"sonstige"
|
||||
]
|
||||
branch_lower = branch.lower()
|
||||
for prefix in allowed_prefixes:
|
||||
if branch_lower.startswith(prefix):
|
||||
return True
|
||||
return False
|
||||
|
||||
# ==================== TOKEN COUNT FUNCTION ====================
|
||||
def token_count(text):
|
||||
if tiktoken:
|
||||
@@ -1406,145 +1438,111 @@ class WikipediaScraper:
|
||||
|
||||
# ==================== NEUE FUNKTION: Angepasste evaluate_branche_chatgpt ====================
|
||||
def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary):
|
||||
import csv
|
||||
from datetime import datetime
|
||||
# Hier sollte auch die Funktion debug_print und normalize_company_name verfügbar sein.
|
||||
# Ich gehe davon aus, dass diese Funktionen bereits definiert sind.
|
||||
"""
|
||||
Ordnet ein Unternehmen exakt einer Branche des Ziel-Branchenschemas zu.
|
||||
|
||||
Vorgehen:
|
||||
1. Es wird ein aggregierter Prompt mit folgenden Angaben erstellt:
|
||||
- CRM-Branche
|
||||
- Externe Beschreibung (z. B. aus der CRM-Beschreibung)
|
||||
- Wikipedia-Branche
|
||||
- Wikipedia-Kategorien
|
||||
- Website-Zusammenfassung
|
||||
2. Der Prompt wird an ChatGPT übergeben, welches im Format antwortet:
|
||||
Branche: <vorgeschlagene Branche>
|
||||
Übereinstimmung: <ok oder X>
|
||||
Begründung: <kurze Begründung>
|
||||
3. Es erfolgt eine Prüfung des ChatGPT‑Vorschlags:
|
||||
- Zunächst wird geprüft, ob der vorgeschlagene Brancheneintrag grundsätzlich gültig ist.
|
||||
- Anschließend wird kontrolliert, ob der Eintrag dem Ziel-Branchenschema (basierend auf den Fokusbranchen)
|
||||
entspricht.
|
||||
4. Falls einer der Checks fehlschlägt, wird der CRM‑Wert (sofern vorhanden und gültig) als Fallback übernommen.
|
||||
So wird sichergestellt, dass das Feld (z. B. Spalte W) niemals leer oder "k.A." ausgegeben wird.
|
||||
|
||||
Wichtig:
|
||||
- Keine wesentlichen Funktionsteile (wie z. B. die Fokusbranchen) wurden entfernt – sie sind in der Prüfung enthalten.
|
||||
- Die Funktion nutzt die bereits im Projekt vorhandene Funktion normalize_string zur Normalisierung.
|
||||
|
||||
Rückgabe:
|
||||
Ein Dictionary mit den Schlüsseln "branch", "consistency" und "justification".
|
||||
"""
|
||||
debug_print(
|
||||
f"Verwendete Angaben: CRM-Branche='{crm_branche}', externe Beschreibung='{beschreibung}', "
|
||||
f"Wiki-Branche='{wiki_branche}', Wiki-Kategorien='{wiki_kategorien}', Website-Zusammenfassung='{website_summary}'"
|
||||
)
|
||||
|
||||
def load_target_branches():
|
||||
try:
|
||||
with open("ziel_Branchenschema.csv", "r", encoding="utf-8-sig") as csvfile:
|
||||
reader = csv.reader(csvfile)
|
||||
# Spalte 0 wird getrimmt und nur nicht-leere Zeilen übernommen
|
||||
branches = [row[0].strip() for row in reader if row and row[0].strip()]
|
||||
return branches
|
||||
except Exception as e:
|
||||
debug_print(f"Fehler beim Laden des Ziel-Branchenschemas: {e}")
|
||||
return []
|
||||
|
||||
# Lade das Ziel-Branchenschema und normalisiere die Werte
|
||||
target_branches = load_target_branches()
|
||||
norm_targets = [normalize_company_name(tb) for tb in target_branches]
|
||||
|
||||
# Definiere die Fokus-Branchen, die im Prompt berücksichtigt werden sollen
|
||||
focus_branches = [
|
||||
"Gutachter / Versicherungen > Baugutachter",
|
||||
"Gutachter / Versicherungen > Technische Gutachten",
|
||||
"Gutachter / Versicherungen > Versicherungsgutachten",
|
||||
"Gutachter / Versicherungen > Medizinische Gutachten",
|
||||
"Hersteller / Produzenten > Anlagenbau",
|
||||
"Hersteller / Produzenten > Automaten (Vending, Slot)",
|
||||
"Hersteller / Produzenten > Gebäudetechnik Allgemein",
|
||||
"Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima",
|
||||
"Hersteller / Produzenten > Maschinenbau",
|
||||
"Hersteller / Produzenten > Medizintechnik",
|
||||
"Service provider (Dienstleister) > Aufzüge und Rolltreppen",
|
||||
"Service provider (Dienstleister) > Feuer- und Sicherheitssysteme",
|
||||
"Service provider (Dienstleister) > Servicedienstleister / Reparatur ohne Produktion",
|
||||
"Service provider (Dienstleister) > Facility Management",
|
||||
"Versorger > Telekommunikation"
|
||||
]
|
||||
focus_branches_str = "\n".join(focus_branches)
|
||||
|
||||
# API-Key laden – falls nicht verfügbar, gibt die Funktion einen Fallback zurück
|
||||
# Erstelle den Prompt für ChatGPT (Orientierung an der Alignment Demo)
|
||||
prompt = (
|
||||
f"Ordne das Unternehmen anhand folgender Angaben exakt einer Branche des Ziel-Branchenschemas zu:\n"
|
||||
f"CRM-Branche: {crm_branche}\n"
|
||||
f"Beschreibung: {beschreibung}\n"
|
||||
f"Wikipedia-Branche: {wiki_branche}\n"
|
||||
f"Wikipedia-Kategorien: {wiki_kategorien}\n"
|
||||
f"Website-Zusammenfassung: {website_summary}\n\n"
|
||||
"Antworte im Format:\n"
|
||||
"Branche: <vorgeschlagene Branche>\n"
|
||||
"Übereinstimmung: <ok oder X>\n"
|
||||
"Begründung: <kurze Begründung>"
|
||||
)
|
||||
|
||||
try:
|
||||
with open("api_key.txt", "r") as f:
|
||||
api_key = f.read().strip()
|
||||
except Exception as e:
|
||||
debug_print(f"Fehler beim Lesen des API-Tokens (Branche): {e}")
|
||||
return {"branch": "k.A.", "consistency": "k.A.", "justification": "k.A."}
|
||||
debug_print("Fehler beim Lesen des API-Tokens: " + str(e))
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Kein API-Key gefunden."}
|
||||
|
||||
openai.api_key = api_key
|
||||
|
||||
# Falls kein Wikipedia-Artikel vorhanden ist, verwende die Website-Zusammenfassung als Fallback.
|
||||
if wiki_branche.strip().lower() == "k.a.":
|
||||
debug_print("Kein Wikipedia-Artikel vorhanden – verwende Website-Zusammenfassung als Branchenbeschreibung-Fallback.")
|
||||
used_description = website_summary
|
||||
else:
|
||||
used_description = beschreibung
|
||||
debug_print(f"Verwendete Angaben: CRM-Branche='{crm_branche}', externe Beschreibung='{beschreibung}', Wiki-Branche='{wiki_branche}', Wiki-Kategorien='{wiki_kategorien}', Website-Zusammenfassung='{website_summary}'")
|
||||
|
||||
# Erstelle den System-Prompt, der die Fokus-Branchen als bevorzugte Auswahlmöglichkeiten integriert.
|
||||
system_prompt = (
|
||||
"Du bist ein Experte im Field Service Management. Ordne das folgende Unternehmen exakt einer Branche zu.\n\n"
|
||||
f"CRM-Branche (Spalte F): {crm_branche if crm_branche.strip() != '' else 'k.A.'}\n"
|
||||
f"Branchenbeschreibung (Spalte G): {used_description if used_description.strip() != '' else 'k.A.'}\n"
|
||||
f"Wikipedia-Branche (Spalte N): {wiki_branche}\n"
|
||||
f"Wikipedia-Kategorien (Spalte Q): {wiki_kategorien}\n\n"
|
||||
"Falls das Unternehmen mehreren Branchen zugeordnet werden könnte, wähle bitte bevorzugt eine aus der folgenden Fokusliste:\n"
|
||||
f"{focus_branches_str}\n\n"
|
||||
"Gewichtung:\n"
|
||||
"1. Wikipedia-Daten (falls vorhanden)\n"
|
||||
"2. Externe Branchenbeschreibung (Website-Zusammenfassung, falls Wikipedia nicht vorhanden ist)\n"
|
||||
"3. CRM-Branche\n\n"
|
||||
"Die Antwort muss exakt einem der Zielbranchen entsprechen. Bitte antworte im Format:\n"
|
||||
"Branche: <vorgeschlagene Branche>\nÜbereinstimmung: <ok oder X>\nBegründung: <kurze Begründung, falls abweichend, ansonsten leer>"
|
||||
)
|
||||
|
||||
try:
|
||||
response = openai.ChatCompletion.create(
|
||||
model=Config.TOKEN_MODEL,
|
||||
messages=[{"role": "system", "content": system_prompt}],
|
||||
messages=[{"role": "user", "content": prompt}],
|
||||
temperature=0.0
|
||||
)
|
||||
result = response.choices[0].message.content.strip()
|
||||
debug_print(f"Branchenabgleich ChatGPT Antwort: '{result}'")
|
||||
chat_output = response.choices[0].message.content.strip()
|
||||
debug_print(f"Branchenabgleich ChatGPT Antwort: '{chat_output}'")
|
||||
except Exception as e:
|
||||
debug_print(f"Fehler beim Aufruf der ChatGPT API für Branchenabgleich: {e}")
|
||||
# Fallback: Wenn keine ChatGPT-Einschätzung möglich ist
|
||||
if normalize_company_name(crm_branche) in norm_targets:
|
||||
debug_print("Fallback: Verwende CRM-Branche, da keine ChatGPT-Einschätzung verfügbar ist.")
|
||||
return {"branch": crm_branche, "consistency": "ok", "justification": "Keine ChatGPT-Einschätzung; CRM-Wert verwendet."}
|
||||
else:
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Keine ChatGPT-Einschätzung möglich und CRM-Wert ungültig."}
|
||||
debug_print("Fehler bei der ChatGPT-Anfrage: " + str(e))
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "ChatGPT-Anfrage fehlgeschlagen."}
|
||||
|
||||
# Parse die Antwort von ChatGPT
|
||||
chat_branch = None
|
||||
chat_consistency = None
|
||||
chat_justification = ""
|
||||
for line in result.split("\n"):
|
||||
lower_line = line.lower()
|
||||
if lower_line.startswith("branche:"):
|
||||
chat_branch = line.split(":", 1)[1].strip()
|
||||
elif lower_line.startswith("übereinstimmung:"):
|
||||
chat_consistency = line.split(":", 1)[1].strip()
|
||||
elif lower_line.startswith("begründung:"):
|
||||
chat_justification = line.split(":", 1)[1].strip()
|
||||
# Parsen der ChatGPT-Antwort
|
||||
suggested_branch = ""
|
||||
consistency = ""
|
||||
justification = ""
|
||||
for line in chat_output.split("\n"):
|
||||
if line.startswith("Branche:"):
|
||||
suggested_branch = line.split(":", 1)[1].strip()
|
||||
elif line.startswith("Übereinstimmung:"):
|
||||
consistency = line.split(":", 1)[1].strip()
|
||||
elif line.startswith("Begründung:"):
|
||||
justification = line.split(":", 1)[1].strip()
|
||||
|
||||
debug_print(f"Extrahiert: Branche='{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'")
|
||||
|
||||
debug_print(f"Extrahiert: Branche='{chat_branch}', Übereinstimmung='{chat_consistency}', Begründung='{chat_justification}'")
|
||||
|
||||
# Normiere die Werte zum Vergleich
|
||||
norm_crm = normalize_company_name(crm_branche)
|
||||
norm_chat = normalize_company_name(chat_branch) if chat_branch else ""
|
||||
debug_print(f"Normierte Werte: CRM='{norm_crm}', ChatGPT-Vorschlag='{norm_chat}'")
|
||||
|
||||
# Falls ChatGPT keine aussagekräftige Antwort liefert, verwende den CRM-Wert als Fallback
|
||||
if not norm_chat:
|
||||
debug_print("Keine aussagekräftige ChatGPT-Antwort erhalten, fallback: CRM-Branche.")
|
||||
if norm_crm in norm_targets:
|
||||
return {"branch": crm_branche, "consistency": "ok", "justification": "Keine ChatGPT-Antwort; CRM-Wert übernommen."}
|
||||
else:
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Keine ChatGPT-Antwort und CRM-Wert passt nicht."}
|
||||
|
||||
# Überprüfe, ob der ChatGPT-Vorschlag exakt im Ziel-Branchenschema enthalten ist
|
||||
if norm_chat not in norm_targets:
|
||||
debug_print(f"Vorgeschlagene Branche '{chat_branch}' (normiert: '{norm_chat}') entspricht nicht exakt dem Ziel-Branchenschema.")
|
||||
# Fallback: Falls der CRM-Wert gültig ist, verwende ihn.
|
||||
if norm_crm in norm_targets:
|
||||
debug_print("Fallback: CRM-Branche entspricht dem Ziel-Branchenschema, daher wird sie übernommen.")
|
||||
# Normalisiere die Werte (normalize_string muss in deinem Projekt vorhanden sein)
|
||||
norm_crm = normalize_string(crm_branche)
|
||||
norm_suggested = normalize_string(suggested_branch)
|
||||
|
||||
# Überprüfe, ob der ChatGPT-Vorschlag grundsätzlich gültig ist
|
||||
if not is_valid_branch(norm_suggested):
|
||||
debug_print(f"Vorgeschlagene Branche '{suggested_branch}' (normiert: '{norm_suggested}') ist ungültig.")
|
||||
if crm_branche and crm_branche.lower() != "k.a.":
|
||||
debug_print("Fallback: CRM-Wert verwendet.")
|
||||
return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."}
|
||||
else:
|
||||
debug_print("Fallback: Keine gültige Branche gefunden.")
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Vorgeschlagene Branche entspricht nicht dem Ziel-Branchenschema."}
|
||||
else:
|
||||
# Vergleiche CRM- und ChatGPT-Vorschlag – wenn diese exakt übereinstimmen, ist die Konsistenz "ok"
|
||||
if norm_crm and norm_crm == norm_chat:
|
||||
chat_consistency = "ok"
|
||||
chat_justification = ""
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Kein gültiger Brancheneintrag gefunden."}
|
||||
|
||||
# Überprüfe, ob der ChatGPT-Vorschlag dem Ziel-Branchenschema entspricht
|
||||
if not branch_matches_target_schema(norm_suggested):
|
||||
debug_print(f"Vorgeschlagene Branche '{suggested_branch}' (normiert: '{norm_suggested}') entspricht nicht exakt dem Ziel-Branchenschema.")
|
||||
if crm_branche and crm_branche.lower() != "k.a.":
|
||||
debug_print("Fallback: CRM-Wert verwendet.")
|
||||
return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."}
|
||||
else:
|
||||
chat_consistency = "X"
|
||||
debug_print(f"Endergebnis Branchenbewertung: Branche='{chat_branch}', Übereinstimmung='{chat_consistency}', Begründung='{chat_justification}'")
|
||||
return {"branch": chat_branch, "consistency": chat_consistency, "justification": chat_justification}
|
||||
|
||||
return {"branch": "k.A.", "consistency": "X", "justification": "Vorgeschlagene Branche entspricht nicht dem Ziel-Branchenschema."}
|
||||
|
||||
debug_print(f"Endergebnis Branchenbewertung: Branche='{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'")
|
||||
return {"branch": suggested_branch, "consistency": consistency, "justification": justification}
|
||||
|
||||
|
||||
def evaluate_servicetechnicians_estimate(company_name, company_data):
|
||||
|
||||
Reference in New Issue
Block a user