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

188 lines
8.1 KiB
Markdown

# 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
```bash
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):
```bash
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):
```bash
./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
```bash
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).