UI in izvorni dokumenti: kako preprečiti halucinacije jezikovnih modelov
TL;DR. „Halucinacija“ velikega jezikovnega modela (LLM) je simptom, ne diagnoza. Diagnoza je običajno slabo iskanje po dokumentih, slabo navajanje virov ali oboje. V članku pokažemo, kako odgovore UI na podlagi dokumentov stranke zgradimo tako, da je vsaka trditev povezana s konkretnim odlomkom konkretnega dokumenta in da lahko vsak inženir odgovor preveri v desetih sekundah. Pristop sestavljajo obvezno navajanje virov pri generiranju, hibridno iskanje, strukturiran zajem dokumentov in zanesljiv evalvacijski nabor.
1. Problem: „UI si je to izmislila“
Prizor iz prakse: UI-asistent dela z dokumenti stranke. Teden dni pozneje vodja oddelka napiše: „Asistent pravi, da naš pravilnik določa X. Odprl sem pravilnik in X-a tam ni. Kaj pa zdaj?“
Če ekipa odgovori, da gre pač za halucinacijo LLM-ja, ki se včasih zgodi, in da bodo izboljšali poziv, je projekt praktično mrtev. Stranka je izgubila zaupanje, zaupanje pa se le redko vrne.
Problem je rešljiv, rešitev pa je v arhitekturi, ne v tem, da LLM prosimo, naj bo „bolj previden“. Ključna ideja: vsaka trditev v odgovoru mora imeti sklic na konkreten odlomek konkretnega dokumenta, sklic pa mora biti mogoče samodejno preveriti.
2. Za koga je to pomembno
- Regulirane panoge: varnost in zdravje pri delu, medicina, pravo, računovodstvo, torej povsod, kjer „UI se je zmotila“ pomeni dejansko škodo.
- Interne baze znanja: zaposleni morajo sistemu zaupati in odgovor hitro preveriti.
- Podpora strankam: napačen odgovor pomeni vračilo blaga, pritožbo ali izgubljeno stranko.
- Vsak projekt, v katerem človek prej ali slej preveri, kaj je odgovorila UI, čas za to preverjanje pa se pomnoži s tisoči poizvedb.
3. Pogosta napaka
- Vzamemo standardno predlogo RAG: razrez na odlomke → vektorske vložitve → top-K → LLM.
- Predpostavimo: „LLM je pameten, vire bo našel sam.“
- Dobimo odgovore brez navedbe virov ali pa z navedbami, kot je „v skladu z dokumentom X“, a brez številke strani.
- Ko pravnik prosi, naj mu pokažemo, kje točno to piše, odgovora ni.
Dodatne pasti:
- Razrez po številu znakov. Struktura dokumenta se izgubi, tabele so razrezane na pol.
- Samo gosto (vektorsko) iskanje. Točnih oznak, šifer in identifikatorjev sistem ne najde.
- Poziv „odgovarjaj samo na podlagi konteksta“ brez naknadnega preverjanja. LLM ga upošteva, kadar se mu zahoče.
- Ni evalvacijskega nabora za zavrnitve. Nihče ni preveril, ali sistem res zavrne odgovor, kadar ga v virih ni.
4. Tehnični pristop: strukturiran zajem in obvezno navajanje virov
Tako bi moral biti videti cevovod:
flowchart LR RAW[PDF / DOCX / HTML] --> PARSE[Strukturiran razčlenjevalnik<br/>stran, poglavje, tabela] PARSE --> CHUNK[Pameten razrez<br/>ob upoštevanju strukture] CHUNK --> META[Metapodatki<br/>stran, poglavje, jezik] META --> EMB[Vložitve + FTS] EMB --> PG[(Postgres + pgvector)] QUERY[Vprašanje uporabnika] --> HYBRID[Hibridno iskanje<br/>BM25 + vektorsko + RRF] HYBRID --> RERANK[Ponovno razvrščanje<br/>s cross-encoderjem] RERANK --> PROMPT[Poziv z obveznim navajanjem virov] PROMPT --> LLM[LLM] LLM --> VALIDATE[Preverjanje navedb virov] VALIDATE -- ok --> ANSWER[Odgovor z oznakami<br/>doc:42#p:18#§:4.2] VALIDATE -- fail --> RETRY[Ponovni poskus<br/>z obveznimi top-3] RETRY --> VALIDATE
4.1. Strukturiran razčlenjevalnik
Ne „PDF → besedilo“, temveč „PDF → zaporedje zapisov (stran, poglavje, razpon znakov, besedilo, tabela?, slika?)“. Za to uporabljamo:
- za PDF
pdfplumberalipymupdf, pri čemer ohranimo koordinate; - za DOCX
python-docxs prehodom po slogih; - za zahtevne skenirane dokumente OCR (Tesseract ali PaddleOCR) s prepoznavanjem jezika.
4.2. Pameten razrez na odlomke
Pravila, ki jih uporabljamo:
- Stavka nikoli ne razrežemo na pol.
- Tabele ne razrežemo.
- Če je poglavje kratko (< 500 žetonov), je celotno poglavje en odlomek.
- Če je dolgo, uporabimo drseče okno 400 žetonov s prekrivanjem 80 žetonov, na meji vsakega poglavja pa začnemo znova.
- Vsak odlomek nosi metapodatke:
{stran, poglavje, nadrejeno poglavje, jezik, URI vira}.
4.3. Poziv z obveznim navajanjem virov
Predloga (poenostavljena):
Odgovarjate samo na podlagi spodnjih odlomkov.
Vsaka trditev v odgovoru se MORA končati z oznako
v obliki [doc:ID#chunk:N].
Če odlomki ne vsebujejo odgovora, odgovorite:
„Priloženi viri ne vsebujejo odgovora na to vprašanje.“
Ne dodajajte dejstev, ki jih v odlomkih ni.
Odlomki:
[doc:42#chunk:7] (stran 18, §4.2) ... besedilo ...
[doc:42#chunk:8] (stran 19, §4.3) ... besedilo ...
...
Vprašanje: {vprašanje}
4.4. Naknadno preverjanje
Po generiranju odgovor preveri preprosta koda:
def validate_citations(answer: str, allowed_chunk_ids: set[str]) -> bool:
sentences = split_sentences(answer)
for s in sentences:
markers = re.findall(r"\[doc:(\d+)#chunk:(\d+)\]", s)
if not markers:
return False # stavek brez navedbe vira
for doc_id, chunk_id in markers:
if f"{doc_id}#{chunk_id}" not in allowed_chunk_ids:
return False # navedba napačnega vira
return True
Če preverjanje ne uspe, LLM pokličemo znova, tokrat s tremi najboljšimi odlomki kot obveznim kontekstom in s strožjim pozivom. Če tudi to ne pomaga, sistem odgovori: „Viri ne vsebujejo odgovora.“
5. Tabela: merila za „pravo“ navajanje virov
| Merilo | Šibko | Dobro | Odlično |
|---|---|---|---|
| Dokument je naveden | „v skladu s pravilnikom“ | „v skladu s pravilnikom X“ | ID dokumenta + naziv |
| Stran je navedena | ne | na koncu odstavka | pri vsakem stavku |
| Poglavje je navedeno | ne | poglavje | poglavje + člen ali točka |
| Navedbo je mogoče samodejno preveriti | ne | z regularnim izrazom | strojno berljivo + povezava |
| Zavrnitev, ko vira ni | ne | včasih | vedno |
| Evalvacijski nabor za zavrnitve | ne | 5–10 vprašanj | 30 ali več testnih vprašanj |
6. Primer iz prakse SINTARIS
Worksafety Superassistant je naša najzahtevnejša izvedba z obveznim navajanjem virov. Korpus obsega približno 3000 strani predpisov s področja varnosti in zdravja pri delu ter interne standardne postopke (SOP) stranke.
Kaj smo naredili:
- Strukturiran razčlenjevalnik, ki ohranja stran, poglavje in člen.
- Hibridno iskanje s prednostjo BM25 (predpisi so polni točnih izrazov in oznak).
- Poziv z obveznim navajanjem virov in naknadnim preverjanjem.
- Testni nabor s 30 zavajajočimi vprašanji, na primer „Kateri standardi za osebno varovalno opremo veljajo za podvodno varjenje na višini?“. Takega standarda ni, zato je pričakovani odgovor „v virih ni odgovora“.
- Evalvacijski nabor s 120 resničnimi vprašanji in referenčnimi navedbami.
Metrike po šestih mesecih:
- Natančnost navedb (navedba vodi do odlomka, ki trditev res podpira): 94 %.
- Pravilnost zavrnitev (delež pravilnih zavrnitev na testnem naboru): 96 %.
- Mediana zakasnitve: 2,1 s (vključno s ponovnim razvrščanjem).
- Pritožbe varnostnih inženirjev na četrtletje: 0.
Podrobnosti: Worksafety § 6 RAG pipeline.
7. Kontrolni seznam (15 točk) za RAG z obveznim navajanjem virov
- Razčlenjevalnik ohrani strukturo (stran, poglavje, člen) in ne obravnava celotnega PDF-ja kot enega kosa besedila.
- Razrez upošteva strukturo in ne reže stavkov ali tabel.
- Metapodatki (stran, poglavje) so shranjeni pri vsakem odlomku.
- Hibridno iskanje (BM25 + vektorsko + RRF), ne samo vložitve.
- Ponovno razvrščanje s cross-encoderjem: top-12 → top-5.
- Predloga poziva zahteva oznake virov.
- Naknadno preverjanje ugotovi, ali so oznake navedene.
- Vzorec zavrnitve: „tega v virih ni“ je enakovreden, načrtovan odgovor.
- Evalvacijski nabor: najmanj 30 vprašanj z referenčnimi navedbami.
- Testni nabor za zavrnitve: najmanj 20 vprašanj, na katera v virih ni odgovora.
- Natančnost navedb se meri in je prikazana v Grafani, ne pa preverjena „kdaj pa kdaj ročno“.
- Delež zavrnitev se meri ločeno od natančnosti.
- Spremljanje odstopanj: vsak teden preverimo, ali so metrike padle.
- Prag za iskanje: če je ocena najboljšega zadetka nižja od X, sistem odgovor zavrne.
- Povezave v odgovorih: uporabnik klikne in vidi izvorni dokument.
8. Tveganja
- Zakasnitev. Ponovno razvrščanje in preverjanje dodata 200–400 ms. Za poslovne scenarije je to sprejemljivo, za klepet v realnem času pa slabo. Rešitev: pogoste poizvedbe hranimo v predpomnilniku.
- „Preveč previden“ sistem. Če je prag previsok, sistem prepogosto odgovori „ne vem“. Pomaga umerjanje na evalvacijskem naboru.
- Strukturirano razčlenjevanje zahtevnih PDF-jev. Skenirane tabele so še vedno trd oreh, zato načrtujte čas za pregled rezultatov OCR.
- Večjezične navedbe. Če so predpisi v nemščini, odgovori pa v slovenščini, morajo biti oznake navedb neodvisne od jezika. Številčni ID-ji so boljši kot „§4.2“.
9. Kaj storiti naprej
Če že imate pilotni sistem RAG, ki si občasno kaj „izmisli“, začnite z dvema korakoma:
- Vklopite poziv z obveznim navajanjem virov in naknadno preverjanje.
- Sestavite testni nabor z 20 vprašanji, na katera v korpusu ni odgovora, in izmerite pravilnost zavrnitev.
Pogosto že ta dva koraka dvigneta kakovost z „včasih pravilno“ na „vedno bodisi pravilno bodisi pošteno zavrnjeno“.
Za celoten cikel je na voljo pilot z UI, ki traja 4–8 tednov in kot standardni vzorec vključuje gradnjo sistema RAG z obveznim navajanjem virov. Obseg in ceno določimo po uvodnem pogovoru.
10. Viri
- Lewis, P. et al. (2020). Retrieval-Augmented Generation for Knowledge-Intensive NLP Tasks. arXiv:2005.11401.
- Karpukhin, V. et al. (2020). Dense Passage Retrieval for Open-Domain Question Answering. arXiv:2004.04906.
- Cormack, G., Clarke, C., Buettcher, S. (2009). Reciprocal Rank Fusion outperforms Condorcet and individual rank learning methods. SIGIR '09.
- Liu, N. F. et al. (2023). Lost in the Middle: How Language Models Use Long Contexts. arXiv:2307.03172.
- Es, S. et al. (2024). RAGAS: Automated Evaluation of Retrieval-Augmented Generation. arXiv:2309.15217.
- pdfplumber: https://github.com/jsvine/pdfplumber
- PaddleOCR: https://github.com/PaddlePaddle/PaddleOCR
SINTARIS gradi sisteme RAG s preverljivimi navedbami virov za podjetja v EU. Uvodni pogovor je brezplačen in traja 30 minut.