Sa ngayon, minsan ay hindi maganda ang tingin sa RAG, dahil iniisip ng ilan na napakasimple nito (madaling simulan, pero hindi madaling palawakin) o nalampasan na ito ng mga “agentic system” (na sa maraming kaso, kapag sinuri nang mabuti, ay agad ding nagmumukhang katulad ng RAG…).
Naglalahad ang blog na ito ng isa o dalawang detalyadong halimbawa upang ipakita kung paano namin hinaharap ang ilang karaniwang hamon, gaya ng:
Pangangasiwa sa pinaghalong text at numeric data at kung bakit hindi ito kaya ng payak na RAG: nagkakapatong ang mga keyword at walang semantikong kahulugan ang mga numero.
Kung bakit nakatutulong ang pagdisenyo ng summary-first embeddings: bumuo ng maikling mapaglarawang buod para sa bawat chunk, saka i-embed at i-query ang buod na iyon.
Paano bumuo ng mga buod na may konteksto: isama ang konteksto ng parent document upang malinaw na mapag-iba ang mga estadistikang magkakahawig ang anyo.
Kailan dapat umasa sa code at mga Pydantic model: kapag mahalaga ang eksaktong nilalaman, pagsamahin ang custom code at/o Pydantic model sa mga LLM call para sa pagiging maaasahan.
Mga pangunahing kaalaman
Pinapagana ng mga RAG system ang lahat mula sa mga support bot hanggang sa mga internal knowledge assistant.
Sa likod nito, karaniwan mong ginagawa ang sumusunod:
Hatiin sa mga chunk ang iyong mga source document
I-embed ang bawat chunk sa isang vector space
Kunin ang mga top-K chunk kapag may query
Bumuo ng sagot batay sa mga chunk na iyon
Ginagawang halos napakasimple ng mga popular na toolkit—LangChain, LlamaIndex, at Filestore ng OpenAI—ang mga hakbang na iyon. Ngunit sa mga pipeline sa totoong mundo, makakaharap ka ng data na hindi lamang siksik na text, at maaaring mahirapan dito ang karaniwang RAG. Sa mga susunod na seksyon, magpapakita kami ng mga kongkretong halimbawa ng mga hamon sa data at unti-unting bubuuin ang solusyon habang nadaragdagan ang pagiging kumplikado.
Kapag hindi lamang text ang iyong data (na karaniwan naman talaga)
Isaalang-alang ang sumusunod na data chunk sa konteksto ng gaming:
JSON
Gumagana ang embeddings dahil sa mga natutuhang ugnayan ng mga salita batay sa semantikong kahulugan at gramatika. Sa data sa itaas, magkahalo ang text at mga numero, at kapag wala sa eksaktong kontekstong ito, walang kaugnayan ang mga numero sa mga salita. Kaya masasabi nating ang data chunk na ito ay halos kumbinasyon ng medyo mapaglarawang mga salita na sinusundan ng ilang random na numero.
Hindi sana ito magiging problema kung ito lamang ang uri ng data natin, dahil maaari pa rin tayong kumuha batay sa embeddings ng iilang mapaglarawang salitang available (o gumamit na lang ng text-to-SQL). Ngunit paano kung nakabaon ang chunk na ito sa napakaraming chunk na siksik sa text at lumilitaw rin doon ang mga salitang ito? Halimbawa:
JSON
Ngayon, isipin nating gusto nating kunin ang “What is the attack range with Draconic Ascension?” Malamang na hindi natin makukuha ang nauugnay na chunk na gusto natin dahil natatabunan ito ng ingay mula sa iba pang chunk na may parehong mga keyword.
Ang pangunahing problema ay hindi natin mahusay na mapag-iba ang mga data chunk na ito, kahit na naglalaman ang mga ito ng magkakaibang uri ng impormasyon tungkol sa iisang paksa. Maaari ba natin itong pagyamanin o pahusayin? Siyempre naman:smile:
Pagyamanin ang iyong data sa pamamagitan ng pagbubuod—oo, tama ang nabasa mo
Sa halip na direktang i-embed ang chunk, maaari muna tayong bumuo ng buod na naglalarawan sa nilalaman ng data, saka i-embed at gawin ang retrieval batay sa buod. Sa hakbang ng pagbuo, gagamitin pa rin natin ang orihinal na data na naka-link sa buod.
Kaya para sa dalawang halimbawa ng chunk sa itaas, bubuo tayo ng mga buod na tulad nito:
Mga estadistika ng attack range, bilis, at damage (default at kapag may Draconic Ascension).
Paglalarawan at mga detalye ng Draconic Ascension, kabilang ang mga kondisyon ng activation, visual effect, at lore.
Pagkatapos, dinaragdagan din natin ang query upang “itugma” ito sa buod. Halimbawa, babaguhin natin ang “What is the attack range with Draconic Ascension?” at gagawin itong “What is the statistics of attack range with Draconic Ascension?” Lalo itong mahalaga kapag nagmumula ang retrieval query sa mga user na wala sa teknikal na larangan at nagtatanong sa ~~“freestyle”~~ normal na wika ng tao, dahil hindi naman nila kailangang malaman o alalahanin kung paano gumagana ang RAG upang i-maximize ang precision/recall.


