Cum se colectează datele

Agenții rulează local, pe modele Ollama. Site-ul doar afișează rezultatele.

research — modulul „Carne: producători locali vs. supermarket"

Contextul complet și regulile obligatorii sunt în ../CLAUDE.md. Citește-l înainte de orice rulare.

Ordinea de rulare

Agenții se rulează în ordine: 01 → 02 → 03 → 04 → 05. Prompturile sunt în agents/.

AgentCe faceScrie
01-colector-producatori.mdgăsește producători autorizați SV în județul pilotdata/processed/producers.json
02-colector-preturi-retail.mdcolectează prețurile din supermarketuridata/processed/prices-retail.json
03-colector-preturi-producatori.mdcolectează prețurile producătorilordata/processed/prices-producers.json
04-verificator.mdverifică sursele și marchează statusurile (modifică in place cele 3 fișiere)cele 3 fișiere + logs/YYYY-MM-DD_verificare.md
05-raportor.mdgenerează raportul comparativreports/YYYY-MM_<judet>_carne.md

Loguri și snapshot-uri

  • Fiecare rulare se loghează în logs/YYYY-MM-DD_run.md (ce a rulat, ce a găsit, ce a eșuat).
  • Textul fiecărei pagini sursă se salvează în data/raw/ ca YYYY-MM-DD_domeniu_slug.txt. Snapshot-urile se commit-ează, ca datele să rămână verificabile.

Configurare

  • Schimbare județ: editează config/regions.json.
  • Schimbare coș: editează config/basket.json (și reflectă schimbarea în prompturile 02 și 03).
  • Surse și URL-uri de pornire: config/sources.json.

research/runners — rularea locală a agenților cu Ollama

run-agent.mjs rulează agenții din research/agents/ pe modele locale (Ollama). Promptul agentului + regulile din CLAUDE.md secțiunea 4 devin system prompt-ul modelului.

Prerechizite

  • Node.js 20+ și dependențele proiectului (npm install la rădăcină — include cheerio și zod).
  • Ollama pornit, cu modelele:
    ollama pull qwen3.5:9b     # extragere (agenții 01, 02, 03, 05)
    ollama pull gpt-oss:20b    # verificare (agentul 04)
    
  • Județul pilot setat în research/config/regions.json:
    { "judet_pilot": { "nume": "Cluj", "cod_siruta": "..." } }
    

Rulare

npm run research          # usage + lista agenților
npm run research:01       # producători      -> data/processed/producers.json
npm run research:02       # prețuri retail   -> data/processed/prices-retail.json
npm run research:03       # prețuri producători -> data/processed/prices-producers.json
npm run research:04       # verificator (in place) -> logs/YYYY-MM-DD_verificare.md
npm run research:05       # raport           -> reports/YYYY-MM_<judet>_carne.md

Fiecare rulare adaugă o secțiune în logs/YYYY-MM-DD_run.md. Fiecare pagină descărcată e salvată în data/raw/.

Rulare din site (job queue)

Pagina /research/control pune job-uri în MongoDB (DB rodebate, colecția jobs, șterse automat după 60 de zile). Pollerul de pe laptop le preia și rulează agenții local:

npm run research:watch                      # pornește pollerul (CTRL+C pentru oprire)
node research/runners/poller.mjs --check    # doar verifică conexiunea și coada
nohup npm run research:watch > research/logs/poller.out 2>&1 &   # în background, 24/7
  • Necesită în .env.local la rădăcina proiectului: MONGODB_URI și RESEARCH_CONTROL_TOKEN (aceleași valori ca în Vercel).
  • Pollerul ia câte un job o dată (cel mai vechi pending), rulează run-agent.mjs <agent> și trimite fiecare linie de output în logurile job-ului; site-ul le afișează live.
  • Pagina arată „Poller local online/offline" după ultimul semnal al pollerului (la fiecare 3 secunde).
  • Datele noi (data/processed/, reports/) rămân pe laptop: apar pe site abia după commit + deploy.
  • La repornire, job-urile rămase running dintr-o rulare întreruptă sunt marcate failed.

Ce face runner-ul în afară de model (garanții deterministe)

  • Orice înregistrare al cărei extras nu apare literal în pagina descărcată e respinsă (contează la „respinse" în log).
  • CUI, număr autorizare SV, localitate și contacte care nu apar în pagină devin null.
  • Prețul trebuie să apară în extras; normalizarea la lei/kg e calculată în cod (formula_normalizare), nu de model.
  • Producătorii fără număr de autorizare SV primesc neautorizat_sau_neconfirmat; agentul 01 nu marchează niciodată verificat — doar 04 o face.
  • 05 calculează min/medie/max și diferențele în cod; modelul scrie doar un rezumat opțional, marcat ca generat.

Surse cu mai multe pagini

url_start din config/sources.json e de obicei o pagină de pornire. Runner-ul acceptă opțional, pe fiecare sursă:

  • "urls": ["...", "..."] — listă de pagini concrete de procesat (în loc de / pe lângă url_start);
  • "url_carne": "..." (doar retail) — pagina categoriei de carne a lanțului.

Schimbarea modelului

Editează research/runners/config.json:

  • default_model — agenții 01, 02, 03, 05;
  • verifier_model — agentul 04;
  • timeout_ms — timeout per apel la model;
  • max_text_chars — paginile mai lungi se împart în fragmente de această mărime (un apel per fragment).

Orice model din ollama list funcționează; modelele mai mici extrag mai prost (mai multe respingeri).

Troubleshooting

ProblemăMesajSoluție
Ollama opritPornește Ollama: ollama serve &ollama serve & sau brew services start ollama
Model lipsăollama pull <model>rulează comanda afișată sau schimbă modelul în config.json
Județ nesetatSetează județul în research/config/regions.jsoncompletează judet_pilot
HTTP 403 / pagină goalăîn log: HTTP 403 (posibil protecție anti-bot) sau randată din JavaScriptsite-ul blochează boții sau încarcă prețurile din JS; salvează pagina manual în data/raw/ sau adaugă un URL alternativ în urls
PDF / XLS (ex.: liste DSVSA)content-type neprocesatrunner-ul nu citește PDF/XLS; descarcă și convertește manual
Timeout model... aborted due to timeoutcrește timeout_ms sau scade max_text_chars

Un URL care eșuează nu oprește rularea — eroarea apare în log și se trece la următorul.