fix(maria): mesajul si captura sunt acelasi lucru, iar cosinusul singur nu mai e dovada
Doua greseli din grupul "Maria Test", 2026-09-01, pe care 27409fb le-a atenuat dar
nu le-a rezolvat.
1. La o reclamatie vaga Maria cauta, in loc sa intrebe. "Buna . Am si eu o factura
pe luna august cu eroare in spv la trimitere AUTO SULE" nu spune CE eroare e —
n-are ce cauta in documente si n-are ce trimite la suport, fiindca programatorul
ar pune exact aceeasi intrebare. Gardul (triaj.prea_vag) exista, dar cerea text
sub 12 cuvinte; mesajul are 14. Gresea si invers: "nu pot incarca factura in SPV,
imi da eroare de certificat" are 9 cuvinte si ARE raspuns in documente, si era
oprita degeaba. Lungimea nu masoara cat de precis e mesajul.
-> gardul nu mai numara cuvinte si se aplica DUPA cautare, doar cand nu exista
acoperire. O singura data pe fir: daca nici intrebat omul nu spune mai mult,
mesajul pleaca la suport.
2. Textul si captura, trimise una dupa alta, erau tratate ca doua probleme fara
legatura. `este_continuare` rupea firul la ORICE imagine — dar cazul frecvent e
tocmai omul care scrie problema si trimite captura imediat dupa, sau care
raspunde la "trimite-mi o captura". Textul se pierdea, captura se cauta doar pe
OCR-ul ei, se deschideau doua fire si se puteau deschide doua escaladari pentru
aceeasi problema.
-> o captura in primele FIR_IMAGINE_MIN (5) minute continua firul: textul citit
din ea intra in ancora (cu tot cu coduri), cautarea se face pe mesaj +
captura, iar pe o escaladare deschisa pleaca la aceeasi referinta — cu
imaginea, nu doar cu textul citit din ea (_notifica_suport ia si media).
Si, ca urmare a lui (2): RANK_STRONG_COSINE dispare. Interogarea pe un fir e ancora
plus mesajul nou, deci cosinusul urca la fiecare replica fara sa apara vreo dovada
noua — aceeasi captura da 0,778 singura si 0,836 cu mesajul de dinainte, adica peste
0,80 pus ieri. Orice prag fix de sus e trecut de o discutie destul de lunga. Acoperirea
ramane pe dovada lexicala (termenii distinctivi ai intrebarii chiar in chunk-uri),
care e stabila la lungime. Niciun caz cu raspuns in documente nu avea nevoie de
scurtatura.
ops/calibrate-rank.py: 26/26, cu interogarea combinata adaugata la set. Teste: 102 pass.
Verificat end-to-end pe scenariul real (mesaj, apoi captura la 10 secunde): intrebare
de detalii, apoi o singura escaladare care poarta si textul si captura.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
@@ -68,6 +68,11 @@ CAZURI: list[tuple[str, bool]] = [
|
||||
("O) Pers. Juridice (798) O Netrimise DI Trimise (fara raspuns) [5 Timis (cu raspuns Erori) (1). "
|
||||
"O Tamise (cu diferente Reg. V2.) (1)\nMesaj raspuns. | Factura cu id incarcare=6638295792 nu po] "
|
||||
"| | <Error errorMessage=\"CUI cumparator incorect\"/>", False),
|
||||
# aceeasi captura, dar cautata IMPREUNA cu mesajul care a precedat-o (fir.interogare):
|
||||
# textul in plus urca cosinusul la 0,836 fara sa aduca vreo dovada noua.
|
||||
("Buna . Am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE\n"
|
||||
"Mesaj raspuns. | Factura cu id incarcare=6638295792 nu po] "
|
||||
"| | <Error errorMessage=\"CUI cumparator incorect\"/>", False),
|
||||
("care e capitala Frantei", False),
|
||||
("cat costa un bilet de avion la Paris", False),
|
||||
("imi da eroare la imprimanta HP LaserJet, nu trage hartia", False),
|
||||
@@ -119,13 +124,13 @@ def pregateste(cazuri: list[tuple[str, bool]]) -> tuple[consumer.Index, list[dic
|
||||
return index, date
|
||||
|
||||
|
||||
def evalueaza(index: consumer.Index, date: list[dict], strong: float, weak: float,
|
||||
def evalueaza(index: consumer.Index, date: list[dict], weak: float,
|
||||
min_ratio: float) -> tuple[int, list[str]]:
|
||||
corecte, gresite = 0, []
|
||||
for d in date:
|
||||
ok, _ = rank.assess(
|
||||
d["intrebare"], d["best"], d["top_texts"], index.bm25,
|
||||
strong=strong, weak=weak, min_ratio=min_ratio,
|
||||
weak=weak, min_ratio=min_ratio,
|
||||
)
|
||||
if ok == d["asteptat"]:
|
||||
corecte += 1
|
||||
@@ -149,28 +154,24 @@ def main() -> None:
|
||||
|
||||
if args.sweep:
|
||||
rezultate = []
|
||||
for strong in (0.62, 0.66, 0.68, 0.70, 0.72, 0.75):
|
||||
for weak in (0.54, 0.58, 0.60):
|
||||
for ratio in (0.0, 0.34, 0.5, 0.67, 1.0):
|
||||
if weak >= strong:
|
||||
continue
|
||||
corecte, _ = evalueaza(index, date, strong, weak, ratio)
|
||||
rezultate.append((corecte, strong, weak, ratio))
|
||||
for weak in (0.50, 0.54, 0.58, 0.60, 0.64):
|
||||
for ratio in (0.0, 0.34, 0.5, 0.67, 1.0):
|
||||
corecte, _ = evalueaza(index, date, weak, ratio)
|
||||
rezultate.append((corecte, weak, ratio))
|
||||
rezultate.sort(key=lambda r: (-r[0], r[1]))
|
||||
print(f"{'corecte':>8} strong weak ratio")
|
||||
for corecte, strong, weak, ratio in rezultate[:15]:
|
||||
print(f"{corecte:>5}/{len(date)} {strong:.2f} {weak:.2f} {ratio:.2f}")
|
||||
print(f"{'corecte':>8} weak ratio")
|
||||
for corecte, weak, ratio in rezultate[:15]:
|
||||
print(f"{corecte:>5}/{len(date)} {weak:.2f} {ratio:.2f}")
|
||||
return
|
||||
|
||||
strong, weak = rank.strong_cosine(), rank.weak_cosine()
|
||||
ratio = rank.min_rare_ratio()
|
||||
print(f"praguri curente: strong={strong} weak={weak} ratio={ratio}\n")
|
||||
weak, ratio = rank.weak_cosine(), rank.min_rare_ratio()
|
||||
print(f"praguri curente: weak={weak} ratio={ratio}\n")
|
||||
for d in date:
|
||||
ok, motiv = rank.assess(d["intrebare"], d["best"], d["top_texts"], index.bm25)
|
||||
semn = "OK " if ok == d["asteptat"] else "GRESIT"
|
||||
stare = "ACOPERIT " if ok else "ESCALADAT"
|
||||
print(f"{semn} [{stare}] {d['intrebare'][:44]:46} {motiv}")
|
||||
corecte, gresite = evalueaza(index, date, strong, weak, ratio)
|
||||
corecte, gresite = evalueaza(index, date, weak, ratio)
|
||||
print(f"\n{corecte}/{len(date)} corecte")
|
||||
for g in gresite:
|
||||
print(f" - {g}")
|
||||
|
||||
@@ -32,10 +32,9 @@ OCR_TIMEOUT_S=60
|
||||
# Chunk-urile se ordoneaza de doua ori — semantic (embeddings) si lexical (BM25) —
|
||||
# iar clasamentele se fuzioneaza. Vezi rag/rank.py; valorile de mai jos sunt
|
||||
# calibrate pe indexul real, nu alese din burta.
|
||||
# Peste RANK_STRONG_COSINE raspundem din documente fara alte conditii; sub
|
||||
# RANK_WEAK_COSINE escaladam direct; intre ele, raspundem doar daca termenii rari
|
||||
# Sub RANK_WEAK_COSINE escaladam direct; peste, raspundem doar daca termenii rari
|
||||
# din intrebare (coduri de eroare, in general) chiar apar in chunk-urile gasite.
|
||||
RANK_STRONG_COSINE=0.80
|
||||
# Nu exista prag „cosinus destul de mare ca sa nu mai verificam" — vezi rag/rank.py.
|
||||
RANK_WEAK_COSINE=0.58
|
||||
RANK_RECALL_N=12
|
||||
# Ce fractiune din termenii distinctivi ai intrebarii trebuie sa apara in
|
||||
|
||||
Reference in New Issue
Block a user