Înapoi la blog

RAG pe documente românești: OCR, diacritice și chunking

PDF-uri scanate, ș și ț scrise în două feluri, contracte pe articole și alineate: ce trebuie schimbat într-un RAG ca să funcționeze pe documente românești.

Categorii:

Orice echipă care a construit un RAG cunoaște momentul: demo-ul pe zece PDF-uri în engleză merge impecabil, iar prima încărcare de documente reale — contracte scanate, decizii interne, proceduri cu ștampilă — produce răspunsuri goale sau, mai rău, răspunsuri sigure pe ele și greșite. Nu modelul e vinovat. Aproape tot ce se strică într-un RAG pe documente românești se strică înainte ca modelul să vadă vreun text.

Lanțul unui RAG pe documente românești: PDF scanat, OCR, normalizarea diacriticelor, segmentare, căutare hibridă și evaluare

Patru cauze, în ordinea în care lovesc

Într-un sistem care caută în documente românești, problemele apar în această ordine, iar fiecare o amplifică pe următoarea:

  1. Documentul nu conține text, ci imaginea unui text. Extragerea returnează câteva rânduri din antet și nimic altceva.
  2. Diacriticele sunt scrise în două moduri incompatibile la nivel de octeți, iar căutarea nu găsește ce există în index.
  3. Segmentarea taie prin mijlocul unei unități de sens — un alineat, o obligație contractuală, o coloană de tabel.
  4. Nu există niciun mod de a ști dacă o schimbare a îmbunătățit ceva, așa că echipa optimizează pe impresii.

Le luăm pe rând, cu ce se face concret la fiecare. Dacă mecanismul general al regăsirii augmentate nu e clar, punctul de plecare e articolul despre ce este RAG și de ce contează. Arhitectura completă a unui sistem de acest fel — de la ingestie la reordonare și la controlul costurilor — e subiectul cursului RAG: Retrieval-Augmented Generation în practică; aici mă opresc la stratul unde se pierd cel mai des documentele în română.

Etapa 0: află ce fel de PDF ai, de fapt

Prima verificare, înainte de orice cod de ingestie, e trivială și aproape întotdeauna omisă. Un PDF poate fi:

  • nativ — text real, extractibil direct;
  • scanat — o imagine pe pagină, zero text;
  • hibrid — și acesta e cel periculos: text pe primele pagini, anexe scanate la final, sau un strat OCR de proastă calitate lipit peste imagine de un scaner de birou.

Verificarea rapidă, pe un eșantion de 20-30 de documente reprezentative:

pdffonts document.pdf          # fără fonturi listate => pagină-imagine
pdftotext -layout document.pdf - | head -50   # ce text există, de fapt

Contează pentru că determină costul întregului proiect. Un corpus nativ se procesează în minute; unul scanat cere OCR, verificare de calitate și, adesea, o etapă de corectură. Dacă mergi mai departe fără să știi proporția, estimarea de timp va fi greșită cu un ordin de mărime.

Regula pe care o aplic: respinge devreme, nu tăcut. Orice document din care s-au extras sub, să zicem, 200 de caractere pe pagină intră într-o coadă separată, nu în index. Un document intrat prost în index e mai rău decât unul lipsă: primul produce răspunsuri false, al doilea produce o absență vizibilă.

OCR: pachetul de limbă nu e opțional

Pe documente scanate în română, cea mai frecventă eroare de configurare e rularea OCR-ului cu limba implicită. Rezultatul nu e o eroare, ci text plauzibil cu diacriticele înlocuite: ș devine s, ț devine t, ă devine a sau ä. Textul arată aproape corect la o privire rapidă și e inutil la căutare.

Tesseract are pachet dedicat pentru română, ron, disponibil în depozitele oficiale de date antrenate — inclusiv în varianta de precizie maximă din tessdata_best. Se instalează separat de motor și se invocă explicit:

tesseract pagina.png output -l ron --dpi 300

