fix(maria): pragul de acoperire la 0,80 si temperatura 0 — doua raspunsuri inventate

Grupul "Maria Test", 2026-09-01, ambele mesaje au primit raspunsuri inventate:

- "am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE"
  (cosinus 0,768) — mesajul nu spune CE eroare e; Maria a explicat cum se reface
  o factura stearsa si a trimis omul sa verifice drepturile pe CIF in SPV.
- captura cu `<Error errorMessage="CUI cumparator incorect"/>` (0,778) — eroarea
  nu exista in niciun document; Maria a inventat o procedura cu "ID de descarcare",
  Borderou eFactura si retrimitere manuala.

Cauza: `RANK_STRONG_COSINE=0,70` inseamna "cosinusul singur e destul", dar toate
chunk-urile eFactura seamana intre ele — la 0,7x cosinusul nu mai distinge "e in
documente" de "e despre eFactura". Aceeasi greseala ca la codurile Oracle
(ORA-06550 vs ORA-12541, b7d75f0), doar ca fara cod pe care sa te sprijini.

- RANK_STRONG_COSINE 0,70 -> 0,80: peste atat trec doar potrivirile aproape
  textuale, restul cad pe dovada lexicala (termenii rari ai intrebarii chiar in
  chunk-uri). Ambele mesaje escaladeaza acum la suport.
- LLM_TEMPERATURE=0 la apelul modelului. Fara el llama.cpp foloseste 0,8 —
  creativitate exact acolo unde vrem doar ce scrie in context.
- Cele doua cazuri reale sunt adaugate in ops/calibrate-rank.py: 25/25.

Teste: 99 pass.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
This commit is contained in:
Claude Agent
2026-09-01 18:24:37 +00:00
parent 5e60394c20
commit 45b36591c9
6 changed files with 41 additions and 4 deletions

View File

