Files
ROMFASTSQL/proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md
Claude Agent 1c7899753a feat(maria): accepta .xml in depozit, preferat peste .md, cu chunking pe probleme
Dosarul document_store din Drive are 3 surse .xml pe care depozitul le ignora
complet, fiindca store.py accepta doar .txt/.md. La d406_saft_knowledge exista
ambele formate, iar .xml e cu trei luni mai nou (2026-01-28 vs 2025-10-15) si cu
50% mai mare (64 KB vs 41 KB) — deci indexam varianta mai saraca.

- store.py devine sursa unica pentru extensii (DOC_EXTENSIONS = .txt/.md/.xml).
  Cand acelasi nume de baza exista in mai multe formate, la indexare intra unul
  singur, cel mai bogat (.xml > .md > .txt); celalalt ramane pe disc, marcat
  `shadowed_by`. Fara asta, acelasi raspuns ar aparea de doua ori in rezultate.
  `list_documents()` arata tot (dashboard), `documents_for_index()` doar
  castigatorii (indexer).
- indexer.py taie XML-ul altfel: un chunk per element de nivel 1, adica o
  problema = un chunk, cu <mesaj_eroare> si <rezolvare> impreuna. Taierea pe
  linii goale le-ar separa si cautarea ar returna eroarea fara raspuns.
  Etichetele raman prefixe lizibile ("mesaj eroare: ..."), fara paranteze
  unghiulare care doar dilueaza embedding-ul. XML invalid nu opreste indexarea:
  cade pe taierea obisnuita, cu o linie in log. Elementele peste 4000 de
  caractere se taie mai departe pe granite de cuvant — `chunk_text` imparte doar
  pe linii goale, deci un element scris ca un paragraf lung ar fi ramas intreg
  (prins de test).
- sync.py: amprenta si `rclone --include` derivate din DOC_EXTENSIONS.
- dashboard: acelasi filtru si aceeasi preferinta (copie, fiindca nu poate
  importa `store` — coliziune de nume pe `config`), plus marcajul "umbrit de X"
  in tabelul de documente si numarul de documente chiar indexate.
- README: sectiunea Drive rescrisa pe `rclone authorize` (autorizezi pe o masina
  cu browser, muti tokenul) in loc de cont de serviciu — mai putini pasi, fara
  consola Google Cloud. Documentat si ca `sync` sterge local ce nu mai e in Drive.

tests/ nou (20 de teste, fara retea si fara Ollama): preferinta de format,
vizibilitatea in dashboard, taierea XML, entitati, comentarii, XML invalid,
elemente uriase. Suita puntii Discord: 426 pass, neafectata.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
2026-08-31 18:15:03 +00:00

8.1 KiB

Punte WhatsApp + RAG pentru Maria (LXC 171)

Bot de suport ROA pe WhatsApp: puntea WhatsApp (Baileys) primeste mesajele, consumer-ul RAG raspunde folosind DOAR informatiile dintr-un depozit de documente indexat cu embeddings (Ollama). Controlul (start/stop/restart, documente, reindexare, sincronizare Google Drive) se face din dashboard-ul puntii Discord (../discord-bridge/dashboard/), sectiunea "Maria — WhatsApp

  • RAG" — un singur panou comun pentru ambele punti de pe acest container, nu un dashboard separat per serviciu.

Continua prototipul descris in claude-agent/docs/maria-whatsapp-rag-prototype.md (construit initial in /tmp/maria-bridge/) — aici e mutat in git, ca serviciu persistent.

Nu confunda cu:

  • Maria pe Flowise (vfp_roaauto/COMUN/utile/chatbot/) — chatbot web separat.
  • Echo / echo-whatsapp-bridge.service (LXC 110 moltbot) — alt bot, alta punte.
  • Punte Discord -> Claude Code (discord-bridge/, acelasi container) — alt proiect, alt scop (comanda Claude Code de pe Discord, nu suport RAG).

Arhitectura

WhatsApp (self-chat, sau numarul legat)
   |
   v
