17 β RAG'ni yaxshilash va baholash¶
β¬ οΈ Oldingi: 16 β RAG: retrieval va generatsiya Β· π Kitob boshi Β· Keyingi: 18 β Agentlar va ReAct sikli β‘οΈ
Bu bobda: 16-bobdagi oddiy (naive) RAG nega ko'pincha yetarli emasligini ko'ramiz β noto'g'ri chunk topiladi, ko'p shovqin keladi, savol so'zlari hujjat so'zlariga mos kelmaydi. So'ng RAG sifatini oshiruvchi to'rt asosiy texnikani o'rganamiz: re-ranking (ko'p nomzod olib, eng tegishlilarini qayta tartiblash), hybrid search (kalit so'z + vektor qidiruvni birlashtirish), query expansion/rewriting (savolni LLM bilan yaxshilash, multi-query) va metadata filtering (qidiruvni sana/bo'lim bilan toraytirish). Oxirida eng muhim ko'nikma β baholash (evaluation): retrieval sifatini (kerakli chunk topildimi) va javob sifatini o'lchash, LLM-as-judge bilan avtomatik baholash, kichik test to'plami tuzish va
chunko'lchami/top_kni shu o'lchov asosida sozlash.
Muammodan boshlaymiz: naive RAG ko'pincha "deyarli to'g'ri"¶
16-bobda ishlaydigan RAG qurdik: savolni embeddingga aylantirdik, vektor bazadan eng yaqin top_k chunkni oldik, ularni modelga kontekst qilib berdik. Demoda ajoyib ishladi. Lekin haqiqiy, kattaroq bazada uni ishga tushirsangiz, tez-tez shunday holatlar ko'rasiz:
- Foydalanuvchi "504 xato" deb so'raydi, lekin hujjatda u "gateway timeout" deb yozilgan β semantik o'xshashlik past chiqib, kerakli chunk topilmaydi.
top_k=3olganingizda kerakli javob aslida 5-o'rinda edi β modelga yetib bormadi.- Topilgan 3 chunkdan faqat bittasi tegishli, qolgan ikkitasi shovqin β model shovqindan chalg'ib, noaniq javob beradi.
- Savol bir nechta mavzuga tegishli ("narx va yetkazib berish"), lekin yagona qidiruv ikkalasini ham qamrab ololmaydi.
Mana shu β naive RAG'ning "shisha shipi": yarmi ishlaydi, lekin ishonchli production tizim uchun yetmaydi.
Hayotiy o'xshatish. Naive RAG β kutubxonaga kirib, javonlardan tasodifan uchta kitob olib chiqishdek. Ba'zan kerakli kitob tushadi, ba'zan yo'q. Yaxshilangan RAG esa β avval ko'proq kitob (top-20) tortib olib, keyin ularni varaqlab eng mosini tanlaydigan tajribali kutubxonachidek: ko'p oladi, lekin aniq beradi.
Asosiy haqiqat: yaxshi javob = yaxshi retrieval
RAG javobi qanchalik yaxshi bo'lsa, modelga berilgan kontekst shunchalik yaxshi. Agar kerakli chunk umuman topilmasa, dunyodagi eng kuchli model ham to'g'ri javob bera olmaydi β chunki javob unga berilmagan. Shuning uchun bu bobning aksariyati "modelni emas, retrievalni yaxshilash" haqida.
Re-ranking: ko'p ol, keyin aniq tanla¶
Birinchi va eng samarali yaxshilanish β re-ranking. G'oya juda sodda:
- Vektor qidiruvdan ko'proq nomzod oling β
top_k=3o'rniga, masalan,top_k=20. - So'ng re-ranker bilan bu 20 ta nomzodni savolga tegishliligi bo'yicha qayta tartiblang va eng yaxshi 3-5 tasini oling.
Nega bu ishlaydi? Vektor qidiruv tez lekin taxminiy β u savol va chunkni alohida-alohida vektorga aylantirib, masofani o'lchaydi. Re-ranker esa savol va chunkni birga o'qib, "bu chunk shu savolga qanchalik javob beradi?" degan aniqroq baho beradi. U sekinroq, shuning uchun butun bazaga emas, faqat 20 ta nomzodga qo'llanadi.
Hayotiy o'xshatish. Vektor qidiruv β tez ishlaydigan elak: u ko'p arizadan tezda 20 tasini saralab oladi, lekin chuqur o'qimaydi. Re-ranker β bu 20 ta arizani diqqat bilan o'qiydigan komissiya: u sekinroq, ammo ancha aniq. Butun bazani (minglab ariza) komissiyaga o'tkazib bo'lmaydi β shuning uchun avval elak, keyin komissiya.
Eng oson re-ranker β cross-encoder modeli (lokal, bepul). sentence-transformers orqali:
from sentence_transformers import CrossEncoder
# Re-ranker modeli (lokal yuklanadi, internet faqat birinchi marta kerak)
reranker = CrossEncoder("cross-encoder/ms-marco-MiniLM-L-6-v2")
def qayta_tartibla(savol: str, chunklar: list[str], top: int = 3) -> list[str]:
"""Nomzod chunklarni savolga tegishliligi bo'yicha qayta tartiblaydi."""
# Re-ranker (savol, chunk) juftliklariga ball beradi
juftlar = [(savol, ch) for ch in chunklar]
ballar = reranker.predict(juftlar) # har chunk uchun tegishlilik bali
# Balga qarab kamayuvchi tartibda saralaymiz
tartibli = sorted(zip(chunklar, ballar), key=lambda x: x[1], reverse=True)
return [ch for ch, ball in tartibli[:top]] # eng yaxshi `top` tasi
Endi RAG oqimini yangilaymiz: ko'p ol -> qayta tartibla -> kontekst qil:
# 1) Vektor qidiruvdan KO'P nomzod (16-bobdagi qidiruv funksiyasi)
nomzodlar = vektor_qidiruv(savol, top_k=20) # 3 emas, 20
# 2) Re-ranker bilan eng yaxshi 3 tasini tanlaymiz
eng_yaxshi = qayta_tartibla(savol, nomzodlar, top=3)
# 3) Faqat shu 3 ta toza chunkni modelga kontekst qilamiz
kontekst = "\n\n".join(eng_yaxshi)
Agar lokal model o'rnatishni xohlamasangiz, LLM-reranker ham bor: LLM'dan har bir chunkka 0-10 ball berishni yoki nomzodlarni tartiblashni so'raysiz. U kuchli, lekin sekinroq va pulli (har chunk uchun so'rov). Cross-encoder odatda yaxshiroq narx/sifat beradi.
Re-ranking β eng katta foyda/mehnat nisbati
Agar RAG'ingizda bitta narsani yaxshilashga vaqtingiz bo'lsa β re-ranking qiling. U odatda eng katta sifat sakrashini beradi: top_kni 20 ga ko'tarib (recall oshadi), keyin re-ranker bilan 3 ga qisqartirasiz (precision oshadi). Ikkala metrik ham yuqori bo'ladi.
Hybrid search: kalit so'z + semantikni birlashtirish¶
Vektor qidiruv ma'noni tushunadi, lekin aniq so'zlarda zaif. "iPhone 15 Pro" yoki "ERR_504" yoki mahsulot artikuli kabi aniq atamalarni qidirganda, kichik harf farqi yoki noyob so'z semantik o'xshashlikda yo'qolib ketishi mumkin. Bunday holatda eski, sinovdan o'tgan kalit so'z (keyword) qidiruvi β BM25 β kuchliroq.
Yechim β ikkalasini birlashtirish, ya'ni hybrid search:
- BM25 (kalit so'z) β aniq so'z mosligi. Kod, ID, nom, noyob atama uchun zo'r; sinonim/ma'noda zaif.
- Vektor (semantik) β ma'no yaqinligi. Sinonim, qayta ifodalash uchun zo'r; aniq kod/ID'da zaif.
Har biri ikkinchisining zaif tomonini qoplaydi.
Hayotiy o'xshatish. BM25 β so'zma-so'z eshitadigan kotib: "504 deysizmi? Mana 504 yozilgan hujjatlar." Vektor qidiruv β ma'noni tushunadigan maslahatchi: "504 β bu timeout, mana 'sahifa ochilmayapti' haqidagi hujjatlar." Ishonchli javob uchun ikkalasiga ham quloq solasiz.
Ikki ro'yxatni qanday birlashtirish kerak? Eng oddiy va mustahkam usul β Reciprocal Rank Fusion (RRF): har bir hujjat uchun har ro'yxatdagi o'rni (rank) bo'yicha ball beriladi va qo'shiladi. Bu ballarni normallashtirish bilan ovora bo'lmaydi β faqat tartibga tayanadi.
def rrf_birlashtir(rolxatlar: list[list[str]], k: int = 60, top: int = 5) -> list[str]:
"""Bir nechta tartiblangan ro'yxatni RRF bilan bitta ro'yxatga birlashtiradi.
rolxatlar β har biri eng yaxshidan eng yomonga tartiblangan chunk-id ro'yxati."""
ball = {}
for rolxat in rolxatlar:
for orin, hujjat in enumerate(rolxat): # orin: 0, 1, 2, ...
# Yuqori o'rin -> katta ball (1/(k+orin)); k tekislaydi
ball[hujjat] = ball.get(hujjat, 0) + 1 / (k + orin)
# Umumiy ball bo'yicha kamayuvchi tartib
tartibli = sorted(ball.items(), key=lambda x: x[1], reverse=True)
return [hujjat for hujjat, b in tartibli[:top]]
# Ishlatish: BM25 va vektor natijalarini birlashtiramiz
bm25_natija = bm25_qidiruv(savol, top_k=20) # kalit so'z ro'yxati
vektor_natija = vektor_qidiruv(savol, top_k=20) # semantik ro'yxati
birlashgan = rrf_birlashtir([bm25_natija, vektor_natija], top=5)
BM25 uchun Python'da rank_bm25 kutubxonasi eng oddiy yo'l:
from rank_bm25 import BM25Okapi
# Korpus β har chunk so'zlarga ajratilgan (oddiy tokenizatsiya)
korpus = [chunk.lower().split() for chunk in barcha_chunklar]
bm25 = BM25Okapi(korpus)
def bm25_qidiruv(savol: str, top_k: int = 20) -> list[str]:
ballar = bm25.get_scores(savol.lower().split())
# Eng yuqori balli `top_k` chunk indekslarini olamiz
indekslar = sorted(range(len(ballar)), key=lambda i: ballar[i], reverse=True)[:top_k]
return [barcha_chunklar[i] for i in indekslar]
Hybrid ko'pincha bazaning o'zida bor
Ko'p vektor bazalar (Qdrant, Weaviate, pgvector + Postgres FTS, Elasticsearch) hybrid qidiruvni o'zi qo'llab-quvvatlaydi β siz faqat parametrni yoqasiz. Qo'lda BM25 + vektor + RRF yozish β g'oyani tushunish va kichik loyihalar uchun; production'da bazaning tayyor hybrid imkonidan foydalaning.
Hybrid + re-rank = kuchli juftlik
Eng yaxshi natija odatda ikkisini birga ishlatishdan keladi: hybrid bilan keng (BM25 + vektor) nomzod yig'ing, keyin re-ranker bilan eng yaxshilarini tanlang. Hybrid β qamrovni (recall), re-rank β aniqlikni (precision) ko'taradi.
Query expansion va rewriting: savolni yaxshilash¶
Ba'zan muammo retrieval'da emas, savolning o'zida. Foydalanuvchi qisqa, noaniq yoki suhbat kontekstiga bog'liq yozadi:
- "U qancha turadi?" β "u" nima? Oldingi xabarga bog'liq.
- "telfon urishmiyapti" β imlo xato, qisqa.
- "narx" β bitta so'z, juda umumiy.
Bunday savolni to'g'ridan-to'g'ri qidirsa, natija yomon bo'ladi. Query rewriting β savolni qidiruvdan oldin LLM bilan to'liq, aniq shaklga keltirish:
def savolni_qayta_yoz(savol: str, suhbat_tarixi: str = "") -> str:
"""Foydalanuvchi savolini qidiruv uchun aniq, mustaqil shaklga keltiradi."""
yoriqnoma = (
"Quyidagi foydalanuvchi savolini hujjat bazasidan qidirish uchun "
"aniq, mustaqil (kontekstsiz tushunarli) savolga aylantir. "
"Olmoshlarni (u, bu, o'sha) suhbat tarixiga qarab aniq nom bilan almashtir. "
"Faqat qayta yozilgan savolni qaytar, boshqa hech narsa yozma."
)
javob = client.chat.completions.create(
model="gpt-5.4-mini", # nomlar o'zgaradi β ro'yxatni tekshiring
messages=[
{"role": "system", "content": yoriqnoma},
{"role": "user", "content": f"Suhbat:\n{suhbat_tarixi}\n\nSavol: {savol}"},
],
)
return javob.choices[0].message.content.strip()
# "U qancha turadi?" + tarix -> "iPhone 15 Pro narxi qancha?"
toza_savol = savolni_qayta_yoz(savol, suhbat_tarixi)
chunklar = vektor_qidiruv(toza_savol, top_k=20)
Query expansion / multi-query β bir savoldan bir nechta variant yaratib, har biri bilan alohida qidirish va natijalarni birlashtirish. Bu turli so'z tanloviga ega chunklarni ham qamrab oladi:
def kop_savol_yarat(savol: str, n: int = 3) -> list[str]:
"""Bitta savoldan n ta turli ifodalangan variant yaratadi (multi-query)."""
yoriqnoma = (
f"Quyidagi savolni qidiruv uchun {n} xil so'z bilan qayta ifodala. "
"Har birini yangi qatorda, raqamsiz yoz."
)
javob = client.chat.completions.create(
model="gpt-5.4-mini",
messages=[
{"role": "system", "content": yoriqnoma},
{"role": "user", "content": savol},
],
)
variantlar = [s.strip() for s in javob.choices[0].message.content.split("\n") if s.strip()]
return [savol] + variantlar # asl savol ham qoladi
# Har variant bilan qidirib, natijalarni RRF bilan birlashtiramiz
rolxatlar = [vektor_qidiruv(v, top_k=10) for v in kop_savol_yarat(savol)]
birlashgan = rrf_birlashtir(rolxatlar, top=5)
Hayotiy o'xshatish. Multi-query β bir savolni uch xil qilib so'rashdek: "narxi qancha?", "qancha turadi?", "qiymati nima?". Kimdir hujjatda biror ifodani ishlatgan bo'lsa, uchta variantdan biri unga tegadi. Bitta savol β bitta urinish; uch savol β uch baravar ko'p imkon.
Har qo'shimcha LLM chaqiruvi β narx va kechikish
Query rewriting va multi-query har savol uchun qo'shimcha LLM so'rovi qiladi β bu pul va kechikish (latency) qo'shadi. Ularni hamma joyda emas, kerak bo'lganda ishlating: rewriting β suhbatli chatbotda (olmoshlar ko'p), multi-query β qidiruv sifati past bo'lgan murakkab savollarda. Oddiy, aniq savollarga ortiqcha.
Metadata filtering: qidiruvni toraytirish¶
Ko'pincha chunkda nafaqat matn, balki metadata ham bo'ladi: sana, bo'lim, til, manba fayl, muallif, kategoriya. Agar savol "shu yilgi narx siyosati" haqida bo'lsa, butun tarixiy hujjatlardan emas, faqat 2026-yilgi chunklardan qidirish mantiqiy. Bu metadata filtering β semantik qidiruvni oldindan toraytirish.
16-bobda chunklarga metadata qo'shgan edingiz; endi qidiruvda undan foydalanamiz. Vektor bazalar buni filter/where parametri orqali beradi (misol Chroma uslubida):
# Faqat 2026-yilgi, "narx" bo'limidagi chunklar ichidan qidiramiz
natija = kolleksiya.query(
query_texts=[savol],
n_results=20,
where={ # metadata filtri
"$and": [
{"yil": {"$eq": 2026}},
{"bolim": {"$eq": "narx"}},
]
},
)
Filtr qidiruv maydonini kichraytiradi β bu ham aniqlikni (tegishsiz davr/bo'lim chiqib ketadi), ham tezlikni oshiradi. Filtrlash uchun metadatani chunk yaratishda to'g'ri va izchil yozish kerak (yil: 2026, "2026" emas β tip mosligiga e'tibor bering).
Hayotiy o'xshatish. Metadata filtri β kutubxonada "faqat 2026-yil, iqtisod bo'limi" deb javonni oldindan chegaralashdek. Kutubxonachi (qidiruv) endi butun binoni emas, bitta javonni ko'zdan kechiradi β tez va aniq.
Filtr + semantik β qattiq va yumshoq mezon birga
Metadata filtri β qattiq (mantiqiy: shu sana, shu bo'lim β yoki ha, yoki yo'q). Semantik qidiruv β yumshoq (o'xshashlik darajasi). Ikkalasini birga ishlating: filtr keraksiz hududni kesadi, semantik qolgan ichidan eng mosini topadi. "Manbasi rasmiy hujjat bo'lgan, 2026-yilgi, savolga eng yaqin chunk" β kuchli kombinatsiya.
Baholash (evaluation): "yaxshilandimi?"ni o'lchash¶
Yuqorida to'rt texnikani ko'rdik. Lekin qaysi biri sizning bazangizda chindan yordam berdi? "Menimcha yaxshilandi" β yetarli emas. Professional RAG'ning belgisi β o'lchash. Baholashsiz har o'zgartirish β qorong'uda otish.
Baholashning ikki qatlami bor:
- Retrieval sifati β kerakli chunk topildimi? (Model javobidan oldingi qadam.)
- Javob sifati β yakuniy javob to'g'ri va to'liqmi?
1-qatlam: retrieval baholash (recall)¶
Avval kichik test to'plami tuzing: 20-50 ta savol va har biri uchun qaysi chunk(lar) javobni o'z ichiga olishini qo'lda belgilang. So'ng eng muhim metrika β recall@k: kerakli chunk top-k ichida bormi?
# Test to'plami: savol -> javobni o'z ichiga olgan chunk ID(lari)
TEST = [
{"savol": "Qaytarish muddati qancha?", "kerakli": {"c12"}},
{"savol": "Yetkazib berish narxi?", "kerakli": {"c07", "c08"}},
# ... 20-50 ta
]
def recall_baho(qidiruv_fn, k: int = 5) -> float:
"""top-k ichida kerakli chunk topilgan savollar ulushi (recall@k)."""
topildi = 0
for t in TEST:
topilgan_idlar = set(qidiruv_fn(t["savol"], top_k=k))
# Kerakli chunklardan KAMIDA bittasi top-k ichida bo'lsa β hisobladik
if topilgan_idlar & t["kerakli"]:
topildi += 1
return topildi / len(TEST)
print("Naive:", recall_baho(vektor_qidiruv, k=5))
print("Hybrid:", recall_baho(lambda s, top_k: rrf_birlashtir(
[bm25_qidiruv(s, top_k), vektor_qidiruv(s, top_k)], top=top_k)))
Endi siz raqam bilan ayta olasiz: "naive recall@5 = 0.68, hybrid = 0.84". Bu β taraqqiyot.
Hayotiy o'xshatish. Test to'plamisiz RAG'ni sozlash β termometrsiz isitmani davolashdek. "Yaxshi bo'ldiganga o'xshaydi" emas, "harorat 39 dan 37 ga tushdi" deyish kerak. Kichik test to'plami β sizning termometringiz.
2-qatlam: javob sifati va LLM-as-judge¶
Retrieval to'g'ri ishlagach, yakuniy javob sifatini baholaymiz. Javobni qo'lda o'qib baholash aniq, lekin sekin va miqyoslanmaydi. Yechim β LLM-as-judge: boshqa LLM chaqiruvi javobni belgilangan mezon bo'yicha baholaydi.
def llm_baho(savol: str, kontekst: str, javob: str) -> dict:
"""Boshqa LLM chaqiruvi javobni sodiqlik va tegishlilik bo'yicha baholaydi."""
yoriqnoma = (
"Sen qat'iy baholovchisan. Berilgan KONTEKST asosida JAVOBni 1-5 ball bilan bahola:\n"
"- sodiqlik (faithfulness): javob faqat kontekstga tayanganmi, to'qimaganmi?\n"
"- tegishlilik (relevance): javob savolga to'g'ri javob beradimi?\n"
"Faqat JSON qaytar: {\"sodiqlik\": int, \"tegishlilik\": int, \"izoh\": str}"
)
natija = client.chat.completions.create(
model="gpt-5.5", # baholashga kuchliroq model yaxshi
messages=[
{"role": "system", "content": yoriqnoma},
{"role": "user", "content": f"SAVOL:\n{savol}\n\nKONTEKST:\n{kontekst}\n\nJAVOB:\n{javob}"},
],
response_format={"type": "json_object"}, # qat'iy JSON (9-bob)
)
import json
return json.loads(natija.choices[0].message.content)
baho = llm_baho(savol, kontekst, javob)
print(baho) # {"sodiqlik": 5, "tegishlilik": 4, "izoh": "..."}
Eng muhim mezon β sodiqlik (faithfulness): javob faqat berilgan kontekstga tayanganmi yoki model o'zidan to'qib chiqarganmi (hallucination β 1-bob). RAG'ning butun maqsadi β javobni haqiqiy manbaga bog'lash; sodiqlikni o'lchash shu maqsadning bajarilganini tekshiradi.
Hayotiy o'xshatish. LLM-as-judge β imtihon ishini boshqa, tajribali o'qituvchi tekshirishidek. Talabaning o'zi "men 5 oldim" deyishi ishonchsiz; mustaqil baholovchi esa har ishni bir xil mezon bilan, tez va miqyosda tekshira oladi. Mukammal emas, lekin minglab javobni qo'lda o'qishdan ko'ra amaliy.
LLM-as-judge β foydali, lekin mutlaq emas
LLM-baholovchi ham LLM β u ham xato qiladi, uzun yoki ishonchli ko'ringan javobga yuqori ball berishga moyil bo'lishi mumkin. Shuning uchun: (1) baholashga kuchliroq model ishlating; (2) mezonni aniq yozing (1-5 nimani anglatadi); (3) vaqti-vaqti bilan baholovchining bahosini qo'lda tekshiring (kalibrlash). Uni avtomatlashtirilgan birinchi filtr deb biling, yakuniy hakam emas.
Sozlash: chunk o'lchami va top_k¶
Endi sizda o'lchov (recall + LLM-baho) va kichik test to'plami bor. Demak, ilgari "tuyg'u bilan" tanlagan parametrlarni endi raqamga tayanib sozlay olasiz. Eng ta'sirli ikki parametr:
chunko'lchami β juda kichik bo'lsa, kontekst bo'linib ma'no yo'qoladi; juda katta bo'lsa, bitta chunkda ko'p shovqin va kam aniqlik. Odatda 300-800 token oralig'i va chunklar orasida overlap (16-bobda ko'rgansiz) yaxshi ishlaydi.top_kβ kam bo'lsa, kerakli chunk tushib qolishi mumkin (recall past); ko'p bo'lsa, shovqin oshadi va kontekst uzayadi (narx + chalg'itish). Re-rank bilan: olishdatop_k=20, modelga berishda 3-5.
To'g'ri yo'l β bir necha qiymatni sinab, test to'plamida o'lchash:
for olcham in (256, 512, 768):
for k in (3, 5, 8):
baza = bazani_qur(chunk_olcham=olcham) # shu o'lchamda qayta indekslash
r = recall_baho(lambda s, top_k=k: qidir(baza, s, top_k), k=k)
print(f"chunk={olcham}, top_k={k} -> recall@{k}={r:.2f}")
# Eng yuqori recall (va kerakli narx/tezlik) bergan kombinatsiyani tanlaysiz
Bittadan o'zgartiring
Bir vaqtda bitta narsani o'zgartiring va o'lchang: avval re-rank qo'shing va o'lchang, keyin hybrid, keyin chunk o'lchamini sozlang. Hammasini birdan o'zgartirsangiz β qaysi biri yordam berganini bilmaysiz. Bu β eksperiment intizomi; RAG'ni "his bilan" emas, ma'lumot bilan yaxshilashning yagona yo'li.
Hammasini birlashtirgan oqim¶
Yaxshilangan RAG retrieval bosqichi quyidagicha ko'rinadi (generatsiya 16-bobdagidek):
def yaxshilangan_retrieval(savol: str, suhbat_tarixi: str = "") -> list[str]:
# 1) Savolni tozalaymiz (suhbat olmoshlarini ochamiz)
toza = savolni_qayta_yoz(savol, suhbat_tarixi)
# 2) Hybrid: kalit so'z + semantik, har biri KO'P nomzod
bm25_n = bm25_qidiruv(toza, top_k=20)
vektor_n = vektor_qidiruv(toza, top_k=20) # kerak bo'lsa metadata filtri bilan
nomzodlar = rrf_birlashtir([bm25_n, vektor_n], top=15)
# 3) Re-rank: 15 nomzoddan eng yaxshi 4 tasi
return qayta_tartibla(toza, nomzodlar, top=4)
Diqqat: bu β to'liq to'plam emas, asboblar to'plami. Hech kim hamma texnikani birdan qo'shmaydi. Naive RAG'dan boshlang, o'lchang, eng zaif joyni toping (recall pastmi -> hybrid/multi-query; precision pastmi -> re-rank; noto'g'ri davr chiqyaptimi -> metadata filtri), bitta yaxshilanish qo'shing va yana o'lchang. Bu sikl β professional RAG ishlab chiqishning yuragi.
Xulosa¶
- Naive RAG ko'pincha yetmaydi: noto'g'ri chunk topiladi, shovqin keladi, savol so'zlari hujjatga mos kelmaydi. Asosiy haqiqat: yaxshi javob = yaxshi retrieval β kerakli chunk topilmasa, eng kuchli model ham to'g'ri javob bera olmaydi.
- Re-ranking β ko'p nomzod oling (
top_k=20), keyin re-ranker (cross-encoder yoki LLM) bilan eng tegishli 3-5 tasini qayta tartiblang. Recall va precisionni birga ko'taradi; odatda eng katta foyda/mehnat nisbati. - Hybrid search β kalit so'z (BM25, aniq termin) + vektor (semantik, ma'no) natijalarini RRF kabi usul bilan birlashtiring; har biri ikkinchisining zaifligini qoplaydi. Ko'p baza buni o'zi qo'llab-quvvatlaydi.
- Query rewriting/expansion β savolni qidiruvdan oldin LLM bilan aniq, mustaqil shaklga keltiring (olmoshlarni oching) yoki multi-query bilan bir nechta variant qidiring. Har qo'shimcha chaqiruv β narx; kerak bo'lganda ishlating.
- Metadata filtering β qidiruvni sana/bo'lim/manba bilan oldindan toraytiring (qattiq mezon) va semantik bilan birlashtiring (yumshoq mezon). Aniqlik va tezlikni oshiradi.
- Baholash β eng muhim ko'nikma: kichik test to'plami tuzing; retrievalni
recall@kbilan, javobni qo'lda yoki LLM-as-judge (ayniqsa sodiqlik) bilan o'lchang. - Sozlash o'lchovga tayansin:
chunko'lchami vatop_kni test to'plamida sinab tanlang; har safar bitta narsani o'zgartirib o'lchang. - Yondashuv β to'plam emas, asboblar to'plami: naive'dan boshlang, o'lchang, eng zaif joyni toping, bitta yaxshilanish qo'shing, yana o'lchang.
Amaliy mashqlar¶
-
(Oson) 16-bobdagi RAG'ingizda
top_kni 3 dan 20 ga ko'tarib, qaytgan chunklarni chop eting. Kerakli javob 3-o'rindan keyin (4-20 oralig'ida) bormi? Bu β re-ranking nega kerakligini ko'rsatadi. -
(Oson) Bitta savolni
savolni_qayta_yoz(yokikop_savol_yarat) funksiyasidan o'tkazing va asl hamda yaxshilangan variant(lar)ni solishtiring. Qaysi biri ko'proq qidiruv so'zi beradi? -
(O'rtacha)
rank_bm25o'rnatib,bm25_qidiruvyozing. Aniq atama (masalan mahsulot artikuli yoki "ERR_504") bilan BM25 va vektor qidiruv natijalarini solishtiring β qaysi biri aniq so'zda yutadi? So'ngrrf_birlashtirbilan ikkalasini birlashtiring. -
(O'rtacha)
cross-encoder/ms-marco-MiniLM-L-6-v2re-rankerini o'rnatib,qayta_tartiblayozing. 16-bobdagi qidiruvga ulang (ol-20 -> re-rank-3) va re-rank'dan oldingi/keyingi top-3 ni yonma-yon chop eting. Tartib o'zgardimi? -
(Qiyin) 15-20 ta savol va ularning kerakli chunk ID'laridan iborat test to'plami tuzing.
recall_bahobilan naive va hybrid+re-rank uchunrecall@5ni o'lchang va raqamlarni solishtiring. So'ngllm_baho(LLM-as-judge) yozib, bir nechta javobning sodiqlik balini oling. Qaysi yaxshilanish eng katta farq berdi?
β¬ οΈ Oldingi: 16 β RAG: retrieval va generatsiya Β· π Kitob boshi Β· Keyingi: 18 β Agentlar va ReAct sikli β‘οΈ