9 min čteníJohnny UnarJohnny Unar

Většina RAG systémů selže, protože je nikdo nenavrhuje pro audit

RAG se v produkci nerozbíjí kvůli ničemu exotickému, ale kvůli nudným věcem: slabá provenance, rozmazaný retrieval, žádná evaluace. PostgreSQL s pgvector vyřeší víc problémů než další vector DB.

selhání je předvídatelné

Většina produkčních RAG systémů neselže až ve chvíli, kdy model před zákazníkem vypustí nějaký nesmysl. Selžou už ve chvíli, kdy se někdo zeptá na jednoduchou věc: „Který zdrojový dokument tuhle odpověď podložil, jaká verze toho dokumentu byla tehdy aktivní a proč retrieval poslal chunk 184 nad chunk 203?“ Pokud to váš stack neumí zodpovědět během pár sekund, včetně ID, timestampů a deterministických retrieval logů, nemáte produkční systém. Máte demo s embeddingy.

Týmy pořád opakují stejnou chybu. Vezmou naleštěnou vector DB, napojí na ni generický chunker, nasypou do toho PDFka, zavolají text-embedding-3-large nebo text-embedding-3-small a pak se diví, že asistent vymýšlí smluvní klauzule, spojí dva CRM účty do jednoho člověka nebo cituje verzi policy, která byla už tři měsíce neplatná. Není na tom nic záhadného. Kvalita retrievalu bývá průměrná, protože chunking je naivní, metadata jsou mělká a ranking se bere tak, jako by jeden cosine search měl nějak zázračně chápat aktuálnost, typ dokumentu, hranice tenantů a business kritičnost.

Skutečný problém je, že lidi berou vektory jako magii. Nejsou. Jsou to jen souřadnice. Užitečné souřadnice, jasně, ale pořád jen jeden signál. V systémech těžkých na dokumenty, hlavně v ERP, CRM, support portálech nebo interních copilotech, není nejtěžší vygenerovat odpověď. Těžké je udržet dohledatelnost v reálném světě, kde máte duplicitní dokumenty, OCR šum, izolaci tenantů, částečné updaty, permission filtry, pravidla pro právní retenci a product manažery, kteří po první špatné odpovědi stoprocentně přijdou s požadavkem „ukažte mi přesně, odkud se to vzalo“.

Ve Steezru jsme postavili dost AI workflow nad dokumenty na to, abychom přestali romantizovat fancy retrieval stacky. PostgreSQL 16 s pgvector 0.7.x, obyčejná relační metadata, generated tsvectors a retrieval pipeline, kterou umíte vysvětlit auditorovi, skoro vždy porazí black-box vector setup. Možná ne v benchmark divadle. V produkci rozhodně ano.

začněte u postgresu

Setup postavený primárně na pgvector ustojí audit hlavně proto, že PostgreSQL už dávno umí nudné věci, na kterých ve výsledku záleží nejvíc: transakce, constraints, filtrování, row-level security, verzování, JSONB metadata, full-text search a rozumnou observabilitu. Nepotřebujete jeden systém na metadata, druhý na vektory a mezi nimi frontu křehkých sync jobů, které předstírají, že eventual consistency je v pohodě. Není, pokud retrieval musí respektovat ACL dokumentů nebo právní retention windows.

Schéma jako tohle vás dostane většinu cesty:

sql
1create extension if not exists vector;
2
3create table documents (
4 id uuid primary key,
5 tenant_id uuid not null,
6 source_type text not null,
7 source_ref text not null,
8 title text,
9 checksum text not null,
10 version int not null,
11 created_at timestamptz not null default now(),
12 superseded_at timestamptz,
13 metadata jsonb not null default '{}'
14);
15
16create table document_chunks (
17 id uuid primary key,
18 document_id uuid not null references documents(id) on delete cascade,
19 tenant_id uuid not null,
20 chunk_index int not null,
21 content text not null,
22 token_count int not null,
23 page_number int,
24 section_path text,
25 char_start int,
26 char_end int,
27 embedding vector(1536),
28 embedding_model text not null,
29 tsv tsvector generated always as (to_tsvector('english', content)) stored,
30 metadata jsonb not null default '{}'
31);
32
33create index on document_chunks using hnsw (embedding vector_cosine_ops);
34create index on document_chunks using gin (tsv);
35create index on document_chunks (tenant_id, document_id, chunk_index);

Tohle schéma řeší dvě věci, které většina RAG stacků přeskočí. Za prvé ukládá provenance na úrovni chunku: číslo stránky, section path, offsety znaků, source reference, verzi. Za druhé nutí reprodukovatelnost embeddingů přímo do datového modelu. Sloupec embedding_model je důležitý. Pokud potichu přegenerujete polovinu korpusu jiným modelem, kvalita retrievalu vám začne driftovat, nikdo si toho nevšimne a eval čísla přestanou něco znamenat.

