Files
ROMFASTSQL/CONTEXT_HANDOVER_20260901_termeni_distinctivi.md
2026-09-01 19:58:12 +00:00

11 KiB
Raw Blame History

Context handover — 2026-09-01, seara

REZOLVAT in 653f5cb (2026-09-01, noaptea). Pasul (1) facut: setul are 31 de cazuri. Pasul (2) facut, dar cauza s-a dovedit ALTA decat se banuia aici: politetea din ancora nu era ce strica verdictul, ci faptul ca termenii se comparau pe cuvantul intreg — documentul scrie „token"/„tokenuri", omul scrie „tokenul". Reparat prin potrivire pe primele 5 litere (codurile raman intregi) + varianta (a), rank.assess(dovada=...). Varianta (b), lista de formule de politete, a fost scrisa si apoi scoasa: masuratoarea n-a justificat-o. 31/31 cu reparatia, 30/31 fara. Restul documentului se pastreaza ca istoric — §7 (eroarea „CUI cumparator incorect" lipsa din documente) e inca deschis.

Ce e de făcut: două lucruri legate între ele, în proxmox/lxc171-claude-agent/maria-whatsapp-bridge/:

  1. adaugă cazuri de continuare în ops/calibrate-rank.py (azi setul n-are decât două, ambele cu cod de eroare);
  2. repară termenii distinctivi: rank.rare_terms() judecă acoperirea pe interogarea combinată (ancora firului + mesajul nou), nu pe ce a întrebat omul acum.

(1) există ca să poți face (2) fără să ghicești. Nu începe cu (2).

Tot ce e mai jos e comis și împins: ROMFASTSQL 5e60394..af04d97.


1. De unde vine

Sesiunea de azi a plecat de la: „am trimis 2 mesaje pe grupul maria test whatsapp și ambele răspunsuri sunt eronate". Cauza reală s-a dovedit dublă și e reparată (vezi §5), dar reparația a scos la iveală problema care rămâne deschisă.

Din firul de test real (escaladarea M-260901-06EB, ~/.maria-bridge/escalations/1788291399-M-260901-06EB.json):

[consumer] rank: efactura_knowledge.md(0.836), … | cosinus 0.836, iar termenii
distinctivi (buna, luna, august, trimitere, auto) nu apar in documente

„buna" e tratat drept termen distinctiv. E rar în corpusul de ERP, deci rare_terms() îl consideră discriminant, deși e o formulă de salut.

De ce contează acum și nu conta înainte: de azi, la o continuare se caută după ancoră + mesajul nou (consumer.py:574 → fir.interogare()), iar ancora e primul mesaj al omului, cu tot cu „Buna . … Poti te rog sa ma ajuti?". Ancora nu se șterge cât trăiește firul (2h), deci fiecare continuare de pe firul ăla e judecată și după „buna/luna/august", cuvinte care nu vor apărea niciodată în documente.

Efectul

assess() cere ca cel puțin RANK_MIN_RARE_RATIO (0,5) din termenii distinctivi să apară chiar în chunk-uri. Cu 3 cuvinte de politețe care nu apar niciodată, raportul scade artificial la fiecare replică → escaladare în plus.

Direcția e sigură (un om vede întrebarea, nu primește un răspuns inventat), deci nu e urgent. Dar face Maria inutilă exact pe discuțiile lungi, unde ar trebui să fie cea mai bună.

În cazul testat n-a stricat nimic — răspunsul chiar nu era în documente, escaladarea a fost corectă. Deci nu ai încă nicio dovadă că regresia se produce în practică; asta e chiar ce trebuie măsurat la pasul (1).


2. Reparația propusă (nu implementată)

Separă cele două roluri ale interogării:

Rol Ce folosește azi Ce ar trebui
regăsire (ce chunk-uri aduc) ancoră + mesaj nou rămâne așa — ancora chiar ajută, „da, mă blochează" singur nu găsește nimic
acoperire (răspund sau escaladez) ancoră + mesaj nou doar mesajul nou (+ OCR-ul capturii), adică ce a întrebat omul acum

Concret, în consumer.py:131 search(), rank.assess() (consumer.py:144) primește azi același query ca și regăsirea. Ar trebui să primească separat interogarea de dovadă.

