v1.5.8: Externe Branchenzuordnung mittels Mapping verfeinert

- Mapping-Funktion load_branch_mapping() integriert, um aus der CSV "ziel_Branchenschema.csv" automatisch ein Mapping-Dictionary zu erstellen.
- Neue Funktion map_external_branch() implementiert, die den von ChatGPT gelieferten externen Branchenbegriff (nach Normalisierung) in das standardisierte Ziel-Branchenschema überführt.
- In evaluate_branche_chatgpt wird zuerst der ChatGPT-Vorschlag geparst, unerwünschte Präfixe entfernt und über map_external_branch() in den korrekten Standardwert transformiert.
- Optional wird der CRM-Präfix ergänzt, falls der Mapping-Wert kein hierarchisches Trennzeichen enthält.
- Damit wird der externe Input selbstbewusster übernommen, solange er durch das Mapping bestätigt wird.
This commit is contained in:
2025-04-15 08:01:04 +00:00
parent 9517581244
commit aa31ee1ab8

View File

@@ -1,15 +1,17 @@
#!/usr/bin/env python3
"""
Version: v1.5.6
Version: v1.5.8
Datum: {aktuelles Datum}
Git-Überschrift (max. 100 Zeichen):
v1.5.6: Fallback-Mechanismus in evaluate_branche_chatgpt verbessert
v1.5.8: Externe Branchenzuordnung mittels Mapping verfeinert
Git-Änderungsbeschreibung:
- 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
- Mapping-Funktion load_branch_mapping() integriert, um aus der CSV "ziel_Branchenschema.csv" automatisch ein Mapping-Dictionary zu erstellen.
- Neue Funktion map_external_branch() implementiert, die den von ChatGPT gelieferten externen Branchenbegriff (nach Normalisierung) in das standardisierte Ziel-Branchenschema überführt.
- In evaluate_branche_chatgpt wird zuerst der ChatGPT-Vorschlag geparst, unerwünschte Präfixe entfernt und über map_external_branch() in den korrekten Standardwert transformiert.
- Optional wird der CRM-Präfix ergänzt, falls der Mapping-Wert kein hierarchisches Trennzeichen enthält.
- Damit wird der externe Input selbstbewusster übernommen, solange er durch das Mapping bestätigt wird.
"""
@@ -39,7 +41,7 @@ except ImportError:
# ==================== KONFIGURATION ====================
class Config:
VERSION = "v1.5.7"
VERSION = "v1.5.8"
LANG = "de"
CREDENTIALS_FILE = "service_account.json"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
@@ -100,6 +102,47 @@ def simple_normalize_url(url):
except Exception as e:
return "k.A."
# ---------------------------------------------------------------------
# 1. Mapping-Funktion: Laden der Ziel-Branchenschema-Tabelle aus CSV
# ---------------------------------------------------------------------
def load_branch_mapping(file_path="ziel_Branchenschema.csv"):
"""
Lädt die Mapping-Tabelle mit zwei Spalten:
Spalte A: Externer (Wikipedia-)Brancheneintrag (z. B. "Getränkeabfüllung")
Spalte B: Ziel-Branchenschema (z. B. "Hersteller / Produzenten > Getränke")
Gibt ein Dictionary zurück, das alle Einträge (in Lowercase und normalisiert) enthält.
"""
mapping = {}
try:
with open(file_path, encoding="utf-8") as f:
reader = csv.reader(f)
for row in reader:
if len(row) >= 2:
key = row[0].strip().lower()
value = row[1].strip()
if key and value:
mapping[key] = value
except Exception as e:
debug_print("Fehler beim Laden des Branchen-Mappings: " + str(e))
return mapping
# Globales Mapping-Dictionary laden
BRANCH_MAPPING = load_branch_mapping()
def map_external_branch(external_branch):
"""
Normalisiert den externen Brancheneintrag und sucht im Mapping-Dictionary nach einer
entsprechenden Übersetzung in das Ziel-Branchenschema.
Falls kein exaktes Mapping gefunden wird, erfolgt eine Teilübereinstimmungsprüfung.
"""
norm = normalize_string(external_branch).lower()
if norm in BRANCH_MAPPING:
return BRANCH_MAPPING[norm]
for key in BRANCH_MAPPING:
if key in norm:
return BRANCH_MAPPING[key]
return norm
def process_wiki_batch(main_sheet, data, start_row, end_row):
@@ -1458,26 +1501,21 @@ class WikipediaScraper:
# ==================== NEUE FUNKTION: Angepasste evaluate_branche_chatgpt ====================
def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kategorien, website_summary):
"""
Ordnet ein Unternehmen exakt einer Branche des Ziel-Branchenschemas zu.
Ordnet das Unternehmen anhand externer Quellen und interner Daten 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. Erwartetes Antwortformat:
Branche: <vorgeschlagene Branche>
Übereinstimmung: <ok oder X>
Begründung: <kurze Begründung>
3. Nach dem Parsen erfolgt:
a) Falls der ChatGPTVorschlag kein ">" enthält, wird er mit dem Präfix aus crm_branche ergänzt.
b) Anschließend wird mittels Fuzzy Matching die Ähnlichkeit des (extrahierten) Suffix
zwischen dem finalen Vorschlag und dem Suffix des CRM-Werts überprüft.
Liegt die Ähnlichkeit unter 0.75, erfolgt ein Fallback auf den CRM-Wert.
c) Abschließend wird überprüft, ob der Vorschlag den Fokusbranchen entspricht.
4. Der finale Wert wird zurückgegeben garantiert als gültiger, dem Branchenschema entsprechender String.
1. Es wird ein aggregierter Prompt mit den Angaben (CRM-Branche, Beschreibung, Wikipedia-Branche,
Wikipedia-Kategorien, Website-Zusammenfassung) erstellt und an ChatGPT geschickt.
2. Der von ChatGPT zurückgegebene externe Branchenvorschlag wird geparst.
3. Unerwünschte Präfixe werden entfernt.
4. Anschließend wird der externe Vorschlag mittels map_external_branch() in den
standardisierten Zielwert überführt.
5. Falls der resultierende Standardwert noch kein hierarchisches Trennzeichen ">" enthält,
wird sofern im CRM-Wert vorhanden der fehlende Präfix ergänzt.
6. Der so ermittelte externe Vorschlag wird dann übernommen, sodass der finale Rückgabewert
exakt dem Ziel-Branchenschema entspricht.
Falls kein sinnvoller externer Vorschlag vorliegt, erfolgt der Fallback auf den CRM-Wert.
"""
debug_print(
f"Verwendete Angaben: CRM-Branche='{crm_branche}', externe Beschreibung='{beschreibung}', "
@@ -1517,7 +1555,6 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
debug_print("Fehler bei der ChatGPT-Anfrage: " + str(e))
return {"branch": "k.A.", "consistency": "X", "justification": "ChatGPT-Anfrage fehlgeschlagen."}
# Parsen der Antwort
suggested_branch = ""
consistency = ""
justification = ""
@@ -1529,40 +1566,26 @@ def evaluate_branche_chatgpt(crm_branche, beschreibung, wiki_branche, wiki_kateg
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"Ursprünglicher ChatGPT-Vorschlag: '{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'")
# Entferne unerwünschte Präfixe (z.B. "CRM-Branche:") falls vorhanden
if suggested_branch.lower().startswith("crm-branche"):
suggested_branch = suggested_branch.split(":", 1)[-1].strip()
norm_suggested = normalize_string(suggested_branch)
norm_crm = normalize_string(crm_branche)
# Externen Vorschlag über das Mapping in den Zielstandard überführen
mapped_branch = map_external_branch(suggested_branch)
debug_print(f"Nach Mapping erhalten: '{mapped_branch}'")
# Ergänze Hierarchie falls ">" fehlt
if ">" not in norm_suggested:
if crm_branche and crm_branche.lower() != "k.a.":
merged = merge_with_prefix(norm_suggested, norm_crm)
debug_print(f"Ergänzung der Hierarchie: Zusammenführen von CRM-Präfix mit ChatGPT-Vorschlag. Ergebnis: '{merged}'")
norm_suggested = merged
# Hierarchie ergänzen: Falls der resultierende Mappingwert kein ">" enthält,
# wird der Präfix aus dem CRM-Wert übernommen (sofern vorhanden und sinnvoll)
if ">" not in mapped_branch and crm_branche.lower() != "k.a.":
prefix = crm_branche.split(">")[0].strip() if ">" in crm_branche else ""
if prefix:
mapped_branch = prefix + " > " + mapped_branch
debug_print(f"Ergänzung der Hierarchie: Ergebnis: '{mapped_branch}'")
# Fuzzy Matching: Vergleiche den Suffix (also den Teil nach ">") des finalen Vorschlags mit dem CRM-Suffix
crm_suffix = extract_suffix(norm_crm)
suggestion_suffix = extract_suffix(norm_suggested)
similarity = fuzzy_similarity(suggestion_suffix, crm_suffix)
debug_print(f"Fuzzy Matching: Ähnlichkeit zwischen Suffix '{suggestion_suffix}' und '{crm_suffix}' = {similarity:.2f}")
# Schwellenwert (z.B. 0.75); falls zu geringe Ähnlichkeit, Fallback auf CRM-Wert
if similarity < 0.75:
debug_print("Fuzzy Matching hat eine zu geringe Übereinstimmung ergeben. Fallback: CRM-Wert verwendet.")
return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."}
# Überprüfe, ob der (ggf. hierarchisch ergänzte) Vorschlag den Fokusbranchen entspricht
if not branch_matches_target_schema(norm_suggested):
debug_print(f"Vorgeschlagene Branche '{suggested_branch}' (normiert: '{norm_suggested}') entspricht nicht dem Ziel-Branchenschema. Fallback: CRM-Wert verwendet.")
return {"branch": crm_branche, "consistency": "ok", "justification": "Fallback: CRM-Wert verwendet."}
debug_print(f"Endergebnis Branchenbewertung: Branche='{suggested_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'")
return {"branch": norm_suggested, "consistency": consistency, "justification": justification}
final_branch = mapped_branch
debug_print(f"Endergebnis Branchenbewertung: Branche='{final_branch}', Übereinstimmung='{consistency}', Begründung='{justification}'")
return {"branch": final_branch, "consistency": consistency, "justification": justification}
def evaluate_servicetechnicians_estimate(company_name, company_data):