Към съдържанието

Инженеринг

Седем начина RAG да се провали, които демото не показва

Демото пита само каквото корпусът може да отговори. Седем режима на отказ при RAG — chunk bleed, ranking, self-graded evals, изтичане на данни — и проверките.

Публикувано
Четене
10 мин
Стъпва на
Публикувани retrieval бенчмаркове (Anthropic, Databricks, Chroma, τ-bench), таксономията на отказите при RAG на Barnett и съавтори, и собствената ни работа по търсене

Демонстрацията на retrieval е нагласена игра и всички в стаята го знаят освен този, който плаща. Въпросите идват от корпуса. Корпусът е чистен миналата седмица. Питащият вече знае в кой документ е отговорът.

Продукцията обръща и трите. Ето седем режима на отказ, които преживяват доброто демо, и проверката, която хваща всеки от тях.

1. Отговорът е разкъсан между два chunk-а и нито един не се класира

Chunk boundary bleed е най-скучният отказ на терен и най-честият. Фиксиран прозорец от 800 токена отделя стойността на допуска от каталожния номер три абзаца по-горе. Всяка половина сама по себе си е незабележителна, така че нито една не влиза в класацията, а системата отговаря, че информацията я няма в корпуса.

Работата на Anthropic върху contextual retrieval остойностява това: наивен retrieval само с embeddings се проваля при 5,7% от заявките на top-20 — това е 1 − recall@20 — а добавянето на 50–100 токена контекст за документа, написан от модел, пред всеки chunk преди embedding-а сваля числото до 3,7%. Еднократната цена е 1,02 щ.д. на милион токена документ. Late chunking — embedding на целия документ и след това pooling на token embeddings в chunk-ове — връща голяма част от същата полза при обикновена цена на съхранение. Това има значение, защото late interaction от типа на ColBERT заема около 2,46 TB за 100 000 документа по 8k токена срещу около 4,9 GB за обикновени chunk embeddings.

Проверката: вземете 30 реални въпроса, намерете на ръка пасажа, който отговаря на всеки, и проверете дали оцелява цял през вашия chunker. В таксономията на Barnett това отделя FP1, липсващо съдържание, от FP3, налично, но изгубено при консолидацията. Поправките им са различни.

2. Retrieval-ът е наред. Ranking-ът не е.

Това са две числа и се докладват като едно. Recall@20 от 0,94 звучи здравословно точно докато не забележите, че верният chunk стои на позиция 17, а промптът се сглобява от първите 5.

Reranking-ът е мястото, където живее остатъчната грешка. На същия бенчмарк contextual embeddings плюс contextual BM25 се провалят при 2,9% от заявките; добавянето на cross-encoder reranker сваля това до 1,9% — 67% намаление спрямо наивната база, постигнато след retrieval-а, а не по време на него.

Повтарящият се имплементационен бъг е сливането на хибридни резултати чрез нормализиране на BM25 скорове спрямо косинусова близост. Те не са съизмерими и никакво скалиране не ги прави такива. Reciprocal rank fusion заобикаля проблема, като оценява само по позиция, сумирайки 1/(rank_constant + rank) през retriever-ите; Elasticsearch подразбира rank_constant 60. Пазете лексикалния крак независимо колко добри са embeddings-ите — каталожни номера, артикулни кодове и заглавни думи изискват точно съвпадение. В дигиталния речник на МОН, който изградихме на beron.mon.bg, единицата на търсене е заглавната дума и никаква семантична близост не бие точното ѝ съвпадение.

Проверката: докладвайте recall@k и nDCG@10 поотделно и логвайте позицията на верния chunk, а не булево дали изобщо се е появил.

3. Golden set-ът е писан от човека, писал chunker-а

85–95% end-to-end точност е реалистична и защитима цел в тясна предметна област. Докладвани 99% почти винаги означават, че оценъчният набор е писан от този, който е писал промпта, върху chunk-овете, които току-що е донастроил.

Правили сме го. Първият ни вътрешен eval набор беше съставен от инженера, прекарал предната седмица върху splitter-а, и се представи достатъчно добре, че за малко да пуснем върху него. Въпросите бяха формулирани с речника на chunk-овете. Пренаписани от човек, чел само изходните PDF-и, същата система падна с двуцифрен процент.

Етикетите трябва да са бинарни pass/fail от поименно посочен експерт в областта, а не Likert скала 1–5 — тройката е неинтерпретируема и не води до действие. Ако LLM judge оценява, валидирайте го по true-positive и true-negative rate срещу човешките етикети, с цел 0,85+ по всяко от двете. Суровото съгласие ласкае съдията при дисбаланс на класовете, а таванът е реален: MT-Bench поставя силните съдии на около 80% съгласие с хора — толкова, колкото хората постигат помежду си.

Проверката: човекът, който етикетира golden set-а, не е човекът, който е строил pipeline-а. 150–400 случая, а брифът за етикетиране назовава експерта поименно.

4. Индексът е снимка, а никой не е остойностил преизграждането