whatsapp/index.js (Baileys)  -- API HTTP :8099 (/status /send /messages /react /qr)
   |
   v
rag/consumer.py  -- polling la /messages, RAG stateless (FARA memorie intre mesaje)
   |                  vezi docs/maria-whatsapp-rag-prototype.md pentru motiv
   v
rag/store.py     -- depozit documente (.txt/.md) in ~/.maria-bridge/documents/
rag/indexer.py   -- chunking + embeddings Ollama -> rag_index.json
rag/sync.py      -- rclone pull din Google Drive + reindexare conditionata

../discord-bridge/dashboard/api.py -- panou comun; sectiunea Maria controleaza
                    maria-whatsapp.service + maria-rag.service, gestioneaza
                    documentele si declanseaza sincronizare/reindexare

Servicii systemctl --user (vezi ops/):

Unitate Ce face
maria-whatsapp.service Puntea Baileys (Node), port 8099
maria-rag.service Consumer RAG (Python), polling + raspunsuri
maria-sync.service + .timer Sincronizare Drive + reindexare, la 10 min

Instalare

cd maria-whatsapp-bridge
./ops/install.sh            # creeaza ~/.maria-bridge/, venv, npm install, symlink-uri unit

Completeaza manual ~/.maria-bridge/env (copiat din ops/env.example la prima rulare): cel putin LLM_URL (backend-ul de chat) si, daca vrei sincronizare automata cu Drive, DRIVE_REMOTE.