Ukládejte checksumy zdrojových souborů, dokumenty verzujte explicitně a nikdy neměňte obsah chunku na místě bez vytvoření nové verze. Když se legal nebo compliance zeptá, proč se odpověď mezi 3. a 29. dubnem změnila, musíte být schopní zrekonstruovat přesně ten retrieval set, který se tehdy použil. V PostgreSQL je to přímočaré. Většina „AI-native“ úložišť z toho udělá otravnou práci.

retrieval potřebuje dva signály

Samotná cosine similarity je pro špinavá business data slabý retrieval. Dense embeddingy jsou dobré na sémantickou blízkost, mizerné na přesné identifikátory, křehké kolem akronymů a často zmatené z šablon, kde každá smlouva nebo ticket vypadá strukturálně skoro stejně. BM25 nebo PostgreSQL full-text search zachytí doslovné termíny, které embeddingy minou: invoice ID, product SKU, jména zákazníků, čísla klauzulí, error kódy jako psycopg.errors.UndefinedTable, zkrátka všechny ty malé věci, na kterých v produkci záleží.

Používejte hybrid retrieval a pak deterministický rerank. Deterministický znamená, že nad stejným snapshotem a stejným dotazem dostanete stejnou kandidátní sadu. Žádný skrytý LLM reranker v kritické cestě, pokud si nemůžete dovolit varianci a náklady. Ve většině business asistentů nemůžete.

Velmi použitelný pattern vypadá takhle:

1. Nejprve aplikujte tvrdé filtry: tenant, ACL, aktuální verze dokumentu, typ dokumentu, datumové okno. 2. Vytáhněte top 50 podle BM25 nebo ts_rank_cd. 3. Vytáhněte top 50 podle vector cosine. 4. Udělejte union kandidátní sady. 5. Spočítejte score jednoduchým váženým vzorcem, který umíte ladit i logovat.

Například:

sql
1select
2 c.id,
3 c.document_id,
4 0.45 * ts_rank_cd(c.tsv, websearch_to_tsquery('english', $1)) +
5 0.55 * (1 - (c.embedding <=> $2::vector)) as final_score
6from document_chunks c
7join documents d on d.id = c.document_id
8where c.tenant_id = $3
9 and d.superseded_at is null
10 and (c.metadata->>'doc_type') = any($4)
11order by final_score desc
12limit 12;

Váhy doladíte podle svého korpusu. Právní smlouvy obvykle potřebují silnější lexikální složku. CRM poznámky často víc těží z dense similarity, protože lidi píšou ve zkratkách a fragmentech. Ten vzorec nechte nudný. Nudné věci procházejí auditem.

Disciplínu potřebuje i chunking. Fixních 1 000 tokenů s overlapem 200 je lenivé řešení a často špatné. Dělte podle headingů, hranic tabulek, střídání mluvčích nebo sémantických sekcí a teprve pak omezte velikost chunku. Policy PDF potřebuje jiný chunking než CRM timeline nebo export ze Zendesk. Pokud ingestion všechno mele stejně, kvalita retrievalu se zasekne na průměru a dál se nepohne.

ukotvěte každou odpověď

RAG systémy umírají v produkci, protože generátor dostane moc velkou volnost. Když prompt říká „odpověz na základě kontextu“ a v kontextu jsou jen částečně relevantní chunky, model mezery doplní pravděpodobně znějícím nesmyslem. Dělá to GPT-4.1, Claude 3.7 Sonnet i Gemini 2.0 Flash. Model není rozbitý. Máte slabé mantinely.

Každá odpověď potřebuje explicitní vazbu na zdroje. My obvykle vyžadujeme, aby každé faktické tvrzení šlo mapovat na jeden nebo více chunk ID, a finální odpověď ukládáme s citacemi typu doc_8f3/page_14/chunk_03. Pokud model vyprodukuje větu bez pokrytí citací, označí se. V přísnějších workflow, hlavně ve financích nebo interních nástrojích blízko healthcare, zahodíme celou odpověď a pošleme ji do review.

Praktický prompt contract může vypadat takhle:

json
1{
2 "answer": "...",
3 "citations": [
4 {"claim": "Payment terms are net 30.", "chunk_ids": ["...", "..."]}
5 ],
6 "confidence": 0.82,
7 "needs_human_review": false
8}

Pak to validujte v kódu. Nespoléhejte na confidence, kterou si model sám nahlásí, berte ji jen jako jeden vstup. Počítejte si vlastní kontroly. Hustota citací, nepodložená tvrzení, rozptyl relevance mezi chunky, signály rozporů napříč retrieved chunky a délka odpovědi vzhledem k dostupným důkazům korelují s rizikem halucinace překvapivě dobře. Když asistent na úzkou otázku vrátí pětiset slovní syntézu podloženou dvěma drobnými úryvky, je to podezřelé.

Dobré výsledky máme s jednoduchým verifierem po generování: každé tvrzení znovu porovnáte s retrieved chunky pomocí menšího modelu, nebo u přesných polí klidně pravidlovým checkerem, a označíte nepodložené výroky. Očividné nesmysly to zachytí levně. Human review má být součást návrhu, ne trapný fallback přidaný až po třetím incidentu. Do jedné review obrazovky dejte původní dotaz, retrieved chunky, draft odpovědi i mapu citací. HTMX je na takové UI úplně v pohodě, není důvod to overengineerovat.

