| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107 |
- import json, sqlite3, time
- from pathlib import Path
- from concurrent.futures import ThreadPoolExecutor, as_completed
- import requests
- BASE = Path("/opt/3cx-middleware/3cx-telefonie-middleware")
- TRANSCRIPTS = BASE / "data/transcripts"
- OUT = BASE / "data/transcript_relevance.jsonl"
- OLLAMA = "http://192.168.1.101:11434/api/chat"
- MODEL = "qwen3:4b"
- WORKERS = 2
- SYSTEM = """Du entscheidest ausschließlich, ob ein Telefontranskript für
- einen Gartenpflanzen-/Onlineshop-Kundenservice relevant ist.
- RELEVANT:
- Ein echtes Kunden-, Lieferanten- oder Geschäftsgespräch mit verwertbarem
- Inhalt.
- IRRELEVANT:
- Mailbox, reine Ansage, verpasster Anruf ohne Gespräch, Werbung, technische
- Ansage oder praktisch kein Gespräch.
- UNCERTAIN:
- Nicht eindeutig entscheidbar.
- Antworte ausschließlich als JSON:
- {"relevance":"RELEVANT|IRRELEVANT|UNCERTAIN","confidence":0.0,"reason":"kurze Begründung"}
- Keine Intent-Klassifizierung durchführen.
- """
- def classify(item):
- path = item
- text = path.read_text(encoding="utf-8", errors="replace").strip()
- payload = {
- "model": MODEL,
- "messages": [
- {"role": "system", "content": SYSTEM},
- {"role": "user", "content": text[:12000]}
- ],
- "stream": False,
- "think": False,
- "format": "json",
- "options": {"temperature": 0}
- }
- start = time.time()
- r = requests.post(OLLAMA, json=payload, timeout=180)
- r.raise_for_status()
- result = json.loads(r.json()["message"]["content"])
- return {
- "file": path.name,
- "relevance": result.get("relevance", "UNCERTAIN"),
- "confidence": result.get("confidence", 0),
- "reason": result.get("reason", ""),
- "seconds": round(time.time() - start, 1)
- }
- files = sorted(TRANSCRIPTS.glob("*.txt"))
- # offensichtliche Kurz-/Leer-/reine Ansagetexte nicht erneut an Qwen schicken
- candidates = []
- for p in files:
- text = p.read_text(encoding="utf-8", errors="replace").strip()
- words = len(text.split())
- if not text or words < 12:
- continue
- candidates.append(p)
- print(f"Transkripte gesamt: {len(files)}")
- print(f"Qwen-Kandidaten: {len(candidates)}")
- OUT.parent.mkdir(parents=True, exist_ok=True)
- with OUT.open("w", encoding="utf-8") as f:
- with ThreadPoolExecutor(max_workers=WORKERS) as pool:
- futures = {pool.submit(classify, p): p for p in candidates}
- done = 0
- for future in as_completed(futures):
- p = futures[future]
- done += 1
- try:
- result = future.result()
- f.write(json.dumps(result, ensure_ascii=False) + "\n")
- f.flush()
- print(
- f"[{done}/{len(candidates)}] "
- f"{p.name} {result['seconds']}s → "
- f"{result['relevance']} "
- f"{result['confidence']}",
- flush=True
- )
- except Exception as e:
- print(f"ERROR {p.name}: {e}", flush=True)
- print(f"\nErgebnis: {OUT}")
|