Ce influențează calitatea, în ordinea impactului:

  • Rezoluția sursei. Sub 300 DPI, rata de eroare crește vizibil, iar diacriticele sunt primele care se pierd — sunt semne mici, iar ț și t diferă prin câțiva pixeli.
  • Preprocesarea. Îndreptarea paginii scanate strâmb, binarizarea și eliminarea zgomotului aduc adesea mai mult decât schimbarea motorului OCR.
  • Ordinea de citire. Documentele pe două coloane, tabelele și antetele repetate produc text amestecat. Verifică ordinea pe o pagină complexă înainte de a procesa mii.
  • Ștampilele și semnăturile suprapuse peste text — sursă clasică de caractere fantomă în documentele administrative românești.

Pentru corpusuri unde precizia contează mai mult decât costul, serviciile de procesare a documentelor din cloud și modelele multimodale moderne se descurcă mai bine pe pagini dificile, mai ales pe tabele. Două lucruri de decis înainte:

Unde ajung documentele. Dacă trimiți contracte sau dosare de personal către un serviciu extern, ai un transfer de date personale, cu tot ce implică: temei juridic, contract de prelucrare, verificarea locului de stocare. Pentru corpusuri sensibile, OCR local rămâne o alegere rațională chiar dacă e cu câteva procente mai slab.

Cât plătești pe pagină. La zeci de mii de pagini, diferența dintre un motor local și unul din cloud devine o linie de buget, nu un detaliu tehnic.

Indiferent de motor, păstrează numărul paginii și poziția în pagină pentru fiecare fragment extras. Fără ele nu poți construi citări verificabile, iar un RAG fără citări verificabile nu e utilizabil în context profesional.

Diacriticele: eroarea tăcută care îți omoară recall-ul

Aceasta e problema specific românească și e, de departe, cea mai subestimată.

În Unicode, literele românești ș și ț există în două forme:

  • forma corectă, cu virgulă dedesubt: ș (U+0219) și ț (U+021B);
  • forma cu sedilă: ş (U+015F) și ţ (U+0163), moștenită din codificări vechi și încă produsă de aplicații mai vechi, de unele tastaturi și de OCR-ul configurat greșit.

Arată aproape identic pe ecran. Sunt caractere complet diferite pentru orice motor de căutare, orice funcție de potrivire și orice tokenizator. Consecința: documentul e în index, întrebarea utilizatorului e corectă, iar sistemul returnează zero rezultate. Nicio eroare în log-uri, niciun indiciu — doar un răspuns care spune că informația nu există.

Se adaugă alte două surse de variație:

  • â și î. Documentele anterioare reformei ortografice și unele texte tehnice folosesc alt tipar decât cel actual. „Sînt" și „sunt" trebuie să ajungă la același rezultat.
  • Formele de normalizare Unicode. Aceeași literă poate fi codificată ca un singur caracter sau ca literă de bază plus semn diacritic combinat. Vizual identice, diferite la comparație.

Ce faci concret

Normalizează la formă unică, la ingestie și la interogare, cu exact aceeași funcție. Dacă normalizezi doar la indexare, ai mutat problema, nu ai rezolvat-o.

Schema pe care o folosesc:

  1. Aplică normalizarea Unicode în forma compusă (NFC).
  2. Convertește sedila în virgulă dedesubt: şș, ţț, plus majusculele corespunzătoare.
  3. Păstrează textul original, nemodificat, pentru afișare și citare. Normalizarea servește căutării, nu prezentării.
  4. Generează, în plus, o variantă fără diacritice a textului indexat și a interogării, folosită ca rezervă. Mulți utilizatori scriu „contract de prestari servicii" fără nicio diacritică; sistemul trebuie să găsească documentul oricum.

Ultimul punct e cel care aduce cel mai mult câștig cu cel mai puțin efort, iar în interfețele interne e diferența dintre „nu merge" și „merge".

Verificarea că ai făcut-o corect e la fel de simplă: caută în corpusul indexat octeții formei cu sedilă. Dacă mai apar, normalizarea nu se aplică peste tot — de obicei există o cale de ingestie secundară care ocolește funcția.

Segmentare: pe structura documentului, nu pe număr de tokeni

Segmentarea în bucăți de mărime fixă e valoarea implicită în toate tutorialele și e greșită pentru documentele administrative și juridice românești, care au o structură explicită și foarte utilă: capitol, articol, alineat, literă, anexă.

