Mūsdienās par RAG dažkārt runā nievājoši: vieni uzskata, ka tas ir pavisam vienkāršs (sākt tiešām ir viegli, bet mērogošana gan ir sarežģītāka), savukārt citi domā, ka to aizstājušas „aģentiskas sistēmas” (lai gan daudzos gadījumos, parokot dziļāk, tās drīz vien sāk ļoti atgādināt RAG…).
Šajā emuāra ierakstā ar dažiem praktiskiem piemēriem parādīsim, kā risinām izplatītas problēmas, proti:
Jaukta teksta un skaitlisku datu apstrāde un iemesli, kādēļ tā apgrūtina primitīvu RAG: atslēgvārdi pārklājas, bet skaitļiem nav semantiskas nozīmes.
Kādēļ palīdz iegultņu veidošana vispirms no kopsavilkuma: katram fragmentam ģenerējiet īsu aprakstošu kopsavilkumu un pēc tam ieguliet to un veiciet tajā meklēšanu.
Kā ģenerēt kontekstuālus kopsavilkumus: iekļaujiet pamatdokumenta kontekstu, lai varētu atšķirt līdzīgas uzbūves statistikas datus.
Kad izmantot kodu un Pydantic modeļus: ja saturs jāsaglabā precīzi, uzticamības labad apvienojiet pielāgotu kodu un/vai Pydantic modeli ar LVM izsaukumiem.
Pamatprincipi
RAG sistēmas nodrošina gan atbalsta robotu, gan iekšējo zināšanu asistentu darbību.
Sistēmas pamatā parasti veic šādas darbības:
Sadaliet fragmentos avota dokumentus
Ieguliet katru fragmentu vektoru telpā
Izgūstiet K atbilstošākos fragmentus vaicājuma izpildes laikā
Ģenerējiet atbildi, izmantojot šos fragmentus kā kontekstu
Populāri rīkkopumi — LangChain, LlamaIndex un OpenAI Filestore — šīs darbības padara gandrīz elementāras. Taču reālās datu apstrādes plūsmās nākas saskarties arī ar datiem, kas nav tikai blīvs teksts, un vienkāršam RAG ar tiem var rasties grūtības. Nākamajās sadaļās ar konkrētiem piemēriem aplūkosim datu radītos izaicinājumus un, pakāpeniski pieaugot sarežģītībai, izstrādāsim risinājumu.
Kad dati nav tikai teksts (un tas patiesībā nav retums)
Aplūkosim šādu datu fragmentu videospēles kontekstā:
JSON
Iegultnes darbojas, pateicoties apgūtajām vārdu attiecībām, kuru pamatā ir semantiskā nozīme un gramatika. Iepriekš redzamajos datos ir gan teksts, gan skaitļi, taču ārpus šī konkrētā konteksta skaitļiem nav nekādas saistības ar vārdiem. Tādēļ varētu teikt, ka šis datu fragments būtībā ir vairāku aprakstošu vārdu un tiem sekojošu nejaušu skaitļu kombinācija.
Tā nebūtu problēma, ja mūsu rīcībā būtu tikai šāda veida dati, jo vajadzīgo joprojām varētu izgūt pēc dažu pieejamo aprakstošo vārdu iegultnēm (vai vienkārši izmantot teksta pārveidi par SQL). Bet ko darīt, ja šis fragments ir paslēpies starp daudziem teksta ziņā blīviem fragmentiem, kuros parādās tie paši vārdi? Piemēram:
JSON
Tagad iedomāsimies, ka vēlamies izgūt atbildi uz jautājumu „What is the attack range with Draconic Ascension?”Ļoti iespējams, ka vajadzīgo fragmentu neizdosies izgūt, jo tas pazudīs starp citiem fragmentiem, kuros ir tie paši atslēgvārdi.
Problēmas pamatā ir tas, ka šos datu fragmentus nevaram pietiekami labi atšķirt, lai gan tajos par vienu tēmu sniegta dažāda veida informācija. Vai varam tos kaut kā bagātināt vai uzlabot? Protams, ka varam:smile:
Bagātiniet datus, tos apkopojot — jā, jūs izlasījāt pareizi
Tā vietā, lai tieši iegultu pašu fragmentu, varam vispirms ģenerēt kopsavilkumu, kurā aprakstīta datu būtība, un pēc tam iegult kopsavilkumu un veikt izguvi pēc tā. Ģenerēšanas posmā joprojām izmantotu ar kopsavilkumu saistītos sākotnējos datus.
Tātad abiem iepriekš parādītajiem fragmentiem varētu ģenerēt šādus kopsavilkumus:
Uzbrukuma attāluma, ātruma un bojājumu statistika (pēc noklusējuma un ar Draconic Ascension).
Draconic Ascension apraksts un informācija par to, tostarp aktivizēšanas nosacījumi, vizuālie efekti un leģenda.
Pēc tam papildinām arī vaicājumu, lai to „pielāgotu” kopsavilkumam. Piemēram, jautājumu „What is the attack range with Draconic Ascension?”pārveidotu par „What is the statistics of attack range with Draconic Ascension?”Tas ir īpaši svarīgi, ja izguves vaicājumus iesniedz lietotāji ārpus tehniskajām jomām un raksta ~~„brīvā stilā”~~ parastā cilvēku valodā, jo viņiem nav jāzina vai jādomā par to, kā RAG darbojas, lai maksimāli palielinātu precizitāti un pilnīgumu.


