komplett neue Version mit optimiertem Prompt

This commit is contained in:
2025-03-29 21:08:31 +00:00
parent ef59697442
commit aefa89ca15

View File

@@ -1,212 +1,164 @@
import os # Neue Version mit Token-Optimierung, festem Prompt und Begrenzung der Durchläufe
import time
import pandas as pd import os
import gspread import time
import openai import pandas as pd
import wikipedia import gspread
from bs4 import BeautifulSoup import openai
import requests import wikipedia
from oauth2client.service_account import ServiceAccountCredentials from bs4 import BeautifulSoup
from datetime import datetime import requests
from oauth2client.service_account import ServiceAccountCredentials
# === CONFIG === from datetime import datetime
EXCEL = "Bestandsfirmen.xlsx"
SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo" # === KONFIGURATION ===
CREDENTIALS = "service_account.json" EXCEL = "Bestandsfirmen.xlsx" # optional, falls du später exportieren willst
CHUNK = 10 SHEET_URL = "https://docs.google.com/spreadsheets/d/1u_gHr9JUfmV1-iviRzbSe3575QEp7KLhK5jFV_gJcgo"
LANG = "de" CREDENTIALS = "service_account.json"
LANG = "de"
# === AUTHENTICATION === DURCHLÄUFE = int(input("Wieviele Zeilen sollen überprüft werden? "))
scope = ["https://www.googleapis.com/auth/spreadsheets"]
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope) # === GOOGLE SHEET VERBINDUNG ===
sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1 scope = ["https://www.googleapis.com/auth/spreadsheets"]
creds = ServiceAccountCredentials.from_json_keyfile_name(CREDENTIALS, scope)
# OpenAI API-Key aus externer Datei laden sheet = gspread.authorize(creds).open_by_url(SHEET_URL).sheet1
with open("api_key.txt", "r") as f: sheet_values = sheet.get_all_values()
openai.api_key = f.read().strip()
# === WIKIPEDIA KONFIG ===
# === LOAD DATA === wikipedia.set_lang(LANG)
df = pd.read_excel(EXCEL)
for col in ["Wikipedia-URL", "Wikipedia-Branche", "LinkedIn-Branche", "Umsatz (Mio €)", # === SYSTEM PROMPT ===
"Empfohlene Neueinstufung", "Begründung Neueinstufung", "FSM-Relevanz", "Letzte Prüfung", branches = [
"Techniker-Einschätzung (Auto)", "Techniker-Einschätzung (Begründung)", "Techniker-Einschätzung (Manuell)"]: "Hersteller / Produzenten > Maschinenbau",
if col not in df.columns: "Hersteller / Produzenten > Automobil",
df[col] = "" "Hersteller / Produzenten > Anlagenbau",
"Hersteller / Produzenten > Medizintechnik",
# === STARTE BEI ERSTER LEERER ZEILE IN SPALTE 'Letzte Prüfung' (Spalte N) === "Hersteller / Produzenten > Chemie & Pharma",
sheet_values = sheet.get_all_values() "Hersteller / Produzenten > Elektrotechnik",
filled_n = [row[13] if len(row) > 13 else '' for row in sheet_values[1:]] "Hersteller / Produzenten > Lebensmittelproduktion",
start = next((i + 1 for i, v in enumerate(filled_n, start=1) if not str(v).strip() or str(v).lower() == 'nan'), len(filled_n) + 1) "Hersteller / Produzenten > IT / Telekommunikation",
print(f"Starte bei Zeile {start+1} (erste leere Zeile in Spalte N)") "Hersteller / Produzenten > Bürotechnik",
"Hersteller / Produzenten > Automaten (Vending, Slot)",
mapping_dict = {} "Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima",
wikipedia.set_lang(LANG) "Hersteller / Produzenten > Gebäudetechnik Allgemein",
"Hersteller / Produzenten > Schädlingsbekämpfung",
# === ÜBERSETZUNGSTABELLE VORBEREITEN === "Hersteller / Produzenten > Fertigung",
sheet_trans_title = "Branchen-Mapping" "Hersteller / Produzenten > Braune & Weiße Ware",
try: "Versorger > Stadtwerk",
sheet_trans = sheet.spreadsheet.worksheet(sheet_trans_title) "Versorger > Verteilnetzbetreiber",
except gspread.exceptions.WorksheetNotFound: "Versorger > Telekommunikation",
sheet_trans = sheet.spreadsheet.add_worksheet(title=sheet_trans_title, rows="100", cols="3") "Dienstleister > Messdienstleister",
sheet_trans.clear() "Dienstleister > Facility Management",
sheet_trans.update(range_name="A1:B1", values=[["Wikipedia-Branche", "Ziel-Branchenschema"]]) "Dienstleister > Healthcare/Pflegedienste",
"Dienstleister > Servicedienstleister / Reparatur ohne Produktion",
# === BRANCHENSCHEMA === "Handel & Logistik > Auslieferdienste",
branches = [ "Handel & Logistik > Energie (Brennstoffe)",
"Hersteller / Produzenten > Maschinenbau", "Handel & Logistik > Großhandel",
"Hersteller / Produzenten > Automobil", "Handel & Logistik > Einzelhandel",
"Hersteller / Produzenten > Anlagenbau", "Handel & Logistik > Logistik Sonstige",
"Hersteller / Produzenten > Medizintechnik", "Sonstige > Unternehmensberatung (old)",
"Hersteller / Produzenten > Chemie & Pharma", "Sonstige > Sonstige",
"Hersteller / Produzenten > Elektrotechnik", "Sonstige > Agrar, Pellets (old)",
"Hersteller / Produzenten > Lebensmittelproduktion", "Sonstige > Sonstiger Service (old)",
"Hersteller / Produzenten > IT / Telekommunikation", "Sonstige > IT Beratung",
"Hersteller / Produzenten > Bürotechnik", "Sonstige > Engineering",
"Hersteller / Produzenten > Automaten (Vending, Slot)", "Baubranche > Baustoffhandel",
"Hersteller / Produzenten > Gebäudetechnik Heizung, Lüftung, Klima", "Baubranche > Baustoffindustrie",
"Hersteller / Produzenten > Gebäudetechnik Allgemein", "Baubranche > Logistiker Baustoffe",
"Hersteller / Produzenten > Schädlingsbekämpfung", "Baubranche > Bauunternehmen",
"Hersteller / Produzenten > Fertigung", "Gutachter / Versicherungen > Versicherungsgutachten",
"Hersteller / Produzenten > Braune & Weiße Ware", "Gutachter / Versicherungen > Technische Gutachter",
"Versorger > Stadtwerk", "Gutachter / Versicherungen > Medizinische Gutachten"
"Versorger > Verteilnetzbetreiber", ]
"Versorger > Telekommunikation",
"Dienstleister > Messdienstleister", system_prompt = {
"Dienstleister > Facility Management", "role": "system",
"Dienstleister > Healthcare/Pflegedienste", "content": (
"Dienstleister > Servicedienstleister / Reparatur ohne Produktion", "Du bist ein Experte für Brancheneinstufung und FSM-Potenzialbewertung. "
"Handel & Logistik > Auslieferdienste", "FSM steht für Field Service Management Software zur Planung und Unterstützung mobiler Techniker. "
"Handel & Logistik > Energie (Brennstoffe)", "Ziel ist es, Unternehmen zu identifizieren, die mehr als 50 Techniker im Außeneinsatz beschäftigen (z.B. Servicetechniker, Instandhalter, Medizintechniker etc.).\n\n"
"Handel & Logistik > Großhandel", "Dir liegt pro Unternehmen eine strukturierte Eingabezeile vor, bestehend aus:\n"
"Handel & Logistik > Einzelhandel", "Firmenname; Website; Ort; Aktuelle Einstufung; Beschreibung der Branche Extern\n\n"
"Handel & Logistik > Logistik Sonstige", "Bitte führe für jede Firma eine fundierte Bewertung durch:\n"
"Sonstige > Unternehmensberatung (old)", "- Nimm eine Brancheneinstufung anhand des untenstehenden Ziel-Branchenschemas vor.\n"
"Sonstige > Sonstige", "- Berücksichtige dabei alle vorliegenden Informationen (auch externe Beschreibung, Wikipedia, LinkedIn, Website) sowie die bisherige Einstufung.\n"
"Sonstige > Agrar, Pellets (old)", "- Wenn die bisherige Einstufung korrekt ist, bestätige sie wenn nicht, schlage eine neue Einstufung vor und begründe diese.\n"
"Sonstige > Sonstiger Service (old)", "- Gib zusätzlich an, ob das Unternehmen FSM-relevant ist (Ja / Nein / k.A. mit Begründung).\n"
"Sonstige > IT Beratung", "- Schätze die Anzahl mobiler Techniker anhand öffentlich verfügbarer Infos und gib eine Stufe an: <50 / >50 / >100 / >500, mit Begründung.\n\n"
"Sonstige > Engineering", "Ziel-Branchenschema:\n" + "\n".join(branches)
"Baubranche > Baustoffhandel", )
"Baubranche > Baustoffindustrie", }
"Baubranche > Logistiker Baustoffe",
"Baubranche > Bauunternehmen", # === HILFSFUNKTIONEN ===
"Gutachter / Versicherungen > Versicherungsgutachten", def get_wikipedia_data(name):
"Gutachter / Versicherungen > Technische Gutachter", for suchbegriff in [name.strip(), " ".join(name.split()[:2])]:
"Gutachter / Versicherungen > Medizinische Gutachten" try:
] page = wikipedia.page(suchbegriff, auto_suggest=False)
url = page.url
system_prompt = { html = requests.get(url).text
"role": "system", soup = BeautifulSoup(html, 'html.parser')
"content": ( infobox = soup.find("table", {"class": "infobox"})
"Du bist ein Experte für Brancheneinstufung und FSM-Potenzialbewertung. Nutze das folgende ZielBranchenschema als Referenz:\n\n" branche = umsatz = ""
+ "\n".join(branches) if infobox:
) for row in infobox.find_all("tr"):
} th, td = row.find("th"), row.find("td")
if not th or not td:
# === WIKIPEDIA LOOKUP === continue
def get_wikipedia_data(firmenname): if "Branche" in th.text:
suchbegriffe = [firmenname.strip(), " ".join(firmenname.split()[:2])] branche = td.text.strip()
for suchbegriff in suchbegriffe: if "Umsatz" in th.text:
try: umsatz = td.text.strip()
page = wikipedia.page(suchbegriff, auto_suggest=False) if not branche:
url = page.url cats = page.categories
html = requests.get(url).text branche = cats[0] if cats else ""
soup = BeautifulSoup(html, 'html.parser') return url, branche, umsatz
infobox = soup.find("table", {"class": "infobox"}) except:
branche = "" continue
umsatz = "" return "", "", ""
if infobox:
for row in infobox.find_all("tr"): def classify_company(row):
header = row.find("th") user_prompt = {
data = row.find("td") "role": "user",
if not header or not data: "content": f"{row[0]};{row[1]};{row[2]};{row[4]};{row[5]}"
continue }
if "Branche" in header.text: response = openai.chat.completions.create(
branche = data.text.strip() model="gpt-3.5-turbo",
if "Umsatz" in header.text: messages=[system_prompt, user_prompt],
umsatz = data.text.strip() temperature=0
if not branche: )
cats = page.categories parts = [v.strip().strip('"') for v in response.choices[0].message.content.strip().split(";", 7)]
branche = cats[0] if cats else "" while len(parts) < 8:
return url, branche, umsatz parts.append("k.A.")
except: return parts
continue
return "", "", "" # === STARTINDEX SUCHEN ===
filled = [row[11] if len(row) > 11 else '' for row in sheet_values[1:]]
# === KLASSIFIZIERUNG === start = next((i + 1 for i, v in enumerate(filled, start=1) if not v.strip()), len(filled) + 1)
def classify_company(row): print(f"Starte bei Zeile {start+1}")
user_prompt = {
"role": "user", # === VERARBEITUNG ===
"content": ( for i in range(start, min(start + DURCHLÄUFE, len(sheet_values))):
"Bitte prüfe die vorliegenden Informationen zum Unternehmen. Gib die Antwort im CSV-Format zurück:\n" row = sheet_values[i]
"Wikipedia-Branche; LinkedIn-Branche; Umsatz (Mio €); Empfohlene Neueinstufung; Begründung; FSM-Relevanz (Ja/Nein/k.A. mit Begründung); Techniker-Einschätzung (<50/>50/>100/>500); Techniker-Begründung\n\n" print(f"[{time.strftime('%H:%M:%S')}] Verarbeite Zeile {i+1}: {row[0]}")
f"Beschreibung: {row['Beschreibung des Unternehmens'] or ''}\n"
f"Aktuelle Einstufung: {row['Aktuelle Einstufung'] or ''}\n" url, wiki_branche, umsatz = get_wikipedia_data(row[0])
f"Externe Branchenbeschreibung: {row['Beschreibung der Branche Extern'] or ''}\n" wiki, linkedin, umsatz_chat, new_cat, reason, fsm, techniker, techniker_reason = classify_company(row)
f"Website: {row['Website'] or ''}"
) values = [
} wiki or wiki_branche,
resp = openai.chat.completions.create( linkedin,
model="gpt-4", umsatz_chat or umsatz,
messages=[system_prompt, user_prompt], new_cat,
temperature=0 reason,
) fsm,
result = resp.choices[0].message.content.strip() url,
parts = [v.strip().strip('"') for v in result.split(";", 7)] datetime.now().strftime("%Y-%m-%d %H:%M:%S"),
while len(parts) < 8: techniker,
parts.append("k.A.") techniker_reason
return parts ]
# === LOOP === # Schreibe in die Spalten G bis P (716, nullbasiert also 615)
for df_idx in range(start - 1, len(df)): sheet.update(range_name=f"G{i+2}:P{i+2}", values=[values])
row = df.iloc[df_idx] time.sleep(5)
if str(row.get("Letzte Prüfung", "")).strip():
continue print("✅ Durchläufe abgeschlossen")
print(f"[{time.strftime('%H:%M:%S')}] Verarbeite Zeile {df_idx+1}: {row['Firmenname']}")
url, wiki_branche, umsatz = get_wikipedia_data(row['Firmenname'])
df.at[df_idx, "Wikipedia-URL"] = url
df.at[df_idx, "Wikipedia-Branche"] = wiki_branche.strip('"')
if not df.at[df_idx, "Umsatz (Mio €)"]:
df.at[df_idx, "Umsatz (Mio €)"] = umsatz
wiki, linkedin, umsatz_chat, new_cat, reason, fsm_relevant, techniker, techniker_reason = classify_company(row)
df.at[df_idx, "Wikipedia-Branche"] = wiki or wiki_branche
df.at[df_idx, "LinkedIn-Branche"] = linkedin
if not df.at[df_idx, "Umsatz (Mio €)"]:
df.at[df_idx, "Umsatz (Mio €)"] = umsatz_chat
df.at[df_idx, "Empfohlene Neueinstufung"] = new_cat
current_cat = str(row.get("Aktuelle Einstufung") or "").strip().strip('"')
if new_cat != current_cat:
df.at[df_idx, "Begründung Neueinstufung"] = reason
else:
df.at[df_idx, "Begründung Neueinstufung"] = ""
df.at[df_idx, "FSM-Relevanz"] = fsm_relevant
df.at[df_idx, "Techniker-Einschätzung (Auto)"] = techniker
df.at[df_idx, "Techniker-Einschätzung (Begründung)"] = techniker_reason
now = datetime.now().strftime("%Y-%m-%d %H:%M:%S")
df.at[df_idx, "Letzte Prüfung"] = now
key = df.at[df_idx, "Wikipedia-Branche"]
val = df.at[df_idx, "Empfohlene Neueinstufung"]
if key and val and key not in mapping_dict:
mapping_dict[key] = val
sheet_trans.update(range_name=f"A{len(mapping_dict)+1}:B{len(mapping_dict)+1}", values=[[key, val]])
sheet.update(
values=[df.loc[df_idx, [
"Wikipedia-Branche", "LinkedIn-Branche", "Umsatz (Mio €)",
"Empfohlene Neueinstufung", "Begründung Neueinstufung",
"FSM-Relevanz", "Wikipedia-URL", "Letzte Prüfung",
"Techniker-Einschätzung (Auto)", "Techniker-Einschätzung (Begründung)"
]].tolist()],
range_name=f"G{df_idx+2}:Q{df_idx+2}"
)
time.sleep(5)
print("✅ Fertig!")