Pe 2026-08-31 s-a pierdut aproape o ora de diagnostic fiindca Maria raspundea pe WhatsApp, dar verificarile se faceau pe containerul gresit: pe LXC 171 totul parea rupt (niciun LLM pe 8091, WhatsApp neasociat, log fara activitate) in timp ce raspunsurile veneau de pe LXC 104. Nicio pagina nu spunea ca exista doua instalari. docs/chatboti-si-punti.md, verificat live: tabel scurt "cine e cine", cate o sectiune per instanta (Maria pe Flowise, Maria WhatsApp de pe 104 care e cea vie, Maria din git de pe 171 care nu e asociata, Echo pe 110, puntea Discord), tabel de porturi cu notarea ca 8099 si 11434 exista pe ambele containere cu continut diferit, comenzi de verificat cu cine vorbesti, si capcanele (prototipul de pe 104 nu e in git, depozitul e oglinda a Drive-ului, reindexarea dureaza ~21 min, granita Mariei fata de infrastructura). Faptul care lega totul: WhatsApp-ul lui Echo (110) si al Mariei (104) sunt legate la acelasi numar, +40723197939, ca dispozitive diferite ale aceluiasi cont. Documentul e legat din CLAUDE.md si dintr-un citat pus in capul celor cinci README-uri implicate, ca sa fie gasit de cine aterizeaza direct acolo. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
217 lines
9.6 KiB
Markdown
217 lines
9.6 KiB
Markdown
# Punte WhatsApp + RAG pentru Maria (LXC 171)
|
|
|
|
> **Care chatbot e care?** Exista trei lucruri numite „Maria", doi boti pe Discord si
|
|
> doua punti WhatsApp legate la acelasi numar. Inainte de a depana, citeste
|
|
> [`docs/chatboti-si-punti.md`](../../../docs/chatboti-si-punti.md) — spune care instanta raspunde de fapt.
|
|
|
|
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).
|
|
|
|
## ATENTIE: a existat o a doua instalare, pe LXC 104
|
|
|
|
Prototipul din care a plecat acest proiect a fost lasat sa ruleze pe **LXC 104
|
|
(Flowise)**, in `/root/maria-whatsapp-bridge/` — in afara git-ului, cu propriul
|
|
`consumer.py`, propriul `rag_index.json` si propriul `auth/` de WhatsApp.
|
|
|
|
Pana pe 2026-08-31, **aceea era Maria care raspundea de fapt pe WhatsApp**, iar cea
|
|
din git (LXC 171) nu fusese niciodata asociata. Indexul prototipului avea 24 de
|
|
chunk-uri dintr-un singur fisier (`roaauto_instructiuni.txt`), deci raspundea
|
|
inventand la orice intrebare din afara acelui subiect — inclusiv e-Factura.
|
|
|
|
Confuzia a costat timp de diagnostic: pe LXC 171 totul parea rupt (niciun LLM pe
|
|
8091, WhatsApp neasociat, `rag.log` fara activitate) desi utilizatorul primea
|
|
raspunsuri. Explicatia era ca ne uitam la containerul gresit.
|
|
|
|
**Daca depanezi Maria, verifica intai pe ce container ruleaza instanta conectata:**
|
|
|
|
```bash
|
|
# aici (LXC 171)
|
|
curl -s http://127.0.0.1:8099/status
|
|
# pe LXC 104, daca mai exista
|
|
ssh root@10.0.20.201 "pct exec 104 -- curl -s http://127.0.0.1:8099/status"
|
|
```
|
|
|
|
LLM-ul de raspuns (`llama.cpp` cu `Qwen3.5-2B-Q4_K_M`, port 8091) ruleaza tot pe
|
|
LXC 104 si e folosit prin retea: `LLM_URL=http://10.0.20.161:8091`. Pe LXC 171 nu
|
|
exista niciun model de chat — Ollama de aici are doar `nomic-embed-text`, pentru
|
|
embeddings.
|
|
|
|
## 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**, da tokenul scriptului de configurare:
|
|
```
|
|
ops/setup-drive.sh '<TOKEN_JSON>'
|
|
```
|
|
Face restul singur: creeaza remote-ul `gdrive`, verifica accesul la dosar,
|
|
scrie `DRIVE_REMOTE` in `~/.maria-bridge/env` (cu backup) si ruleaza prima
|
|
sincronizare cu reindexare. E idempotent — se poate rula din nou oricand.
|
|
|
|
Tinta scrisa in env e `gdrive,root_folder_id=<ID>:` — sintaxa „connection
|
|
string" a rclone, care fixeaza dosarul dupa ID, fara sa depinda de structura
|
|
de nume din My Drive.
|
|
3. Verifica in dashboard: sectiunea Maria trebuie sa arate „Drive: …" in loc de
|
|
„Sincronizare Drive dezactivata".
|
|
|
|
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).
|