From 52d1e9f2054a7e884c0f8d95e7244f0a68d332ec Mon Sep 17 00:00:00 2001 From: Claude Agent Date: Mon, 31 Aug 2026 22:15:55 +0000 Subject: [PATCH] =?UTF-8?q?docs:=20context=20handover=20=E2=80=94=20de=20c?= =?UTF-8?q?e=20reindexarea=20refacea=20toate=20embeddings-urile?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Co-Authored-By: Claude Opus 5 (1M context) Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY --- CONTEXT_HANDOVER_20260831_reindexare.md | 164 ++++++++++++++++++++++++ 1 file changed, 164 insertions(+) create mode 100644 CONTEXT_HANDOVER_20260831_reindexare.md diff --git a/CONTEXT_HANDOVER_20260831_reindexare.md b/CONTEXT_HANDOVER_20260831_reindexare.md new file mode 100644 index 0000000..7e86158 --- /dev/null +++ b/CONTEXT_HANDOVER_20260831_reindexare.md @@ -0,0 +1,164 @@ +# Context handover — 2026-08-31, seara + +Continuarea sesiunii din [`CONTEXT_HANDOVER_20260831.md`](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`](proxmox/lxc171-claude-agent/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`](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: +```bash +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ă.