Выбір та налаштаванне модэлей эмбеддынга для систем RAG у працоўным режыме.
Дазвольце даклэ научыцца, як моделі эмбеддынга ператвараюць тэкст у векторы, якія можна шукаць, чаму слоўнік домэнына паслабляе семантычны пошук, і як выбіраць, стыскаць та налаштовваць моделі для практычнага викорыстання RAG.
Эта частка ўскладнена пятаяя з серыі, прызначанай для стварэння систем генеравання з падтрымкам адшукання дакументаў высокага стандарта, якія могу адпаведзаць на рэальныя запытанні, пры чым пашліваецца від суровых дакументаў да такой системы. У пачатковых частках гэтай серыі было рассказана пра чыставанне та нормалізаванне выкарыстанага кантэнту, а пасля — пра разбіўку гэтага кантэнту на елементы для адшукання праз ўтварэнне частак. Калі ў нас вяршыцца часткі, наступны крок — ператворыць іх у тое, што можа буць выкарыстана пошуковым індэксам для пораўняння.
Уявіце таго ж менеджера па стосункам з кліентамі, пра якога гаворылася раней, які ўсё ўсё застрэў з пытаннем кредыту у розмяре 12 мільйонаў еўра для высакарызыковага корпоратывнага кліента. Ёж адпаведзе на запытанне, яму патрэбна будзе знайсці трэбавання да паўнейшага аналізу, якое ўкладзенае ў пункт правіл, прагу затверджэння, якая указана ў рядку табелі, а таксама последовнасць кантролю, якая описана ў дыяграме процэсу. На стадіі утварэння частак гэтыя элементы вже былі выдзельваны як окремы, можна праследаваць кантэнты.
Нават так, нічга з гэтаго яшчэ не можа быць адзісканая за дапамою вектарнага індэксу.
Модель умяшчання ператварае кожны фрагмент у вектар фіксаванага дыявоўства. Тая ж модель пасля таго ператварае прыйшлы запит у вектар, а індэкс выбірае тыя фрагменты, якія знаходзяцца найбліжэй да яго ў гэтым вектарным прасторы. Не гарантуецца, што фраза на кшталт "Group Credit Committee approval" у дакументе з правіламі і "GCC sign-off threshold" у запытанні корыстувальніка дзейсна будуць розташованыя недалека адзін ад другога — гэта завышэнае абсалютна ад таго, якую модель вы выбралі і што ўона выучыла пад час навчання.
Самэ гэтае залежнасць і є тэмай гэтага часткі серыі.
Выбір моделі эмбеддінга апошнюецца ставкай на тое, які запас слоў і формулюванняя вашы дакументы маюць спольнага з запитамі вашых корыстувачаў. Якшто вы выберазе не тую модэль для свайго домэна, вам даведзецца гадзіны працаваць над тым, што выглядае як баг у пошуку, а на самай працы ёсць проблема з представленням — самі вектары розташаваны неправільна, таму жадныя налаштаванні індэкса не дапаможуць яе выправіць.
Што на самай працы вычысляе модэль эмбеддінга
У сваей сутнасці модэль эмбеддінга прыймае последоўнасць токэнаў і выдае адны ўпакоены вектар, як правіло, з колькасцю вимераў ад 384 да 3072, залежна ад конкрэтнай модэлі. Цей вектар павінен стаць складзеным кодаванням значэння вхідных дадзеных.
Асумпцыя, яка лежыць у основе адзысквання, заключаецца ў тым, што даннэй, якія маюць падобны значэнне, у тым прасторе атрыбуюцца вектары, якія знаходзяцца падалёку адзін ад другога. Блізкасць зазвычай вимерваюць за дапамою косінусовай схожасці, яка берае за основу кут межы двух вектароў, а не ўсунутасць між ямі — гэта якосць, якая робіць ёе нечувствительной да разніцы ў дужнасці тексту.
Разглянем правило салідарнасці, якое прызначае, што корпоратыўныя кліенты, класифікаваныя як высокарызыковыя, павінны працэсаваць пераканальваныя перагледы прынцыпоў роботы перед тым, як можна будзе падаць прыказку пра кредыт на рассмотрэнне. Спроможны модель гэнеральнага назначэння, верагодна, размістыць свой вектар падалёку ад аналогічных формулюванняў прынцыпоў салідарнасці з іншых банкаў, падалёку ад правовых матэрыялаў пра обавязкі пераканальванага расследавання і падалёку ад норматыўных рэкамендацый ўправління кліентамі высокага рызыку.
Адміністратар роўнаў, пры тым, можа выразіць тую ж самую падстаўную потрэбу аблічна іншым спосабам, запытаючыся, напрыклад, пра тое, якія крокі трэба выконаць перш чым можна падаць заявку на кредыт. Чы гэты повсякдзенны спосаб выражэння падходзіць да формальных правіл, насправды залежыць ад таго, насколькі даных для навчання спаўнілі неформальныя операцыйныя запытання з формальной мовай супакоўкі. Моделі, якія навчаюцца пераважна на шырокім, універсальным веб-тэксте, часта ніколі не асвайваюць гэты конкретны спосаб паўнаравання, калі галузь є спецыялізаванай.
Гэты разлік межаў мовы повсякдзенных запытанняў і мовы спецыялізаваных дакументаў называецца несувяранасцю слоўніку, і гэта є галоўной прычыною проблем з якасцю адзысквання інформацыі ў корпаратыўных системах RAG.
Токанізацыя і вікна контэксту
Перш чым модель абсорбавання вырахоўвае ўсё, яна спачатку разбивае вхідны тэкст на токены за дапамогою свайго внутршняга слоўніка. Колькасць токенаў не завжды адпавядае колькасці слоў чы симвалаў. 500 токенаў у англійскай мове можа вярнуцца прыблізна 350–400 словам, тады як такая ж колькасць токенаў у німецкай мове — дзе слова часта ўтвараюцца з суфіксамі — може включаць менш адміністрацыйных ідэй.
Кожная модель абсорбавання встановляе максимальную дужыну контекста, і ўсё, што перавышае гэты ліміт, або падлягае скорачэнню, або трэбуе спецыяльнага обробкі. Моделі типу Sentence-Transformers зазвычай маюць ліміт у дыапазоне 256–512 токенаў. Модель OpenAI's text-embedding-3-large можа обробляць да 8,191 токенаў. BGE-M3 падтрымлівае аж 8,192 токенаў. Моделі Jina embeddings v3 таксама падтрымліваюць аж 8,192 токенаў.
Практычны вывадак для практычнага викорыстання RAG у працэўным процесе — гэта тое, што межы чакункоў, якія былі выбраны раней у практычным процесе, должны застаўся ў межах таго ліміту контэксту, які накладае выбраныя моделі імбедджавання. Кожны чакунк, які перасягае гэты ліміт, тыхо адсекаецца, і рэзультуючы вектар праблесквае толькі частку асалоднага тексту — гэта спосаб неяксамоцення, які не будзе праказаны нідзе ў логах вашага практычнага процесу.
Семантычны прастор і калі ён перыходзіць межы
Большасць сучасных модэлей імбедджавання — это трансфармерныя энкодэры, якія трэнуюцца з вайсконтрастным цылям: пары текстоў, якія маюць схожыя значэння, з’едынаюцца ў семантычным прасторе, тады як несхожыя пары аддаляюцца. Пасля достатньго трэніравання модель стварае геаметрычныя распаложэння, у якіх близасць выступае як праксічны показнік семантычнай аднасоўанасці.
Такія структуры працююць добра, як толькі запиты і дакументы выкарыстоўваюць той самы тэрмінолагічны апарат, стыль пісьма і канцэптуальную рамку, якія былі викорыстаны пад час навчання моделі. У разе викорыстання таких структур у корпаратыўных цілях яны часта не функцыонуюць за калькуемыя прычыны:
- Спецыфічны тэрмінолагічны апарат паўнейкі вызывае проблемы, якія лёгка занедбаць. Аналітык, який запытаеся пра "прагу для подачы SAR-звіту па структуруванню", можа не знайсці адпаведнасці, якщо фрагмент правіла сформульаваны як "Крэтыяры для подачы звіту пра падозрэльную дзеяльнасьць па структуруванню транзакцый", а модель ніколі не навучылася, што гэтыя два выразы маюць адно значэнне.
Точна разумеў, дзе звычайныя методы інкапсуляцыі часта не функцыонуюць, так сама важна, як і знанне таго, якая модель лідуе ў публічных тэстах.
Выбор моделі інкапсуляцыі у 2025 годзе
Сфера модэляў інкапсуляцыі значна сякрэтасла. Наступныя пораўнання стосуюцца тых модэляў, якія ў значным ступені важны для корпаратыўных систем RAG у банкавасці і фінансовых службах станам середзіны 2025 года.
Няма ўніверсальнага паводзя для всіх сцэнарыёў у предпрыемствах. Вашы выбар залежыць ад набору мов, якія вам трэба падтрымваць, ад бюджету на затрэйтанне часу і інфраструктуры, якая у вас є, ад таго, чы розглядаецца локальная обработка як варыянт, і ад таго, насколькі велика розбіжнасць у тэрмінологіі межы вашай галузі і таго, на чым трэнаваліся універсальныя моделі — гэтая розбіжнасць вярнайша, чы падготовка моделі ў спецыяльных умовах варта зусиль.
Асіметрычны пошук і паведамленне модэлі пра тое, які тип вхідных дадзеных ёй надаецца
Адна з разлікоў, якія багато команд працягваюць ігнараваць, — асіметрычны пошук. Калі вы шукаеце фрагменты, запит і фрагмент, з якім яго паўнарахоўваюць, структурна ёсць дужа разнымі элементамі. Запиты зазвычай ўскорочаныя, фармулююцца як запитанні і часта не маюць большай часткі слоў, якія є у правільнай адпаведзі. Фрагменты, навпакі, ў дужэй меры, практыкуюцца як факты і наполненыя тэрмінамі вашай галузі.
Дзеяныя модэлі створаны для безпосередньага выяўлення гэтай асіметрыі. Модэлі E5 дадаюць до вхіднага тексту прыметаку "query:" або "passage:", ў результате чаго модэль ведае, якую ролю ёй неабходна выпалаваць. Сервіс Cohere's Embed v3 адкрывае гэта можлівасць за дапамою параметра input_type, пры чым дазволеныя значэння включаюць "search_query", "search_document", "classification" і "clustering">.
Выбіранне неправильнага типу вхідных дадзеных пад час індексавання або запиту таямна пагаршае вашыя рэйтынгі схожасці, пры чым важка выявіць асалённую прычыну. Якщо вы выпалаваеце дакумент як запит, то атрыбуты вектара будуць створаныя па геаметрыі запита, а не па геаметрыі тэксту. Процес выкарыстання дакумента не завершваецца абсалютна — проста зменшуецца точнасць, што лёгка працягнуць пад час паверхневых тэстаў.
У працэйных умовах не пакладайцеся на тое, што разработчы запамятаюць правільна ўстановка — прыменяйце яе через налашчэння. Як вызовы для вставкі дадзеных пад час індексавання, так і пад час запитаў должны явна зазначаць тип своіх вхідных дадзеных, калі модель, яку вы викорыстоўвайце, падтрымляе такую можлівасць.
import cohere
from typing import List
co = cohere.Client(api_key="your_api_key")
def embed_documents(chunks: List[str]) -> List[List[float]]:
"""Embed document chunks for indexing with explicit document input type."""
response = co.embed(
texts=chunks,
model="embed-english-v3.0",
input_type="search_document",
embedding_types=["float"]
)
return response.embeddings.float
def embed_query(query: str) -> List[float]:
"""Embed a search query with explicit query input type."""
response = co.embed(
texts=[query],
model="embed-english-v3.0",
input_type="search_query",
embedding_types=["float"]
)
return response.embeddings.float[0]
Розрэдзаныя вектары: калі падбор за ключоўкамі працюе краща за семантычны пошук
Жыцзёсткія вектары фіксуюць значэнне. Розрэдзаныя представлення замест таго фіксуюць, якія тэрміны ўжо є і насколькі сильна іх трэба прыняць у расчытанні. Для значныя часткі типаў запитоў у корпоратывных системах RAG розрэдзаны спосаб выкарыстоўвання дадзеных працюе краща за жыцзёсткі, а ў большасці працэйных системаў сумешчанне обох спосабаў дае кращыя рэзультаты, чым викорыстоўвання толькі аднаго з іх.
BM25 як надзеяны базовы спосаб
BM25 застаецца стандартным падходам да адзыскванню інфармацыі на адпаведнасць ключоўых слоў. Ён ацэнівае релевантнасць на адпаведнасць таму, як часта тэрмін з’являецца ў дакументе, насколькі рэдкасць гэтага тэрміна ў всім корпусе, а таксама за дапамогою фактара нормалізацыі, який урахоўвае дужыну дакумента. Не трэба трэнаваць жадных модэляў, няма патрэбы ў GPU, і не выкананыя якія-лібо вызовы API для эмбеддзінгу.
Разглянем такой запит, як "CRD-EU-047 approval authority threshold." BM25 будзе ставіць высокі ранг любым фрагментам, які мае гэтыя точныя тэрміны. Аднойчы, ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ўжо ёсткі модэль можа не паказаць такі фрагмент, якщо толькі ў корпусе даных для його трэнавання не створылася сильная асоціяцыя межа тым конкрэтным кодам правіл і паняйом адпаведнай уповажненай інстанцыі.
from rank_bm25 import BM25Okapi
import re
from typing import List, Tuple
def tokenise(text: str) -> List[str]:
"""Simple whitespace and punctuation tokeniser for BM25."""
return re.findall(r'\b\w+\b', text.lower())
class BM25Index:
def __init__(self, documents: List[str]):
self.documents = documents
tokenised = [tokenise(doc) for doc in documents]
self.bm25 = BM25Okapi(tokenised)
def search(self, query: str, top_k: int = 10) -> List[Tuple[int, float]]:
"""Return (doc_index, score) pairs for the top_k results."""
tokens = tokenise(query)
scores = self.bm25.get_scores(tokens)
ranked = sorted(enumerate(scores), key=lambda x: x[1], reverse=True)
return ranked[:top_k]
Жыткі адзинаванне добра падходзіць для выяўлення канцэптуальнай супадзімасці, тады калі рэдкае адзинаванне эфективна для знаходжэння точных адпаведнасцей у тэрмінах і ідентыфікаторах. Спалучэнне якога-небудзь з гэтых падходоў — гібрыднае адзинаванне — часта даўа высокія рэзультаты, ў частынах банкавых корпусаў, дзе язык регуляцый є точным і містіць вельмі большую колькасць ідентыфікатораў.
У корпусах, створаных на аднолькіх, чыста апісаных правілах, сам BM25 часта досягае рэвалюцыі жыткага адзинавання пад час вузькіх фактычных пошукоў, пры чым викорыстоўвае значна меншы ресурс інфраструктуры. Яго галоўныя слабасці — сынантазія: запит, які выкарыстоўвае фразу "EDD requirements", не знайдзе параграф, у яком проста напісана фраза "Enhanced Due Diligence requirements", якщо толькі точныя фразы не ператыкаюцца ў яком-небудзь месцы.
SPLADE: Рэдкія вектары, якія выучаюць расшырэнне слоўніка
SPLADE (Sparse Lexical and Expansion Model) знаходзіцца на сяродній пазе межы між простым падборам ключоў і повным, ўсепрацаваным пошукам. Пад час стварэння індекса викорыстоўваецца маскаваны языковы модель, якая абагачае як дакументы, так і запиты семантычна спаднечым слоўнікам, які не павінен быць прысутны ў первасным формулюванні. Рэзультатам яе працы ёст тонкі вектар, чыяя размеры кожная адпавядае конкрэтнаму токену слоўніку, пры чым вага гэтага токена выклікаецца ступенем його значамасці для вхідных дадзенаў.
Такі абзацы, зашифраваныя методам SPLADE і прымененыя для аналізу трэбаванняў EDD, можаць надаць большага значэння такім тэрмінам, як «пераканальванне кліента», «ацэнка рызыку» і «пераканальванне аідэнтычнасці», нават калі гэтыя точныя фразы не прыменяюцца ў самым аднаго тексте. Такое расшырэнне павышае можлівасць аднаходжэння інформацыі пад запитамі, якія выкарыстоўваюць сынантымы, пры тым захоўваючы эфектывнасць і зрозумеласць, якія характерны для рэдкасных представленняў, падатлых на інвертаваны індэкс.
Компрасам ў гэтым є дадатковыя витраты на інференцію пад час стварэння індэкса і большыя запатребаванні да прыемлівасці па супэрфісу, чым у звычнага BM25. Але для корпусаў фінансавых служб, дзе адны і тыя ж палярніцкія концэпты описваюцца розна ў разных юрысдыкціях і па версіях дакументаў, такое расшырэнне можа значна павялічыць дыяпазон аднаходжання інформацыі.
Matryoshka Embeddings: регульяванне размеру вектара для контролю витатаў
Матрыёшкавыя методы навучэння на адпрацоўкі даных (MRL) ствараюць эмбедынгі, у якіх першыя N вимера ўжо складаюць цэласнае, самодостатняе представленне вхідных даных — дадатковыя вимеры дагаўляюць болей дробныя деталі, а не заменяюць тое, што было ранейш.
Гэты метод пачараваў своё імя рускіх нарадных ляльках: 1536-вимерны вектор Матрыёшкі мае цэлком функцыональнае 256-вимернае представленне ў своіх першых 256 пазухах, функцыональнае 512-вимернае представленне ў першых 512 пазухах і так далей.
Семья модэляў text-embedding-3 ад OpenAI падтрымляе гэта безпасова за дапамогою параметра вимера.
from openai import OpenAI
from typing import List
client = OpenAI()
def embed_with_matryoshka(
texts: List[str],
dimensions: int = 256,
model: str = "text-embedding-3-large"
) -> List[List[float]]:
"""
Embed texts at a specified sub-dimension.
Lower dimensions reduce storage and index cost.
Measure retrieval quality drop before committing to a dimension.
"""
response = client.embeddings.create(
input=texts,
model=model,
dimensions=dimensions
)
return [item.embedding for item in response.data]
Эмбедынгі Матрыёшкі размешчаюць ўсе бол дробныя представленні ўнутры адного вектора: першыя 256 вимера вже даюць можна выкарыстоўваецца представленне для пошуку, а кожны наступны ранг дагаўляе семантычную тачнасць за прыбліжны кошт у выразоўванні.
Асалідныя выгоды для працэйскага викорыстання — гэта можлівасць нарабатка кампенсацыі межу запасам і якосцю па захадзе, без павторнаго навчання модэлі чырага перзначэння індексу з нуля. Для корпуса правіл банкавучча можна працаваць з розмірамі 256, 512, 1024 і 3072 выміраў і выявіць, што 512 выміраў даае 97% аб’ёму вярнага выкарыстоўвання дакументаў, вядучы толькі 17% аптовых запасоў, якія патрэбны для цэлага вектара.
У практыцы гэтая кампенсацыя рэдка бывае настолькі чыстай, як выглядае на дыаграмах тэставання. Тонкія разлікі ў даных — асобліва межу блізка спадзярожаных регуляторных паняць — часта знаходзяцца самэлькі ў вышырозмірнай частцы вектара. Перад фіксацыям меньшага розміру для практычнага викорыстання трэба перапрацаваць свой корпус і рэальныя шаблоны запитоў.
Складзенне эмбеддынга без значных падмен у точнасці
Стандартныя эмбеддынгі зберагаюць кожную вясь как 32-бітны флот. Якщо падняць гэты параметр да мільйона фрагментаў дакументаў, кожны з якіх мае 1536 вясей, то практычна будзе трэба 6 ГБ прыродных вектароў, не вылічаючы дадатковых витрац на індексаванне. У масштабах падпрыемства такія запатрэбнасцы ў прыёмнасі і адпаведныя витраты на память вядуць да значных проблем.
Квантызацыя рашае гэту проблему палягчэнням колькасці бітоў, якія викорыстоўваюцца для представлення кожной вясі. У практыцы пераважаюць тры методы: скалярная квантызацыя (пераклад float32 у int8), бінарная квантызацыя (пераклад float32 у адны біт) і квантызацыя прадукту (стисненне кожного вектара ў меншы код).
Скалярная квантызацыя: int8
Скалярная квантызацыя перакладвае цэлазначальны дыяпазон float32 у 256 дискрэтных цэлых значэнняў. Кожныя выміры скарочваюцца з 4 байтаў да 1, чым запас памяці зменшваецца на 75%. Паколькі высокавымірныя моделі інкапсулявання распрадзяляюць інфармацыю равана па многіх вымірах, жадны адзін вымір сам по сабе не несе вялікаг значэння, таму точнасць, якая губіцца з-за такога заокруглення, зазвычай ў мінімальным ступені.
import numpy as np
from typing import Tuple
def quantise_to_int8(
embeddings: np.ndarray
) -> Tuple[np.ndarray, float, float]:
"""
Scalar quantisation to int8.
Returns quantised array plus the scale and zero_point needed for dequantisation.
"""
min_val = embeddings.min()
max_val = embeddings.max()
scale = (max_val - min_val) / 255.0
zero_point = -round(min_val / scale)
quantised = np.clip(
np.round(embeddings / scale) + zero_point,
0, 255
).astype(np.uint8)
return quantised, scale, zero_point
def dequantise_from_int8(
quantised: np.ndarray,
scale: float,
zero_point: float
) -> np.ndarray:
"""Reconstruct approximate float32 embeddings from int8."""
return ((quantised.astype(np.float32) - zero_point) * scale)
Бінарная квантызацыя
Бінарная квантызацыя ідzie ўсё даўжэй, зменшаючы кожны вымір да адного біта, які проста фіксуе, чы было первіснае значэння float паазитным чы негатыўным. Чым запас памяці зменшваецца прыблізна на 97% па апэраванню з float32. Паколькі представленне больш не ёсць цэлазначальным, схожасць вырачваецца за дапамою расстояння Хэммінга заместо косай схожасці.
Гэты метод работае наўзям лепша з моделямі, чыя распады выходных значэнняў сама па сабе ўзорваныя, такім чынам, для будзь-каго вхіднага даных прыбліжна палова вимера знаходзіцца по кожнай стороне нуля. Якщо вимеры моделі асиметрычныя, а не збалансаваныя, бінарная квантызацыя спрычыняе значна большую втрату якосці. Kohere створыла Embed v3, взяўшы гэтыя абмежэння пад увагу, і публікаваная Anthropic ацэнка гэтай моделі паказвае знижэння якосці аднароўна нижыя 1% паўтарэння даных, а таксама зменшэнне прыемкі на 97% у ўмовах тэставання. Спрыймайце гэтыя цифры як пачатковую точку, а не як гаранцію, і пераканайцеся ў ўсаснаемасці ў сваём скарбніку даных, прычым ужываючы іх.
import numpy as np
def quantise_to_binary(embeddings: np.ndarray) -> np.ndarray:
"""
Binary quantisation: positive dimensions become 1, negative become 0.
Packs 8 dimensions per byte using numpy packbits.
"""
binary_matrix = (embeddings > 0).astype(np.uint8)
return np.packbits(binary_matrix, axis=1)
def hamming_similarity(
query_binary: np.ndarray,
corpus_binary: np.ndarray
) -> np.ndarray:
"""Compute normalised Hamming similarity for binary embeddings."""
n_bits = corpus_binary.shape[1] * 8
xor = np.bitwise_xor(
query_binary,
corpus_binary
)
hamming_distances = np.unpackbits(xor, axis=1).sum(axis=1)
return 1.0 - (hamming_distances / n_bits)
У прымэтнай експлуатацыі бінарная квантызацыя зазвычай выкорыстоўваецца як першы этап дваэтапнага процесу пошуку: бінарны індэкс дапамагае шыбкаа аднаходзіць вялікі набор кандыдаатав, а наступны этап з высокай точнасцю перарабатвае тыя найлепшыя рэзультаты. Такая схема дазволяе захаваць большую частку месца для зберагчыка, адночаса вярнуўшы точнасць у тых моментах, якія маюць значэнне — у фінальным кроку ранжыравання.
Тонкая наладка для RAG спецыфічнага домэна
Тонкая наладка є правым рашэнням, калі вы паўнастая пераканаліся, што універсальныя эмбедынгі справа пры нейкіх задачах з вашымі дадзеннямі. Мэтай є научыць модель, што слоўнік, скраценняя і канцэптуальныя зв’язкі, спецыфічныя для вашага домэна, знаходзяцца блізка адзін да другога у сэмантычным прасторы.
Тонкая наладка не завжды ўскладнена і не завжды являецца правильным рашэнням. Якщо проблемы з выкарыстоўванням дадзеных выніклі з-за некальканоснае разбівкі інфармацыі, як гаворылася раней у гэтай серыі, наладка моделі эмбеддінга не дапаможа. Якщо асалодзіцца проблема тым, як настаўленае ранжаванне або як ствараюцца запиты, тонкая наладка абсалютна не паводзіцца да правильнага слоя системы. Перш чым вкладаць рэсурсы, разбейце своія працульнікі з выкарыстоўванням дадзеных па типах запытаў, каб пераканацца, дзе самэй настаўлена проблема.
Калі загальныя эмбеддінгі не работаюць
У спецыфічных ситуацыях банкавага RAG калькі праўільных патэранаў працульнікаў правяжуць неабходнасць інвестырацій у тонкую наладку:
Скорачэнні, спецыфічныя для певных галерэй, часта неправачна адначытваюцца. Модель загальнага прызначэння можа сувязваць „NPA“ з Асоцыяцыёю нацыянальных паркаў у заместо панэфектываўных актываў, а „KYC“ можа толькі слабка сувязвацца з концэпцыямі салідарнасці та інтеграцыі, якія на самай працоўнае выражаюцца ў банкавых запытках.
Сувязі межаў супакойных концэпцый у розных дасягненнях зникаюць. Пошук фразы „палаткі рэструктурызацыі ляйкаў“ должен адкрываць тэксты правілаў пра „рамкі маніпуляцый ляйкаў“, але модель, трэнаваная пераважна на загальным веб-контэнце, можа ніколі не бачыць гэтыя фразы ў такім сабліжным выражэнні, каб стварыць такі мост.
Коды регуляцыйных актов і ідэнтыфікаторы не адказваюць належным чынам. Нумеры версій правілаў, коды регуляцыйных актов і пазнакі юрисдыкцыі должны значна вплываць на ранжыраванне, пры тым часу звычныя моделі анкорацый часто спрацоўваюць з імі як з токенамі нізкай цэнны, якія не несу значных семантычных сигналоў.
Чысла-прагі тыраюць свой контэкст регулювання. Такі показнік, як „10 мільйонаў еўро“, выказаны самастойна, не должен адразу падходзіць пад запит пра „автарытет на затверджэння вялікіх рызыкаў“ — такая зв’язок утвараецца толькі тады, калі модель была натрэнавана на данныя данай галіны, якіе прыяўляюць гэтае чысло да його регуляцыйнага значэння.
Стварэнне пар для натрэнавання на данныях, спецыфічных для данай галіны
Тонкая наладка модэляў sentence-transformers пад кантрастным цылям залежыць ад пазитыўных пар: прыкладаў, якія спароўнуюць запит з абдзялкам, які модэль должен навучыцца спрыяваць як скарыстаны. Негатыўныя прыклады можна выбраць вручную або автаматычна запрашаць з асупраўнага корпусу.
У контэксте RAG у банкавасці гэтыя пазитыўныя пары можна склadaць з калькі практычных джэрел:
Ўжытковыя наборы запытаў і адпаведных адказаў, якія вже створеныя камандамі з дапэўнення састаноўкі і кредытавання, дзе кожны запит прыўязаны да свайго адпаведнага абдзялка.
Прыродная структура дакументаў з правіламі, дзе загалоўкі, спароўнаныя з абдзялкамі пад імі, ствараюць готовыя пазитыўныя прыклады.
Запыты аналітыкаў, зафіксаваныя ў журнале, спароўнаныя з абдзялкамі, якія былі фактычна запрашаныя, калі адказ быў правільным.
Машынныя запиты, створаныя LLM для кожнага фрагмента тексту, дзеяць на адной з тых жа фрагментаў як на пасунке, які лягае ў асоцыяцыю.
Серед гэтых спосабаў стварэння сінтэтычных запытаў зазвычай ёсць найболей прыемны варыянт, калі вже існуе мала колькасць пазначаных дадзенняў.
from openai import OpenAI
import json
from typing import List, Dict
client = OpenAI()
def generate_training_queries(
chunk: str,
chunk_metadata: Dict,
n_queries: int = 3
) -> List[Dict]:
"""
Generate synthetic query-passage pairs for fine-tuning.
The chunk itself is the positive passage for each generated query.
"""
prompt = f"""You are generating training data for a banking RAG system.
Given the following policy passage, generate {n_queries} realistic questions
that a credit analyst, compliance officer, or relationship manager might ask
that this passage directly answers. Each question should use natural language
and may use different terminology than the passage itself.
Passage:
{chunk}
Return a JSON array of objects with keys "query" and "difficulty".
Difficulty should be "narrow" (single fact) or "synthesis" (multiple facts).
Return only the JSON array, no other text."""
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": prompt}],
response_format={"type": "json_object"}
)
try:
result = json.loads(response.choices[0].message.content)
queries = result.get("queries", result) if isinstance(result, dict) else result
return [
{
"query": q["query"],
"passage": chunk,
"document_id": chunk_metadata.get("document_id"),
"chunk_id": chunk_metadata.get("chunk_id"),
"difficulty": q.get("difficulty", "narrow")
}
for q in queries
]
except (json.JSONDecodeError, KeyError):
return []
Контрастная падготовка з апыткамі у стыле трыйцяты
Найэфектывнейшы цялевы пункт падготовкі для модэляў упакоўкі, адзначаных на выкарыстоўванне для пошуку, — это контрастнае навчанне, якое выкарыстоўвае або негатыўныя прыклады з той жа пакетнай групы, або спецыяльна выбраныя складныя негатыўныя прыклады. Sentence-transformers падтрымлівае гэты падход за дапамогою MultipleNegativesRankingLoss, які выкарыстоўвае кожны другі прыклад у пакетнай групе для навчання як неявны негатыўны прыклад для данай пары „анкер-пасунк“.
from sentence_transformers import SentenceTransformer, InputExample
from sentence_transformers.losses import MultipleNegativesRankingLoss
from torch.utils.data import DataLoader
from typing import List, Dict
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
def build_training_examples(
pairs: List[Dict]
) -> List[InputExample]:
"""
Convert query-passage pairs into InputExample objects.
MultipleNegativesRankingLoss expects (anchor, positive) pairs.
Negatives are sampled automatically from other items in the batch.
"""
return [
InputExample(texts=[pair["query"], pair["passage"]])
for pair in pairs
if pair.get("query") and pair.get("passage")
]
def fine_tune_embedding_model(
base_model_name: str,
training_pairs: List[Dict],
output_path: str,
epochs: int = 3,
batch_size: int = 16,
warmup_steps: int = 100
) -> SentenceTransformer:
"""
Fine-tune a sentence-transformers model on domain-specific query-passage pairs.
base_model_name: HuggingFace model identifier or local path.
training_pairs: List of dicts with "query" and "passage" keys.
output_path: Directory to save the fine-tuned model.
"""
model = SentenceTransformer(base_model_name)
logger.info(f"Loaded base model: {base_model_name}")
logger.info(f"Training on {len(training_pairs)} query-passage pairs")
examples = build_training_examples(training_pairs)
loader = DataLoader(examples, shuffle=True, batch_size=batch_size)
loss = MultipleNegativesRankingLoss(model)
total_steps = len(loader) * epochs
logger.info(f"Training for {epochs} epochs, {total_steps} total steps")
model.fit(
train_objectives=[(loader, loss)],
epochs=epochs,
warmup_steps=warmup_steps,
output_path=output_path,
show_progress_bar=True,
checkpoint_path=output_path,
checkpoint_save_steps=len(loader)
)
logger.info(f"Fine-tuned model saved to: {output_path}")
return model
Рэзультаты тэставання: банкавы корпус да і пасля фін-налашоўкі
Разглянем тэст на адзысканне інфармацыі, які выпалоўваецца проты корпаратывнага кредытнага правілніка, складанага з 847 фрагментаў, взятых з дакументаў правілніка, матрыц адзынавання, практык падглęбленай перапрацоўкі інформацыі та рэкамендацый з борьбы з валютнымі праваляваннямі. Набор для ацэнкі складаецца з 120 запытанняў, якія належаць да чатырох категорый: вузкія фактычныя запытанні, запытанні з прагамі, запытанні на адпаведнасць колькам доказам і синтэтычныя запытанні.
Прыжак у рэзультатах, атрыбутных дапрацоўкам домэна, ўпадае наявным пры запытках у стылі «прага», напрыклад, калі запытаецца пра прагу затверджэння, якая застосоўваецца да высокарызыковых корпоратыўных кліянтав ЕС. Самэ тут банкавскія скраценні і терміны регулявання найбольш сутэчна адрозніваюцца ад таго, што бачыла універсальная модель падчас прадзейнаўкі, таму заполненне гэтага прыемку слоў дае самы большы прырост у рэкале. Запыткі, якія выкарыстоўваюць калькуляцыю з колькіх джэрел і синтэзу, менш павышаюць своі рэзультаты ад самых дапрацоўках домэна, але значна павышаюць іх, калі да ўжо існуючага падходу дадаецца гібрыдны спосаб выкарыстоўвання інфармацыі.
Спрытваце гэтыя цыфры як прыклад таго, кашто можа дае правяльна рэалізаваная праця з дакладнай налаштовкай на належна пазначаная банкавая набора дадзеных, а не як абяцкае паветранне. Склад вашага сабору дадзеных, сумешанне запыткаў і тачнасць пазначэнняя зменяюць рынкі. Найважлівейша — гэта разбіўка якосці аднаходжання дадзеных па типах запыткаў, а не выкладчыцтва аднаго сумаванага рэйтингу, адколі кожны тип запыткаў часта не вяршыцца з разных прычын.
Аспекты працэздатнасі для масовага включэння дадзеных
Перадача 100 000 частак через API для включэння дадзеных па адны запытак за раз ёсць і сповольная, і дорогая. У прыладах высокага класу для включэння дадзеных іх уваходы об’едначаюцца ў пакеты, што дазволяе падняць працэздатнасць, даглядаць за сябе па лімітах частоты, швядка вярнуцца да роботы пасля аберанняў і забезпечыць дэтэрміністычнае стваранне выходных дадзеных.
Масовая обработка через API-прадаўцоў
Канцэнтры OpenAI дазваляюць прымкнуць да 2,048 вхідных дадзеных за адны запыт. Канцэнтры Cohere дазваляюць прымкнуць да 96 тэкстоў за запыт, якщо толькі не вы используеце ўпорядкованы API для большых задач. Выконанне інферэнсу локальна з sentence-transformers дае можлівасць налаштаваць розмеры пакетаў, якія лімітуюцца толькі наявной памятай GPU.
import time
import logging
from typing import List, Optional
from openai import OpenAI, RateLimitError, APIError
logger = logging.getLogger(__name__)
client = OpenAI()
def embed_in_batches(
texts: List[str],
model: str = "text-embedding-3-large",
batch_size: int = 512,
max_retries: int = 3,
retry_delay: float = 2.0,
dimensions: Optional[int] = None
) -> List[List[float]]:
"""
Embed a large list of texts using batched API calls with retry logic.
texts: Pre-chunked text strings. Caller is responsible for ensuring
no text exceeds the model's token limit.
batch_size: Number of texts per API call. Stay well below the API limit
to avoid hitting per-request token limits.
dimensions: Optional Matryoshka dimension reduction for supported models.
"""
all_embeddings: List[List[float]] = []
total_batches = (len(texts) + batch_size - 1) // batch_size
for batch_idx in range(0, len(texts), batch_size):
batch = texts[batch_idx: batch_idx + batch_size]
current_batch = batch_idx // batch_size + 1
logger.info(f"Embedding batch {current_batch}/{total_batches} "
f"({len(batch)} texts)")
kwargs = {
"input": batch,
"model": model
}
if dimensions is not None:
kwargs["dimensions"] = dimensions
attempt = 0
while attempt < max_retries:
try:
response = client.embeddings.create(**kwargs)
# Preserve input order: API returns items sorted by index
sorted_items = sorted(response.data, key=lambda x: x.index)
all_embeddings.extend([item.embedding for item in sorted_items])
break
except RateLimitError:
attempt += 1
wait = retry_delay * (2 ** attempt)
logger.warning(f"Rate limit hit on batch {current_batch}. "
f"Waiting {wait:.1f}s before retry {attempt}/{max_retries}")
time.sleep(wait)
except APIError as e:
attempt += 1
logger.error(f"API error on batch {current_batch}: {e}. "
f"Retry {attempt}/{max_retries}")
if attempt >= max_retries:
raise
time.sleep(retry_delay)
logger.info(f"Embedding complete. Total vectors: {len(all_embeddings)}")
return all_embeddings
Выконанне інферэнсу локальна з sentence-transformers
Дзеяныя організацыі сталкнуліся з абмежэннямі ў розмешчанні дадзеных, якія не дазволяюць адправляць дакументы палітыкі да зовнішняго API. У такіх случаях локальны інферэнс з sentence-transformers є аптовым рашэнням.
from sentence_transformers import SentenceTransformer
import numpy as np
from typing import List, Optional
import logging
logger = logging.getLogger(__name__)
class LocalEmbeddingPipeline:
"""
Production-ready local embedding pipeline using sentence-transformers.
Suitable for data-residency-constrained banking environments.
"""
def __init__(
self,
model_name_or_path: str,
device: str = "cpu",
batch_size: int = 64,
normalise: bool = True
):
self.model = SentenceTransformer(model_name_or_path, device=device)
self.batch_size = batch_size
self.normalise = normalise
self.device = device
logger.info(f"Loaded model: {model_name_or_path} on {device}")
def embed(
self,
texts: List[str],
show_progress: bool = True
) -> np.ndarray:
"""
Embed a list of texts. Returns an (N, D) numpy array.
Normalises to unit length if normalise=True (required for cosine similarity).
"""
embeddings = self.model.encode(
texts,
batch_size=self.batch_size,
show_progress_bar=show_progress,
normalize_embeddings=self.normalise,
convert_to_numpy=True
)
logger.info(f"Embedded {len(texts)} texts. "
f"Output shape: {embeddings.shape}")
return embeddings
def embed_query(self, query: str) -> np.ndarray:
"""Embed a single query. Returns a 1D array."""
return self.embed([query], show_progress=False)[0]
Перакананне дужыні токэнаў прыямо перад імбеддаваннем
Якща фрагмент тэксту перавышае ліміт токенам да модэлі, ён адцярваны без паведамлення. У корпусе правіл така тыхая адцярвання можа пазбіраць саме ту частку або числовы порог, якія спачатку зробілі фрагмент значным для запошуку. Пераканальванне колькасці токенам прыяўленні раней, чым воны будуць выкарыстоўваны, дапамагае запобегчы гэтай проблеме ўсё раней, чым яна дасягне вашага індэксу.
from transformers import AutoTokenizer
from typing import List, Tuple
import logging
logger = logging.getLogger(__name__)
def validate_chunk_lengths(
chunks: List[str],
model_name: str,
max_tokens: int,
truncation_strategy: str = "warn"
) -> Tuple[List[str], List[int]]:
"""
Validate that all chunks are within the model's token limit.
truncation_strategy:
"warn" - Log a warning for oversized chunks and include them (will be truncated by model).
"skip" - Remove oversized chunks and return only valid ones.
"raise" - Raise ValueError on the first oversized chunk.
Returns (validated_chunks, oversized_indices).
"""
tokeniser = AutoTokenizer.from_pretrained(model_name)
oversized = []
for idx, chunk in enumerate(chunks):
token_count = len(tokeniser.encode(chunk, add_special_tokens=True))
if token_count > max_tokens:
oversized.append(idx)
msg = (f"Chunk {idx} has {token_count} tokens, "
f"exceeds model limit of {max_tokens}. "
f"First 80 chars: {chunk[:80]!r}")
if truncation_strategy == "raise":
raise ValueError(msg)
else:
logger.warning(msg)
if truncation_strategy == "skip" and oversized:
valid = [c for i, c in enumerate(chunks) if i not in set(oversized)]
logger.info(f"Removed {len(oversized)} oversized chunks. "
f"{len(valid)} chunks remain.")
return valid, oversized
return chunks, oversized
Даўленне метаданых і інформаціі пра паводзжэнне да эмбеддынгам
Сама по сабе необработаная вектарная структура эмбеддынга недастатня для правільнай роботы системы RAG. Кожны вектар патрэбуе даданых метаданых, якія будуць даўлены разам з ёю, каб на стадіях запошуку, перыярктування і генеравання было можна выявіць месца паходжэння контэнту, застосавіць правы на доступ, обмежыць рэзультаты за юрысдикцыяй і перакінуцца на автантыфікованный даследжвальны документ.
Вяртаючыся да сцэнарыя праекту кредыту у розмерзе 12 мільйонаў еўра, кожны вбудоўваны фрагмент павінен, прынеймна, мяць тыя поля, якія паказаны ўжо:
from dataclasses import dataclass, field
from typing import Optional, List
import uuid
@dataclass
class EmbeddedChunk:
"""
Production embedding record for a banking policy RAG system.
The vector enables retrieval. The metadata enables everything else.
"""
# Vector
vector: List[float]
vector_dimensions: int
embedding_model: str
embedding_model_version: str
# Content
text: str
content_type: str # "narrative", "table_row", "proposition", "image_description"
# Provenance
document_id: str
document_version: str # e.g. "7.2"
policy_id: Optional[str] # e.g. "CRD-EU-047"
jurisdiction: Optional[str] # e.g. "EU"
effective_date: Optional[str]
# Chunk structure
chunk_id: str = field(default_factory=lambda: str(uuid.uuid4()))
parent_id: Optional[str] = None
section: Optional[str] = None
page_number: Optional[int] = None
source_artifact_path: Optional[str] = None # path to original image/table
# Access control
classification: str = "INTERNAL" # "PUBLIC", "INTERNAL", "CONFIDENTIAL"
permitted_roles: List[str] = field(default_factory=list)
# Indexing
indexed_at: Optional[str] = None
indexing_pipeline_version: Optional[str] = None
Стабільна выкарыстоўванне гэтых метадаў на кожным этапе процесу — ад разбівання на фрагменты да ўбудоўвання і далей да індексавання вектараў — не толькі є правильной практыкай. У регулююцамым банкавым сераўысе отрыманне тэхнічна правильнай адпаведзі, якая базуецца на застарэлай версіі правіла, лягчае на адмову ад павіннасэй. Задача вектара — знайсці фрагмент; задача метадаў — паказаць, што фрагмент прыйшоў з правильнай, актуальной версіі выхіднага матэрыялу.
Ацэнка якосці убудоўвання
Аб’явныя табліцы рэйтингаў, такія як MTEB, паказваюць загальныя балы за пошук у шырокамасштабных наборах акадэмічных данных. Гэтыя цифры дапамагаюць адсіяваць моделі, якія явна паспелююць слабка. Аднак яны не ўсунуліся пры выборы найкращай моделі для спецыялізаванага корпусу, такога як внутрашняя бібліятэка правілаў банку.
Едынай важлівай меркай є тое, як модель паспелюе з вашымі сабеўласненымі дакументамі, з вашымі сабеўласненымі запитамі, пры чым балаванне водзіцца з урахоўваннем вашых сабеўласненых адгуківаў пра релевантнасць.
Стварэнне набора для ацэнкі пошуку
Набор тестаў для пошуку, створаны для практыкі RAG у банкавасці, должен аб’являць калькі разных типаў запытанняў:
Часткавыя фактычныя запытанні, якія адносуюцца да адного автарытатывнага фрагмента, напрыклад, запытанне пра тое, як часта высакарызыковыя корпоратыўныя кліенты должны праходзіць свой годовы адзор.
Запытанні, які базуюцца на праге і спаўнаюць адпаведную числовую умоўу з правіламі керавання, прыкладом якога є запытанне пра тое, якая інстанція затверджэння неабходна, калі активы еўропейскай корпаратывной установы перасягаюць 10 мільйонаў еўро.
Запытанні, які выкарыстоўваюць калькульнае доказаванне, дзе цэлы адказ залежыць ад агульнага аналізу колькасці дакументаў, прыкладам якога є запытанне пра тое, якія пераказы неабходна адрабаты пры падачы запрошэння на корпаратывны кредыт высокага рызыку.
Сінтэтычныя запытанні, якіе аб’яднаюць інфармацыю з калькосці разных частак, прыкладам якога є запытанне пра аб’ёмныя выявленні рамкі кантролю проты валютных афераў, якія дэйстуюць у сфере корпаратывных позык высокага рызыку.
Запытанні, якіе аб’яднаюць інфармацыю з разных дакументаў, актуальныя там, дзе правілы цінуецца ў адносах да іншых дакументаў.
import numpy as np
from typing import List, Dict, Set
def recall_at_k(
retrieved_ids: List[str],
relevant_ids: Set[str],
k: int
) -> float:
"""
Compute Recall@k for a single query.
relevant_ids is the ground truth set of chunk identifiers.
retrieved_ids is the ordered list of retrieved chunk identifiers.
"""
if not relevant_ids:
return 0.0
top_k_retrieved = set(retrieved_ids[:k])
return len(top_k_retrieved & relevant_ids) / len(relevant_ids)
def mean_reciprocal_rank(
retrieved_ids: List[str],
relevant_ids: Set[str]
) -> float:
"""Compute MRR for a single query."""
for rank, chunk_id in enumerate(retrieved_ids, start=1):
if chunk_id in relevant_ids:
return 1.0 / rank
return 0.0
def evaluate_embedding_model(
model_name: str,
evaluation_queries: List[Dict],
corpus_chunks: List[Dict],
k_values: List[int] = [1, 5, 10, 20]
) -> Dict:
"""
Evaluate an embedding model on a labelled retrieval dataset.
evaluation_queries: List of dicts with "query" and "relevant_chunk_ids" keys.
corpus_chunks: List of dicts with "chunk_id" and "text" keys.
Returns per-query-type and aggregate retrieval metrics.
"""
from sentence_transformers import SentenceTransformer
model = SentenceTransformer(model_name)
corpus_texts = [c["text"] for c in corpus_chunks]
corpus_ids = [c["chunk_id"] for c in corpus_chunks]
corpus_embeddings = model.encode(corpus_texts, normalize_embeddings=True)
results_by_type: Dict[str, List] = {}
all_recall: Dict[int, List[float]] = {k: [] for k in k_values}
all_mrr: List[float] = []
for query_item in evaluation_queries:
query = query_item["query"]
relevant = set(query_item["relevant_chunk_ids"])
query_type = query_item.get("query_type", "unspecified")
query_embedding = model.encode(query, normalize_embeddings=True)
scores = corpus_embeddings @ query_embedding
ranked_indices = np.argsort(scores)[::-1]
retrieved = [corpus_ids[i] for i in ranked_indices]
mrr = mean_reciprocal_rank(retrieved, relevant)
all_mrr.append(mrr)
for k in k_values:
r = recall_at_k(retrieved, relevant, k)
all_recall[k].append(r)
if query_type not in results_by_type:
results_by_type[query_type] = {"mrr": [], "recall": {k: [] for k in k_values}}
results_by_type[query_type]["mrr"].append(mrr)
for k in k_values:
results_by_type[query_type]["recall"][k].append(
recall_at_k(retrieved, relevant, k)
)
aggregate = {
"model": model_name,
"n_queries": len(evaluation_queries),
"mrr": float(np.mean(all_mrr)),
"recall": {k: float(np.mean(all_recall[k])) for k in k_values}
}
per_type = {
qt: {
"mrr": float(np.mean(data["mrr"])),
"recall": {k: float(np.mean(data["recall"][k])) for k in k_values},
"n_queries": len(data["mrr"])
}
for qt, data in results_by_type.items()
}
return {"aggregate": aggregate, "by_query_type": per_type}
Паракаметры адзыявлення дакументаў не трэба ацэніваць ізольавана, адоколе не берэцася ў расчыт тое, як яны вплываюць на якасць фінальнай адпаведзі. Падазроўваючы, што паракаметр Recall@5 падняўся на тры балы, але частка адзыявленых майже ідэнтычных фрагментаў вырасла на 30 працэнтав — такі компроміс можа насправды не дапамогчы ШІ, адколе падача яму трохі ідэнтычных тэкстаў замест адного кориснага не дадае рэальных доказаў.
Правы падход — ацэніваць весь ланцуг, ад пачатковага запиту да фінальнай адпаведзі, якая прадаўаецца корыстніку. Роль модэлю эмбеддзінга складаецца толькі у тым, каб прадаць правильныя доказы ў вікна контэксту. Чы гэтыя доказы прыводзяць да адпаведзі, якая ў той жа час є точной і адпаведнай, вяліцца кожным наступным этапам.
Інфраструктура у вачынку эмбеддзінгу
Калі частка завершыць процес перадачы через паўтаральную сістэму, яй неабходна выйсці з іншай стороны з прыўязаным вектаром, абавесцянай метадаў і стабільным ідэнтыфікаторам, який дазволяе пазначыць, апдэйтаваць чы выдаліць яе пазней, не паспяшаючы да паказання некоректных данных суседніх елементаў у індэксе.
Это частка інфраструктуры, а не разовы скрыпт. Паўтаральныя сістэмы для прыемлівых банкавскіх средаў высокага ступеня адпаведнасці трэбуюць наступнага:
- Ідэмпотентнасць. Якщо частка будзе перапрацавана знову чераз змену версійы модэлю, гэта павінна заменіць існуючы запис, а не стварыць дублікат.
Нічога з гэтага не ўзято за апцыю, калі вы вже працуеце у працэсе. Гэтыя якосці ўскладнююць разлік межаў пайплайна, яны проста працуюць у дэмаверсіі, і таго, які можна эксплуатаваць, аудытаваць і падтрымваць працэс з часам у регулюючайся средзе.
Працяг чысткі пра прапанову кредыту у розмяре 12 мільйонаў еўра
Першыя запитанні менеджера па адносах не змяніліся: якія правы на затверджэння трэба для гэтага угоды, і якія контралі трэба працягнуць, перш чым яе можна будзе падаць?
- У частцы 4 было рассказана, як проектаваць елементы выкарыстоўвання, якія зберагаюць тые адпаведзіць у ўсунутай форме: пункт правіл EDD, рядок матрыцы затверджэння, які вказвае на автантыя GCC, і дыяграма рабочага прайсупутку, якае паказвае контрольны моменты перед адправкай.
- У гэтай частцы мы пераклалі тые елементы выкарыстоўвання у вектары, якія можна шукати, выкарыстоўваючы модель, якая была працавана на тэрміналагіі, спецыфічной для банкавасці, налаштованую так, каб ёй было зрозумела связь регуляторных скрацанняў з іх контекстам салідарнасці, а таксама даданыя пра паводзжэнне, якія дазволяюць пазней падтвердзіць версію правіла і юрысдыкцыю пад час выкарыстоўвання.
Калі надходзіць запит, вектарны індэкс знаходзіць рядок матрыцы затверджэння, яка охопляе рызыкаваныя актывы на суму падаючую за межы 10 мільйонаў еўро, пункт EDD і дыяграму, якая паказвае последовасць кантролю — і вяртае ўсё гэта разам з метаданымі, якія паўтараюць, што всё трое належыць да полісы CRD-EU-047, версія 7.2, пад юрысдикцыяй ЕС, чыныстая з 15 студзеня 2026 года.
Тое, што доходзіць да слою генеравання, — это падтверджэнні, якія ўсё правильныя, цэлыя і можна праследаваць.
Это і є разліка межа практыкай умяшчання, якая проста завантажвае фрагменты ў вектарную базу дадзеных, і тойя, якая зберагае ўсё неабходнае для атрыбутыўных і пераверканых адказаў.
Перш чым перайсці да вектарнага індэксавання: чыртак перапісу
Перш чым даваць фрагменты, умяшчаныя ў базе дадзеных, у вектарны індэкс, паўтарыць наступнае:
- Чы правільна настаўлена параметр типу вхідных дадзеных у модэлі для інкорпоравання як пад час запытань на індексацыю, так і пад час самых запытань? Такая несувязнасць пасляўліва паграджае тачнасць адзыскання інформацыі.
- Чы довжына токена кожнага фрагмента была пераканалена прыяўленнею ў модэль? Таямніца скорачэння зменшае тое, што на самай працэ ўтварыўся текст, без выяўлення якіх-леба адказак у всім прыемніку.
- Чы кожны вектарны запис уключае всю інформацыю пра паводзкі — версію дакумента, ID правіл, юрыдычную тэриторію і дату набыця чыннасці?
- Чы модэль для інкорпоравання была практычна працавана з вашым сабскладным корпусам дадзеных і патэрнамі запытань, а не выбрана выключна на адваге рэйтынгаў у публічных тэстах?
- Якщо вы налаштавалі модэль, чы у вас є паказаткі колькасці адзысканых рэзультатаў да і пасля на незалежным наборы запытань?
Якщо чаго-небудзь з гэтага не будзе, вектарны індекс не зможа гэта выяўіць — ён проста будзе зберагаць усё, што яму даюць. Ніч у базе дадзеных не пакажае проблемы з вектарам, створаным з скрашанага фрагмента, запитам з неправым типам дадзенняў або фрагментам, які быў уключаны з версіяй модэлю, несувязанай з рэштой індекса. Гэтыя прычыны не адзначаюць ся самі; яны з’яўляюцца пазней у вачынку проблем з якасцю выкарыстоўвання дадзеных, якія ззаўні выглядаюць як проблемы з самым LLM.
У пачцы матэрыяла раней, у частцы 4, было показана, як ператварыць чыстыя дакументы на елементы для пошуку, захаваючы ў той жа час іх структуру недзеяным. У гэтай частцы паказана, як ператварыць гэтыя елементы для пошуку на векторы, якія можна шукати, захаваючы ў той жа час іх паходжэнне.
Далей выклікаецца тое, дзе гэтыя векторы насправды зустрэчаюцца з індексам: інтэнсыўны пошук за векторамі, рыхлы пошук, гібрыдныя падходы, апроксиматывальныя алгорытмы найбліжэйшых суседаў, а таксама фільтрацыя метаданых, якая вяршыць выбір тых вектораў, якія ўзагалі будуць включаныя у процес пошуку.
Спадневаная літэратура
- Архітектура для прымэнных корпаратыўскіх агентных систем AI — Дзеянне пра ключовыя слоі, стратэгіі памяці, методы адзыскання інфармацыі та правіла, неабходныя для пераходу агентных систем AI з пратыпаў у надзеяныя прымэнные корпаратыўскія системы.
- Адказы на пытанні пра вектарныя базы дадзэнняў: двойнік за RAG і пошук у AI — Дзеянне пра тое, як вектарныя базы дадзэнняў ператвараюць тэкст у вектары, апрацоўваюць сэмантычны пошук і праграмы RAG, а таксама якія ролі ўплываюць у практычныя прыклады использовання AI, такія як рэкамендацыі.