chore: sterge handoff-urile temporare CONTEXT_HANDOVER_*
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_013oz3nLpbEDB1J8uyrFkxTA
This commit is contained in:
@@ -1,198 +0,0 @@
|
|||||||
# CONTEXT HANDOVER — Punte Discord → Claude Code (LXC 171)
|
|
||||||
|
|
||||||
**Data:** 2026-08-30 13:00 UTC · **Stare:** LIVRATA, in productie, pe `master` (impins)
|
|
||||||
**Commit curent:** `c8a5d41` · **Repo:** romfast/ROMFASTSQL · **Branch:** `master`
|
|
||||||
|
|
||||||
Nu e un task intrerupt. Puntea functioneaza si e folosita. Documentul asta exista ca o
|
|
||||||
sesiune noua sa poata prelua operarea si continuarea fara sa redescopere totul.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Ce e
|
|
||||||
|
|
||||||
Bot Discord care comanda LXC 171 (si, prin el, infrastructura) dintr-un canal privat, de pe
|
|
||||||
telefon. Bot subtire `discord.py` peste CLI-ul `claude`, cu **proces persistent per fir**
|
|
||||||
alimentat pe stdin cu `--input-format stream-json` — de aceea un mesaj trimis in timpul unui
|
|
||||||
tur functioneaza ca **steering**, nu deschide tur nou.
|
|
||||||
|
|
||||||
Plan sursa: `~/.gstack/projects/romfast-ROMFASTSQL/claude-master-plan-discord-bridge-20260830.md`
|
|
||||||
(15 taskuri, toate facute). Executat cu 5 agenti in 3 lane-uri paralele.
|
|
||||||
|
|
||||||
**Cod:** `proxmox/lxc171-claude-agent/discord-bridge/`
|
|
||||||
**Documentatie:** `discord-bridge/README.md` (operare completa), `discord-bridge/INTERFACES.md`
|
|
||||||
(contractul intre module — de citit inainte de orice modificare), `discord-bridge/security/README.md`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Stare de rulare (verificata la 12:58 UTC)
|
|
||||||
|
|
||||||
| | |
|
|
||||||
|---|---|
|
|
||||||
| Serviciu | `systemctl --user status claude-discord` → **active**, `NRestarts=0` |
|
|
||||||
| Bot | `ClaudeAgent#7891`, app id `1543576449624186880` |
|
|
||||||
| Guild | Romfast `1430476698264145922` |
|
|
||||||
| Canal | **privat** `#claude-agent` `1543581063496859679` |
|
|
||||||
| User permis | `949388626146517022` (marius.mutu) |
|
|
||||||
| Director de lucru | `/workspace/claude-agent/` (repo git propriu) |
|
|
||||||
| Model | `sonnet` (default), `/model opus` per fir |
|
|
||||||
| Cost ziua curenta | $1.5456 (plafon `COST_CAP_USD_DAY=5.00`) |
|
|
||||||
| Comenzi slash | 7, inregistrate pe guild |
|
|
||||||
|
|
||||||
**Stare pe disc:** `~/.claude-discord/` — `env` (0600, token+allowlist), `state.json`,
|
|
||||||
`bot-settings.json` (hook), `approvals/`, `logs/`, `venv/`.
|
|
||||||
Cod versionat in repo; secretele NU.
|
|
||||||
|
|
||||||
### Comenzi (slash, nu prefix `!` — migrat)
|
|
||||||
`/new [fork]` · `/cd <cale>` · `/model <sonnet|opus>` · `/status` · `/stop` · `/cleanup [force]` · `/help`
|
|
||||||
|
|
||||||
Mesajele obisnuite (fara comanda) merg la Claude ca prompt.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Ce s-a verificat EMPIRIC in productie (nu doar teste)
|
|
||||||
|
|
||||||
Astea sunt afirmatii cu dovada, nu presupuneri:
|
|
||||||
|
|
||||||
| Ce | Dovada |
|
|
||||||
|---|---|
|
|
||||||
| Steering mid-tur | mesaj la 6s intr-un tool call de 25s a schimbat raspunsul final (ALFA→BETA), CLI 2.1.251 |
|
|
||||||
| Comenzi slash | 7 citite inapoi din API-ul Discord, nu doar din log |
|
|
||||||
| Confirmare operatiuni ireversibile | `12:24:25 PENDING` → butoane in canal → `12:24:39 ALLOW` → `/tmp/test-punte` sters |
|
|
||||||
| Fail-closed | acelasi `rm -rf` fara aprobare → `DENY` la timeout, directorul intact |
|
|
||||||
| Proces persistent | pid viu la 2 min dupa incheierea turului (reaper la 20 min) |
|
|
||||||
| `--resume` peste restart | aceeasi sesiune reluata dupa ce restartul a omorat procesul |
|
|
||||||
| `KillMode=control-group` | restart mid-sesiune, zero orfani |
|
|
||||||
| Parser tolerant | `rate_limit_event` aparut real in stream, ignorat cu un singur log |
|
|
||||||
| `/cleanup` dupa fix | rulare seaca pe container: „Niciun proces orfan" |
|
|
||||||
| Suita | **322 passed** cu discord.py, **319 passed + 3 skipped** fara |
|
|
||||||
|
|
||||||
Rulare teste: `cd proxmox/lxc171-claude-agent/discord-bridge && python3 -m pytest`
|
|
||||||
E2E (CLI real, ~40s, costa): `pytest -m e2e`
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Capcane gasite pe parcurs — NU le reintroduce
|
|
||||||
|
|
||||||
Astea au costat timp si unele erau periculoase. Sunt fixate; sunt scrise aici ca sa nu
|
|
||||||
fie „simplificate" inapoi.
|
|
||||||
|
|
||||||
1. **`/cleanup` omora sesiunile de lucru.** Definitia initiala („orice proces `claude`
|
|
||||||
absent din `state.json`") prindea sesiunile Claude interactive de pe container:
|
|
||||||
rularea seaca propunea 25 de procese / 3864 MB, inclusiv sesiunea din care ar fi fost
|
|
||||||
data comanda. **Apartenenta la cgroup-ul `claude-discord.service` e conditie NECESARA
|
|
||||||
pentru toate familiile**, fail-closed la cgroup necitibil. Sesiunile de lucru stau in
|
|
||||||
`tmux-spawn-*.scope`. Exista test de regresie pe instantaneul real; verificat prin
|
|
||||||
mutant ca musca (fara filtru pica 3 teste).
|
|
||||||
|
|
||||||
2. **`pid_start_time` e in SECUNDE**, nu ticks (`session_store.py` converteste; `state.json`
|
|
||||||
viu contine secunde). Un consumator care compara ticks nu se potriveste niciodata — si
|
|
||||||
cum acea comparatie protejeaza turul in desfasurare, esecul e tacut si face eligibil
|
|
||||||
pentru omorare exact ce trebuia protejat. Documentat in `INTERFACES.md`.
|
|
||||||
|
|
||||||
3. **`/proc/uptime` e virtualizat de lxcfs, `starttime` nu.** Calculul naiv al varstei dadea
|
|
||||||
`4123168064` pentru un proces de 10 minute. Se foloseste `btime` din `/proc/stat`.
|
|
||||||
|
|
||||||
4. **Utilizatorul containerului se numeste `claude`**, deci potrivirea pe subsirul `/claude`
|
|
||||||
marca orice proces din home-ul lui. Potrivire pe basename per argument.
|
|
||||||
|
|
||||||
5. **Log dublat:** unitul redirecteaza stdout in `bot.log` (`StandardOutput=append:`) SI
|
|
||||||
codul avea `FileHandler` pe acelasi fisier. `FileHandler` ramane doar la rulare manuala
|
|
||||||
(`INVOCATION_ID` absent).
|
|
||||||
|
|
||||||
6. **`View Channel` lipsea** din permisiunile de invitatie — fara ea botul nu vede canalul,
|
|
||||||
oricat de permis ar fi in allowlist. Link corect (`permissions=309237763136`):
|
|
||||||
`https://discord.com/oauth2/authorize?client_id=1543576449624186880&scope=bot%20applications.commands&permissions=309237763136`
|
|
||||||
|
|
||||||
7. **Comenzile slash au nevoie de scope `applications.commands`** (nu doar `bot`) si de
|
|
||||||
**sync pe guild** (instantaneu; global ~1h). Fiecare comanda face `defer()` — altfel
|
|
||||||
Discord marcheaza interactiunea esuata dupa 3s desi comanda a rulat.
|
|
||||||
|
|
||||||
8. **`~/bin` trebuie sa fie in PATH-ul unitului**, altfel wrapperul `infra` e prezent pe
|
|
||||||
disc dar negasibil de bot (stratul 4 de securitate inert).
|
|
||||||
|
|
||||||
9. **Raportul `/cleanup`** depasea 2000 de caractere cu rezultatele atasate → mesaj respins
|
|
||||||
de Discord exact la `force:True`. Buget de caractere `MAX_REPORT_CHARS=1800`.
|
|
||||||
|
|
||||||
10. **Discord API respinge User-Agent-ul implicit al `urllib`** cu `error code: 1010`
|
|
||||||
(Cloudflare). Arata identic cu un refuz de permisiuni. Pune un User-Agent real.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Decizii de arhitectura — luate deliberat, NU le redeschide
|
|
||||||
|
|
||||||
Sunt in plan si au fost reconfirmate de utilizator. Un agent nou care le „descopera" ca
|
|
||||||
probleme pierde timp si risca sa strice lucruri.
|
|
||||||
|
|
||||||
- **`--permission-mode bypassPermissions` e intentionat.** Regulile `deny` sunt strat
|
|
||||||
cosmetic, NU bariera — verificat: `/usr/bin/ssh -V` si `bash -c "ssh -V"` trec pe langa.
|
|
||||||
Bariera reala e hook-ul PreToolUse.
|
|
||||||
- **Accesul larg la infrastructura si la `/workspace` e FUNCTIONALITATE ceruta**, nu bug.
|
|
||||||
De aceea: fara user separat `cdbot`, fara allowlist de proiecte pentru `/cd`.
|
|
||||||
- **Fara audit append-only** (stratul 3), respins constient. **Consecinta asumata:** la o
|
|
||||||
problema nu exista jurnal independent; logurile pot fi sterse de chiar procesul care le
|
|
||||||
scrie. Hook-ul si botul ruleaza sub acelasi user, deci un agent isi poate scrie singur
|
|
||||||
`"status":"allow"`.
|
|
||||||
- **Fara reluare automata a turului pierdut** — risc de dubla executie sub bypassPermissions.
|
|
||||||
- Fara Agent SDK, fara dashboard web, fara user separat, fara test golden pe JSONL inregistrat.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. CE A RAMAS DE FACUT
|
|
||||||
|
|
||||||
### 6.1 Token Proxmox cu ACL (singurul lucru neterminat)
|
|
||||||
|
|
||||||
**Nu e conditie de functionare.** Puntea merge acum folosind cheile SSH root deja prezente
|
|
||||||
pe container — adica orice tur are drepturi depline pe cluster. Tokenul restrange asta.
|
|
||||||
|
|
||||||
Comenzile exacte sunt in `discord-bridge/security/README.md`. Pe scurt, ca root pe `pvemini`:
|
|
||||||
|
|
||||||
```
|
|
||||||
pveum user add claude-bridge@pve
|
|
||||||
pveum role add ClaudeBridge -privs "Datastore.Audit,Sys.Audit,Sys.Console,Sys.Syslog,VM.Audit,VM.Monitor,VM.Console,VM.PowerMgmt"
|
|
||||||
pveum acl modify /
|
|
||||||
pveum user token add claude-bridge@pve discord --privsep 1
|
|
||||||
pveum acl modify / --tokens 'claude-bridge@pve!discord'
|
|
||||||
```
|
|
||||||
|
|
||||||
`VM.Allocate` (creare/distrugere guest) e lasat afara INTENTIONAT.
|
|
||||||
|
|
||||||
Apoi: secretul (afisat o singura data) in `~/.claude-discord/env` ca `PVE_TOKEN_ID` /
|
|
||||||
`PVE_TOKEN_SECRET`, `chmod 600`. De decis daca ACL ramane pe `/` sau se restrange la un
|
|
||||||
subset de guest-uri.
|
|
||||||
|
|
||||||
### 6.2 Idei ramase, neprioritizate
|
|
||||||
- Atasamente Discord → Claude (download in /tmp + cale in prompt) — era v1.1 in plan.
|
|
||||||
- Rate limit-ul Discord la render e singura degradare tacuta ramasa: are tratare (interval
|
|
||||||
adaptiv 1s→5s), nu are test. Acceptat, fiindca esecul e intarziere, nu pierdere.
|
|
||||||
- `/cleanup` nu prinde procese iesite complet din cgroup — pret deliberat, ca sa nu atinga
|
|
||||||
niciodata sesiunile interactive.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. Fisiere cheie
|
|
||||||
|
|
||||||
| Fisier | De ce conteaza |
|
|
||||||
|---|---|
|
|
||||||
| `discord-bridge/INTERFACES.md` | contractul intre module + proprietatea pe fisiere. **De citit primul.** |
|
|
||||||
| `discord-bridge/README.md` | operare, instalare, depanare, limitari oneste |
|
|
||||||
| `discord-bridge/runner.py` | proces persistent, reaper, respawn `--resume` |
|
|
||||||
| `discord-bridge/session_store.py` | state.json atomic, lock per fir, PID reuse |
|
|
||||||
| `discord-bridge/cleanup.py` | vezi capcana #1 — cea mai periculoasa zona |
|
|
||||||
| `discord-bridge/security/confirm_hook.py` | hook PreToolUse, fail-closed |
|
|
||||||
| `discord-bridge/security/infra` | wrapper cu lista explicita de hosturi (exit 3 la host necunoscut) |
|
|
||||||
| `discord-bridge/ops/claude-discord.service` | `KillMode=control-group`, MemoryHigh 3G / Max 6G, PATH |
|
|
||||||
| `~/.gstack/.../claude-master-plan-discord-bridge-20260830.md` | planul original, cu justificarile |
|
|
||||||
|
|
||||||
## 8. Comenzi de operare
|
|
||||||
|
|
||||||
```bash
|
|
||||||
systemctl --user status claude-discord
|
|
||||||
journalctl --user -u claude-discord -n 200
|
|
||||||
tail -f ~/.claude-discord/logs/bot.log
|
|
||||||
tail -f ~/.claude-discord/logs/confirm_hook.log # cine a cerut/primit aprobare
|
|
||||||
cd proxmox/lxc171-claude-agent/discord-bridge && python3 -m pytest
|
|
||||||
python3 cleanup.py # rulare seaca, sigura
|
|
||||||
```
|
|
||||||
|
|
||||||
**Memorie:** un fir costa ~440 MB (claude 300 + servere MCP 137). Plafon 4 procese → ~1,8 GB,
|
|
||||||
sub `MemoryHigh=3G`. Containerul are 16 GB.
|
|
||||||
@@ -1,127 +0,0 @@
|
|||||||
# Context handover — 2026-08-31
|
|
||||||
|
|
||||||
Sesiune lungă, patru fire de lucru terminate și unul în curs. Totul e comis și împins.
|
|
||||||
|
|
||||||
> **Infrastructura e deja documentată în acest repo** — nu o repet aici. Punctele de
|
|
||||||
> intrare: [`CLAUDE.md`](CLAUDE.md), [`proxmox/README.md`](proxmox/README.md) și, pentru
|
|
||||||
> chatboți, [`docs/chatboti-si-punti.md`](docs/chatboti-si-punti.md) (scris azi).
|
|
||||||
> Documentul ăsta e doar despre **ce s-a schimbat azi și ce a rămas**.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Ce rulează acum (starea la finalul sesiunii)
|
|
||||||
|
|
||||||
| | Stare |
|
|
||||||
|---|---|
|
|
||||||
| **Maria (WhatsApp + RAG)** | **LXC 171**, asociată la +40723197939, funcțională. 11 documente, sincronizare Drive la 10 min. |
|
|
||||||
| Prototipul Maria de pe LXC 104 | **oprit și dezactivat**. `llama-qwen35.service` (LLM-ul, port 8091) și `flowise.service` rămân pornite acolo. |
|
|
||||||
| Puntea Discord | activă, cu atașamente (imagini + fișiere text). |
|
|
||||||
| Reindexare RAG | **era în curs la finalul sesiunii** — vezi „De verificat întâi" mai jos. |
|
|
||||||
|
|
||||||
## 2. De verificat întâi, la reluare
|
|
||||||
|
|
||||||
Reindexarea pornită după corectarea `efactura-erori-rag.xml` s-ar putea să nu fi terminat.
|
|
||||||
|
|
||||||
```bash
|
|
||||||
pgrep -af "sync.py" # gol = gata
|
|
||||||
ls -la ~/.maria-bridge/rag_index.json # mtime recent = reconstruit
|
|
||||||
python3 -c "
|
|
||||||
import json,collections
|
|
||||||
e=json.load(open('/home/claude/.maria-bridge/rag_index.json'))
|
|
||||||
c=collections.Counter(x['source'] for x in e)
|
|
||||||
print(len(e),'chunk-uri,',len(c),'documente')
|
|
||||||
print(c.get('efactura-erori-rag.xml'),'din efactura-erori-rag.xml (8 = structurat corect, ~41 = inca text simplu)')
|
|
||||||
"
|
|
||||||
```
|
|
||||||
|
|
||||||
O reindexare completă durează **~18–21 de minute** (embedding CPU-only, ~7,3 s per chunk).
|
|
||||||
Nu e blocată — chiar durează atât. Dacă a eșuat, se reia cu:
|
|
||||||
`cd proxmox/lxc171-claude-agent/maria-whatsapp-bridge/rag && MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python sync.py --force`
|
|
||||||
|
|
||||||
## 3. Ce s-a făcut azi
|
|
||||||
|
|
||||||
### Git și Gitea
|
|
||||||
- **`romfast/workspace` dădea 500** de pe 2026-04-06: patru gitlink-uri fără `.gitmodules`
|
|
||||||
făceau Gitea 1.24.3 să dea panic pe listarea rădăcinii. Scoase din index. **Nu era vina
|
|
||||||
botului**, cum se bănuia inițial.
|
|
||||||
- **`/workspace/claude-agent` e acum repo propriu** (`git@gitea.romfast.ro:romfast/claude-agent.git`),
|
|
||||||
scos din repo-ul `workspace` unde fusese comis din greșeală.
|
|
||||||
|
|
||||||
### Memorie și mod de lucru al botului Discord
|
|
||||||
- `~/.claude/projects/-workspace-claude-agent/memory` e **symlink** către memoria lui
|
|
||||||
`-workspace-romfastsql`. Firele Discord porneau amnezice fiindcă memoria e per director
|
|
||||||
de proiect. Copia veche: `memory.bak-2026-08-31`.
|
|
||||||
- **Regula nouă:** orice cerere de dezvoltare → citește `claude-agent/TODO.md`, planifică
|
|
||||||
cu `Agent(subagent_type: "Plan", model: "opus")`, execută pe Sonnet, actualizează
|
|
||||||
`TODO.md`. Scrisă în `claude-agent/CLAUDE.md` + `claude-agent/docs/flux-dezvoltare.md`.
|
|
||||||
Testată pe sesiuni reale: prima versiune era prea blândă și agentul sărea peste plan la
|
|
||||||
sarcini mici — întărită explicit cu „mărimea nu e criteriu".
|
|
||||||
- **`claude-agent/TODO.md`** e registrul viu al funcționalităților. Nimic blocat acum.
|
|
||||||
|
|
||||||
### Puntea Discord — atașamente
|
|
||||||
Imagini (png/jpeg/gif/webp, max 4, max 3,5 MB brut) → blocuri `image`; fișiere text →
|
|
||||||
inserate în prompt (max 4, trunchiate la 100 KB); restul doar numite. Merge și mid-tur ca
|
|
||||||
steering. 31 de teste noi; suita: 426 pass.
|
|
||||||
|
|
||||||
### Maria — mutare completă pe LXC 171
|
|
||||||
- **Sincronizare Drive** prin `rclone authorize` (fără cont de serviciu). Procedura
|
|
||||||
completă: [`docs/rclone-google-drive-headless.md`](docs/rclone-google-drive-headless.md).
|
|
||||||
Script: `maria-whatsapp-bridge/ops/setup-drive.sh`.
|
|
||||||
- **`.xml` acceptat și preferat peste `.md`** la același nume de bază; chunking per
|
|
||||||
problemă (un `<eroare_x>` = un chunk, cu mesajul și rezolvarea împreună).
|
|
||||||
- **Lacăt `flock` între reindexări** + scriere atomică a indexului. Descoperit când
|
|
||||||
timer-ul a pornit peste o rulare manuală și două procese scriau același fișier.
|
|
||||||
- **Fix LID**: după asociere nu răspundea nimic. WhatsApp livrează self-chat-ul ca
|
|
||||||
`51947713372214@lid`, iar filtrul compara doar cu `<număr>@s.whatsapp.net` — arunca tot,
|
|
||||||
tăcut. Reparat; mesajele respinse se loghează acum cu motivul.
|
|
||||||
- 26 de teste noi (prima suită a proiectului Maria).
|
|
||||||
|
|
||||||
### Documentație
|
|
||||||
- **[`docs/chatboti-si-punti.md`](docs/chatboti-si-punti.md)** — care chatbot e care.
|
|
||||||
Trei lucruri numite „Maria", doi boți pe Discord, două punți WhatsApp pe **același
|
|
||||||
număr**. A costat o oră de diagnostic azi: Maria răspundea de pe LXC 104, iar toate
|
|
||||||
verificările se făceau pe 171.
|
|
||||||
- **[`proxmox/lxc301-docker-template/README.md`](proxmox/lxc301-docker-template/README.md)** —
|
|
||||||
singurul guest nedocumentat. E un template Proxmox, nu un container oprit.
|
|
||||||
|
|
||||||
## 4. Ce a rămas de făcut
|
|
||||||
|
|
||||||
### a) LXC 301 — nimic urgent (corectat 2026-08-31)
|
|
||||||
Versiunea inițială a acestei secțiuni descria un conflict de IP activ cu VM 109. **Greșit:**
|
|
||||||
301 e marcat `template: 1`, deci nu poate fi pornit deloc și `onboot: 1` e ignorat. Nu
|
|
||||||
revendică 10.0.20.37 la reboot-ul lui pveelite, deci `pct set 301 -onboot 0` nu e necesar.
|
|
||||||
|
|
||||||
Verificat tot atunci: `basevol-301-disk-0@__base__` nu are niciun clon, deci `pct destroy 301`
|
|
||||||
ar fi sigur (~916 MB pe local-zfs). Opțional, la decizia utilizatorului — nu e urgent.
|
|
||||||
|
|
||||||
Singurul caz în care IP-ul redevine o problemă: dacă cineva convertește template-ul înapoi
|
|
||||||
în container. Atunci `-onboot 0` sau alt IP **înainte** de pornire.
|
|
||||||
|
|
||||||
### b) Auditul de infrastructură, dacă se dorește
|
|
||||||
Coverage-ul e bun — LXC 301 era singurul guest fără pagină proprie. Ce **nu** s-a făcut:
|
|
||||||
verificarea sistematică a fiecărui README existent față de starea live. Divergențele
|
|
||||||
găsite azi (prototipul Maria pe 104, LLM-ul pe 8091, Gitea rupt de 5 luni) au ieșit la
|
|
||||||
iveală accidental, nu dintr-un audit.
|
|
||||||
|
|
||||||
### c) Model de răspuns al Mariei
|
|
||||||
Rămâne `Qwen3.5-2B-Q4` pe LXC 104 — decizia utilizatorului a fost „păstrez deocamdată, cu
|
|
||||||
RAG-ul complet". Răspunsurile factuale (e-Factura) sunt acum corecte și ancorate în
|
|
||||||
documente; cele explicative sunt încă vagi. De reevaluat după câteva întrebări reale.
|
|
||||||
|
|
||||||
## 5. Capcane de reținut
|
|
||||||
|
|
||||||
- **Verifică întâi pe ce container ești.** `8099` și `11434` există și pe 104 și pe 171,
|
|
||||||
cu conținut diferit. Nu repeta ora pierdută azi.
|
|
||||||
- **Nu reporni prototipul de pe 104.** Ar răspunde în paralel, pe același număr, cu un
|
|
||||||
index de 24 de chunk-uri.
|
|
||||||
- **`rclone sync` e distructiv pe destinație.** Depozitul Mariei e oglindă a Drive-ului;
|
|
||||||
un document adăugat manual din dashboard dispare la următoarea sincronizare.
|
|
||||||
- **Maria nu are voie la infrastructură** — e chatbot pentru clienții ERP ROA. Nimic din
|
|
||||||
`proxmox/`, niciun IP sau credențial în depozitul ei. Regula e în `claude-agent/CLAUDE.md`.
|
|
||||||
- **Nu tăia ieșirea comenzilor de diagnostic cu `head`.** Concluzia „nu ascultă nimic pe
|
|
||||||
8091" a fost greșită exact din cauza asta și a trimis diagnosticul pe pistă falsă.
|
|
||||||
|
|
||||||
## 6. Comiteri
|
|
||||||
|
|
||||||
`ROMFASTSQL`: `b29b9f2..824b215` (14 comiteri) · `claude-agent`: `6d0bfe1..c97ba0c` (9 comiteri)
|
|
||||||
· `workspace`: `d24dca3`. Toate împinse pe gitea.romfast.ro.
|
|
||||||
@@ -1,164 +0,0 @@
|
|||||||
# Context handover — 2026-08-31, seara
|
|
||||||
|
|
||||||
Continuarea sesiunii din [`CONTEXT_HANDOVER_20260831.md`](CONTEXT_HANDOVER_20260831.md).
|
|
||||||
Tot ce e mai jos e comis și împins (`ROMFASTSQL`: `b447251..c514818`, `claude-agent`: două
|
|
||||||
comiteri noi).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. Întrebarea care a declanșat handover-ul
|
|
||||||
|
|
||||||
> *„nu vreau ca reindexarea să refacă toate embeddings-urile de fiecare dată. de ce le face?"*
|
|
||||||
|
|
||||||
**Nu le mai face.** Reparat în `c514818` (parte din commit-ul `ca6b53e`), verificat pe viu:
|
|
||||||
|
|
||||||
```
|
|
||||||
[indexer] 12 documente, 169 chunk-uri (0 embeddings noi, 169 refolosite)
|
|
||||||
real 0.15s # înainte: ~20 de minute
|
|
||||||
```
|
|
||||||
|
|
||||||
### De ce le refăcea
|
|
||||||
|
|
||||||
`rag/indexer.py:build()` era scris ca o reconstrucție totală: pentru fiecare document, pentru
|
|
||||||
fiecare chunk, `embed(chunk)` — necondiționat. Nu exista nicio noțiune de „chunk-ul ăsta e
|
|
||||||
neschimbat". Indexul vechi era pur și simplu suprascris.
|
|
||||||
|
|
||||||
Costul: un embedding pe CPU (nomic-embed-text, fără GPU pe LXC 171) durează **~7 secunde**.
|
|
||||||
La 169 de chunk-uri asta înseamnă ~20 de minute — plătiți integral chiar și când se schimba
|
|
||||||
un singur document. Adăugarea dicționarului de erori Oracle (29 de chunk-uri noi) ar fi
|
|
||||||
costat 20 de minute în loc de 3.
|
|
||||||
|
|
||||||
Nu era o scăpare evidentă la citirea codului: `sync.py` avea deja o optimizare care *arăta*
|
|
||||||
ca și cum ar rezolva problema — amprenta pe nume+mtime+mărime, care sărea reindexarea când
|
|
||||||
nimic nu se schimbase. Dar aia decide **dacă** se reindexează, nu **cât** se reindexează.
|
|
||||||
Odată ce un singur fișier se atingea, se plătea totul.
|
|
||||||
|
|
||||||
### Cum se rezolvă acum
|
|
||||||
|
|
||||||
`rag/indexer.py:_vectori_existenti()` citește indexul precedent și îl transformă într-un
|
|
||||||
dicționar `text_chunk -> embedding`. La reconstrucție, fiecare chunk se caută întâi acolo.
|
|
||||||
|
|
||||||
Cheia e **textul chunk-ului**, nu numele documentului și nu mtime-ul. Motivul: dacă nu s-a
|
|
||||||
schimbat niciun caracter în chunk, vectorul e identic prin definiție. Un document reformatat
|
|
||||||
în Drive, ale cărui chunk-uri ies la fel, nu costă nimic. Un document în care s-a schimbat un
|
|
||||||
rând costă doar chunk-urile atinse.
|
|
||||||
|
|
||||||
**Sursa e chiar `rag_index.json`, nu un al doilea fișier de cache.** Deliberat: nu există un
|
|
||||||
al doilea fișier care să se poată desincroniza, iar dacă indexul lipsește se recalculează tot,
|
|
||||||
exact ca înainte. Logul spune de fiecare dată câte au fost calculate și câte refolosite.
|
|
||||||
|
|
||||||
### Când tot se recalculează (și e corect așa)
|
|
||||||
|
|
||||||
| Situație | Ce se întâmplă |
|
|
||||||
|---|---|
|
|
||||||
| `rag_index.json` lipsește sau e corupt | se recalculează tot — nu are de unde refolosi |
|
|
||||||
| Textul unui chunk s-a schimbat, oricât de puțin | acel chunk se re-embedează (corect: alt text = alt vector) |
|
|
||||||
| Se schimbă **strategia de chunking** (`indexer.py`) | toate chunk-urile ies altfel → se recalculează tot |
|
|
||||||
| Se schimbă `EMBED_MODEL` în env | **capcană: NU se recalculează**, deși ar trebui |
|
|
||||||
|
|
||||||
Ultima linie e singura problemă rămasă și e reală: cheia de cache e doar textul, fără modelul
|
|
||||||
care l-a produs. Dacă cineva schimbă `EMBED_MODEL`, indexul rămâne un amestec de vectori din
|
|
||||||
două modele, iar căutarea dă rezultate aiurea fără niciun mesaj de eroare. Nu s-a întâmplat
|
|
||||||
(modelul e `nomic-embed-text` de la început), dar merită reparat — vezi „De făcut" mai jos.
|
|
||||||
|
|
||||||
**Fișiere:** `rag/indexer.py` (`_vectori_existenti`, `build`), teste în
|
|
||||||
`tests/test_store_si_chunking.py` (`test_vectorii_din_indexul_vechi_se_refolosesc`,
|
|
||||||
`test_textul_modificat_se_reembedeaza`).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Ce s-a mai făcut în sesiune
|
|
||||||
|
|
||||||
Toate în `proxmox/lxc171-claude-agent/`. Documentația completă e în
|
|
||||||
[`maria-whatsapp-bridge/README.md`](proxmox/lxc171-claude-agent/maria-whatsapp-bridge/README.md) —
|
|
||||||
aici doar ce nu se vede din cod.
|
|
||||||
|
|
||||||
### Asociere WhatsApp prin cod, nu doar QR
|
|
||||||
`/pair` exista dar nu era folosibil: fără descriptor `browser` explicit WhatsApp refuză codul,
|
|
||||||
iar după introducerea lui corectă serverul cere un restart (515) care consuma din bugetul de
|
|
||||||
reîncercări și putea opri puntea la a cincea asociere. Cod valabil ~3 minute, cu marcarea
|
|
||||||
expirării în dashboard. Folosit pe bune pentru re-legarea lui Echo.
|
|
||||||
|
|
||||||
### Capturi de ecran cu erori (OCR)
|
|
||||||
tesseract `ron+eng`; modelul de răspuns e strict text, deci OCR nu e o opțiune de calitate.
|
|
||||||
**Căutarea în index folosește doar liniile care arată a eroare**, nu toată fereastra — un ecran
|
|
||||||
de meniuri diluează embedding-ul. Modelul primește captura întreagă, marcată ca OCR.
|
|
||||||
|
|
||||||
### Rank hibrid (`rag/rank.py`)
|
|
||||||
Embeddings + BM25, fuzionate prin RRF. Motivul, măsurat: intervalele de cosinus **se suprapun**
|
|
||||||
— întrebări bune 0,600–0,816, străine 0,534–0,685. Codurile (`ORA-01722`, `D406`, `CIF`) sunt
|
|
||||||
exact ce ratează căutarea semantică. Praguri calibrate cu `ops/calibrate-rank.py`, **19/19**.
|
|
||||||
|
|
||||||
### Escaladare la suport
|
|
||||||
Când nu există acoperire, modelul **nu mai e întrebat deloc**. Referință (`M-260831-FC34`),
|
|
||||||
confirmare onestă („am trimis" doar dacă notificarea chiar a plecat), captura se retrimite ca
|
|
||||||
imagine. Jurnal în `~/.maria-bridge/escalations/`, vizibil în dashboard.
|
|
||||||
|
|
||||||
### Dicționar de 29 de erori Oracle
|
|
||||||
`maria-whatsapp-bridge/knowledge/oracle-erori-uzuale.xml`, scris pentru clienți — fără nume de
|
|
||||||
servere sau pași de administrare.
|
|
||||||
|
|
||||||
### Două bug-uri găsite în log
|
|
||||||
- Fiecare mesaj din self-chat sosea **de două ori** (`@s.whatsapp.net` și `@lid`) → Maria
|
|
||||||
răspundea dublu. Dedup pe `key.id` în punte.
|
|
||||||
- Documentele adăugate din dashboard ajungeau în oglinda Drive și **dispăreau la următorul
|
|
||||||
`rclone sync`**. Acum există `~/.maria-bridge/documents-local/`.
|
|
||||||
|
|
||||||
### Echo re-legat la WhatsApp
|
|
||||||
Dispozitivul `:14`. S-a lămurit de ce nu răspund ambele punți în chatul „Eu": Echo aruncă tot
|
|
||||||
ce e `fromMe && !isGroup`, iar în self-chat tot ce scrii e `fromMe`. **Echilibrul e accidental** —
|
|
||||||
detalii în [`docs/chatboti-si-punti.md`](docs/chatboti-si-punti.md).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. De făcut
|
|
||||||
|
|
||||||
### a) `EMBED_MODEL` în cheia de cache (mic, real)
|
|
||||||
Vezi tabelul de mai sus. Cea mai simplă variantă: pune modelul în fiecare intrare de index
|
|
||||||
(`"model": config.get("EMBED_MODEL")`) și refolosește doar intrările cu modelul curent.
|
|
||||||
Intrările vechi, fără câmp, se tratează ca „model necunoscut" → se recalculează o dată.
|
|
||||||
|
|
||||||
### b) `SUPPORT_JID` — decizia utilizatorului
|
|
||||||
E setat pe **propriul număr** (`40723197939@s.whatsapp.net`), pentru testare. De schimbat cu
|
|
||||||
numărul sau grupul echipei de suport, în `~/.maria-bridge/env`.
|
|
||||||
|
|
||||||
### c) Dicționarul Oracle trăiește doar pe container
|
|
||||||
E în `documents-local/`, deci supraviețuiește sincronizărilor, dar **nu-l vede nimeni altcineva**.
|
|
||||||
Locul lui pe termen lung e `document_store` din Drive. Când ajunge acolo, șterge copia locală —
|
|
||||||
altfel rămân două versiuni și câștigă tăcut cea din Drive.
|
|
||||||
|
|
||||||
### d) Recalibrare când se schimbă documentele
|
|
||||||
Pragurile din `env` sunt măsurate pe indexul de **acum**. La schimbări mari de conținut:
|
|
||||||
```bash
|
|
||||||
cd proxmox/lxc171-claude-agent/maria-whatsapp-bridge
|
|
||||||
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py
|
|
||||||
```
|
|
||||||
Adaugă în `CAZURI` (în capul scriptului) întrebările reale la care Maria a greșit — e singurul
|
|
||||||
mod în care calibrarea rămâne onestă.
|
|
||||||
|
|
||||||
### e) Rămase din handover-ul precedent
|
|
||||||
LXC 301 (nimic urgent), auditul de infrastructură, modelul de răspuns al Mariei.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Stare la finalul sesiunii
|
|
||||||
|
|
||||||
| | |
|
|
||||||
|---|---|
|
|
||||||
| Index RAG | 169 chunk-uri, 12 documente (din care `oracle-erori-uzuale.xml` e local) |
|
|
||||||
| Servicii | `maria-whatsapp`, `maria-rag`, `claude-discord-dashboard` — active |
|
|
||||||
| WhatsApp | Maria conectată (`40723197939`), Echo re-legat ca dispozitiv `:14` |
|
|
||||||
| Teste | **73 pass** (26 la începutul zilei) |
|
|
||||||
| `SUPPORT_JID` | propriul număr — de schimbat |
|
|
||||||
|
|
||||||
## 5. Capcane (în plus față de handover-ul precedent)
|
|
||||||
|
|
||||||
- **`systemctl` pe LXC 110 are nevoie de `--user`.** Serviciile lui Echo sunt unități de
|
|
||||||
utilizator (`moltbot`, uid 1000). Ca root, `systemctl is-active echo-core` răspunde
|
|
||||||
`inactive` deși botul rulează.
|
|
||||||
- **Nu adăuga documente în `~/.maria-bridge/documents/`.** E oglinda Drive-ului; `rclone sync`
|
|
||||||
le șterge. Folosește `documents-local/`.
|
|
||||||
- **Nu ridica pragurile de rank „ca să fie sigur".** Escaladarea prea agresivă e la fel de
|
|
||||||
proastă ca răspunsul generic — rulează calibrarea, nu intuiția.
|
|
||||||
- **Fiecare embedding costă ~7 secunde.** Orice schimbare în strategia de chunking înseamnă
|
|
||||||
recalcularea întregului index; nu e o schimbare de făcut în grabă.
|
|
||||||
@@ -1,154 +0,0 @@
|
|||||||
# Context handover — 2026-09-01, seara
|
|
||||||
|
|
||||||
> **REZOLVAT in `653f5cb`** (2026-09-01, noaptea). Pasul (1) facut: setul are 31 de
|
|
||||||
> cazuri. Pasul (2) facut, dar cauza s-a dovedit ALTA decat se banuia aici:
|
|
||||||
> politetea din ancora nu era ce strica verdictul, ci faptul ca termenii se
|
|
||||||
> comparau pe **cuvantul intreg** — documentul scrie „token"/„tokenuri", omul scrie
|
|
||||||
> „tokenul". Reparat prin potrivire pe primele 5 litere (codurile raman intregi) +
|
|
||||||
> varianta (a), `rank.assess(dovada=...)`. Varianta (b), lista de formule de
|
|
||||||
> politete, a fost scrisa si apoi **scoasa**: masuratoarea n-a justificat-o.
|
|
||||||
> 31/31 cu reparatia, 30/31 fara. Restul documentului se pastreaza ca istoric —
|
|
||||||
> §7 (eroarea „CUI cumparator incorect" lipsa din documente) e inca deschis.
|
|
||||||
|
|
||||||
**Ce e de făcut:** două lucruri legate între ele, în `proxmox/lxc171-claude-agent/maria-whatsapp-bridge/`:
|
|
||||||
|
|
||||||
1. adaugă **cazuri de continuare** în `ops/calibrate-rank.py` (azi setul n-are decât două, ambele cu cod de eroare);
|
|
||||||
2. repară **termenii distinctivi**: `rank.rare_terms()` judecă acoperirea pe *interogarea combinată* (ancora firului + mesajul nou), nu pe ce a întrebat omul acum.
|
|
||||||
|
|
||||||
(1) există ca să poți face (2) fără să ghicești. **Nu începe cu (2).**
|
|
||||||
|
|
||||||
Tot ce e mai jos e comis și împins: `ROMFASTSQL` `5e60394..af04d97`.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 1. De unde vine
|
|
||||||
|
|
||||||
Sesiunea de azi a plecat de la: *„am trimis 2 mesaje pe grupul maria test whatsapp și ambele răspunsuri sunt eronate"*. Cauza reală s-a dovedit dublă și e reparată (vezi §5), dar reparația a scos la iveală problema care rămâne deschisă.
|
|
||||||
|
|
||||||
Din firul de test real (escaladarea `M-260901-06EB`, `~/.maria-bridge/escalations/1788291399-M-260901-06EB.json`):
|
|
||||||
|
|
||||||
```
|
|
||||||
[consumer] rank: efactura_knowledge.md(0.836), … | cosinus 0.836, iar termenii
|
|
||||||
distinctivi (buna, luna, august, trimitere, auto) nu apar in documente
|
|
||||||
```
|
|
||||||
|
|
||||||
**„buna" e tratat drept termen distinctiv.** E rar în corpusul de ERP, deci `rare_terms()` îl consideră discriminant, deși e o formulă de salut.
|
|
||||||
|
|
||||||
De ce contează acum și nu conta înainte: de azi, la o continuare se caută după **ancoră + mesajul nou** (`consumer.py:574` → `fir.interogare()`), iar ancora e primul mesaj al omului, cu tot cu „Buna . … Poti te rog sa ma ajuti?". Ancora nu se șterge cât trăiește firul (2h), deci **fiecare continuare de pe firul ăla e judecată și după „buna/luna/august"**, cuvinte care nu vor apărea niciodată în documente.
|
|
||||||
|
|
||||||
### Efectul
|
|
||||||
|
|
||||||
`assess()` cere ca cel puțin `RANK_MIN_RARE_RATIO` (0,5) din termenii distinctivi să apară chiar în chunk-uri. Cu 3 cuvinte de politețe care nu apar niciodată, raportul scade artificial la fiecare replică → **escaladare în plus**.
|
|
||||||
|
|
||||||
Direcția e sigură (un om vede întrebarea, nu primește un răspuns inventat), deci **nu e urgent**. Dar face Maria inutilă exact pe discuțiile lungi, unde ar trebui să fie cea mai bună.
|
|
||||||
|
|
||||||
În cazul testat n-a stricat nimic — răspunsul chiar nu era în documente, escaladarea a fost corectă. Deci **nu ai încă nicio dovadă că regresia se produce în practică**; asta e chiar ce trebuie măsurat la pasul (1).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 2. Reparația propusă (nu implementată)
|
|
||||||
|
|
||||||
**Separă cele două roluri ale interogării:**
|
|
||||||
|
|
||||||
| Rol | Ce folosește azi | Ce ar trebui |
|
|
||||||
|---|---|---|
|
|
||||||
| **regăsire** (ce chunk-uri aduc) | ancoră + mesaj nou | rămâne așa — ancora chiar ajută, „da, mă blochează" singur nu găsește nimic |
|
|
||||||
| **acoperire** (răspund sau escaladez) | ancoră + mesaj nou | doar mesajul nou (+ OCR-ul capturii), adică ce a întrebat omul acum |
|
|
||||||
|
|
||||||
Concret, în `consumer.py:131 search()`, `rank.assess()` (`consumer.py:144`) primește azi același `query` ca și regăsirea. Ar trebui să primească separat interogarea de dovadă.
|
|
||||||
|
|
||||||
**Capcana, și motivul pentru care n-am făcut-o azi:** o continuare scurtă („da, mă blochează complet", „eram la salvarea unei facturi") **n-are termeni distinctivi proprii**. Judecată singură, escaladează — exact regresia pe care `fir.py` a fost scris ca s-o prevină (vezi tabelul din docstring-ul lui `rag/fir.py`, cazurile măsurate). Deci reparația poate strica mai mult decât repară, și nu se poate valida pe setul actual de cazuri.
|
|
||||||
|
|
||||||
Variante de luat în calcul, în ordinea preferinței:
|
|
||||||
- **a)** acoperirea se judecă pe mesajul nou, dar dacă acesta n-are termeni distinctivi proprii se cade înapoi pe interogarea combinată (continuările scurte se comportă ca azi);
|
|
||||||
- **b)** `rare_terms()` ignoră formulele de politețe — listă mică de stopwords („buna", „salut", "buna ziua", „multumesc", „va rog", „poti", „ajuti"). Whack-a-mole, dar o linie și zero risc;
|
|
||||||
- **c)** ancora se curăță la creare: se păstrează doar liniile care arată a eroare, cum face deja `ocr.retrieval_query()` pentru capturi (`rag/ocr.py`).
|
|
||||||
|
|
||||||
(b) e cel mai ieftin și poate fi făcut singur, imediat. (a) e cel corect. Nu le confunda: (b) ascunde simptomul măsurat azi, (a) rezolvă clasa.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 3. Ce trebuie adăugat în `ops/calibrate-rank.py` — pasul (1)
|
|
||||||
|
|
||||||
Setul (`CAZURI`, o listă de `(intrebare, are_raspuns_in_documente)`) are azi **26 de cazuri, 26/26 corecte**. Continuări are doar două, și **ambele conțin un cod de eroare**, deci trec pe regula codurilor și nu spun nimic despre termenii distinctivi:
|
|
||||||
|
|
||||||
```python
|
|
||||||
("ORA-06550: line 1, column 7 PLS-00906 object invalid\nda, ma blocheaza complet", True),
|
|
||||||
("ORA-12154 TNS could not resolve the connect identifier\neram la salvarea unei facturi", True),
|
|
||||||
```
|
|
||||||
|
|
||||||
**Lipsesc continuările fără cod de eroare** — exact cazul în care termenii distinctivi decid. De adăugat, în forma pe care o produce `fir.interogare()` (ancoră `\n` mesaj nou):
|
|
||||||
|
|
||||||
- ancoră politicoasă + continuare scurtă, cu răspuns în documente → `True`
|
|
||||||
(ex. `"Buna ziua . Am o problema la trimiterea in SPV . Puteti sa ma ajutati?\nimi zice ca tokenul e expirat"`)
|
|
||||||
- ancoră politicoasă + continuare scurtă, fără răspuns în documente → `False`
|
|
||||||
- aceeași continuare **fără** formula de salut în ancoră → același verdict ca varianta cu salut
|
|
||||||
(dacă verdictele diferă, ai măsurat exact regresia)
|
|
||||||
- ancoră + a treia, a patra replică (fir lung) → verdictul nu trebuie să se degradeze cu lungimea
|
|
||||||
- continuare pe un fir deschis de o **captură** (ancora = OCR, plin de zgomot: „Total", „Lit", „Selecteaz!")
|
|
||||||
|
|
||||||
Scrie-le pornind de la fire reale, nu inventate: `~/.maria-bridge/escalations/*.json` are câmpul `fir` cu schimburile, iar `~/.maria-bridge/conversations/*.json` are ancora exactă.
|
|
||||||
|
|
||||||
**Atenție:** fiecare caz nou costă un embedding (~7s pe CPU, fără GPU pe LXC 171). Se pun în cache automat în `~/.maria-bridge/calibrare-cache.json`, deci doar prima rulare e lentă.
|
|
||||||
|
|
||||||
### Cum se rulează
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cd /workspace/romfastsql/proxmox/lxc171-claude-agent/maria-whatsapp-bridge
|
|
||||||
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py
|
|
||||||
MARIA_BRIDGE_DIR=$HOME/.maria-bridge ~/.maria-bridge/venv/bin/python ops/calibrate-rank.py --sweep
|
|
||||||
python3 -m pytest -q # 103 teste; venv-ul n-are pytest, se ruleaza cu python3 de sistem
|
|
||||||
```
|
|
||||||
|
|
||||||
`--sweep` mătură `RANK_WEAK_COSINE` × `RANK_MIN_RARE_RATIO` (nu mai există prag „strong", vezi §5).
|
|
||||||
|
|
||||||
**Criteriu de acceptare pentru (2):** setul extins rămâne 100% corect *și* cu reparația, *și* fără ea nu e — altfel n-ai dovedit că reparația face ceva.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 4. Fișiere care contează
|
|
||||||
|
|
||||||
| Fișier | De ce |
|
|
||||||
|---|---|
|
|
||||||
| `rag/rank.py:168 rare_terms()` | inima problemei: `len(t) >= 4 and df <= 25% din corpus` → „buna" trece |
|
|
||||||
| `rag/rank.py:212 assess()` | decizia acoperirii; docstring-ul explică de ce nu mai există prag pe cosinus |
|
|
||||||
| `rag/consumer.py:131 search()` | aici `assess()` primește același query ca regăsirea — punctul de separat |
|
|
||||||
| `rag/consumer.py:574` | `search_query = fir_mod.interogare(fir, search_query)` — unde se combină |
|
|
||||||
| `rag/fir.py:140 interogare()` | ancoră + mesaj nou |
|
|
||||||
| `rag/fir.py:126 extinde_ancora()` | ancora crește cu OCR-ul capturii (adăugat azi) |
|
|
||||||
| `rag/fir.py` docstring | tabelul cu răspunsurile greșite care justifică existența ancorei — citește-l înainte să scoți ancora din ceva |
|
|
||||||
| `ops/calibrate-rank.py` | setul de cazuri + `--sweep` |
|
|
||||||
| `tests/test_rank.py`, `tests/test_fir.py` | testele care prind regresiile |
|
|
||||||
| `README.md` §„Cum se ordonează rezultatele (rank)" | documentația de actualizat la final |
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 5. Ce s-a reparat azi (nu reface)
|
|
||||||
|
|
||||||
Trei comiteri, toate împinse, serviciile repornite, verificate pe viu în grupul „Maria Test".
|
|
||||||
|
|
||||||
**`45b3659`** — două răspunsuri inventate: mesaj vag (cosinus 0,768) și captură cu `errorMessage="CUI cumparator incorect"` (0,778, eroare inexistentă în documente). Cauza: `RANK_STRONG_COSINE=0,70` însemna „cosinusul singur e destul", dar toate chunk-urile eFactura seamănă între ele. Plus `LLM_TEMPERATURE=0` (llama.cpp folosea 0,8).
|
|
||||||
|
|
||||||
**`47110d7`** — două probleme de fond:
|
|
||||||
- gardul „mesaj prea vag" (`triaj.prea_vag`) cerea text sub 12 cuvinte; mesajul real avea 14. Acum nu mai numără cuvinte și se aplică **după căutare, doar când nu există acoperire**, o singură dată pe fir (`fir["detalii_cerute"]`);
|
|
||||||
- `fir.este_continuare()` rupea firul la **orice** imagine. Cazul frecvent e tocmai text + captură trimise una după alta. Acum o captură în `FIR_IMAGINE_MIN` (5 min) continuă firul, OCR-ul intră în ancoră, iar pe o escaladare deschisă captura pleacă la aceeași referință;
|
|
||||||
- **`RANK_STRONG_COSINE` a dispărut de tot.** Motivul e important pentru (2): *cosinusul crește cu lungimea interogării*. Aceeași captură: 0,778 singură, **0,836** împreună cu mesajul de dinainte. Orice prag fix de sus e trecut de o discuție destul de lungă. Acoperirea se sprijină acum exclusiv pe dovada lexicală — adică pe chiar `rare_terms()`, ceea ce face (2) mai important decât părea.
|
|
||||||
|
|
||||||
**`af04d97`** — găsite citind logul: `requests` nu ridică excepție la 4xx/5xx, deci escaladarea se scria `notified: true` chiar când puntea răspundea 503; puntea nu loga nimic la trimitere; mesajele primite se logau tăiate la 80 de caractere fără semn (m-a dus la o concluzie greșită în diagnostic).
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 6. Constrângeri de mediu
|
|
||||||
|
|
||||||
- **Modelul de chat e Qwen3.5-2B Q4** pe LXC 104 (`LLM_URL=http://10.0.20.161:8091`). Inventează dacă îl lași. Poarta de acoperire e singura apărare reală — de asta merită reparată bine.
|
|
||||||
- **Nu încerca un model mai mare pe LXC 171.** Testat azi: `qwen3:4b-instruct` prin Ollama, 4 nuclee, fără GPU → 600s timeout fără niciun token; un prompt de 15 tokeni a scos 6 tokeni în 10 min 20s. Modelul a fost șters. Fără GPU sau API extern (vezi `docs/supliment-glm-zai.md`, neimplementat) nu e altă variantă.
|
|
||||||
- **Ollama pe 171 are doar `nomic-embed-text`**, pentru embeddings. Un embedding ≈ 7s.
|
|
||||||
- Servicii: `systemctl --user restart maria-rag maria-whatsapp`; loguri în `~/.maria-bridge/logs/{rag,whatsapp}.log`.
|
|
||||||
- Grup de test: `120363409761730101@g.us` („Maria Test"), `SUPPORT_JID` e chiar numărul propriu, deci escaladările se văd.
|
|
||||||
- **Șterge firele vechi înainte de a testa** (`rm -f ~/.maria-bridge/conversations/*.json`), altfel primul mesaj e luat drept continuare a discuției precedente — TTL 120 min.
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
## 7. De reținut, separat de task
|
|
||||||
|
|
||||||
**„CUI cumparator incorect" nu există în niciun document** (`grep -ri "cumparator incorect" ~/.maria-bridge/documents*` → zero). Maria escaladează corect, dar ar putea răspunde. E o eroare eFactura reală și frecventă. N-am scris textul — nu inventez procedură ROA. De pus în `document_store` (Google Drive), de către cineva care știe procedura.
|
|
||||||
Reference in New Issue
Block a user