Качеството на retrieval се разпада, без никъде да има грешка. Отменена SOP е семантично идеална и уверено грешна, така че метаданните за ревизия и дата на влизане в сила трябва да я филтрират, а не просто да я класират по-ниско. Embedding моделите биват извеждани от употреба, което налага пълен re-embed на корпуса. Пълен HNSW rebuild над 10 млн. вектора е часове стенно време, а скоростта на изграждане се сгромолясва напълно, щом графът надхвърли maintenance_work_mem.

Проверката: мерете времето от промяна в изходния документ до търсимост като първокласна метрика и вкарайте цената — в евро и в часове — на едно пълно преиндексиране в текущите разходи, преди да подпишете каквото и да е. Оферта без ред за преиндексиране и за поддръжка на eval-ите не е писана от някой, който е оперирал такава система.

5. Tenant филтрите текат в едната посока и събарят recall в другата

Филтрирайте след retrieval, в приложението, и ще извадите глобалния топ 50, ще изхвърлите каквото този потребител няма право да види и ще подадете на модела каквото е оцеляло — понякога три chunk-а, понякога нула. Изключените документи междувременно са минали през вашия reranker и вашите traces, което е въпрос на разкриване на данни толкова, колкото и на качество.

Филтрирайте вътре в индекса и удряте в другата стена: рестриктивните филтри по метаданни фрагментират HNSW графа и recall-ът деградира тихо. Точно затова съществуват filterable HNSW и ACORN. Итеративният scan на pgvector по подразбиране спира на max_scan_tuples = 20000, така че тесен филтър може безшумно да върне по-малко от k реда.

Третият вариант е архитектурен: retrieval услуга, която държи собствени статични credentials и заявява от името на извикващ, когото не е оторизирала — „confused deputy“. Точно затова MCP забранява token passthrough. Списъкът на OWASP за 2025 носи и двете половини: LLM02 разкриване на чувствителна информация и LLM08 слабости на вектори и embeddings.

Проверката: eval случаи, които твърдят отрицание — тази самоличност не трябва да извади този документ — пускани при всяка промяна на индекс, филтър или права, а не само при старта.

6. Два източника си противоречат, а моделът избира тон вместо страна

Дайте на компетентен модел политиката от 2023 и тази от 2026 в един и същи контекст и обикновено няма да откаже. Ще произведе един гладък абзац, съдържащ и двете, с цитати и към двете, и никой читател няма да забележи.

Изследването Context Rot на Chroma през 18 модела установява, че един-единствен distractor измеримо влошава точността, а четири се натрупват, и че представянето пада с дължината на входа дори при задачи, които моделът решава тривиално на къс вход. Тоест очевидното лекарство — подай повече контекст — не е лекарство. Databricks измерва генерацията да деградира далеч преди recall-ът на retrieval-а да се насити: Claude 3 Sonnet над 16k токена, GPT-4-0125 над 64k, като DBRX преминава към резюмиране вместо отговор в 50,4% от случаите при 32k, а процентът откази заради авторско право при Claude 3 Sonnet се качва от 3,7% при 16k до 49,5% при 64k.

Проверката: заредете golden set-а с известни противоречия и изисквайте системата да назове конфликта, вместо да го разрешава. После проверявайте, че цитатите се разрешават и че цитираният пасаж наистина подкрепя изречението — счупените цитати са най-честата тиха регресия в работеща система.

7. Harness-ът пита само неща, чийто отговор някой вече е знаел

Всеки eval набор, съставен от реална употреба, наследява survivorship bias: съдържа въпросите, които хората са задали, защото са очаквали отговор. Въпросът без отговор — политиката, която не съществува, годината, за която нямате данни — е този, който произвежда измислицата, слагаща край на проекта.

Здравословният процент въздържане върху реален корпус е 5–15%. Под 2% системата гадае; над 25% retrieval-ът е счупен. И мерете консистентност, не пик: τ-bench намира челните модели над 50% успех при едно минаване, но под 25% при pass^8 — успех и в осемте независими пускания на една и съща задача. Пускайте всеки golden случай по 5–10 пъти.

Проверката: искайте провалените traces, преди да искате демо. Error analysis върху реални провали се насища след около 30 traces, когато не се появява нов режим на отказ, и обикновено пренасочва цялата поправка.

Къде това спира да важи

Ако корпусът ви е малък и стабилен — няколкостотин страници вътрешни правила, един език, едно ниво на достъп — пропуснете retrieval-а. Дълъг контекст под праговете на деградация по-горе е легитимна архитектура и премахва пет от тези седем режима наведнъж. RAG заслужава сложността си, когато корпусът е твърде голям, твърде свеж или твърде разграничен по права, за да бъде подаден цял.

Забележете и какво не е в списъка. Нищо от изброеното не е проблем на модела. Шест от седемте са водопровод на данни и дисциплина на измерването, а седмият е спор кой пише въпросите. Това е трайната част от системата, което е удобно, защото моделът е най-евтиният и най-заменимият компонент, който ще купите.

Абстрактна топла светлина върху тъмен фон

Това ли е проблемът, с който живеете?

Ако този текст описва вашата ситуация, най-бързата следваща стъпка е разговор с човека, който го е написал.

30 минути, без ангажимент, а каквото стигнем, остава при вас.