Files
ROMFASTSQL/CONTEXT_HANDOVER_20260831_reindexare.md
2026-08-31 22:15:55 +00:00

8.3 KiB
Raw Blame History

Context handover — 2026-08-31, seara

Continuarea sesiunii din CONTEXT_HANDOVER_20260831.md. Tot ce e mai jos e comis și împins (ROMFASTSQL: b447251..c514818, claude-agent: două comiteri noi).


1. Întrebarea care a declanșat handover-ul

„nu vreau ca reindexarea să refacă toate embeddings-urile de fiecare dată. de ce le face?"

Nu le mai face. Reparat în c514818 (parte din commit-ul ca6b53e), verificat pe viu:

[indexer] 12 documente, 169 chunk-uri (0 embeddings noi, 169 refolosite)
real 0.15s        # înainte: ~20 de minute

De ce le refăcea

rag/indexer.py:build() era scris ca o reconstrucție totală: pentru fiecare document, pentru fiecare chunk, embed(chunk) — necondiționat. Nu exista nicio noțiune de „chunk-ul ăsta e neschimbat". Indexul vechi era pur și simplu suprascris.

Costul: un embedding pe CPU (nomic-embed-text, fără GPU pe LXC 171) durează ~7 secunde. La 169 de chunk-uri asta înseamnă ~20 de minute — plătiți integral chiar și când se schimba un singur document. Adăugarea dicționarului de erori Oracle (29 de chunk-uri noi) ar fi costat 20 de minute în loc de 3.

Nu era o scăpare evidentă la citirea codului: sync.py avea deja o optimizare care arăta ca și cum ar rezolva problema — amprenta pe nume+mtime+mărime, care sărea reindexarea când nimic nu se schimbase. Dar aia decide dacă se reindexează, nu cât se reindexează. Odată ce un singur fișier se atingea, se plătea totul.

Cum se rezolvă acum

rag/indexer.py:_vectori_existenti() citește indexul precedent și îl transformă într-un dicționar text_chunk -> embedding. La reconstrucție, fiecare chunk se caută întâi acolo.

Cheia e textul chunk-ului, nu numele documentului și nu mtime-ul. Motivul: dacă nu s-a schimbat niciun caracter în chunk, vectorul e identic prin definiție. Un document reformatat în Drive, ale cărui chunk-uri ies la fel, nu costă nimic. Un document în care s-a schimbat un rând costă doar chunk-urile atinse.

Sursa e chiar rag_index.json, nu un al doilea fișier de cache. Deliberat: nu există un al doilea fișier care să se poată desincroniza, iar dacă indexul lipsește se recalculează tot, exact ca înainte. Logul spune de fiecare dată câte au fost calculate și câte refolosite.

Când tot se recalculează (și e corect așa)

Situație Ce se întâmplă
rag_index.json lipsește sau e corupt se recalculează tot — nu are de unde refolosi
Textul unui chunk s-a schimbat, oricât de puțin acel chunk se re-embedează (corect: alt text = alt vector)
Se schimbă strategia de chunking (indexer.py) toate chunk-urile ies altfel → se recalculează tot
Se schimbă EMBED_MODEL în env capcană: NU se recalculează, deși ar trebui

Ultima linie e singura problemă rămasă și e reală: cheia de cache e doar textul, fără modelul care l-a produs. Dacă cineva schimbă EMBED_MODEL, indexul rămâne un amestec de vectori din două modele, iar căutarea dă rezultate aiurea fără niciun mesaj de eroare. Nu s-a întâmplat (modelul e nomic-embed-text de la început), dar merită reparat — vezi „De făcut" mai jos.

Fișiere: rag/indexer.py (_vectori_existenti, build), teste în tests/test_store_si_chunking.py (test_vectorii_din_indexul_vechi_se_refolosesc, test_textul_modificat_se_reembedeaza).


2. Ce s-a mai făcut în sesiune

Toate în proxmox/lxc171-claude-agent/. Documentația completă e în maria-whatsapp-bridge/README.md — aici doar ce nu se vede din cod.

Asociere WhatsApp prin cod, nu doar QR

/pair exista dar nu era folosibil: fără descriptor browser explicit WhatsApp refuză codul, iar după introducerea lui corectă serverul cere un restart (515) care consuma din bugetul de reîncercări și putea opri puntea la a cincea asociere. Cod valabil ~3 minute, cu marcarea expirării în dashboard. Folosit pe bune pentru re-legarea lui Echo.

Capturi de ecran cu erori (OCR)