Huwag alisin sa konteksto ang mga bagay (naaangkop din sa buhay sa pangkalahatan)
Ang susunod na sitwasyon ay ang pagharap sa napakaraming data chunk na magkakamukha, gaya ng nasa ibaba:
Plain Text
Kung susundin natin ang parehong paraan, isipin ang pagtatanong ng “what is character X’s attack range?” Magiging larong hulaan na nakabatay sa suwerte ang paggamit natin sa mga kabubuo pa lamang na buod dahil magkakamukha rin ang mga ito. Kaya paano natin sila mapag-iiba?
Simple ang sagot: magbigay ng konteksto. Maaari nating isama sa data chunk ang reference sa parent document ng chunk, halimbawa, {”character”: “X”} sa kasong ito. Sa gayon, tumpak na nating makukuha ang tamang data para sa tauhang X kahit mayroon din tayong parehong data para sa mga tauhang Y at Z.
Gayunman, mas mahusay at mas madaling ilapat sa iba’t ibang sitwasyon ang pagbuo ng buod ng chunk na may konteksto. Ibig sabihin, sa halip na buod ng data chunk lamang ang buuin, maaari nating ipasa ang parent document at ang chunk upang makabuo ng pangkalahatang buod na may konteksto. Isasama natin sa buod kung paano nauugnay ang chunk na ito sa parent document nito, halimbawa:
Nagbibigay ang chunk na ito ng mga detalyadong estadistika ng … para sa tauhang X. Nauugnay ang chunk sa buong dokumento dahil ipinapakita nito ang lakas ni X sa bilis ng pag-atake…
Nagbibigay ang chunk na ito ng mga detalyadong estadistika ng … para sa tauhang Y. Nauugnay ang chunk sa buong dokumento dahil ipinapakita nito ang pinahusay na stats ni Y kapag ginagamit ang kaniyang espesyal na kakayahan…
Nagbibigay ang chunk na ito ng mga detalyadong estadistika ng … para sa tauhang Z. Nauugnay ang chunk sa buong dokumento dahil ipinapakita nito ang stats ni Z na angkop na angkop sa pagiging tank sa mga team match…
Maaaring mukhang sobra ang pamamaraang ito (na bahagyang hango sa Anthropic) para sa halimbawang nasa itaas, ngunit napakaepektibo nito para sa mga chunk na maaaring magkamali ng interpretasyon kapag “wala sa konteksto.” Nagbibigay rin ito ng iisang pamamaraang gumagana sa lahat ng chunk at nagpapanatiling maayos sa engineering pipeline.


Kapag kailangan mong maging ~~kontrolado ang lahat~~ mahigpit
Karaniwan, nakukuha natin ang data nang buo at hinahati ito sa mga chunk para sa isang RAG system. Sa halimbawang ito, may ipapakita kaming bahagyang naiiba—data na hinati sa mga chunk, ngunit hindi maayos ang mga chunk. Mga random na seksyon ang mga ito mula sa isang lohikal na chunk na kailangang muling pagsama-samahin. Ang lohikal na chunk ay isang bahagi ng content na natural na dapat magkakasama, gaya ng subsection ng isang dokumento o magkakaugnay na talata.


Sa unang pagtatangka namin sa data na ito, ipinasa namin ang lahat sa isang LLM call at hiniling dito na pagpangkat-pangkatin ang mga ito ayon sa tingin nitong angkop, saka ibalik ang pinangkat na content. Dapat ay mahusay dito ang LLM, hindi ba? Oo at hindi.
Natuklasan namin, pati sa ilang iba pang pagkakataon, na may tendensiyang magtipid ng pagsisikap at hindi maaasahan ang mga LLM kapag kailangan ang buo at eksaktong content, lalo na kung mahaba ang konteksto. At makatuwiran naman iyon. Ngunit hindi iyon katanggap-tanggap para sa partikular na use case na ito dahil kailangan namin ang eksaktong content, salita por salita—walang buod at walang anumang bahaging lalaktawan mula sa orihinal na content. Wala kaming maaaring makaligtaang detalye.
At siyempre, ang bahaging “oo” ay napakahusay nitong naunawaan ang semantika at mga estruktura ng mga watak-watak na chunk. Basta hindi ito tumangging ibalik ang eksaktong content. Bwisit:/
Paano natin magagamit ang mahusay gawin ng LLM habang iniiwasan ang mga bagay na hindi nito maaasahang gawin? Bumaling kami sa dati naming maaasahang kaibigan: code (ibig sabihin, customized na Python function). At sa isang “napakasimpleng” Pydantic model. Narito ang solusyon:
Isa-isahin ang mga seksyon habang pinapanatili ang kasalukuyang lohikal na chunk
Sa bawat seksyon, itanong sa LLM: kabilang ba ang seksyong ito sa kasalukuyang lohikal na chunk? Sumagot ng oo o hindi (ayon sa Pydantic model).
Kung oo, idagdag ang seksyon sa chunk; kung hindi, ilabas ang kasalukuyang lohikal na chunk dahil kumpleto na ito, saka magsimula ng bago gamit ang seksyon.


Siyempre, gumagamit kami rito ng kaunti pang token kaysa sa isang pasada sa buong content. Ngunit para sa partikular na use case kung saan pangunahing priyoridad ang pagpapanatili sa eksaktong content, sulit na sulit ang (maliit na) karagdagang gastos.
Napakasimple ng solusyong ito, ngunit sumusunod ito sa isang mahalagang prinsipyo: kapag kailangan ang pagiging mahigpit, hindi tayo dapat umasa lamang sa mga LLM dahil probabilistic pa rin ang mga ito.
Maaaring gamitin ang custom code/functions at mga Pydantic model upang makamit ang predictable at maaasahang resulta habang lubos pa ring napakikinabangan ang mga kakayahan ng LLM.
Ang pagbuo ng generative AI solution ay hamon sa engineering tulad din ng pagiging hamon nito sa AI. Umaasa kaming nabigyang-inspirasyon kayo ng mga halimbawang ito na harapin ang sarili ninyong natatanging mga hamon. Para magbasa pa tungkol sa engineering-first na mga generative AI solution, tingnan ang aming blog post tungkol sa disenyo ng agentic system na nakabatay sa router.