Праектаванне RAG на 100 мільёнаў дакументаў: шардзіраванне, гібрыдны пошук і кошт
Што мяняецца, калі RAG-сістэма вырастае да мільярдаў фрагментаў: асінхронная загрузка, маршрутызацыя, шардзіраванне, гібрыдны пошук, пераранжаванне, аўтарызацыя, кэш і кантроль кошту.
Ёнтэрпрыяцеўка дэмаверыянта RAG займае адзін вечар. Вы дзеліцее PDF-файл на часткі, умяшваеце іх, зберагаеце вектары ў базе дадзэнняў, выбіраеце пяць найбліжэйшых „суседаў“, прыкладзеце іх да запіту і запускаеце модэль.
Такі падход абліковы пры колькісці каля кількох тысяч дакументаў. Калі ж корпус дадзэнняў вырастае да 100 мільйонаў дакументаў, такі падход перастае работаць, і пытанне змініцца. Ён больш не будзе звучаць як „каторую базу вектарных дадзэнняў выбраць?“, а стане такім: як скасціць мільярды кандыдатаў-доказаў да 10-20 значных фрагментаў, не паспяшаючы час адпаведзі, не падвяшваючы якасць пошуку, не парадукаючы доступнасць чы бюджэт?
Гэта зусім іншая інжынерная проблема. Пры такых масштабах RAG ёсць распраўнай сістэмай для дадзэнняў і пошуку, да якой ззаду прыўязаны мовны модэль.
Што павінна рабіць сістэма
Ёжы размову быць конкрэтной, прыймім следуючыя трэбаванні:
- Больш чым 100 мільйонаў дакументаў у такіх фарматах, як PDF, HTML і DOCX
- Мільйоны корыстуначаў, якія ставяць запытанні на натуральнай мове
- Цітаты, прыўязаныя да кожнай адпаведзі
- Дакументы, якія з часам апошнаваюцца і выдалююцца
- Шыранне інфармацыі за короткі час — прыблізна 1-3 секунды прычымоў да пачатку генеравання
- Высокая доступнасць
- Працэс прыемкі дакументаў, які выконваецца асінхронна
Усё зводзіцца да адной важкай трэбаванні: знайсці правільныя 10-50 елементаў інфармацыі сярод потэнцыяльна мільярдаў частак.
Пачнім з арыметыкі
Першая памылка ў большых праектах RAG — гэта спрыяжэнне дакумента і вектара ў аднае рошчыну. Прыймім:
Documents = 100,000,000
Average chunks/document = 30
Embedding dimensions = 768
Storage/dimension = 4 bytes (float32)
Колькасць вектара ў такім случае ёсьць:
100,000,000 × 30
= 3,000,000,000 vectors
Трыяцьце мільярда вектараў. Працоўны запас памяці для іх зберагчэння становіць апошнейша:
3B × 768 × 4 bytes
≈ 9.2 TB
І гэтыя — толькі самыя цыфры. Рэальная разгортка таксама включае:
ANN index structures
chunk text
document metadata
ACL metadata
IDs
lexical indexes
replicas
snapshots
WALs
temporary indexes
operational headroom
З двума копіямі індэксу вектараў вы ужо перасіглі 18 ТБ сырых вектараў, яшчэ не з урахоўванням структур пошуку. Якшчуль дакументы складаюцца з 50 частак, а не з 30:
100M documents × 50 chunks
= 5 billion vectors
Іменна гэта прычына, чаму фраза «мы падамлем усе фрагменты ў базу даных вектараў і запускаем пошук сэмплярнасці» не апісвае архітэктуру большага масштабу. Яна апісвае толькі пачатак.
Два роўні, якія застаюцца раздзеленымі
Найчыстэйшы спосаб арганізаваць сістэму — цэлкам як два незалежныя роўні: адны для прыемкі дадзеных і другі для ўзяць іх. Гэты раздзел мае важнае значэнне. Кліент, які завантажвае 20 мільйонаў новых дакументаў, не павінен прыводзіць да таго, каб час адпаведзення на запыт з 300 мс вырасіў да 8 секунд. Працэс прыемкі дадзеных налаштовваецца для высокай праўоцэснасці, а працэс выкарыстоўвання дадзеных — для мінімальнага часу адпаведзення; прымусовая обработка ўсьго праз адной і той жа лінію обработкі — гэта надзея на пазнейшыя проблемы з эксплуатацыяй.
База даных вектараў не ёсць вашым аднаковым джэрэлам правды
Зберагаюце аднаковыя дакументы ў недорогіх сховішчах об’ектаў, такіх як S3, GCS чыя Azure Blob. База дадзеных вектораў ўособліва індэкс, а індэксы можна перзбудаваць. Якшча за шасць месцаў вы перэйдзеце з модэлю імпліментацыі E1 на E2, вам патрэбна будзе перзгенераваць векторы з адзначаных аднаковых дакументаў, а не працаваць з самым сховішчам вектораў.
Версіяванне об’ектаў таксама дапамагае, таму што апдэйт можна зафіксаваць як новую версію заместо знішчэння старой. Напрыклад, Amazon S3 можа зберагаць калькі версій аб’екта, а політыкі жыццёвага цыклу могу перыявіць старэйшыя версіі на дышэўшыя рывні зберагання чы прыпініць іх дзейнасць.
Працэс прыемкі дадзеных павінен быць асінхронным
Спадзяйце, кліент завантажыў 500 000 файлаў PDF. Вы не хачаце, каб запыт POST /documents парсаваў, выкананы OCR, чыставаў, дзеляў на часткі, убудовваў і індэксаваў, і толькі пасля чаго вярнуў код 200. Такі запыт могаў бы працаваць гадзінамі. У замен канцэнтрык павінен выканаць мінімум задач:
POST /documents
│
▼
Store document
│
▼
Create ingestion event
│
▼
Return 202 Accepted
Усе іншае выконваецца па боку, пад вплывам падзей. Kafka ўзьмамся як адна з альтэрнатываў для основы, адколі тэмы дзеляцца на партыцыі, якімі спожывачы можаць працаваць паралельна, а ў кожной партыцыі палягае архітэктура ардынацыі, што ўжоць карыстна, калі падзеі, якія дзеляць дакумент або ключ аб’екта, патрабуюць прыцягнучага парадку.
Конкрэтны інструмент мае меньшое значэнне, чым якосты, якія ён з’являе: для прыёму дадзеных патрабуецца буфер. Калі ваша служба эмбедынгу обрабоцвае 30 000 частак у секунду, а раптам пачынае працаваць з 100 000 частак у секунду, сістэма должна стварыць рэзервную кольчатку дадзеных і не зламацца.
Зробіце кожны крок прыёму дадзеных ідэмпатным
У такім масштабе заданні выкананы два разы. Паведамленні перадаюцца знову. Рабочыя выміраюць пасля таго, як запісваюць даны, але перш чым падтвердзяюць выкананне задання. Запросы выключаюцца, нават калі удалённая сторона завершыла працу. Неабяжна з самага пачатку планаваць усё гэта, ствараючы дэтэрміністычныя ідэнтыфікаторы на аднойчынных вхідных даных:
document_id
document_version
chunk_index
embedding_model_version
ID часткі можа быць хэшам частакоў, якія яе візначаюць:
hash(
tenant_id +
document_id +
document_version +
chunk_index
)
Прыем таго ж самога запісу індэксавання два разы больш не стварае дублікатав кантэнту, таму што
upsert(chunk_id, ...)
Можна без апатыя спробаваць знову. Дасяжэнне выканання толькі аднае раз працоюе. Частае выкананне зазвычай ёсць болей рэалістычныя мета.
Разбіўка на часткі — гэта рашэнне інфраструктуры
У маленькай канторцы змяна размару частака здаецца безнэпасабельной. Але калі мова йдзе пра мільярды вектароў, гэта коштуе рэальных грошаў. Узьмім 100 мільйона дакументаў. Стратэгія, якая дае 20 частакаў на дакумент, дае 2 мільярды вектароў, тады як стратэгія, якая дае 60 частакаў, дае 6 мільярдаў. Змяна, якая здаецца нейкай незначнай, проста утроіла ўсе гэтыя значэнні:
embedding compute
vector storage
indexing work
retrieval fan-out
reindexing time
replication traffic
Такім чынам, разбіўка на часткі — гэта не проста настройка NLP. Гэта параметр планавання ўместкі.
Кожная частка таксама должна мець достатню колькасць метаданых, каб можна было праследаваць яе да пачатку:
{
"chunk_id": "c_982...",
"document_id": "doc_51...",
"document_version": 8,
"tenant_id": "tenant_17",
"page": 43,
"section": "Risk Factors",
"language": "en",
"created_at": "...",
"acl_groups": ["finance", "executives"],
"embedding_version": "embed_v4"
}
Сам тэкст можа быць збераганы ў іншым месцы, залежна ад вашай стратэгіі зберагачвання, але інфармацыя пра яго паходжэнне должна застацца доступной пасля выкарыстання. Інакшыя цітаты раней чым пазней стануць проблемай.
Не шукайце ў всім корпусе дакладна, калі толькі не трэба
Гэта можа быць найважлівейшая ідэя ў всім дызайне. Возьмем прыклад запіта „Што змянілася ў нашай еўропейскай політыке вярнення грошаў у гэты квартал?“ Наўмовітная наладка проста шукае ўсе 3,000,000,000 вектараў. Але сам запіт ужо дае нам багато інфармацыі:
organization = current tenant
region = Europe
topic = refund policy
time = current quarter
permissions = documents user may access
Архітэктура адміністравання даных выкарыстоўвае гэтыя абмежэння актыўна, зменшаючы мільярды фрагментаваных частак да невялікага ўзорку прычаму пачатку пошуку сэмабільнасці. Точныя цыфры залежаць ад навантажэння, таму спрыймайце іх як ілюстрацыі. Прынцып заўсёды застаецца тым самым: спачатку маршрутызацыя, пасля — адміністрацыя.
Шардаванне робіць векторны шар распадзеленым
У певны момант адна вузел не можа хаваць усё корпус даных, які можна шукаць, і індэкс павінен быць разбіты на часткі. Распадзеленыя векторныя двыжухі, такія як Qdrant, масштабуюцца горызонтальна, размешчаючы точкі між шардамі, пры чым рэплікацыя ставіць копіі гэтых шардоў на калькі вузелаў для забезпечэння доступнасці і дадатковай прыемлівасці.
Але ёсць адзін недзея. Калі кожны запіт павінен дастыся да кожнага шарда, дадзенне новых шарда зрэшты прыносіць большую кантрольную работу, а не меншае яе колькасць. Лепшыя прыемы дазволяюць направляць як магчыма большыя колькасці запытоў. Такія атрыбуты могу падказаць, якія шарды насправды павінны браты удзел:
tenant_id
region
document namespace
language
time partition
Гэта дае наібольшыя выгоды ў сістэмах з калькуючыміся корыстнікамі. У працоўным рэжыме
Query
↓
128 shards
Запіт можа пройсці.
tenant_id = 429
↓
Shard group 18
↓
4 shards
Напрыклад, чырвоны дзялёны дызайн Qdrant падтрымляе заданнея корыстуначам шардавання, тады аператывы можаць нацэлівацца на выбраныя ключы шарда і ухіліцца ад працы з усім калекцыям. Гэты прынцып праўільны незалежна ад віробніка: гарна партыцыяванне зменшае колькасць запытак.
Рэплікацыя адпаведзае на іншы вопыт
Людзі плутаюць шардавання і рэплікацыю, але яны рашаюць разныя проблемы. Шардавання адпаведзае на пытанне, як разлічыць набор дадзеных, які занадта вялік для адной машыны. Рэплікацыя адпаведзае на пытанне, што будзе, калі адна з гэтых машын знікне. Уявіце сабе такую структуру:
Shard A → Node 1
Shard B → Node 2
Shard C → Node 3
Калі Node 2 з’явіцца, частка корпусу знікне разам з яйом. З рэплікацыяй:
Shard A → Node 1 + Node 4
Shard B → Node 2 + Node 5
Shard C → Node 3 + Node 6
Сістэма продовжае адпавядаць на запыткі, калі дзеяныя вузлы не працуюць. Рэдандантнастка выклікае дапэўненні прыстрою, падвышаны пропускную здатнасць для запісі і додатковыя зусіллі ў эксплуатацыі, але кластэр для пошуку без яе мае некалькі некомфортных спосабаў выключэння.
Толькі векторны пошук недастатковы
Уявіце, калі хтось запытае: «Што гаворыцца ў правілі HR-48291?» Семантычны пошук можа вернуць матэрыялы пра правілы кадровай справы ў цэлым, тады як лексычны пошук чырпаецца ў знаходжэнні самэго точнага токена HR-48291. А цяпер іншы прыклад: «Чы могу працавальнікі взяць дадатковыя канікулы пасля адкуплення дзеці?» У гэтым случае семантычны пошук выказвае ся лепшая. Гэтыя два падходы перакрываюцься, але охопляюць разныя аспекты, таму звычайным выборам являецца гібрыдны этап пошуку, які выкорыстоўвае як адны, так і другія падходы.
Чырвоныя нарады Elastic па гібрыдным пошуку таксама выкорыстоўваюць гэты падход, спаўнюючы лексычны і вектарны пошук і прымоўляючы метод Reciprocal Rank Fusion як адны з спосабаў з’едынення рангавання. Якойсць прыемная для RRF ў тым, што не трэба адагацца, што значэнні BM25 і косай схожасці ёсць параднымі цыфрамі; замест таго спаўнююцца пазіцыі рангавання. У спрасцаванай форме:
RRF(d) = Σ 1 / (k + rank_i(d))
дзе rank_i(d) — гэта пазіцыя дакумента d у сістэме аднаходжэння i.
Будзьце абераглівы ў параднай прадзеявцы
Існуе прываблівы шлях аптэчкі:
3B documents
↓
BM25 returns 10,000
↓
vector search only those
Ён можа працаваць для дзеяных навантажэнняў, але таксама можа разрушыць сэмантычнае вярнаванне інфармацыі. Уявіце, ўжо корыстувач прабуюча запыткаць: «Якія правілы дапэўнююцца для працавнікаў, якія ўсваймаюць?», а даследжуваны документ гаворыць: «Працавнікі, якія прыймаюць дзецю праз нештатную усваймоўку, маюць права на шыстнацца тыдзёў сямейнага відпачынку». Адпаведнасць ключоўых слоў у гэтым докуменце з фразай корыстувача ёсць слабая. Калі BM25 адхіляе яго ўжо на пачатку, перш чым сэмантычны механізм вярнавання зможа яго пераканаць, ніякі модэль умяшчэння не зможа яго адзначыць.
Таму, пасля строгіх абмежэнняў у маршрутацыі, крашчай практыкай ёсць паралельна ствараць кандыдатавакі з абоўх механізмаў вярнавання, а пасля — перэранжаваць з’едынены скупак. Самыя строгія абмежэння ёсць окрэмая катагорыя:
tenant
permissions
document status
Іх зазвычай трэба выканаць прычымо да пачатку адчытання.
Автарызацыя выканаецца раней, чым LLM бачыць што-небудзь
Уявіце корпоратыўны корпус, які мае:
Engineering documents
HR documents
Board documents
Legal documents
Executive compensation
Customer contracts
Сэмантычная адактуальнасць не ўзначае дазволу. Дакумент фінансовага дырэктара можа быць матэматычна самым блізкім адпаведнікам запытання працавіка, але гэта ўсё равна не дае працавіку права чытаць яго. Неправільны дызайн выглядае так:
Vector Search
↓
Retrieve confidential chunk
↓
Send chunk to LLM
↓
Check permission
Калі выконваецца перагляд прав, конфідэнцыйны тэкст ужэ перайшоў у наступны компонент. Правільны лямоўкі спачатку выявляе, хто ёсць выкарыстоўчык:
User
↓
Identity
↓
Groups / roles / ACL
↓
Authorized search space
↓
Retrieval
Гэта значыць, што метаданы кантролю доступу павінны эфкасна браты удзел у працэсе адзыскання. У вельмі большых масштабах схема ACL, якой патрэбна перасканаванне мільйонав запісаў пасля адзыскання, стае проблемай як з безпекай, так і з выдатнасцю.
Адзысканне дае кандыдацав, а не кантэкст
Сказаўмо, гібрыдны спосаб адзыскання дае 400 кандыдацав. Не трэба включаць у запіт 400 фрагментаў. Спачатку пераранжавайце іх:
Vector candidates: 200
Lexical candidates: 200
│
▼
Fusion
│
▼
~250 unique chunks
│
▼
Reranker
│
▼
top 20–40
Першыя стадзіі пошуку створаны для шырокага адзінчасна знаходжэння правдападобна значыцьных рэзультатаў. Рэранкер прызначаны для іншай задачы: на адной з меньшых колькасці элементаў ён прыме рашэнне пра значыцьнасць з большай адказальнасцю. Такі двухстадзійны падход дазволяе дорогаму модэлю адказальнасці працаваць з сотнямі элементаў уместо мільярдаў.
Потым неабходна адмахнуцца ад зайвага
Нават хорашы рэранкер можа даць ў такім выглядзе:
Chunk 1 → page 14
Chunk 2 → page 14
Chunk 3 → page 15
Chunk 4 → page 14
Chunk 5 → page 15
Усе пяць можа выказваць практычна тое ж сама месца, і адправка кожнага з іх выкорыстоўвае зайвыя токены кантэксту. Перш чым ствараць фінальны запіт, выконайце певную сумешанню эйха крокаў:
deduplication
document diversity
section diversity
near-duplicate detection
adjacent-chunk merging
token budgeting
Інодзе для правільнага раштунку таксама патрэбны суседні элементы. Калі частка 47 мае адказ, але пачынаецца насерэдзіне пояснення, прыстрой для стварэння кантэксту можа яго запрашаць.
chunk 46
chunk 47
chunk 48
У зямест на тое, каб вектарны пошук самасталяваліся знаходзіць кожны суседзячы фрагмент.
Стварэнне кантэксту патрабуе сабе адзінокага слоя
Дзівнычна для большасці рэалізацыяй спрыяе таму, што кантэкст розглядаецца як нешта простае:
context = "\n\n".join(retrieved_chunks)
Сістэмы вырабаткі лепша працуюць з спецыяльным будоўнікам кантэксту. Яго задача — взяць рангаваныя доказы і стварыць эфектыўны пакет запіта, не выкрадаючы інфармацыю пра цітаты. Канцэптуальна:
Retrieved Evidence
↓
Deduplicate
↓
Merge related chunks
↓
Enforce token budget
↓
Preserve source IDs
↓
Order evidence
↓
Generate context
Модэль можа тады практычна падаць кашто-таце структурна падобна гэтаму:
SOURCE S1
Document: Employee Handbook
Page: 42
Version: 18
Text: ...
SOURCE S2
Document: European Leave Addendum
Page: 7
Version: 4
Text: ...
QUESTION
...
І ўсё час патрабуецца цітаваць S1, S2 і так далей. Гэта набагато безпечней, чым прасіць мадэль перадаць URL-адрэсы чыста ідэнтыфікатары дакументаў з памяці.
Розумеўце запыткаў должна быць дашчацая
Дзеяныя запыткі выгадваюць ад нормалізацыі пры ўзяцці дакументаў. Корыстнік пытаецца: «На сколькі вырослі доходы ў Еврапе ў празыдзеўшы год?» У завіснасці ад сённяшней даты і саўстаннічай тэрміналогіі компаніі, узяцце дакументаў можа быць эфектывней, якшо выкарыстоўваць структураваныя поля, выкарыстаныя з гэтага запытку:
metric = revenue
region = Europe
year = 2025
intent = financial comparison
У размове падзець на кшталт «А як з Азіяй?» трэба перапісаць, выкарыстоўваючы паканальны кантэкст:
How much did revenue grow in Asia in 2025?
Сэрцаўная ідэя заключаецца ў тым, што для адбыцьця гэтых крокаў рэдка патрабуецца вашыя найдрогейшыя мадэлі аналізу. Ўжывайце найдэшавшы компонент, які надзеямо выпалюе задачу. Готавая структура AI зазвычай выглядае так:
small model
↓
classification
small model
↓
query rewriting
embedding model
↓
retrieval
reranker
↓
relevance
large model
↓
final reasoning
а не так:
largest model everywhere
Разбіўка для запітаў з калькуляцыямі
Дзеяныя запітанні нельга надзеяна адпаведзець на адной тэкстовай частцы. Запітанне на кшталт "які еврапейскі продукт мае найбольшы спад у выручцы, і як кераванне цэе паводзіцца адгукнуцца?" дзеліцца на два пошукі:
Question 1:
Which European product had the largest decline?
Question 2:
What explanation did management provide for that product?
Пасля чаго сістэма можа збірваць паказнікі для кожнага падпытання окаласна, а рэзультаты з’еднваюцца пры стварэнні выходу.
Complex Query
│
▼
Query Decomposer
/ \
/ \
▼ ▼
Subquery A Subquery B
│ │
▼ ▼
Retrieval Retrieval
\ /
\ /
▼ ▼
Evidence Join
│
▼
LLM
У гэты момант большасцовыя сістэмы RAG пачынаюць выглядаць больш як сістэмы планавання запітоў, чым простыя методы пошуку за найбліжэйшым суседам.
Свежасць зменяе спосаб стварэння індэксаў
Падазроўваючы, што корпус містыць 100 мільйонаў дакументаў, з якіх 2 мільйонаў зменяюцца ў кожны дзень, перыядычная перзбудова індэкса ўвечары, верагодна, не будзе правільным рашэнням, таму патрэбна інкрэментальная індэксацыя:
Document updated
│
▼
new document version
│
▼
parse changed document
│
▼
generate new chunks
│
▼
embed
│
▼
write new index entries
│
▼
mark previous version inactive
Логічная версіяванне лепшая за негайную разрушаючую замену. Напрыклад, для адзінаго дакумента:
document_id = D17
version 41 → status=inactive
version 42 → status=active
Фільтрацыя запыткаў па
status = active
А старыя элементы можна збіраць у сметанку на фоне. З точкі зору прыкладнага програміста апдэйт выкананы атамічна, без патрэбы, каб кожная сістэма зберохвату зменялася ў той жа момент.
Таксама версіюйце мадэлі
Раней чым пазней вам даведзецца змініць мадэлі для вбудоввання, і вы ніколі не должны зберагаць вектары, не ведаючы, што іх створыла. Як мінімум, трэба зберагаць інформацыю, якая дазволяе ўсё праследаваць:
embedding_model
embedding_version
embedding_dimensions
chunking_version
parser_version
Інакшыява, чэрэз шасць месца вы можэце адкрыць, што 15% індэкса было створана за дапамою старэйшага прайсупутніка, і не будзе простог спосабу з’ясавіць, канкрэтна гэта 15%. Міграцыю можна адкрыць на паралельных індэксах:
Documents
│
┌─────────┴─────────┐
▼ ▼
embedding model V3 embedding model V4
│ │
▼ ▼
Index V3 Index V4
│
▼
shadow traffic
│
▼
evaluate
│
▼
switch alias
Не трэба перамешчаць мільярды вектароў і проста спадзявацца, што новы спосаб выкарыстоўвання будзе лепшы. Змяркуйце гэта пры пераходзе.
Кеш на калькі лэвелаў
Не кожны запіт заслуговае на выкарыстоўванне всіх етапаў працы. Калі тыясчы спавнаўцаў запытаюць: «Скількі дней вакацый мы маем?», то неякога
embedding
→ distributed retrieval
→ reranking
→ large LLM
Кожны раз працавац такім чынам — гэта марна трата рэсурсаў. Болейш адпаведны варыянт — настаўка кэшаў у шарах:
User Query
│
▼
Exact Response Cache
│ miss
▼
Semantic Cache
│ miss
▼
Retrieval Cache
│ miss
▼
Search
Кэш умякчэнняяў ёсць явным супакомпанентам:
hash(normalized_query)
↓
query embedding
Справы стаюць складнейкімі, калі змініцца контэнт. Адказ, захаваны ў кэшы, стае неактуальным.
Policy version 17
Можа змяніцца на неправільную адзін раз.
Policy version 18
З’являецца у працэ. Таму ключ кеша або схема анулювання павінны ведаць пра межы корпусу і версій. Выкарыстоўванне мадэлі кэшавання без правіл свежасці ёсць небяспечным.
Планаваць не толькі успех, але і можлыя непрацэздатнасці
Вялікія сістэмы перастаюць працаваць поступова. Можна спакою чакаць такіх рэзультатаў:
embedding provider unavailable
one vector shard unavailable
search cluster degraded
reranker timeout
LLM rate limited
LLM provider outage
queue backlog growing
parser crashing on malformed PDF
OCR worker running out of memory
Кожная неудача трэбуе чыткай політыкі. Можа, так:
Vector search fails
↓
Can lexical search answer?
↓
yes
↓
return degraded retrieval mode
альбо:
Primary LLM unavailable
↓
Fallback model
↓
Lower quality but service continues
альбо:
Document parser fails 5 times
↓
Dead-letter queue
↓
record failure reason
↓
operator / automated remediation
Постаўленне спробаў зноў і зноў не ўскладнівае сітуацыю. Інодзе гэта проста бесканечны цыкл, які супакоўваеся додатковымі расходамі на хмарныя сервісы.
Зворачны тырс
Уявіце, што працэс прыемкі дадзеных зазвычай працюе нормальна.
20M chunks/hour
а ўместнасць для вбудоўвання хапае
25M chunks/hour
Усё гаразд. Потым кліент запускае процэдуру міграцыі і стварае
200M chunks/hour
Без зворотнага тыску абяранне выглядае так:
embedding service overloaded
↓
timeouts
↓
retries
↓
more load
↓
more failures
↓
retry storm
З буферамі перад працавальнікамі:
incoming workload
↓
durable queue
↓
workers consume at safe rate
↓
backlog temporarily grows
Здольнасць цяпер можа быць налаштаваная незалежна ад частоты прыбыці дадзеных. Гэта ўжо адна з прычын, чаму сцэанкі прабрамвання дадзеных патрэбуеяць сае архітэктуры.
Можлівасць абзярнення павінна включаць кантроль якасці прыбірання дадзеных
Звычныя справаздачы пра монітарынг паказваюць такія даныя:
HTTP 200 rate
CPU
memory
disk
p95 latency
error rate
requests/second
Усе гэта корыстна, але сервіс RAG можа выдаты код HTTP 200 за 800 мс і пры тым даст абсалютна некорэктную адпаведзь. Таксама неабходны тэлеметрыя-даныя, спецыяльнае для AI. Корыстны трагі з кожнага запіту можа выглядаць так:
request_id: rq_912
query rewrite:
"parental policy?" → "parental leave policy"
vector retrieval:
200 candidates
145 ms
lexical retrieval:
200 candidates
91 ms
fusion:
278 unique candidates
reranking:
278 → 20
212 ms
context:
13 chunks
8,420 tokens
LLM:
input 9,104 tokens
output 681 tokens
1.8 sec
citations:
3 documents
total:
2.4 sec
Для афлайн-ацэнкі патрабуюцца паказначыкі, якія даюць адказ на такія запітанні: чырпаліся правільныя доказы, чы ранг такіх доказоў быў достатньва высокі, чы адказ падтрымлівалі тыя доказы, чы пераранжаванне значна паспрабавало паўнэчы запіты, каб праверыць цярплівасць процэсу, і якія класы запітоў частавейша не выкананы? Гэта ёсць больш практычнае, чым проста сочыць за галюцынаціямі як за аднам глобальным паказначыкам.
Ацэніце чырпанне окрэма ад генеравання
Калі фінальны адказ ўскладнены, існуе прынеймна два разныя класы неудач. Пры неудачы чырпання неабходныя доказы так і не патрапілі ў кантэкст:
Correct document exists
↓
retriever misses it
↓
LLM never sees it
↓
wrong answer
Няхай які бы ні было правкі заповедзі не можаюць гэта паспраўліць. У разе неспэшчання генеравання правыя доказы адзначыліся:
correct evidence
↓
context
↓
LLM
↓
incorrect interpretation
Цякож можа маты значэнне дызайн запіту, выбор мадэлі, арганізацыя кантэксту чым стратэгія абаранення. Калі оцэнюваць толькі фінальныя адказы, яны выглядаюць ідэнтычна, таму серйозны працэс ацэнкі павінен пераглядаць промежуточныя этапы.
Задайце ліміт на затрымку
Фраза «Сістэма павінна працаваць быстро» не ўсёўказвае на патрэбу. Замест таго задайце ліміт кожнаму этапу. Для ілюстрацыі, скажымо, цэль — адказ за прыблізна 2,5 секунды, з такым распадам:
API + auth 50 ms
query understanding 100 ms
retrieval 300 ms
fusion 20 ms
reranking 250 ms
context construction 30 ms
LLM time-to-first-token 1,200 ms
network / safety margin 550 ms
-----------------------------------
target ~2,500 ms
Этыя цыфры не ўніверсальныя. Гэты эксперымент важны, таму што ён паказвае, куды на самай працоўга ідзе час. Калі час на запрашэнне дадзеных станавіць 1,8 секунды, змена прадаўца LLM не паўлічыць галоўнага бутлнэка. Калі LLM станавіць 90% затрымкі, працаваць месяц, каб зменіць 40 мс у вектарнам пошуку, скораячаеся, не будзе найкрашчым спосабам распыткі зусіль.
Косцы таксама трэба контролаваць
Большы сістэма RAG мае многа аддзельных центраў косцоў:
object storage
document parsing
OCR
embedding generation
vector storage
vector replicas
lexical search
metadata database
network transfer
reranking
LLM input tokens
LLM output tokens
observability
backups
Тады вылічыце кост на адну корысную едыніцу, напрыклад:
cost / 1,000 indexed documents
cost / million chunks
cost / query
cost / successful answer
cost / tenant
Пасляпэўны пункт мае найбольшое значэнне ў SaaS. Вы можаце адкрыць:
Tenant A
5% of revenue
42% of LLM spend
Без выклікання лічыльніка на кожнага корыстувальця вы бачыце неспадзевана веліка месчынная сума за інфраструктуру.
Храніце дорогія рэсурсы там, дзе яны ўпражнены
Корыстным абразам для розумення ёсць градыент ад дорогіх і шырокаспектных рэшэнняў да танных і велікага прыемнасці:
expensive / fast
▲
│
vector indexes
search indexes
Redis caches
│
metadata DB
│
object storage
│
▼
cheap / large
Полныя PDF-файлы не маюць прычын находзіцца ў вектарным індэксе. Заставьце дорогі прыёмны прыстрой для тых задач, дзе ён неабходны, нехай сховыш для об’ектаў храніць самыя дакументы, а інфраструктура пошуку — ўможлівыя для пошуку ўтварэнняя дакументаў. Калі об’ём корпусу дасягне сотняў тэрабайт, гэта раззьяবленне становіцца вельмі важлівым.
Гарачыя і холадныя даны
Не всі 100 мільйонаў дакументаў запытваюцца на однаковай адзінкавасці. Можа:
Last 30 days → 70% of queries
Last 12 months → 25%
Older archive → 5%
Адаптаванне усяго на однаковы лад спамляе інфраструктуру. Адзін з вароў — такое раздзеленне:
Query
│
├────→ Hot index
│
└────→ Archive index when necessary
Маршруты, заснованы на часе, таксама не дазволяюць пытанню пра заходы гэтаго тыжня сустрэцца з дзесяцьма гадамі історычных фрагментаў. Большыя сістэмы становяцца эфектывнымі, ухілваючыся ад працы, а не проста выканаючы непатрэбную працу быстрэй.
Мнагакліентная архітэктура зменяе все
Скажымо, ваша платформа служыць
10,000 organizations
Вы практычна не хачэце
10,000 completely independent vector clusters
Адзначыце, што размешчэнне всіх арэнднікаў у аднам просторэ імен прызводзіць да проблем з шумнымі суседамі і праблемаў з автарызацыяй. Рэальны падход — групаваць арэнднікаў па ўжоўвышэнні:
Small tenants
↓
shared shard groups
Medium tenants
↓
partitioned shared infrastructure
Very large tenants
↓
dedicated shard groups
Гэта не дазволяе адзінам кліентам імпортаваць 500 мільйона частак, які замаскаваюць тыячы меншых, і гарантуе шлях да ізоляціі для корпоратыўных кліянтав, якім гэта патрэбна. Жадная стратэгія партыцыявання не падходзіць усім.
LLM прыбліжаецца да канца
Паглядзіце, сколькі аспектаў інжынерыі мы рассмотрэлі прыходзячы да самага модэлю. Гэта намеравана тактыка. Запрос RAG у зрэлым стадыю праходзіць так:
User
↓
API Gateway
↓
Authentication
↓
Authorization
↓
Rate Limiter
↓
Query Understanding
↓
Shard Routing
↓
Hybrid Candidate Retrieval
↓
Rank Fusion
↓
Reranker
↓
Deduplication
↓
Context Builder
↓
LLM
↓
Citation Validation
↓
Response
LLM мае важна роль, але пры 100 мільйонах дакументаў ён яе толькі адна з частак.
Пэўны вялікі практычны карантын
Калі з’едыніць усё разам, то спачатку ідзе шлях запыту, потым — шлях прыемкі дакументаў, а пасля — сістэмы, якія падтрымліваюць аба ўзгаледы:
CLIENTS
│
▼
┌──────────────┐
│ API Gateway │
└──────┬───────┘
│
┌──────▼───────┐
│ Auth / ACL │
│ Rate Limits │
└──────┬───────┘
│
▼
┌────────────────────┐
│ Query Orchestrator │
└─────────┬──────────┘
│
┌─────────────┼──────────────┐
│ │ │
▼ ▼ ▼
Cache Query Rewrite Metadata
Filters
│ │ │
└─────────────┼──────────────┘
▼
Shard Router
│
┌────────────┴────────────┐
▼ ▼
Lexical Search Vector Search
│ │
└───────────┬─────────────┘
▼
Rank Fusion
│
▼
Reranker
│
▼
Deduplicate
│
▼
Context Builder
│
▼
LLM
│
▼
Citation Validation
│
▼
RESPONSE
================================================================
INGESTION SYSTEM
Documents
│
▼
Object Store
│
▼
Event Queue
│
┌────────────┼────────────┐
▼ ▼ ▼
Parser Parser Parser
│ │ │
└────────────┼────────────┘
▼
Normalization
│
▼
Chunker
│
┌──────────────┴──────────────┐
▼ ▼
Embedding Queue Text Index Queue
│ │
┌─────┼─────┐ ▼
▼ ▼ ▼ Lexical Index
GPU GPU GPU
│ │ │
└─────┼─────┘
▼
Vector Index
================================================================
SUPPORTING SYSTEMS
Metadata DB → document state, versions, ownership
Object Storage → original source documents
Vector Cluster → semantic retrieval
Search Cluster → lexical retrieval
Redis → caches and rate limiting
Message Queue → asynchronous ingestion
Observability → traces, metrics, evaluation
Secrets / IAM → service authorization
Evaluation System → retrieval + answer-quality tests
Анты-шаблоны, якія трэба адхоўваць
Прааналізаваючы такую архітэктуру, наступныя моманты вызываюць негатыўную увагу:
One giant vector collection with no routing strategy
Synchronous PDF ingestion
Only vector retrieval, no lexical search
ACL filtering after retrieval
No document versioning
No embedding model version
No reranking
Sending top-100 chunks directly to the LLM
Using the LLM for every trivial classification task
No dead-letter queue
No retrieval-level evaluation
No tenant-level cost attribution
Treating the vector DB as permanent document storage
Re-embedding the entire corpus for every small content change
Benchmarking only average latency instead of tail latency
Будзь-кой з іх можа прынесці прататып. Разам, у масштабе мільярда вектараў, яны стаюць чырвонымі для работы.
Асновны прынцып
Найкорыстнейшы спосаб разумець вялікі RAG — не гэты:
question
→ vector search
→ LLM
Гэта такое:
billions of possible evidence units
↓
routing constraints
↓
relevant partitions
↓
cheap candidate search
↓
hundreds of items
↓
expensive reranking
↓
tens of passages
↓
context optimization
↓
LLM
↓
grounded answer
Сістэма ўжо як воронка. На кожным этапе мы выдзейліваем больш вычысляльных рэсурсоў на меншыя колькіць кандыдатаў:
Cheap operation → huge search space
Moderate operation → smaller candidate set
Expensive operation → tiny candidate set
Most expensive model → final context only
Гэта ідэя якраз і робіць архітэктуру эканамічнаю.
Што трэба запамятаць
Сістэма RAG для калькі тысяч дакументаў — гэта пераважна задачэ інжынерыі прыемоў. Калі ж мова йдэ пра 100 мільйонаў дакументаў, ствараецца патрэба ў інфраструктурэ для пошуку, распадзеленых сістэмах, інжынерыі дадзэнняў, абеспеке і кантролі віток, якія ўсе трэба адразу уважна расследзіць. Векторныя базы дадзэння маюць значэнне, так сама як і модэлі імбеддынгу і LLM, але архітэктура, якая іх аб’ёднае, мае ўжо большое значэнне, ніж прызнаюць большасць нарадчыкаў.
Ментальная мадэль, якая расце: зберагаюць выхадны матэрыял дышча, обрабоцваюць асінхронна, ствараюць версіі всего, намеравана разбіваюць інфармацыю, з самага пачатку застосоўваюць правілы автарызацыі, адзначаюць даныя пры выкарыстоўванні колькома сігналамі, жорсткая ранжырацыя, даюць модэлі толькі высокакалітэтныя доказы і адзлічваюць кожны этап окрэма. Калі корпус дасягне мільярдаў фрагментаў, найлепшая оптымізацыя больш не ў скорасці пошуку. Гэта аранжаванне рэчаў так, каб большая частка сістэмы ніколі не ступала на большую частку корпуса.