Neizraujiet neko no konteksta (tas vispār noder arī dzīvē)
Tagad aplūkosim nākamo situāciju: jāapstrādā milzum daudz vienādu datu fragmentu, piemēram, šādu:
Plain Text
Turpinot izmantot to pašu pieeju, iedomāsimies vaicājumu „what is character X’s attack range?”Ar tikko ģenerētajiem kopsavilkumiem tā būtu uz veiksmi balstīta minēšana, jo arī tie izskatītos ļoti līdzīgi. Kā tos varētu atšķirt?
Atbilde ir vienkārša: sniedziet kontekstu. Datu fragmentā varētu vienkārši iekļaut atsauci uz tā pamatdokumentu, piemēram, šajā gadījumā {”character”: “X”}. Tagad varētu precīzi izgūt pareizos datus par tēlu X pat tad, ja mūsu rīcībā būtu tādi paši dati arī par tēliem Y un Z.
Tomēr labāka un plašāk izmantojama pieeja būtu ģenerēt fragmenta kontekstuālu kopsavilkumu. Proti, tā vietā, lai ģenerētu tikai paša datu fragmenta kopsavilkumu, varētu ievadīt gan tā pamatdokumentu, gan fragmentu un ģenerēt vispārīgu kontekstuālu kopsavilkumu, kurā paskaidrots, kā fragments iekļaujas pamatdokumentā, piemēram:
Šajā fragmentā sniegta detalizēta … statistika par tēlu X. Fragments iekļaujas pilnajā dokumentā, parādot X priekšrocības uzbrukuma ātruma ziņā…
Šajā fragmentā sniegta detalizēta … statistika par tēlu Y. Fragments iekļaujas pilnajā dokumentā, parādot Y īpašās spējas uzlabotos rādītājus…
Šajā fragmentā sniegta detalizēta … statistika par tēlu Z. Fragments iekļaujas pilnajā dokumentā, parādot, ka Z rādītāji ir labi piemēroti tanka lomai komandu cīņās…
Šī metode (kuru daļēji iedvesmojis Anthropic) iepriekš aplūkotajam piemēram varētu šķist pārmērīga, tomēr tā ir ļoti efektīva fragmentiem, kurus „ārpus konteksta” varētu pārprast. Turklāt tā nodrošina vienotu pieeju visiem fragmentiem un ļauj uzturēt pārskatāmu izstrādes plūsmu.


Kad jābūt ~~kontroles maniakam~~ īpaši rūpīgam
Parasti saņemam pilnīgus datus un sadalām tos RAG sistēmai piemērotos fragmentos. Šajā piemērā parādīsim ko nedaudz atšķirīgu — dati jau ir sadalīti, taču sliktos fragmentos. Tās ir nejaušas loģiska fragmenta daļas, kas patiesībā atkal jāapvieno. Loģisks fragments ir satura daļa, kam dabiski jāatrodas kopā, piemēram, dokumenta apakšsadaļa vai saturiski vienota rindkopa.


Pirmajā mēģinājumā visus šos datus ievadījām LVM izsaukumā, lūdzām tos sagrupēt pēc modeļa ieskatiem un pēc tam atgriezt sagrupēto saturu. LVM ar to vajadzētu tikt galā diezgan labi, vai ne? Gan jā, gan nē.
Šajā un vairākos citos gadījumos esam konstatējuši, ka LVM mēdz darboties pavirši un nav uzticami, ja vajadzīgs pilnīgs un precīzs saturs, jo īpaši gara konteksta gadījumā. Un tas ir pilnīgi saprotami. Tomēr šajā konkrētajā lietojumā tas nederēja, jo mums bija vajadzīgs precīzs saturs vārds vārdā — bez kopsavilkumiem un neizlaižot nevienu sākotnējā satura daļu. Nedrīkstējām palaist garām nevienu detaļu.
Pozitīvais, protams, bija tas, ka modelis lieliski saprata sadalīto fragmentu semantiku un struktūru. Ja vien tas neatteicās precīzi atkārtot visu saturu. Sasodīts:/
Kā izmantot LVM stiprās puses, vienlaikus izvairoties no tā, ko tas nespēj paveikt uzticami? Vērsāmies pie sava vecā, labā drauga — koda (lasi: pielāgotas Python funkcijas). Un pavisam vienkārša Pydantic modeļa. Risinājums ir šāds:
Secīgi pārskatiet sadaļas, vienlaikus uzturot pašreizējo loģisko fragmentu
Katrā sadaļā vaicājiet LVM: vai šī sadaļa pieder pašreizējam loģiskajam fragmentam? Atbildei jābūt „jā” vai „nē” atbilstoši Pydantic modelim.
Ja atbilde ir „jā”, pievienojiet sadaļu fragmentam; ja „nē”, izvadiet pabeigto pašreizējo loģisko fragmentu un sāciet jaunu ar šo sadaļu.


Protams, šādi izmantojam nedaudz vairāk marķieru nekā tad, ja visu saturu apstrādātu vienā reizē, taču šajā lietojumā precīza satura saglabāšana bija galvenā prioritāte, tādēļ nelielās papildu izmaksas bija tā vērtas.
Šis ir ļoti vienkāršs risinājums, taču tas atbilst svarīgam principam: ja nepieciešama stingra precizitāte, nevajadzētu paļauties tikai uz LVM, jo tie pēc būtības ir varbūtiski.
Pielāgots kods un funkcijas kopā ar Pydantic modeļiem ļauj iegūt paredzamu un uzticamu rezultātu, vienlaikus pilnībā izmantojot LVM iespējas.
Ģeneratīvā MI risinājuma izstrāde ir vienlīdz liels izaicinājums gan inženierijas, gan MI jomā. Ceram, ka šie piemēri jūs iedvesmoja ķerties pie savu unikālo problēmu risināšanas. Lai uzzinātu vairāk par inženierijā balstītiem ģeneratīvā MI risinājumiem, lasiet mūsu emuāra ierakstu par maršrutētājā balstītu aģentisku sistēmu projektēšanu.