@@ -362,6 +362,19 @@ Fuziunea da mereu un clasament, si la o intrebare complet straina. De aceea deci
Fractiunea din pasul 3 nu e cosmetica: cu un singur termen gasit, „cum imi resetez Fractiunea din pasul 3 nu e cosmetica: cu un singur termen gasit, „cum imi resetez
parola de la Windows" trecea drept acoperita fiindca „parola" apare in documente. parola de la Windows" trecea drept acoperita fiindca „parola" apare in documente.
`RANK_STRONG_COSINE` a fost 0,70 si a lasat sa treaca doua raspunsuri inventate
(2026-09-01, grupul „Maria Test"): „am o factura pe luna august cu eroare in SPV"
(0,768 — nu spune ce eroare e) si o captura cu `errorMessage="CUI cumparator
incorect"` (0,778 — eroarea nu exista in documente). **Toate chunk-urile eFactura
seamana intre ele**, deci pe 0,7x cosinusul nu mai distinge „e in documente" de „e
despre eFactura"; e acelasi tip de greseala ca la codurile Oracle de mai sus, doar
ca fara cod pe care sa te sprijini. La **0,80** trec doar potrivirile aproape
textuale, iar restul cad pe dovada lexicala de la pasul 3. `ops/calibrate-rank.py`:
25/25, cu ambele cazuri adaugate la set.
Modelul primeste `temperature=0` (`LLM_TEMPERATURE`). Fara ea, llama.cpp raspunde
cu 0,8 — creativitate exact acolo unde vrem sa se rezume la context.
Pasul 0 e mai tare decat cosinusul pentru ca **doua erori Oracle diferite se scriu Pasul 0 e mai tare decat cosinusul pentru ca **doua erori Oracle diferite se scriu
aproape la fel**. O captura cu `ORA-06550 / PLS-00906` a primit cosinus 0,736 pe aproape la fel**. O captura cu `ORA-06550 / PLS-00906` a primit cosinus 0,736 pe
chunk-ul despre `ORA-12541: TNS no listener` — peste pragul „sigur", deci Maria a chunk-ul despre `ORA-12541: TNS no listener` — peste pragul „sigur", deci Maria a

View File

@@ -61,6 +61,13 @@ CAZURI: list[tuple[str, bool]] = [
("ORA-06550: line 1, column 7 PLS-00906 object invalid\nda, ma blocheaza complet", True), ("ORA-06550: line 1, column 7 PLS-00906 object invalid\nda, ma blocheaza complet", True),
("ORA-12154 TNS could not resolve the connect identifier\n" ("ORA-12154 TNS could not resolve the connect identifier\n"
"eram la salvarea unei facturi", True), "eram la salvarea unei facturi", True),
# 2026-09-01, grup „Maria Test": ambele au primit raspunsuri inventate.
# Prima e o reclamatie vaga (nu spune CE eroare), a doua e o eroare eFactura
# care nu exista in documente — dar toate chunk-urile eFactura ii seamana.
("Buna . Am si eu o factura pe luna august cu eroare in spv la trimitere AUTO SULE", False),
("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),
("care e capitala Frantei", False), ("care e capitala Frantei", False),
("cat costa un bilet de avion la Paris", False), ("cat costa un bilet de avion la Paris", False),
("imi da eroare la imprimanta HP LaserJet, nu trage hartia", False), ("imi da eroare la imprimanta HP LaserJet, nu trage hartia", False),

View File

@@ -35,7 +35,7 @@ OCR_TIMEOUT_S=60
# Peste RANK_STRONG_COSINE raspundem din documente fara alte conditii; sub # Peste RANK_STRONG_COSINE raspundem din documente fara alte conditii; sub
# RANK_WEAK_COSINE escaladam direct; intre ele, raspundem doar daca termenii rari # RANK_WEAK_COSINE escaladam direct; intre ele, raspundem doar daca termenii rari
# din intrebare (coduri de eroare, in general) chiar apar in chunk-urile gasite. # din intrebare (coduri de eroare, in general) chiar apar in chunk-urile gasite.
RANK_STRONG_COSINE=0.70 RANK_STRONG_COSINE=0.80
RANK_WEAK_COSINE=0.58 RANK_WEAK_COSINE=0.58
RANK_RECALL_N=12 RANK_RECALL_N=12
# Ce fractiune din termenii distinctivi ai intrebarii trebuie sa apara in # Ce fractiune din termenii distinctivi ai intrebarii trebuie sa apara in
@@ -63,6 +63,8 @@ OLLAMA_URL=http://127.0.0.1:11434
EMBED_MODEL=nomic-embed-text EMBED_MODEL=nomic-embed-text
TOP_K=3 TOP_K=3
MAX_TOKENS=250 MAX_TOKENS=250
# 0 = raspunsul e mereu acelasi si sta in context. Nu urca fara motiv.
LLM_TEMPERATURE=0
POLL_INTERVAL_S=2 POLL_INTERVAL_S=2
# --- Depozit de documente + sincronizare Google Drive ----------------------- # --- Depozit de documente + sincronizare Google Drive -----------------------

View File

@@ -39,6 +39,7 @@ DEFAULTS: dict[str, str] = {
"EMBED_MODEL": "nomic-embed-text", "EMBED_MODEL": "nomic-embed-text",
"TOP_K": "3", "TOP_K": "3",
"MAX_TOKENS": "250", "MAX_TOKENS": "250",
"LLM_TEMPERATURE": "0",
"POLL_INTERVAL_S": "2", "POLL_INTERVAL_S": "2",
"TEST_MODE_SELF_CHAT_ONLY": "true", "TEST_MODE_SELF_CHAT_ONLY": "true",
# Grupuri in care Maria are voie sa raspunda (JID-uri separate prin virgula). # Grupuri in care Maria are voie sa raspunda (JID-uri separate prin virgula).
@@ -56,7 +57,7 @@ DEFAULTS: dict[str, str] = {
"OCR_TIMEOUT_S": "60", "OCR_TIMEOUT_S": "60",
# Ordonarea chunk-urilor si pragul de la care se considera ca raspunsul chiar # Ordonarea chunk-urilor si pragul de la care se considera ca raspunsul chiar
# e in documente (vezi rag/rank.py pentru masuratoarea din spatele valorilor). # e in documente (vezi rag/rank.py pentru masuratoarea din spatele valorilor).
"RANK_STRONG_COSINE": "0.70", "RANK_STRONG_COSINE": "0.80",
"RANK_WEAK_COSINE": "0.58", "RANK_WEAK_COSINE": "0.58",
"RANK_RECALL_N": "12", "RANK_RECALL_N": "12",
"RANK_MIN_RARE_RATIO": "0.5", "RANK_MIN_RARE_RATIO": "0.5",

View File

@@ -175,6 +175,11 @@ def ask_llm(chunks: list[str], question: str, istoric: list[dict] | None = None)
json={ json={
"messages": mesaje, "messages": mesaje,
"max_tokens": config.get_int("MAX_TOKENS", 250), "max_tokens": config.get_int("MAX_TOKENS", 250),
# Fara asta llama.cpp raspunde cu temperatura lui implicita (0,8).
# Aici nu vrem creativitate: raspunsul trebuie sa fie ce scrie in
# context, la fel de fiecare data. Vezi raspunsurile inventate din
# 2026-09-01 (grup „Maria Test").
"temperature": float(config.get("LLM_TEMPERATURE", "0")),
}, },
timeout=60, timeout=60,
) )

View File

@@ -39,8 +39,17 @@ RRF_K = 60 # constanta standard din Reciprocal Rank Fusion
def strong_cosine() -> float: def strong_cosine() -> float:
"""Peste atat, singur cosinusul e de ajuns (RANK_STRONG_COSINE).""" """Peste atat, singur cosinusul e de ajuns (RANK_STRONG_COSINE).
return float(config.get("RANK_STRONG_COSINE", "0.70"))
Era 0,70 si a fost prea jos: pe 2026-09-01, „am o factura cu eroare in SPV"
(0,768) si o captura cu `errorMessage="CUI cumparator incorect"` (0,778) au
trecut drept acoperite, desi prima nu spune ce eroare e, iar a doua nu exista
in documente. Toate chunk-urile eFactura seamana intre ele: 0,7x nu distinge
„e in documente" de „e despre eFactura". La 0,80 trec doar potrivirile
aproape textuale, restul cad pe dovada lexicala (termenii rari din intrebare
chiar in chunk-uri). Verificat cu ops/calibrate-rank.py: 25/25.
"""
return float(config.get("RANK_STRONG_COSINE", "0.80"))
def weak_cosine() -> float: def weak_cosine() -> float: