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
This commit is contained in:
@@ -91,57 +91,93 @@ puntii Discord — vezi `../discord-bridge/README.md` pentru URL si autentificar
|
||||
|
||||
## Depozitul de documente
|
||||
|
||||
Fisiere `.txt`/`.md` in `~/.maria-bridge/documents/`. Se pot administra:
|
||||
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 `D:\GoogleDrive\romfast\document_store` de pe Windows (Google
|
||||
Drive Desktop). Containerul e headless, deci sincronizarea foloseste
|
||||
**rclone cu un cont de serviciu** — nu OAuth interactiv in browser.
|
||||
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. **Instaleaza rclone** pe container: `sudo apt-get install -y rclone`.
|
||||
✅ Deja instalat pe LXC 171 (2026-08-31), `rclone v1.60.1`. Pasii 2-7 de mai
|
||||
jos raman de facut — cer acces la consola Google Cloud a utilizatorului, nu
|
||||
se pot automatiza din container.
|
||||
2. **Creeaza un cont de serviciu Google** cu acces la Drive API (Google Cloud
|
||||
Console -> IAM -> Service Accounts -> Create -> descarca cheia JSON).
|
||||
3. **Partajeaza folderul** `document_store` din Google Drive cu adresa de email
|
||||
a contului de serviciu (click dreapta pe folder -> Share), exact cum ai
|
||||
partaja cu o persoana. Fara acest pas, contul de serviciu nu vede nimic.
|
||||
4. Pune cheia JSON pe container, ex. `~/.maria-bridge/gdrive-service-account.json`
|
||||
(0600).
|
||||
5. Configureaza remote-ul rclone (`rclone config`, fara sesiune interactiva de
|
||||
browser cu tip `service_account_file`):
|
||||
1. **Pe Windows**, ia `rclone.exe` de la <https://rclone.org/downloads/> (arhiva
|
||||
portabila, nu cere instalare) si ruleaza in acel dosar:
|
||||
```
|
||||
rclone config create gdrive drive \
|
||||
scope=drive.readonly \
|
||||
service_account_file=/home/claude/.maria-bridge/gdrive-service-account.json
|
||||
rclone.exe authorize "drive" --drive-scope=drive.readonly
|
||||
```
|
||||
6. Gaseste ID-ul folderului `document_store` (din URL-ul Drive) si testeaza:
|
||||
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 lsf gdrive: --drive-root-folder-id=<FOLDER_ID>
|
||||
rclone config create gdrive drive scope=drive.readonly token='<TOKEN_JSON>'
|
||||
```
|
||||
ID-ul curent al folderului `document_store` (cont `mmarius28@gmail.com`,
|
||||
gasit prin cautare Drive): `1C4e75zgH1_7ZK-_oBP5ZZBvUPh3iEo1O`.
|
||||
Sau, mai simplu, foloseste calea prin nume daca folderul e in "My Drive" al
|
||||
contului care a facut share (rclone urmareste shared-with-me cu
|
||||
`--drive-shared-with-me` daca e nevoie).
|
||||
7. Pune tinta gasita in `~/.maria-bridge/env`:
|
||||
3. Testeaza ca vede dosarul:
|
||||
```
|
||||
DRIVE_REMOTE=gdrive:romfast/document_store
|
||||
rclone lsf gdrive: --drive-root-folder-id=1C4e75zgH1_7ZK-_oBP5ZZBvUPh3iEo1O
|
||||
```
|
||||
8. Testeaza manual: `systemctl --user start maria-sync.service` apoi
|
||||
`journalctl --user -u maria-sync -n 50`, sau butonul „sincronizeaza din Drive
|
||||
acum" din dashboard.
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user