Ce se strică la segmentarea pe număr fix de caractere: alineatul (3) ajunge în alt fragment decât alineatul (2) de care depinde, definiția din articolul 2 se rupe de utilizarea ei, iar tabelul cu termene se împarte în trei bucăți fără cap de tabel.

Ce funcționează:

Segmentează pe unitatea logică a documentului. Pentru contracte și proceduri: un fragment per articol sau per alineat, cu titlul secțiunii părinte adăugat la începutul fiecărui fragment. Pentru rapoarte: pe subcapitol. Costul e o etapă de detectare a structurii; câștigul e că fiecare fragment răspunde singur la o întrebare.

Adaugă context în fragment, nu doar în metadate. Un fragment care începe cu „(3) Termenul se prelungește cu 15 zile" e inutil scos din context. Același fragment precedat de „Contract de prestări servicii nr. 128/2026 — Art. 7 Termene de execuție" e utilizabil și de model, și de om.

Tratează tabelele separat. Un tabel convertit în text liniar își pierde sensul. Variantele care merg: îl păstrezi ca Markdown și îl indexezi împreună cu un rezumat generat al conținutului, sau extragi rândurile ca propoziții („Pentru serviciul X, termenul este Y zile"). A doua e mai scumpă la ingestie și mult mai bună la căutare.

Metadatele nu sunt opționale. Minimul: identificatorul documentului, tipul, data, versiunea, secțiunea, pagina și nivelul de confidențialitate. Fără versiune și dată, sistemul va cita cu egală încredere procedura din 2023 și pe cea din 2026. E cea mai frecventă cauză de răspuns „corect" și complet greșit.

Suprapunerea între fragmente ajută, dar nu compensează o segmentare pe criteriu greșit. Câteva zeci de cuvinte de suprapunere sunt suficiente când tai pe structură.

Căutare hibridă: lexicalul nu e opțional în română

RAG-ul care caută doar prin similaritate vectorială eșuează previzibil pe două categorii de întrebări: cele cu numere și identificatori („contractul 128/2026", „art. 36²") și cele cu termeni rari, specifici organizației.

Soluția e căutarea hibridă: lexicală și vectorială în paralel, cu fuziunea rezultatelor și o etapă de reordonare.

Pentru partea lexicală, două observații practice pentru română:

  • Limba are flexiune bogată: „contractului", „contractelor" și „contract" trebuie să ajungă la aceeași rădăcină. PostgreSQL include configurații de căutare full-text bazate pe Snowball, iar româna e printre limbile pentru care Snowball publică un algoritm de stemming. Verifică pe instanța ta ce configurații ai disponibile (\dF în psql) înainte de a presupune.
  • Aplică aceeași normalizare a diacriticelor în configurația de indexare lexicală ca în restul lanțului. Altfel obții exact eroarea tăcută descrisă mai sus, dar doar pe jumătate din sistem — cazul cel mai greu de diagnosticat.

Pentru partea vectorială, alegerea modelului de embedding: familiile multilingve mari — cele de la furnizorii comerciali și alternativele deschise antrenate pe corpusuri multilingve — se comportă rezonabil pe română. Nu alege însă din clasamente generale. Un model care iese primul pe un benchmark internațional poate fi al treilea pe documentele tale, iar diferența dintre locul unu și locul trei pe corpusul tău e singura care contează. Măsoară pe setul propriu.

Reordonarea rezultatelor cu un model dedicat, aplicată peste primele 20-50 de rezultate, e de regulă îmbunătățirea cu cel mai bun raport efort/beneficiu într-un sistem deja funcțional. Are și un efect secundar util pe factură: trimiți modelului mai puține fragmente, dar mai bune — logica costurilor pe context lung e detaliată în articolul despre costul unui agent AI pe context lung. Cum se leagă aceste componente în producție, cu tot cu tratarea erorilor și a costurilor, e detaliat în cursul de integrare avansată LLM.

Evaluarea: fără ea, optimizezi pe impresii

Aici se despart sistemele care rezistă în producție de cele care rămân demo-uri.

Construiește un set de evaluare în română, cu întrebări reale. 30-50 de întrebări sunt suficiente pentru a începe. Le iei de la oamenii care vor folosi sistemul, nu le inventezi. Pentru fiecare întrebare notezi: răspunsul așteptat, documentul-sursă și secțiunea exactă.

Măsoară separat regăsirea de generare. Sunt două eșecuri diferite, cu remedii diferite:

  • Regăsirea — apare documentul corect în primele k rezultate? Metrica de bază e recall@k, la k egal cu numărul de fragmente pe care le trimiți efectiv modelului. Dacă documentul corect nu e acolo, niciun model nu poate salva răspunsul.
  • Generarea — având fragmentele corecte, răspunsul e corect și citează sursa reală? Aici urmărești rata de răspunsuri nefondate și corectitudinea citării.

Măsoară și rata de abținere. Un sistem care spune „nu găsesc informația în documentele disponibile" când chiar nu o are e mai valoros decât unul care improvizează. Include în setul de evaluare 5-10 întrebări la care răspunsul corect este tocmai abținerea.

Rulează setul la fiecare schimbare. Alt model de embedding, altă strategie de segmentare, alt prompt — toate se validează pe același set, cu cifrele înainte și după. Fără asta, echipa va crede că a îmbunătățit sistemul de fiecare dată când a schimbat ceva.

Pune-l în integrarea continuă, cu un prag minim. O regresie la regăsire e la fel de gravă ca un test unitar picat, dar nu se vede în niciun log. Metodologia completă de evaluare, cu seturi, praguri și automatizare, e subiectul cursului de evaluare a modelelor AI în producție.

Partea legală, pe scurt

Un RAG intern se construiește pe documentele firmei, iar acelea conțin aproape sigur date cu caracter personal: nume, funcții, salarii, evaluări, uneori date din categorii speciale.

Ce trebuie clarificat înainte de prima ingestie, nu după:

  • Temeiul prelucrării și scopul. Indexarea unui dosar de personal pentru un asistent intern e o prelucrare nouă, cu scop propriu.
  • Minimizarea. Nu indexa tot ce ai „pentru orice eventualitate". Selectează sursele; exclude ce nu servește scopului declarat.
  • Controlul accesului la nivel de fragment. Dacă cinci departamente folosesc același index, filtrarea pe drepturi trebuie să se aplice la regăsire, nu în prompt. Un model instruit să „nu arate" un document îl poate arăta oricum.
  • Unde ajung documentele. Fiecare serviciu extern folosit la OCR, la generarea vectorilor sau la generarea răspunsului e un destinatar. Verifică termenii privind reținerea datelor și folosirea lor la antrenare — aceleași întrebări pe care le pui înainte de a trimite date de clienți către un serviciu AI.
  • Jurnalizarea. Cine a întrebat, ce a primit, din ce document. Necesar pentru audit și, practic, singura cale de a investiga un răspuns greșit.

Regulamentul european privind inteligența artificială adaugă obligații de informare atunci când oamenii interacționează cu un sistem AI, iar dacă asistentul intern e folosit în decizii care privesc angajații, discuția depășește zona tehnică. Cadrul de guvernanță — cine aprobă ce sursă intră în index și cum se documentează — e tratat în articolul despre inventarul sistemelor AI din companie.

Acest articol are scop informativ și nu constituie consultanță juridică. Pentru prelucrări cu miză, consultați specialistul în protecția datelor.

Cinci reguli învățate pe documente reale

  1. Nu porni de la corpusul complet. 50 de documente reprezentative, lanțul complet până la evaluare, apoi scalezi. Ingestia a 10.000 de documente cu o segmentare greșită înseamnă 10.000 de documente de reprocesat.
  2. Normalizarea diacriticelor e o singură funcție, folosită peste tot. Duplicarea ei în două locuri produce, la prima modificare, exact eroarea tăcută.
  3. Fără citare verificabilă, sistemul nu se lansează. Document, pagină, secțiune — altfel nimeni nu poate verifica, iar încrederea se pierde la prima greșeală.
  4. Versiunea documentului e metadată obligatorie. Procedurile se schimbă; indexul trebuie să știe care e în vigoare.
  5. Ce nu e măsurat nu e îmbunătățit. Setul de evaluare se scrie în prima săptămână, nu în ultima.

Întrebări frecvente

Î: Chiar contează atât de mult diacriticele? Modelele nu se descurcă oricum? R: Modelul, da. Regăsirea, nu. Problema apare înainte de model: dacă interogarea normalizată nu se potrivește cu textul indexat, fragmentul corect nu ajunge niciodată la model. Nu primești o eroare, ci un răspuns care spune că informația nu există.

Î: Ce OCR folosesc pentru PDF-uri scanate în română? R: Tesseract cu pachetul de limbă ron acoperă bine documentele curate, la 300 DPI sau mai mult, și rulează local — util când documentele nu au voie să părăsească infrastructura. Pentru pagini dificile, cu tabele și mai multe coloane, serviciile de procesare a documentelor și modelele multimodale dau rezultate mai bune, cu costul unui transfer de date către un terț.

Î: Cât de mari trebuie să fie fragmentele? R: Întrebarea corectă nu e „câți tokeni", ci „ce unitate de sens". Pentru contracte și proceduri, un articol sau un alineat, cu titlul secțiunii părinte adăugat la început. Mărimea rezultă din structură, nu invers.

Î: Îmi trebuie neapărat căutare hibridă? R: Dacă utilizatorii caută vreodată după numere de document, articole sau termeni interni — da. Căutarea pur vectorială ratează sistematic aceste cazuri, iar ele sunt frecvente în mediul administrativ românesc.

Î: Cum știu că am îmbunătățit ceva după o schimbare? R: Rulând același set de evaluare înainte și după, cu regăsirea măsurată separat de generare. Fără cifre, orice schimbare pare o îmbunătățire în ziua în care ai făcut-o.

Î: Pot indexa documente cu date personale? R: Tehnic, da. Legal, doar cu temei, scop declarat, minimizare și control de acces aplicat la regăsire, nu în prompt. Începe cu sursele fără date personale, până când guvernanța e clarificată.

Concluzie

Un RAG pe documente românești nu cere un model mai bun, ci un lanț de ingestie care ține cont de ce sunt documentele: scanate, structurate pe articole și alineate, scrise cu diacritice pe care jumătate din unelte le codifică greșit.

Ordinea în care merită investit efortul e inversă intuiției: mai întâi verifici ce fel de fișiere ai, apoi normalizezi diacriticele peste tot cu aceeași funcție, apoi segmentezi pe structura documentului, apoi adaugi căutarea lexicală lângă cea vectorială. Alegerea modelului vine ultima și contează cel mai puțin.

Iar primul lucru pe care îl scrii, înainte de toate acestea, e setul de 30 de întrebări la care sistemul trebuie să răspundă corect. Fără el, restul e opinie.

Surse

Articol tehnic informativ, publicat la 30 august 2026. Recomandările privind uneltele reflectă situația la data publicării; validați alegerile pe propriul corpus înainte de a le duce în producție.

Cursul care continuă acest articol

RAG: Retrieval-Augmented Generation în Practică

  • 27 lecții
  • ~24h de conținut
  • IT Pro

Ai citit teoria. În curs o aplici: lecții structurate pe module, exerciții și quiz-uri cu feedback imediat, Profesorul AI integrat în fiecare lecție și progres salvat automat. La final primești o atestare privată de finalizare.

499 lei pe lună pentru acest curs, TVA 21% inclus · sau 1.999 lei pe lună pentru toate cele 25 de cursuri IT Pro.

Abonament lunar, cu reînnoire automată · anulezi oricând din contul tău · conținut digital cu acces imediat, vezi condițiile de retragere. Atestarea confirmă parcurgerea cursului și este privată — nu este diplomă și nu este calificare recunoscută de stat.

Ți-a plăcut articolul? Lasă o apreciere sau salvează-l pentru mai târziu.
Comunitate

Întrebări & sugestii

Ce au întrebat cititorii despre acest articol — și răspunsurile echipei Cursuri AI.

Mesajele sunt verificate de un moderator înainte de publicare.

Fii primul care lasă o întrebare sau o sugestie pe acest articol.

Continuă să înveți

Aplică ce ai citit, pe 50 de cursuri în română

Peste 1.374 de lecții și 1.213 de ore de conținut structurat, cu quiz-uri după fiecare lecție și profesor AI integrat. De la 499 lei pe lună pentru un curs, TVA inclus, sau 1.499 lei pe lună pentru un parcurs complet.