Capcana, și motivul pentru care n-am făcut-o azi: o continuare scurtă („da, mă blochează complet", „eram la salvarea unei facturi") n-are termeni distinctivi proprii. Judecată singură, escaladează — exact regresia pe care fir.py a fost scris ca s-o prevină (vezi tabelul din docstring-ul lui rag/fir.py, cazurile măsurate). Deci reparația poate strica mai mult decât repară, și nu se poate valida pe setul actual de cazuri.

Variante de luat în calcul, în ordinea preferinței:

  • a) acoperirea se judecă pe mesajul nou, dar dacă acesta n-are termeni distinctivi proprii se cade înapoi pe interogarea combinată (continuările scurte se comportă ca azi);
  • b) rare_terms() ignoră formulele de politețe — listă mică de stopwords („buna", „salut", "buna ziua", „multumesc", „va rog", „poti", „ajuti"). Whack-a-mole, dar o linie și zero risc;
  • c) ancora se curăță la creare: se păstrează doar liniile care arată a eroare, cum face deja ocr.retrieval_query() pentru capturi (rag/ocr.py).

(b) e cel mai ieftin și poate fi făcut singur, imediat. (a) e cel corect. Nu le confunda: (b) ascunde simptomul măsurat azi, (a) rezolvă clasa.


3. Ce trebuie adăugat în ops/calibrate-rank.py — pasul (1)

Setul (CAZURI, o listă de (intrebare, are_raspuns_in_documente)) are azi 26 de cazuri, 26/26 corecte. Continuări are doar două, și ambele conțin un cod de eroare, deci trec pe regula codurilor și nu spun nimic despre termenii distinctivi:

("ORA-06550: line 1, column 7 PLS-00906 object invalid\nda, ma blocheaza complet", True),
("ORA-12154 TNS could not resolve the connect identifier\neram la salvarea unei facturi", True),