evaluace je celá práce

Většina týmů vyhodnocuje RAG tak, že položí pět interních otázek, dostane docela slušné odpovědi a shipne to. Přesně tak skončíte u debugování halucinací s customer success manažerem v pátek večer. Retrieval systémy potřebují offline evaluaci, online monitoring a business SLA navázané na konkrétní třídy selhání.

Začněte test setem postaveným z reálných dotazů, ne ze syntetických promptových bonbonků. Vzorkujte ze support ticketů, CRM hledání, interních knowledge requestů, procurement dokumentů, prostě odkudkoliv, kde bude asistent opravdu fungovat. Označte relevantní chunky, nebo aspoň relevantní dokumenty, a měřte recall@k a precision@k dřív, než vůbec začnete ladit prompt. Pokud je recall@10 špatný, kvalita generování je jedno, protože model správné důkazy nikdy neviděl.

Nastavte explicitní thresholdy. Třeba asistent pro odpovědi na policy dotazy v interních operacích může vyžadovat recall@8 nad 0,92 na aktuálních verzích dokumentů, citation coverage nad 0,95 a unsupported-claim rate pod 0,02. Sales copilot, který shrnuje CRM poznámky, snese větší rozmazanost, ale i tam chcete tvrdé guardy kolem vlastnictví účtu, data renewalu a citovaných cen.

Logujte každou retrieval událost. Text dotazu, normalizovaný dotaz, embedding model, ID kandidátních chunků, lexical score, vector score, finální score, zvolené citace, výsledek review, uživatelskou opravu. Bez téhle stopy je debugování hádání. S ní umíte odpovědět na konkrétní otázky typu: „Selhal retrieval proto, že BM25 netrefilo SKU, protože vector search upřednostnil sémanticky podobnou policy, nebo protože prompt dovolil modelu zobecnit nad rámec důkazů?“

Jedna nepříjemná pravda: eval datasety stárnou rychle. Dokumenty se mění, zákazníci přejmenovávají produkty, support týmy si vymýšlejí nové zkratky. Evaluaci znovu spusťte pokaždé, když měníte chunking, embedding model, váhy v reranku, verze parseru nebo access-control logiku. Pokud se korpus podstatně změnil, změnil se i benchmark. Předstírat opak je přesně ten způsob, jak se týmy utvrzují, že kvalita je stabilní, zatímco uživatelé mezitím potichu přestávají asistentovi věřit.

recept, který pořád shipujeme

Recept bezpečný pro produkci je schválně nudný. PostgreSQL 16.4, pgvector 0.7.4, na aplikační straně Django nebo FastAPI, pro ingestion Celery nebo obyčejné joby nad Postgresem, object storage na raw soubory a retrieval path, kterou vytisknete na jednu stránku. Přijde raw dokument, parser z něj vytáhne strukturovaný text, chunker vyrobí chunky podle sekcí, embeddingy se vygenerují pinned verzí modelu, chunky se transakčně vloží i s provenance poli, hybrid retrieval vytáhne kandidáty, deterministický rerank je seřadí, generování vrátí strukturovaný JSON s citacemi, verifier zkontroluje oporu a odpověď jde buď uživateli, nebo do review queue.

Není na tom nic exotického. Právě o to jde.

Pro týmy, které staví dokumentové asistenty, CRM copilots nebo interní knowledge nástroje, se tenhle setup provozuje líp než stack složený z pěti specializovaných služeb slepených webhooky. Navíc dobře sedí na způsob, jak o systémech přemýšlejí seniorní inženýři: jeden source of truth, explicitní schémata, silné filtry, pozorovatelné pipeline, reverzibilní migrace. Pokud už PostgreSQL provozujete, přidat pgvector je menší operační skok než zavádět další stateful službu s vlastním backupem, security a modelem konzistence.

Největší přínos je sociální, ne technický. Product, legal, support i engineering si umí vyložit, co ten systém dělá. Můžete ukázat přesný chunk, přesnou verzi dokumentu, přesné score i přesný výsledek validace. Auditoři to mají rádi. Zákazníci to mají rádi. Inženýři mají rádi, když můžou v noci spát.

RAG nepotřebuje víc magie. Potřebuje disciplínu ve zdrojích, disciplínu v rankingu a disciplínu v evaluaci. Dejte vektory dovnitř systému, který tyhle mantinely respektuje, a celý ten setup bude výrazně méně křehký.

Johnny Unar

Napsal/a

Johnny Unar

Chcete s námi spolupracovat?

RAG se v produkci nerozbíjí kvůli ničemu exotickému, ale kvůli nudným věcem: slabá provenance, rozmazaný retrieval, žádná evaluace. PostgreSQL s pgvector vyřeší víc problémů než další vector DB.