Pe captura cu ORA-06550, avand in context chiar intrarea din dictionar care spune
"nu se rezolva din aplicatie", modelul i-a raspuns unui contabil sa verifice
schema de date si sa foloseasca "procedura alternativa MI_pack_parteneri_old" —
inventata. Si nu escalada, fiindca escaladarea se declansa doar cand NU exista
acoperire in documente, iar aici exista.
rag/triaj.py, trei decizii inaintea modelului:
- **Mesaj prea vag.** "Am o eroare" -> Maria cere operatiunea, ecranul si textul
erorii, in loc sa ghiceasca. Regula e ingusta: text scurt, fara captura, fara cod.
- **Erori marcate "Intotdeauna"/"Imediat" in dictionar** (9 din 29) -> raspunsul se
compune din campurile dictionarului, fara model, si pleaca automat la suport cu
referinta si captura. Textul de acolo e scris pentru clienti; parafrazarea lui nu
adauga nimic si poate strica tot.
- **Urgenta se intreaba, nu se deduce.** Raspunsul ("ma blocheaza" / "pot continua")
se ataseaza escaladarii deschise, nu deschide alta referinta. Fereastra 30 min.
In plus: SYSTEM_PROMPT interzice explicit vocabularul tehnic (procedura, schema,
tabela, PL/SQL, compilare, sesiune) si propunerile de verificat baza de date; cele
mai tehnice cinci intrari din dictionar rescrise in limbaj de client.
Teste: 85 pass. Calibrare 21/21. Reindexare: 5 embeddings noi, 164 refolosite.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
96 lines
3.8 KiB
Python
96 lines
3.8 KiB
Python
"""Triajul: cand se cer detalii, cand pleaca automat la programatori."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import json
|
|
import time
|
|
|
|
import triaj
|
|
|
|
|
|
CHUNK_06550 = """[eroare ORA 06550]
|
|
keywords: ORA-06550 PLS-00201 eroare compilare
|
|
mesaj eroare: ORA-06550: eroare de compilare in program
|
|
ce inseamna: Programul nu e adus la zi peste tot.
|
|
rezolvare: Nu se rezolva din aplicatie si nu ajuta sa incerci din nou.
|
|
cand suni suportul: Intotdeauna. Trimite si o captura de ecran."""
|
|
|
|
CHUNK_04068 = """[eroare ORA 04068]
|
|
mesaj eroare: ORA-04068: existing state of packages has been discarded
|
|
ce inseamna: Programul a fost actualizat in timp ce aplicatia ta era deschisa.
|
|
rezolvare: Inchide complet aplicatia si redeschide-o.
|
|
cand suni suportul: Daca revine si dupa restartul aplicatiei."""
|
|
|
|
|
|
def test_nivelul_se_citeste_din_dictionar():
|
|
assert triaj.nivel_suport(CHUNK_06550) == "intotdeauna"
|
|
assert triaj.nivel_suport(CHUNK_04068) is None
|
|
assert triaj.nivel_suport("cand suni suportul: Imediat. Blocheaza tot.") == "imediat"
|
|
|
|
|
|
def test_raspunsul_nu_vorbeste_ca_pentru_programatori():
|
|
"""Cazul din 2026-09-01: modelul ii cerea unui contabil sa verifice schema."""
|
|
text = triaj.raspuns_escaladat(CHUNK_06550, "intotdeauna", "M-260901-A1B2",
|
|
True, cu_imagine=True)
|
|
jos = text.lower()
|
|
for cuvant in ("procedura", "schema", "pl/sql", "obiect", "variabila", "compilare"):
|
|
assert cuvant not in jos, cuvant
|
|
assert "M-260901-A1B2" in text
|
|
assert "captura" in jos
|
|
assert "blocheaza complet" in jos # intreaba cat e de urgent
|
|
|
|
|
|
def test_cand_notificarea_nu_a_plecat_nu_promitem_ca_a_plecat():
|
|
text = triaj.raspuns_escaladat(CHUNK_06550, "intotdeauna", "M-1", False)
|
|
assert "am inregistrat" in text.lower()
|
|
assert "te contacteaza cineva" not in text.lower()
|
|
|
|
|
|
def test_prea_vag_doar_pentru_acuzatii_fara_continut():
|
|
assert triaj.prea_vag("am o eroare", False)
|
|
assert triaj.prea_vag("nu merge nimic", False)
|
|
# o intrebare adevarata nu se opreste ca sa i se ceara detalii
|
|
assert not triaj.prea_vag("cum trimit declaratia D406 SAF-T", False)
|
|
# un cod inseamna ca avem deja dupa ce cauta
|
|
assert not triaj.prea_vag("imi da ORA-01722 la salvare", False)
|
|
# captura vorbeste de la sine
|
|
assert not triaj.prea_vag("am o eroare", True)
|
|
|
|
|
|
# --- completarea la o escaladare deschisa -----------------------------------
|
|
|
|
def _escaladare(ref: str) -> None:
|
|
import config
|
|
d = config.STATE_DIR / "escalations"
|
|
d.mkdir(parents=True, exist_ok=True)
|
|
(d / f"{int(time.time())}-{ref}.json").write_text(
|
|
json.dumps({"ref": ref, "text": "eroare initiala"}), encoding="utf-8")
|
|
|
|
|
|
def test_raspunsul_despre_urgenta_intra_in_escaladarea_deschisa(monkeypatch):
|
|
import config
|
|
import consumer
|
|
|
|
monkeypatch.setattr(config, "get", lambda k, d=None: "" if k == "SUPPORT_JID"
|
|
else config.DEFAULTS.get(k, d))
|
|
_escaladare("M-TEST-1")
|
|
consumer.pending_set("40712@s.whatsapp.net", "M-TEST-1")
|
|
assert consumer.pending_get("40712@s.whatsapp.net")["ref"] == "M-TEST-1"
|
|
|
|
assert consumer.append_followup("M-TEST-1", {"id": "x"}, "ma blocheaza, nu pot factura")
|
|
|
|
fisier = next((config.STATE_DIR / "escalations").glob("*-M-TEST-1.json"))
|
|
record = json.loads(fisier.read_text(encoding="utf-8"))
|
|
assert record["completari"][0]["text"] == "ma blocheaza, nu pot factura"
|
|
|
|
consumer.pending_clear("40712@s.whatsapp.net")
|
|
assert consumer.pending_get("40712@s.whatsapp.net") is None
|
|
|
|
|
|
def test_completarea_expira(monkeypatch):
|
|
import consumer
|
|
consumer.pending_set("40799@s.whatsapp.net", "M-TEST-2")
|
|
peste_o_ora = time.time() + consumer.PENDING_TTL_S + 1
|
|
monkeypatch.setattr(consumer.time, "time", lambda: peste_o_ora)
|
|
assert consumer.pending_get("40799@s.whatsapp.net") is None
|