Lipsesc continuările fără cod de eroare — exact cazul în care termenii distinctivi decid. De adăugat, în forma pe care o produce fir.interogare() (ancoră \n mesaj nou):

  • ancoră politicoasă + continuare scurtă, cu răspuns în documente → True (ex. "Buna ziua . Am o problema la trimiterea in SPV . Puteti sa ma ajutati?\nimi zice ca tokenul e expirat")
  • ancoră politicoasă + continuare scurtă, fără răspuns în documente → False
  • aceeași continuare fără formula de salut în ancoră → același verdict ca varianta cu salut (dacă verdictele diferă, ai măsurat exact regresia)
  • ancoră + a treia, a patra replică (fir lung) → verdictul nu trebuie să se degradeze cu lungimea
  • continuare pe un fir deschis de o captură (ancora = OCR, plin de zgomot: „Total", „Lit", „Selecteaz!")

Scrie-le pornind de la fire reale, nu inventate: ~/.maria-bridge/escalations/*.json are câmpul fir cu schimburile, iar ~/.maria-bridge/conversations/*.json are ancora exactă.

Atenție: fiecare caz nou costă un embedding (~7s pe CPU, fără GPU pe LXC 171). Se pun în cache automat în ~/.maria-bridge/calibrare-cache.json, deci doar prima rulare e lentă.

Cum se rulează

cd /workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py --sweep
python3 -m pytest -q          # 103 teste; venv-ul n-are pytest, se ruleaza cu python3 de sistem

--sweep mătură RANK_WEAK_COSINE × RANK_MIN_RARE_RATIO (nu mai există prag „strong", vezi §5).

Criteriu de acceptare pentru (2): setul extins rămâne 100% corect și cu reparația, și fără ea nu e — altfel n-ai dovedit că reparația face ceva.


4. Fișiere care contează

Fișier De ce
rag/rank.py:168 rare_terms() inima problemei: len(t) >= 4 and df <= 25% din corpus → „buna" trece
rag/rank.py:212 assess() decizia acoperirii; docstring-ul explică de ce nu mai există prag pe cosinus
rag/consumer.py:131 search() aici assess() primește același query ca regăsirea — punctul de separat
rag/consumer.py:574 search_query = fir_mod.interogare(fir, search_query) — unde se combină
rag/fir.py:140 interogare() ancoră + mesaj nou
rag/fir.py:126 extinde_ancora() ancora crește cu OCR-ul capturii (adăugat azi)
rag/fir.py docstring tabelul cu răspunsurile greșite care justifică existența ancorei — citește-l înainte să scoți ancora din ceva
ops/calibrate-rank.py setul de cazuri + --sweep
tests/test_rank.py, tests/test_fir.py testele care prind regresiile
README.md §„Cum se ordonează rezultatele (rank)" documentația de actualizat la final

5. Ce s-a reparat azi (nu reface)

Trei comiteri, toate împinse, serviciile repornite, verificate pe viu în grupul „Maria Test".

45b3659 — două răspunsuri inventate: mesaj vag (cosinus 0,768) și captură cu errorMessage="CUI cumparator incorect" (0,778, eroare inexistentă în documente). Cauza: RANK_STRONG_COSINE=0,70 însemna „cosinusul singur e destul", dar toate chunk-urile eFactura seamănă între ele. Plus LLM_TEMPERATURE=0 (llama.cpp folosea 0,8).

47110d7 — două probleme de fond:

  • gardul „mesaj prea vag" (triaj.prea_vag) cerea text sub 12 cuvinte; mesajul real avea 14. Acum nu mai numără cuvinte și se aplică după căutare, doar când nu există acoperire, o singură dată pe fir (fir["detalii_cerute"]);
  • fir.este_continuare() rupea firul la orice imagine. Cazul frecvent e tocmai text + captură trimise una după alta. Acum o captură în FIR_IMAGINE_MIN (5 min) continuă firul, OCR-ul intră în ancoră, iar pe o escaladare deschisă captura pleacă la aceeași referință;
  • RANK_STRONG_COSINE a dispărut de tot. Motivul e important pentru (2): cosinusul crește cu lungimea interogării. Aceeași captură: 0,778 singură, 0,836 împreună cu mesajul de dinainte. Orice prag fix de sus e trecut de o discuție destul de lungă. Acoperirea se sprijină acum exclusiv pe dovada lexicală — adică pe chiar rare_terms(), ceea ce face (2) mai important decât părea.

af04d97 — găsite citind logul: requests nu ridică excepție la 4xx/5xx, deci escaladarea se scria notified: true chiar când puntea răspundea 503; puntea nu loga nimic la trimitere; mesajele primite se logau tăiate la 80 de caractere fără semn (m-a dus la o concluzie greșită în diagnostic).


6. Constrângeri de mediu

  • Modelul de chat e Qwen3.5-2B Q4 pe LXC 104 (LLM_URL=http://10.0.20.161:8091). Inventează dacă îl lași. Poarta de acoperire e singura apărare reală — de asta merită reparată bine.
  • Nu încerca un model mai mare pe LXC 171. Testat azi: qwen3:4b-instruct prin Ollama, 4 nuclee, fără GPU → 600s timeout fără niciun token; un prompt de 15 tokeni a scos 6 tokeni în 10 min 20s. Modelul a fost șters. Fără GPU sau API extern (vezi docs/supliment-glm-zai.md, neimplementat) nu e altă variantă.
  • Ollama pe 171 are doar nomic-embed-text, pentru embeddings. Un embedding ≈ 7s.
  • Servicii: systemctl --user restart maria-rag maria-whatsapp; loguri în ~/.maria-bridge/logs/{rag,whatsapp}.log.
  • Grup de test: 120363409761730101@g.us („Maria Test"), SUPPORT_JID e chiar numărul propriu, deci escaladările se văd.
  • Șterge firele vechi înainte de a testa (rm -f ~/.maria-bridge/conversations/*.json), altfel primul mesaj e luat drept continuare a discuției precedente — TTL 120 min.

7. De reținut, separat de task

„CUI cumparator incorect" nu există în niciun document (grep -ri "cumparator incorect" ~/.maria-bridge/documents* → zero). Maria escaladează corect, dar ar putea răspunde. E o eroare eFactura reală și frecventă. N-am scris textul — nu inventez procedură ROA. De pus în document_store (Google Drive), de către cineva care știe procedura.