tesseract ron+eng; modelul de răspuns e strict text, deci OCR nu e o opțiune de calitate. Căutarea în index folosește doar liniile care arată a eroare, nu toată fereastra — un ecran de meniuri diluează embedding-ul. Modelul primește captura întreagă, marcată ca OCR.

Rank hibrid (rag/rank.py)

Embeddings + BM25, fuzionate prin RRF. Motivul, măsurat: intervalele de cosinus se suprapun — întrebări bune 0,600–0,816, străine 0,534–0,685. Codurile (ORA-01722, D406, CIF) sunt exact ce ratează căutarea semantică. Praguri calibrate cu ops/calibrate-rank.py, 19/19.

Escaladare la suport

Când nu există acoperire, modelul nu mai e întrebat deloc. Referință (M-260831-FC34), confirmare onestă („am trimis" doar dacă notificarea chiar a plecat), captura se retrimite ca imagine. Jurnal în ~/.maria-bridge/escalations/, vizibil în dashboard.

Dicționar de 29 de erori Oracle

maria-whatsapp-bridge/knowledge/oracle-erori-uzuale.xml, scris pentru clienți — fără nume de servere sau pași de administrare.

Două bug-uri găsite în log

  • Fiecare mesaj din self-chat sosea de două ori (@s.whatsapp.net și @lid) → Maria răspundea dublu. Dedup pe key.id în punte.
  • Documentele adăugate din dashboard ajungeau în oglinda Drive și dispăreau la următorul rclone sync. Acum există ~/.maria-bridge/documents-local/.

Echo re-legat la WhatsApp

Dispozitivul :14. S-a lămurit de ce nu răspund ambele punți în chatul „Eu": Echo aruncă tot ce e fromMe && !isGroup, iar în self-chat tot ce scrii e fromMe. Echilibrul e accidental — detalii în docs/chatboti-si-punti.md.


3. De făcut

a) EMBED_MODEL în cheia de cache (mic, real)

Vezi tabelul de mai sus. Cea mai simplă variantă: pune modelul în fiecare intrare de index ("model": config.get("EMBED_MODEL")) și refolosește doar intrările cu modelul curent. Intrările vechi, fără câmp, se tratează ca „model necunoscut" → se recalculează o dată.

b) SUPPORT_JID — decizia utilizatorului

E setat pe propriul număr (40723197939@s.whatsapp.net), pentru testare. De schimbat cu numărul sau grupul echipei de suport, în ~/.maria-bridge/env.

c) Dicționarul Oracle trăiește doar pe container

E în documents-local/, deci supraviețuiește sincronizărilor, dar nu-l vede nimeni altcineva. Locul lui pe termen lung e document_store din Drive. Când ajunge acolo, șterge copia locală — altfel rămân două versiuni și câștigă tăcut cea din Drive.

d) Recalibrare când se schimbă documentele

Pragurile din env sunt măsurate pe indexul de acum. La schimbări mari de conținut:

cd proxmox/lxc171-claude-agent/maria-whatsapp-bridge
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py

Adaugă în CAZURI (în capul scriptului) întrebările reale la care Maria a greșit — e singurul mod în care calibrarea rămâne onestă.

e) Rămase din handover-ul precedent

LXC 301 (nimic urgent), auditul de infrastructură, modelul de răspuns al Mariei.


4. Stare la finalul sesiunii

Index RAG 169 chunk-uri, 12 documente (din care oracle-erori-uzuale.xml e local)
Servicii maria-whatsapp, maria-rag, claude-discord-dashboard — active
WhatsApp Maria conectată (40723197939), Echo re-legat ca dispozitiv :14
Teste 73 pass (26 la începutul zilei)
SUPPORT_JID propriul număr — de schimbat

5. Capcane (în plus față de handover-ul precedent)

  • systemctl pe LXC 110 are nevoie de --user. Serviciile lui Echo sunt unități de utilizator (moltbot, uid 1000). Ca root, systemctl is-active echo-core răspunde inactive deși botul rulează.
  • Nu adăuga documente în ~/.maria-bridge/documents/. E oglinda Drive-ului; rclone sync le șterge. Folosește documents-local/.
  • Nu ridica pragurile de rank „ca să fie sigur". Escaladarea prea agresivă e la fel de proastă ca răspunsul generic — rulează calibrarea, nu intuiția.
  • Fiecare embedding costă ~7 secunde. Orice schimbare în strategia de chunking înseamnă recalcularea întregului index; nu e o schimbare de făcut în grabă.