LXC 171 a fost asociat la WhatsApp (+40723197939, dispozitiv nou al aceluiasi cont), iar pe 104 `maria-whatsapp-bridge` si `maria-whatsapp-consumer` au fost oprite si dezactivate ca sa nu raspunda amandoua la acelasi mesaj. `llama-qwen35.service` ramane pornit acolo: el e LLM-ul de raspuns, folosit acum de 171 prin retea. docs/chatboti-si-punti.md a fost scris cu cateva ore inainte de mutare si spunea ca instanta vie e cea de pe 104 — actualizat peste tot (tabel, sectiuni, porturi, comenzi de verificare), plus un avertisment sa nu se reporneasca prototipul ca "reparatie": ar raspunde in paralel, pe acelasi numar, cu un index de 24 de chunk-uri. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Q4uzvgm7AyJch5WH8QHRhY
220 lines
9.9 KiB
Markdown
220 lines
9.9 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. In acea zi rolul a trecut aici:
|
|
LXC 171 a fost asociat la acelasi numar, iar pe 104 serviciile
|
|
`maria-whatsapp-bridge` si `maria-whatsapp-consumer` au fost oprite si dezactivate.
|
|
`llama-qwen35.service` (LLM-ul de raspuns) si `flowise.service` raman pornite acolo. 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).
|