5.15 (propagare design + dashboard editare) si 5.14 (mapare LLM distilata) inchise dupa /code-review high. 8 buguri reparate TDD: - HIGH modal nu se deschidea pe randul slim (base.html: trimitere-slim) - HIGH /repune trunchia prestatii (declaratie incompleta la RAR) -> iterare peste existing, codes pozitional - HIGH embeddings incarca model ~230MB degeaba pe corpus gol -> poarta has_corpus() - HIGH picker chips gol pe re-render eroare -> conn/account_id pe toate ramurile - MED obs re-derivat dupa stergere explicita -> _merge_override pastreaza obs='' - MED mapare salvata fara denumire poluă GOLD -> _record_gold_validation guard - MED typo nome_prestatie -> nume_prestatie in select /repune - MED bucketare timp +3h gresita iarna -> SQLite localtime + TZ=Europe/Bucharest Embeddings WIRE-uit functional (PRD #15, decizie user): ensure_embeddings_corpus construieste corpus din nomenclator, gated pe AUTOPASS_EMBEDDINGS_ENABLED (default off). Marime model corectata ~50MB->~230MB (estimare PRD gresita). Cleanup: hoist load_* din bucla bulk-fix; import re la top. Regresie: 1256 passed, 1 deselected (live), 0 failed. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
389 lines
28 KiB
Markdown
389 lines
28 KiB
Markdown
<!-- plan sub /autoplan -->
|
||
# PRD 5.14 — Mapare automata operatii service prin distilare LLM
|
||
|
||
**Stare**: inchis (2026-06-28; CLOSE dupa `/code-review high` -> embeddings „mort dar scump" reparat + WIRE functional la decizia user: corpus din nomenclator gated pe `AUTOPASS_EMBEDDINGS_ENABLED`; marime model corectata ~50MB->~230MB; regresie 1256 passed)
|
||
|
||
## Stories de executie (decompozitie lead, 2026-06-28)
|
||
|
||
> PRD-ul a fost aprobat prin /autoplan ca DESIGN (Decision Audit Trail #11-20). Aici lead-ul
|
||
> il sparge in stories atomice executabile (ROADMAP §5.4), FARA a re-deschide deciziile.
|
||
> **Secventiere fata de 5.15 (D9 + cerinta user "prioritate design 5.15"):** partile DISJUNCTE
|
||
> de fisier ruleaza in PARALEL cu 5.15 acum; integrarea in editor (`mapping.py`/`routes.py`)
|
||
> ASTEAPTA 5.15 si se aplica PESTE designul 5.15, fara sa-l suprascrie.
|
||
|
||
| Story | Tip | Fisiere (disjunct?) | Depinde de |
|
||
|-------|-----|---------------------|-----------|
|
||
| **L14-S1** Layer 1 etichetator offline | tool | `tools/mapare-llm/or_label.py` + teste (mock OpenRouter) — DISJUNCT | — |
|
||
| **L14-S2** Temporal holdout (GATE Premisa 1) | tool | `tools/mapare-llm/holdout.py` + raport — DISJUNCT | — |
|
||
| **L14-S3** Schema suggestions + shared store | backend | `app/schema.sql` (aditiv), store module nou, seeder, teste — owns schema.sql | — |
|
||
| **L14-S4** Modul embeddings in-proces | backend | `app/embeddings.py` NOU + teste — DISJUNCT (modul; fara wiring) | — |
|
||
| **L14-S5** Set held-out eval (BLOCANT auto-send) | tool | `tools/mapare-llm/heldout_eval.py` + metodologie — DISJUNCT | — |
|
||
| **L14-S6** Integrare Layer 2/3 in editor | backend+UI | `app/mapping.py`, `app/web/routes.py` (editor) — **DUPA 5.15** | L14-S3,S4; 5.15 US-007/US-009 |
|
||
|
||
**Invariante de respectat (din Decision Audit Trail):** auto-send DOAR GOLD propriu (F-A/#11);
|
||
silver in tabela SEPARATA, niciodata in resolve_prestatii (#13); seeder INSERT OR IGNORE, nu
|
||
clobber uman (#2); scrub PII inainte de LLM (#3); NUL = ancore negative + supresie (#4);
|
||
provenance source/confidence (#5); embeddings doar SUGESTIE + degradare gratioasa (#16b);
|
||
held-out etichetat de OM = blocant pt orice auto-send peste GOLD (#19); tier "Inalta" sters din v1 (#17).
|
||
|
||
**Rezultat GATE Premisa 1 (L14-S2, 2026-06-28) — VERDICT: SLABA.** Validarea temporala STRICTA e
|
||
imposibila (CSV-urile `docs/operatii-service/*.csv` au doar frecvente agregate, fara timestamp). Proxy
|
||
Zipf + leave-first-out pe 155.195 operatii: pentru 90% acoperire de volum e nevoie de **4.368 denumiri
|
||
distincte (25.4% din total)**, nu "cateva sute"; leave-first-out (limita superioara de stationaritate)
|
||
= **88.9% agregat, SUB 90%**. Implicatie: etichetarea offline (L14-S1) trebuie sa proceseze ordine de
|
||
MII de denumiri per client; coada `needs_mapping` ramane semnificativa chiar dupa bootstrap. Premisa nu e
|
||
falsa, dar randamentul auto-rezolvarii e mai mic decat estima PRD-ul. NU blocheaza build-ul (piesele sunt
|
||
utile + auto-send ramane conservator pe GOLD), dar recalibreaza asteptarile de acoperire. Tool: `tools/mapare-llm/holdout.py`.
|
||
|
||
**Raport VERIFY 5.14** (subagent independent context curat, 2026-06-28) — **VERDICT: PASS, zero FAIL,
|
||
zero regresie 5.15.** `pytest -q -m "not live"` → **1245 passed, 0 failed**. Invariante confirmate cu cod+test:
|
||
- **F1/#11/#17 auto-send DOAR GOLD propriu**: `load_mapping` citeste EXCLUSIV `operations_mapping` al
|
||
contului; `resolve_prestatii` nu atinge DB (primeste `mapping` dict); singura cale spre `queued` =
|
||
GOLD propriu. SILVER/GOLD-partajat/embedding = sugestie. Teste `test_f1_*` PASS. Tier "Inalta" sters (#17).
|
||
- **#13 separare structurala**: grep confirma — `shared_store`/`mapping_suggestions`/`shared_mappings`
|
||
apar DOAR in `enrich_suggestions` (apelat din `pending_unmapped`), niciodata in `resolve_prestatii`/`load_mapping`.
|
||
- **#16b degradare gratioasa**: `is_available()=False` → `suggest_nearest=[]` fara exceptie; ingestia nu se blocheaza.
|
||
- **#2** seeder INSERT OR IGNORE (nu clobber uman); **#4** NUL nu devine cod; **#5** provenance source/confidence;
|
||
**#3** scrub PII nr/VIN inainte de LLM (`or_common.scrub`); **#19** held-out cu `cod_gold` GOL + kill-criterion
|
||
(`wrong_code_rate<0.5%` AND `coverage>50%`) — toate PASS cu teste.
|
||
- **GATE Premisa 1**: verdict **SLABA** documentat onest (proxy Zipf, fara pretentie de validare temporala).
|
||
- fastembed 0.8.0 INSTALAT; testul real de embedding trece.
|
||
|
||
**Riscuri reziduale (LOW, non-blocant)**: (1) fastembed 0.8.0 foloseste mean-pooling (warning) — relevant doar
|
||
daca se persista corpusul de vectori intre versiuni (acum re-indexat la nevoie din nomenclator); (2) `record_human_validation`
|
||
ON CONFLICT nu suprascrie `cod_prestatie` (by design — corectie = override per-cont sau DELETE explicit);
|
||
(3) lazy-load fastembed la prima cerere `/mapari` cand `AUTOPASS_EMBEDDINGS_ENABLED=true` (~230MB, cateva
|
||
zeci de secunde daca modelul nu e in cache — acceptat la decizia CLOSE). **CLOSE 2026-06-28: embeddings WIRE-uit
|
||
functional** (era „mort dar scump"): `ensure_embeddings_corpus(conn)` construieste corpusul din nomenclator
|
||
(`nume_prestatie`->`cod_prestatie`), apelat in `pending_unmapped` + `_nemapate_pentru_submission` inainte de
|
||
bucla, gated pe `AUTOPASS_EMBEDDINGS_ENABLED` (default OFF). Re-index doar la schimbarea semnaturii
|
||
nomenclatorului. Corpusul se construieste din nomenclator (18 coduri largi), NU per-confirmare umana — sugestia
|
||
embedding e similaritate denumire-prezentare vs. nume_prestatie RAR.
|
||
|
||
---
|
||
|
||
## Problema
|
||
|
||
La ingestie (canal API si import web), o prestatie poate veni cu `cod_op_service`
|
||
+ `denumire` in loc de `cod_prestatie` RAR. Daca nu exista mapare, submission-ul
|
||
intra in `needs_mapping` si asteapta confirmare umana. Service-urile reale au
|
||
**volume mari de denumiri particulare** (masurat: 17.435 denumiri DISTINCTE in 4
|
||
CSV-uri de clienti reali — `automotive` 13.170, `sigma` 3.743, `clever` 1.668,
|
||
`south` 875). Maparea manuala a acestora, prin editorul `needs_mapping`, e
|
||
prohibitiva: zeci de mii de operatii × confirmare umana.
|
||
|
||
Nomenclatorul RAR are doar **18 coduri** foarte largi (REPARATIE, INTRETINERE,
|
||
REVIZIE PERIODICA, etc. — `nomenclator_seed.py`). Deci problema nu e potrivire de
|
||
sinonime, ci **clasificare** a mii de operatii granulare in 18 categorii abstracte
|
||
+ detectare de „gunoi" (linii care nu sunt operatii: `ITP CT 12 ABC`, `DISCOUNT
|
||
MATERIALE 5%`, `MANOPERA`, nr. inmatriculare).
|
||
|
||
## Viziune (pivot 2026-06-28)
|
||
|
||
LLM-ul **NU ruleaza la runtime**. Rol unic: **etichetator offline** care
|
||
construieste un set de date (denumire -> cod). La runtime ruleaza un **clasificator
|
||
local mic, fara API** (similaritate / fuzzy / embeddings), „distilat" din etichetele
|
||
LLM + maparile validate de oameni. Trei straturi:
|
||
|
||
1. **Etichetare offline (LLM, periodic):** acopera denumirile cu cele mai multe
|
||
aparitii (frecventa) si grupeaza denumirile asemanatoare ca sa eticheteze ieftin.
|
||
2. **Clasificator runtime (fara AI):** exact -> fuzzy/substring -> similaritate
|
||
semantica (embeddings) peste baza de cunostinte. Zero cost per cerere, ruleaza pe
|
||
LXC.
|
||
3. **Baza de cunostinte PARTAJATA:** maparile validate de oameni din TOATE conturile
|
||
de service contribuie la clasificare (strat „gold" comun), peste etichetele LLM
|
||
(„silver" bootstrap). Munca de validare a unui service ajuta toate service-urile.
|
||
|
||
Viitor (nu acum): un LLM generativ local pe LXC. Pasul curent foloseste un model de
|
||
**embedding** (nu generativ): mic, CPU, milisecunde/text.
|
||
|
||
## Premise
|
||
|
||
1. **Volumul de denumiri distincte e finit si se schimba lent.** Odata etichetate,
|
||
90%+ din traficul viitor sunt repetari ale acelorasi denumiri (service-ul
|
||
refoloseste propriul vocabular). Lege Zipf: top 100 denumiri = 43.6% volum,
|
||
top 500 = 67.7%, top 1000 = 76.2% (din 155.195 operatii totale).
|
||
2. **RAR accepta NUMAI coduri din nomenclator.** Un cod necunoscut -> HTTP 500
|
||
(`ORA-12899`) + record PARTIAL FINALIZATA (terminal). Deci orice cod propus de
|
||
un sistem automat TREBUIE validat fata de nomenclator inainte de enqueue
|
||
(invariant existent in `resolve_prestatii(..., valid_codes)`).
|
||
3. **Maparea gresita are cost asimetric:** un cod gresit trimis = FINALIZATA
|
||
ireversibil la RAR. Deci pragul de auto-trimitere ramane conservator; incertul
|
||
ramane `needs_mapping` cu om in bucla. Etichetele LLM NEVALIDATE = sugestie, nu
|
||
auto-trimitere (vezi scara de incredere).
|
||
4. **Hardware LLM local generativ e prea lent acum** (masurat: Ollama LXC 104
|
||
generativ 180-320s/op). Embeddings locale insa sunt rapide pe CPU si suficiente
|
||
pentru similaritate la runtime.
|
||
5. **Datele nu sunt sensibile** (confirmat utilizator): denumirile de operatii pot
|
||
merge la API-uri cloud pentru etichetare. PII incidental (nr. inmatriculare/VIN)
|
||
se face scrub inainte de trimitere (F3).
|
||
|
||
## Masuratori
|
||
|
||
### Bootstrap (anterior, Groq)
|
||
- Groq `llama-3.3-70b`: 28ms/op, acord 94% cu heuristica pe cazuri clare, detectare
|
||
gunoi excelenta (`NUL`). Abandonat ca furnizor: cap zilnic free atins + cheie expusa.
|
||
|
||
### OpenRouter free — NVIDIA Nemotron (masurat 2026-06-28)
|
||
Furnizor nou pentru etichetare: cheie utilizator, modele GRATUITE, date ne-sensibile.
|
||
|
||
- **Capcane de cont (rezolvate):** modelele free dau initial `404 No allowed
|
||
providers` din cauza unui allowlist de provideri pe cont (venice/together/fireworks/
|
||
atlas-cloud) — `open-inference`/`google-ai-studio`/`nvidia` erau excluse. Fix:
|
||
eliminat restrictia in Settings -> Preferences + activat toggle-ul de privacy
|
||
„free endpoints may publish/train". WAF: User-Agent `Mozilla/5.0` obligatoriu.
|
||
- **Set fiabil = familia NVIDIA Nemotron.** Restul modelelor sunt 429 (rate-limited
|
||
upstream, partajat global: llama-3.3-70b, qwen3-next, gemma, hermes, dolphin) sau
|
||
404 (gpt-oss). Cap free tier ~50 cereri/zi fara credit.
|
||
- **Test ensemble pe top 120 dupa frecventa (46.4% din volum), 2026-06-28:**
|
||
|
||
| Model | ms/op | parse-fail | acord vs Groq (overlap) |
|
||
|---|---|---|---|
|
||
| nemotron-3-super-120b | 1463 | 0 | 100% |
|
||
| nemotron-nano-9b-v2 | 1248 | 0 | 100% |
|
||
| nemotron-3-ultra-550b | 6450 | 0 | 100% |
|
||
|
||
Acord ensemble ponderat pe volum: **3/3 unanim = 87% volum**, 2/3 = 13%, dezacord
|
||
total = **0%**. Din unanim: 7 NUL (gunoi), 100 coduri reale.
|
||
- **Decizie model:** pastram `super-120b` + `nano-9b`; **aruncam `ultra-550b`**
|
||
(4-5x mai lent, zero castig de acuratete). Caveat: ensemble din aceeasi familie
|
||
NVIDIA -> acordul supraestimeaza increderea fata de un ensemble cross-family.
|
||
- **Dezacordurile (13%) sunt cazuri de granita taxonomica reala**, nu zgomot:
|
||
`REGLAT DIRECTIE/FARURI` (OE-2 intretinere vs OE-4 reglare), `MANOPERA
|
||
TINICHIGERIE` (NUL vs OE-1), `DEZECHIPAT usa/bara` (pas de demontare), `INLOCUIT
|
||
FILTRU AER` (OE-1 vs OE-3). Astea trebuie sa cada in `needs_mapping`.
|
||
|
||
## Solutia
|
||
|
||
### Stratul 1 — Etichetare offline (LLM, fara cod runtime)
|
||
|
||
Tool CLI (`tools/mapare-llm/`, stil `tools/apikey`). Etichetatorul OpenRouter
|
||
(`or_common.py` + `or_label.py`) clasifica denumirile in cele 18 coduri RAR + `NUL`:
|
||
|
||
1. **Prioritizare pe FRECVENTA (NR), nu alfabetic.** Etichetam intai denumirile cu
|
||
cele mai multe aparitii (acopera cel mai mult volum per apel).
|
||
2. **Grupare pe similaritate inainte de etichetare.** Denumirile aproape identice
|
||
(`REGLAT DIRECTIE` / `REGLAT DIRECTIA` / `REGLARE DIRECTIE`) se grupeaza; LLM
|
||
eticheteaza doar **reprezentantul grupului**, codul se propaga la tot grupul.
|
||
Maximizeaza acoperirea per apel LLM (critic pe cap free de ~50 cereri/zi).
|
||
3. **Ensemble NVIDIA** (`super-120b` + `nano-9b`): acord -> incredere mai mare;
|
||
dezacord -> ramane pentru `needs_mapping`. Vot pe coduri, nu self-confidence.
|
||
4. **Scrub PII** (regex nr. inmatriculare/VIN) inainte de trimitere (F3, exista).
|
||
5. Output: dataset etichetat cu `denumire, cod, sursa, confidence` (provenienta).
|
||
`NUL` marcat separat (ancore negative + supresie), NU se promoveaza la cod RAR.
|
||
|
||
Prompt cu reguli explicite (avarii grave DOAR la accident; vopsire = reparatie;
|
||
ulei+filtru = revizie; gunoi -> NUL). Batch mare (cap free tier), retry/backoff pe
|
||
429, respecta `Retry-After`.
|
||
|
||
### Stratul 2 — Clasificator runtime (FARA AI, fara API)
|
||
|
||
Pentru o denumire din prezentare (canal API sau import), in `app/mapping.py`:
|
||
|
||
1. **Exact** in baza de cunostinte (`operations_mapping` + strat partajat) -> cod direct.
|
||
2. **Fuzzy/substring** (`operation_text_rules`, `rapidfuzz`) — exista deja.
|
||
3. **Similaritate semantica (embeddings)** — NOU: model multilingv mic (ex.
|
||
`intfloat/multilingual-e5-small` sau `paraphrase-multilingual-MiniLM`), CPU.
|
||
Vectorizam baza etichetata o data; la runtime vectorizam denumirea noua si luam
|
||
cel mai apropiat vecin (sau top-k cu vot). Optional: clasificator `scikit-learn`
|
||
(regresie logistica / kNN) antrenat pe (embedding -> cod) pentru generalizare
|
||
dincolo de vecinul exact. „Antrenarea pe datele de test" = acest pas, secunde,
|
||
ruleaza oriunde.
|
||
4. Cod propus -> validat OBLIGATORIU `valid_codes` (garda ORA-12899). Peste pragul
|
||
de incredere -> conform scarii; altfel `needs_mapping`.
|
||
|
||
Decizie de gazduire runtime: ramane deschisa pentru reviziile plan (in-proces in
|
||
gateway vs microserviciu pe LXC/Flowise). Default propus: in-proces (cel mai simplu).
|
||
|
||
### Stratul 3 — Baza de cunostinte PARTAJATA cross-account
|
||
|
||
**Schimbare fata de versiunea anterioara** (care izola corpusul per cont):
|
||
|
||
- **Strat GOLD partajat:** maparile **validate de oameni** (din `needs_mapping`, in
|
||
ORICE cont) intra intr-un store partajat `denumire_normalizata -> cod`. Astfel
|
||
validarea facuta de un service ridica increderea pentru toate. Cheia = denumire
|
||
normalizata (scrub PII, lower, strip), nu textul brut.
|
||
- **Strat SILVER:** etichetele LLM (bootstrap) — sugestii, NU auto-trimitere.
|
||
- **Override per-cont:** daca un cont mapeaza explicit o denumire la alt cod decat
|
||
cel partajat (conflict legitim de vocabular), override-ul contului castiga pentru
|
||
acel cont. Conflictele inter-cont se rezolva cu provenienta + (optional) majoritate.
|
||
|
||
Confirmarile umane curg organic prin folosirea normala a editorului `needs_mapping`
|
||
— FARA sesiune separata de adjudecare manuala (cerinta utilizator).
|
||
|
||
### Scara de incredere (runtime, per operatie din prezentare)
|
||
|
||
| Treapta | Sursa | Actiune | Frictiune |
|
||
|---|---|---|---|
|
||
| Certa | exact in stratul GOLD (validat de om, orice cont) sau override cont | auto-trimite | zero |
|
||
| Inalta | embedding NN cu similaritate FOARTE inalta la o mapare GOLD + ensemble LLM unanim | auto-trimite (prag calibrat) | zero |
|
||
| Medie | LLM silver / similaritate medie | `needs_mapping` cu sugestie pre-completata -> 1 click | minima |
|
||
| Joasa | similaritate slaba / coduri apropiate | `needs_mapping` manual | normala |
|
||
| NUL | non-operatie (ITP, discount, nr. inmatriculare) | marcat „nu e operatie", suprimat | — |
|
||
|
||
**Invariant F1 (pastrat):** o eticheta pur-LLM NEVALIDATA nu auto-trimite singura;
|
||
auto-send cere ori GOLD (validat de om), ori treapta „inalta" calibrata. Tensiunea
|
||
centrala (utilizatorul se bazeaza pe LLM, dar FINALIZATA e ireversibil) = intrebarea
|
||
cheie pentru reviziile plan: unde fix se aseaza bara treptei „inalta".
|
||
|
||
## Integrare
|
||
|
||
- Stratul 1: tool CLI offline `tools/mapare-llm/` (exista: `or_common.py`,
|
||
`or_modeltest.py`; de adaugat `or_label.py` cu grupare + propagare).
|
||
- Stratul 2: similaritate embeddings in `app/mapping.py` (`enrich_suggestions` ->
|
||
`suggest_nearest`), apelata in `pending_unmapped` / `_nemapate_pentru_submission`
|
||
pentru sugestia din editor. Corpusul se construieste din nomenclator via
|
||
`ensure_embeddings_corpus` (gated pe `AUTOPASS_EMBEDDINGS_ENABLED`, default off):
|
||
lazy-load model fastembed/ONNX (~230MB) la prima cerere /mapari cand flagul e activ,
|
||
re-index doar la schimbarea nomenclatorului (semnatura). Off -> no-op (cade pe
|
||
GOLD/SILVER + fuzzy). SUGGESTION-ONLY: NU intra in resolve_prestatii/enqueue (#13).
|
||
- Stratul 3: store partajat (tabela noua `shared_mappings` sau coloana de scope pe
|
||
`operations_mapping`), seed la confirmare umana; override per-cont.
|
||
- Validare `valid_codes` pe tot lantul (exista).
|
||
|
||
## Non-obiective
|
||
|
||
- Nu inlocuim confirmarea umana pentru cazuri incerte.
|
||
- Nu trimitem automat coduri sub prag / etichete LLM nevalidate.
|
||
- Nu adaugam dependenta cloud la RUNTIME (LLM doar offline pentru etichetare).
|
||
- Nu antrenam un LLM generativ local acum (viitor).
|
||
|
||
## Riscuri
|
||
|
||
- Etichete LLM gresite tratate ca adevar daca scapa garda F1 (seed direct in GOLD).
|
||
- Ensemble aceeasi familie (NVIDIA) -> acord corelat-gresit; supraestimare incredere.
|
||
- Strat partajat cross-account: o denumire poate insemna lucruri diferite la
|
||
service-uri diferite -> conflict; mitigat prin override per-cont + provenienta.
|
||
- Drift: denumiri noi neacoperite; embeddings ajuta dar nu elimina.
|
||
- Free tier OpenRouter flaky (429/404, cap 50/zi) -> etichetarea bulk e lenta;
|
||
e offline, deci tolerabil, dar nu pe calea critica de productie.
|
||
- Model embedding ales: calitate pe limba romana de verificat empiric.
|
||
|
||
<!-- AUTONOMOUS DECISION LOG -->
|
||
## Decision Audit Trail
|
||
|
||
| # | Faza | Decizie | Clasificare | Principiu | Rationament | Respins |
|
||
|---|------|---------|-------------|-----------|-------------|---------|
|
||
| 1 | Eng | Seed-ul NU intra direct in stratul auto-send; etichetele LLM = strat SILVER (sugestii). Auto-send cere GOLD (validat de om) sau treapta inalta calibrata | TASTE (critic) | P1, P5 | `resolve_prestatii`->`queued` direct => seed auto = AUTO-TRIMITERE ghiciri la FINALIZATA ireversibil (Premisa 3) | seed direct in auto-send |
|
||
| 2 | Eng | Seeder = `INSERT OR IGNORE` / refuza overwrite pe randuri validate de om | MECHANICAL | P1 | re-rularea ar clobber-ui maparile umane cu ghiciri LLM | ON CONFLICT UPDATE |
|
||
| 3 | Eng | Scrub regex (nr. inmatriculare/VIN) inainte de trimitere la LLM | TASTE | P1 | gunoiul contine `ITP CT 12 ABC` = nr. inmatriculare = PII | trimitere text brut |
|
||
| 4 | Eng | NUL = ancore negative in corpus + lista supresie | MECHANICAL | P1 | altfel gunoiul recurent reintra mereu in needs_mapping si fuzzy ii da cod gresit | exclude NUL |
|
||
| 5 | Eng | Coloana `source`/`confidence` (provenienta) pe baza de cunostinte | MECHANICAL | P1 | audit + rollback batch model prost + safe re-seed | fara provenienta |
|
||
| 6 | Eng | Runtime = embeddings + clasificator mic (sklearn), NU LLM generativ | TASTE | P3, P5 | LLM generativ local prea lent (Premisa 4); embeddings CPU suficiente + rapide | LLM la runtime |
|
||
| 8 | Eng | **SUPERSEDED:** corpus partajat cross-account (strat GOLD comun), NU per-cont izolat; override per-cont pe conflict | TASTE | P1, P2 | cerinta utilizator: validarea unui service ajuta toate; muncă compusa. Conflictul de vocabular rezolvat prin override + provenienta | (vechi: corpus strict per-cont) |
|
||
| 9 | Eng | Furnizor etichetare = OpenRouter free, ensemble NVIDIA (super-120b + nano-9b); aruncat ultra-550b | MECHANICAL | P3 | masurat 2026-06-28: doar NVIDIA routeaza fiabil; ultra 4-5x lent fara castig | Groq (cap atins) / ultra |
|
||
| 10 | Eng | Etichetare prioritizata pe frecventa + grupare pe similaritate (eticheteaza reprezentant, propaga) | MECHANICAL | P2 | acopera mult mai mult volum per apel; critic pe cap free ~50/zi | etichetare alfabetica |
|
||
| 11 | CEO | **F-A: cross-account GOLD = suggestion-only**, nu auto-send cross-cont; doar GOLD PROPRIU (validat de omul contului) auto-trimite | GATE (user) | P1 | prima-intalnire cross-cont = FINALIZATA gresit ireversibil; override per-cont e post-hoc | cross-account auto-send (PRD scris) |
|
||
| 12 | CEO | Premisa 1 (90% repeat) validata cu **temporal holdout INAINTE** de build | GATE (user) | P1 | concentrare-in-corpus != future-repeats-past; ieftin de verificat | build pe asumtie |
|
||
| 13 | Eng | **Strat SILVER in TABELA SEPARATA** (mapping_suggestions), citita DOAR de suggest_codes/pending_unmapped; NICIODATA de load_mapping/resolve_prestatii | MECHANICAL | P5,P1 | scope-column pe operations_mapping auto-trimite silver (8+ call-site); separare structurala | scope column pe operations_mapping |
|
||
| 14 | Eng | Shared store = tabela noua pe cheia `denumire_normalizata` (NU coloana pe operations_mapping: cheie diferita cod_op_service + UNIQUE) | MECHANICAL | P5 | spatii de chei diferite; conflict UNIQUE | scope column |
|
||
| 15 | Eng | **Embeddings Layer 2 RAMANE in v1** (utilizatorul a respins amanarea la gate; mentine Decision #6). Recomandarea ambelor voci era amanare la v2 | USER CHALLENGE -> override user | P3,P5 | voci: 2GB pe ipoteza nemasurata, 18 clase acoperite de exact+fuzzy. User: vrea castig pe coada RO + control infra | (amanare v2) |
|
||
| 16 | Eng | Embeddings = **IN-PROCES fastembed/ONNX** (~230MB pe disc, ONNX quantizat, fara torch; estimarea initiala de ~50MB a fost gresita — modelul multilingv `paraphrase-multilingual-MiniLM-L12-v2` are ~231MB chiar quantizat), in procesul API; model BAKED in imaginea Docker (sau volum cache) -> ZERO dependenta de retea la runtime. NU serviciu separat. Lazy-load la pornire, nu pe /healthz; worker NU incarca modelul | TASTE (user, revizuit) | P5,P3 | user: "embedding in interiorul aplicatiei, nu mai depind de alte resurse". Mai simplu + mai robust decat serviciu HTTP; ruleaza identic local si in Docker/LXC | serviciu separat Ollama/HTTP (revocat) / sentence-transformers+torch |
|
||
| 16b | Eng | **Degradare gratioasa**: daca modelul nu se incarca -> ingestia NU se blocheaza, NU auto-trimite; cade pe exact+fuzzy, incertul -> needs_mapping. Embeddings raman doar SUGESTIE (consecinta F-A), in afara verdictului de enqueue (invariant dry-run/commit, Eng-F8) | MECHANICAL | P1 | esecul incarcarii modelului nu trebuie sa rupa coada; fara retea la runtime | block ingest pe model lipsa |
|
||
| 17 | Eng | **Tier "Inalta" auto-send STERS din v1**; GOLD auto-trimite, restul (silver/NN/LLM-unanim) = needs_mapping 1-click | MECHANICAL | P1 | fara ground-truth; unanimitate same-family = eroare corelata, nu validitate | tier Inalta pe unanimitate LLM |
|
||
| 18 | Eng | sklearn classifier scos din v1 | MECHANICAL | P5 | al doilea artefact antrenabil + pickle, castig marginal pe 18 clase | sklearn in v1 |
|
||
| 19 | Eng | **Set held-out etichetat de OM = BLOCANT** pt orice tier auto-send peste GOLD propriu | MECHANICAL | P1 | "antrenare pe test" invalideaza orice precizie raportata | prag din etichete LLM |
|
||
| 20 | CEO | OpenRouter: free OK pt bootstrap unic; credit mic ($5-20) pt drift steady-state (nu arhitecta pe cap 50/zi) | TASTE | P3 | juggling free > cost credit in timp eng | totul pe free tier |
|
||
|
||
## Istoric review (pre-pivot)
|
||
|
||
Versiunea anterioara a trecut prin `/autoplan` (mod SELECTIVE EXPANSION, subagent-only,
|
||
Codex indisponibil). Constatari portante atunci: **F1 CRITIC** (seed=auto-send),
|
||
F2/F3/F4 HIGH (idempotenta seed, scrub PII, ancore NUL), F5/F6/F7/F8 MEDIUM. Acele
|
||
decizii sunt incorporate in Decision Audit Trail de mai sus. Pivotul 2026-06-28
|
||
(LLM offline-only + runtime embeddings + strat partajat cross-account) NECESITA o
|
||
noua rulare de review (CEO / Eng / Design) — de aceea sectiunea GSTACK REVIEW REPORT
|
||
e goala momentan si se completeaza la urmatoarea rulare.
|
||
|
||
## GSTACK REVIEW REPORT
|
||
|
||
Rulat prin `/autoplan` 2026-06-28 (SELECTIVE EXPANSION). Voci: Claude subagent independent
|
||
(CEO + Eng) + analiza orchestrator pe cod. **Codex INDISPONIBIL** (usage limit, reset 18 iul)
|
||
-> mod single-reviewer. UI scope: NU (editorul needs_mapping exista deja). DX scope: borderline
|
||
(CLI intern operator) -> Phase 3.5 sarit, considerente DX in Eng.
|
||
|
||
### Decizii GATE (confirmate de utilizator)
|
||
- **F-A: cross-account = suggestion-only.** Maparile validate de orice cont PRE-COMPLETEAZA
|
||
editorul needs_mapping (1-click) dar NU auto-trimit. Doar exact-match pe GOLD-ul PROPRIU
|
||
(validat de omul contului) auto-trimite. Elimina riscul de FINALIZATA gresit cross-tenant.
|
||
- **Premisa 1 validata cu temporal holdout INAINTE de build** (corpus primele N luni/client ->
|
||
hit-rate exact pe lunile urmatoare). Ieftin, datele exista.
|
||
|
||
### Consens CEO (single-reviewer; Codex N/A)
|
||
| Dimensiune | Claude | Verdict |
|
||
|---|---|---|
|
||
| Premise valide | NO (P1, P5) | flagged |
|
||
| Problema corecta | PARTIAL | flagged |
|
||
| Scope calibrat | NO (over-eng) | flagged |
|
||
| Alternative explorate | NO | flagged |
|
||
| Riscuri piata | PARTIAL | flagged |
|
||
| Traiectorie 6 luni | AT RISK | flagged |
|
||
|
||
### Consens Eng (single-reviewer; Codex N/A)
|
||
| Dimensiune | Claude | Verdict |
|
||
|---|---|---|
|
||
| Arhitectura | PARTIAL | flagged |
|
||
| Acoperire teste | NO | flagged |
|
||
| Footprint/perf | NO (2GB torch) | flagged |
|
||
| Siguranta F1 | INTENT-OK | flagged |
|
||
| Cai de eroare | PARTIAL | flagged |
|
||
| Risc deploy | NO | flagged |
|
||
|
||
### Constatari portante (severitate)
|
||
- **F-A / Eng-F1 (CRITIC):** auto-send DOAR pe GOLD. Strat SILVER in TABELA SEPARATA
|
||
(`mapping_suggestions`), citita doar de suggest_codes/pending_unmapped, NICIODATA de
|
||
load_mapping/resolve_prestatii. `auto_send` col e moarta (mapping.py:436); singura cale
|
||
spre `queued` (auto-send, mapping.py:414) trebuie sa fie GOLD. Separarea = structurala.
|
||
- **F-B (CRITIC):** toate masuratorile sunt ACORD (100% vs Groq, 87% unanim), nu ACURATETE
|
||
vs ground-truth. Same-family NVIDIA = eroare corelata. Niciun tier auto-send peste GOLD
|
||
pana nu exista set held-out etichetat de OM (esantion aleator stratificat).
|
||
- **Eng-F2 (HIGH):** shared store pe cheia `denumire_normalizata` (NU `cod_op_service`) ->
|
||
tabela noua obligatorie; precedenta override pinnata: account override > account GOLD >
|
||
shared GOLD > text rules > unmapped.
|
||
- **F-C / Eng-F3 (HIGH):** embeddings Layer 2 = over-engineering pe 18 clase Zipf-head.
|
||
AMANAT v2. Daca se construieste: fastembed/ONNX (~230MB pe disc, ONNX quantizat;
|
||
estimarea initiala de ~50MB a fost gresita), API-process-only, lazy, nu pe
|
||
/healthz. NU in resolve_prestatii (altfel worker-ul ar avea nevoie de torch).
|
||
- **Eng-F4 (HIGH):** tier "Inalta" sters din v1 (consecinta F-A + lipsa ground-truth).
|
||
- **F-D (HIGH):** Premisa 1 nevalidata temporal -> gate (rezolvat).
|
||
- **F-E (HIGH):** fara metrica de succes/baseline/kill-criterion -> de instrumentat
|
||
(% linii auto-rezolvate la rata cod-gresit < 0.X%).
|
||
- **MEDIUM:** NUL short-circuit inainte de suggest_codes + structura separata (Eng-F6);
|
||
OpenRouter 429 resumabil + group radius conservator + provenance (Eng-F7); divergenta
|
||
dry-run/commit (Eng-F8); credit mic vs free-tier (F-F); omisiune silentioasa NUL (F-G);
|
||
calitate embedding RO de verificat (F-H); versionare cheie normalizare; drop sklearn v1.
|
||
|
||
### Teme cross-faza (semnalate independent in ambele faze)
|
||
1. Auto-send DOAR GOLD; silver/embeddings/unanimitate-LLM = sugestie (CEO F-A/F-B + Eng F1/F4).
|
||
2. Embeddings over-engineered pe 18 clase; amana sau fastembed (CEO F-C + Eng F3).
|
||
3. Fara set ground-truth; masoara precizia inainte de orice tier auto-send (CEO F-B/F-E + Eng F4).
|
||
|
||
### NU in scope (amanat)
|
||
- **sklearn classifier** peste embeddings (v2; embeddings raman doar NN suggestion in v1).
|
||
- Orice tier auto-send peste exact-match GOLD propriu (pana la set held-out).
|
||
- LLM generativ local la runtime (deja non-obiectiv PRD).
|
||
- Tier "Inalta" calibrat (re-introdus doar cu eval cross-family + ground-truth).
|
||
|
||
**Embeddings Layer 2 RAMANE in v1** (override user la gate), IN-PROCES (fastembed/ONNX,
|
||
model baked in imagine), DOAR sugestie, cu fallback gratios pe exact+fuzzy daca modelul nu
|
||
incarca. Zero dependenta de retea la runtime. Vezi audit #15/#16/#16b.
|
||
|
||
### Ce exista deja (de refolosit, nu rescris)
|
||
- `resolve_prestatii` / `classify_prezentare` / `reresolve_account` (mapping.py): precedenta
|
||
cod direct > exact mapping > text rules > unmapped; garda valid_codes (ORA-12899).
|
||
- `suggest_codes` (rapidfuzz token_sort) + `pending_unmapped`: punct de injectie sugestii.
|
||
- `operation_text_rules` (substring) + `operations_mapping` (GOLD per-cont).
|
||
- `tools/mapare-llm/` (or_common.py, or_modeltest.py) + pattern `*-partial.json` resumabil.
|
||
- Scrub PII (F3), `normalize_for_match`, seed nomenclator (18 coduri).
|
||
|
||
### Artefact test plan
|
||
`~/.gstack/projects/romfast-rar-autopass/mmarius-main-test-plan-20260628.md`
|
||
(test F1-regression CRITIC + precedenta override + NUL + idempotenta seed + held-out eval).
|
||
|
||
### Stare review
|
||
Aprobat prin `/autoplan` (vezi Decision Audit Trail #11-20 + #16b). Plan livrabil:
|
||
v1 = Layer 1 (etichetare offline) + Layer 2 (embeddings ca SERVICIU SEPARAT configurabil,
|
||
doar sugestie, fallback gratios) + Layer 3 (GOLD propriu auto-send + shared suggestion-only)
|
||
+ exact/fuzzy existent + temporal holdout + metrica de succes + set held-out (blocant pt
|
||
orice auto-send peste GOLD). v2 = sklearn classifier (dupa masurare).
|