OLLAMA_URL (implicit http://127.0.0.1:11434, folosit pentru embeddings la indexare) presupune un Ollama instalat local pe container — nu exista alt Ollama documentat in infrastructura. Instalare (facuta deja pe LXC 171, 2026-08-31):

sudo apt-get install -y zstd   # dependinta a instalatorului Ollama
curl -fsSL https://ollama.com/install.sh | sh
ollama pull nomic-embed-text   # ~274 MB, CPU-only pe acest container

Bridge-ul WhatsApp NU porneste automat la instalare — cere scanarea unui cod QR (actiune manuala, o singura data):

./ops/install.sh --start
# apoi deschide dashboard-ul puntii Discord si scaneaza codul QR din
# sectiunea "Maria — WhatsApp + RAG" -> cardul "Conectare WhatsApp"

Dashboard (comun cu puntea Discord)

Nu exista un dashboard separat pentru Maria. Controlul se face din dashboard-ul puntii Discord — vezi ../discord-bridge/README.md pentru URL si autentificare (DASHBOARD_TOKEN din ~/.claude-discord/env, tunel SSH sau Tailscale la /claude). Acolo, sectiunea "Maria — WhatsApp + RAG":

  • start/stop/restart pentru puntea WhatsApp si consumer-ul RAG
  • starea conexiunii WhatsApp si codul QR de asociere (cand nu e conectat)
  • listare, adaugare si stergere documente din depozitul RAG
  • reconstruire index manual, sau sincronizare Drive imediata
  • ultimele linii din logurile fiecarui serviciu Maria (whatsapp.log/rag.log)

Depozitul de documente

Fisiere .txt/.md/.xml in ~/.maria-bridge/documents/ (lista exacta: store.DOC_EXTENSIONS). Se pot administra:

  1. manual din dashboard-ul comun (adaugare/stergere text, reindexare automata la salvare);
  2. prin sincronizare Google Drive — vezi mai jos.

Sincronizare cu Google Drive

Sursa: dosarul document_store din Drive-ul contului mmarius28@gmail.com (pe Windows apare ca D:\GoogleDrive\romfast\document_store, prin Google Drive Desktop). ID-ul dosarului: 1C4e75zgH1_7ZK-_oBP5ZZBvUPh3iEo1O.

Containerul e headless (fara browser pentru OAuth), deci autorizarea se face pe o masina cu browser si se muta aici ca token — fara cont de serviciu si fara consola Google Cloud. rclone e deja instalat pe LXC 171 (rclone v1.60.1).

Pasi (o singura data):

  1. Pe Windows, ia rclone.exe de la https://rclone.org/downloads/ (arhiva portabila, nu cere instalare) si ruleaza in acel dosar:
    rclone.exe authorize "drive" --drive-scope=drive.readonly
    
    Se deschide browserul; autentifica-te cu mmarius28@gmail.com si accepta. In consola apare un token JSON intre ---> si <---. Copiaza-l intreg.
  2. Pe container, creeaza remote-ul cu tokenul copiat:
    rclone config create gdrive drive scope=drive.readonly token='<TOKEN_JSON>'
    
  3. Testeaza ca vede dosarul:
    rclone lsf gdrive: --drive-root-folder-id=1C4e75zgH1_7ZK-_oBP5ZZBvUPh3iEo1O
    
  4. Pune tinta in ~/.maria-bridge/env:
    DRIVE_REMOTE=gdrive,root_folder_id=1C4e75zgH1_7ZK-_oBP5ZZBvUPh3iEo1O:
    
    (sintaxa „connection string" a rclone — fixeaza dosarul fara sa depinda de structura de nume din My Drive.)
  5. Testeaza: butonul „sincronizeaza din Drive acum" din dashboard, sau systemctl --user start maria-sync.service apoi journalctl --user -u maria-sync -n 50.

Tokenul se reimprospateaza singur (refresh token) cat timp aplicatia ramane autorizata in contul Google. Daca expira, dashboard-ul arata sincronizarea ca esuata — reia pasii 1-2.

Ce se sincronizeaza

rclone sync aduce doar *.txt, *.md si *.xml (vezi store.DOC_EXTENSIONS). Restul din dosar — chatflow-uri Flowise .json, scripturi .ps1, .docx — sunt ignorate deliberat: nu sunt cunostinte de suport.

sync sterge local ce nu mai exista in Drive, deci depozitul e o oglinda a dosarului din Drive, nu o colectie care creste. Documentele adaugate manual din dashboard dispar la prima sincronizare daca nu exista si in Drive.

Acelasi document in doua formate

Cand exista X.xml si X.md, in index intra doar .xml (ordinea de preferinta: .xml > .md > .txt, in rag/store.py). Sursele .xml sunt structurate pe probleme si de regula mai noi decat exporturile .md — la d406_saft_knowledge, .xml era cu trei luni mai nou si cu 50% mai mare. Fisierul umbrit ramane pe disc si apare in dashboard marcat „umbrit de …", ca sa se vada de ce nu e indexat; daca ar fi indexate ambele, acelasi raspuns ar aparea de doua ori in rezultatele RAG.

Cum se taie XML-ul in chunk-uri

Un chunk per element de nivel 1 — adica o problema = un chunk, cu mesajul de eroare si rezolvarea impreuna. Taierea pe linii goale (cea folosita la .md) le-ar separa, iar cautarea ar gasi eroarea si ar returna un chunk fara raspuns. Etichetele raman ca prefixe lizibile (mesaj eroare: …, rezolvare: …), fara paranteze unghiulare. Un XML care nu se poate parsa nu opreste indexarea: cade pe taierea obisnuita, cu o linie in log. Vezi rag/indexer.py si tests/test_store_si_chunking.py.

Dupa configurare, maria-sync.timer trage la fiecare 10 minute; reindexarea ruleaza DOAR daca s-a schimbat efectiv ceva in depozit (amprenta pe nume + mtime + marime, vezi rag/sync.py), ca sa nu reface embeddings degeaba.

Teste

cd /workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge
python3 -m pytest      # preferinta de format + taierea XML, fara retea si fara Ollama

Context conversational

rag/consumer.py nu retine memorie intre mesaje — fiecare intrebare e o interogare RAG independenta (system prompt + top-K chunk-uri + intrebare). Vezi claude-agent/docs/maria-whatsapp-rag-prototype.md pentru motiv si comparatie cu celelalte punti (Discord: context nelimitat + /new; Maria pe Flowise: fereastra fixa de 5 schimburi).