16 β RAG: retrieval va generatsiya¶
β¬ οΈ Oldingi: 15 β RAG: chunking va indekslash Β· π Kitob boshi Β· Keyingi: 17 β RAG'ni yaxshilash va baholash β‘οΈ
Bu bobda: RAG'ning ikkinchi fazasini β so'rov vaqtida ishlaydigan qismni β quramiz. 15-bobda tayyorlagan indeks ustida ishlaymiz: foydalanuvchi savolini olib, uni embeddingga aylantiramiz, vektor bazadan eng yaqin top-k chunkni topamiz, ularni kontekst sifatida promptga qo'shamiz va modeldan faqat shu kontekstga tayanib javob olamiz. Promptni qanday yig'ishni (tizim ko'rsatmasi + kontekst + savol), "bilmasam bilmayman" deyish bilan hallucinationni kamaytirishni, javobda manbani ko'rsatishni (citations) va
top_kni to'g'ri tanlashni o'rganamiz. Yakunda boshidan oxirigacha ishlaydigan bittarag_javob(savol)funksiyasini yozamiz.
Muammodan boshlaymiz: indeks tayyor, endi nima?¶
15-bobda biz hujjatlarni chunklarga bo'lib, har birini embeddingga aylantirib, vektor bazaga (Chroma) joyladik. Endi bazada yuzlab, minglab chunk yotibdi β har biri o'z matni, vektori va metadatasi bilan. Lekin bu hozircha shunchaki anbor: ma'lumot bor, ammo foydalanuvchi savoliga javob yo'q.
Tasavvur qiling, kompaniyangizning ichki qo'llanmasini indeksladingiz. Xodim so'raydi:
"Ta'til kuni necha kun va uni qanday rasmiylashtiraman?"
Bu savolga javob qo'llanmaning bir-ikki chunkida bor β lekin model uni o'zidan bilmaydi (sizning hujjatingiz uning o'qigan ma'lumotida yo'q). Bizning ishimiz: shu savol uchun kerakli chunklarni topib, ularni modelga kontekst qilib berib, "mana shu matnga tayanib javob ber" deyish.
Mana shu β RAG'ning ikkinchi fazasi: retrieve -> augment -> generate (top -> boyit -> javob ber).
Hayotiy o'xshatish. 15-bob β kutubxonaga kitoblarni javonlarga tartib bilan terib qo'yish (indekslash). 16-bob β kimdir savol bilan kelganda, kerakli sahifalarni topib, stolga qo'yib, so'ng o'sha sahifalarga qarab javob yozib berish. Model β bilimli, lekin sizning kutubxonangizni yoddan bilmaydigan xodim; siz unga aynan kerakli sahifalarni tutqazasiz.
RAG β eslatma
RAG (Retrieval-Augmented Generation) β modelning javobini tashqi haqiqiy ma'lumot bilan boyitish usuli. Ikki faza: (1) indekslash (offlayn, 15-bob) β hujjatlarni chunklarga bo'lib, vektor bazaga joylash; (2) so'rov (onlayn, shu bob) β savol kelganda kerakli chunklarni topib, modelga kontekst qilib berish. Bu hallucination'ni kamaytiradi va modelga sizning ma'lumotingizni "biladigan" qiladi.
So'rov fazasining oqimi: 5 qadam¶
Bir foydalanuvchi savoli RAG tizimidan o'tganda quyidagi besh qadam bajariladi:
- Embed β foydalanuvchi savolini xuddi chunklar kabi embeddingga (vektorga) aylantiramiz. Muhim: indekslashda ishlatilgan embedding modeli bilan bir xil model bo'lishi shart.
- Retrieve β savol vektorini vektor bazaga beramiz; baza unga eng yaqin
top_kta chunkni qaytaradi (cosine o'xshashlik bo'yicha, 13-bobdagidek). - Augment β topilgan chunklarni kontekst sifatida promptga qo'shamiz va tizim ko'rsatmasi hamda savol bilan birlashtiramiz.
- Generate β to'liq promptni modelga yuboramiz; model faqat berilgan kontekstga tayanib javob yozadi.
- Javob β javobni (ixtiyoriy: manbalar bilan) foydalanuvchiga ko'rsatamiz.
Diqqat: birinchi ikki qadam (embed + retrieve) β RAG'ning yuragi; uchinchi-to'rtinchi (augment + generate) β oddiy LLM so'rovi, lekin endi promptda kontekst bor. Ya'ni RAG β sizga tanish bo'lgan chat.completions.create(...) chaqiruvi, oldiga "kerakli ma'lumotni topish" bosqichi qo'shilgani.
Bir xil embedding modeli β qoida
Savolni embeddingga aylantirganda 15-bobda chunklar uchun ishlatgan aynan o'sha model va o'lcham bo'lishi shart (masalan, text-embedding-3-small). Boshqa model boshqa vektor fazosi yaratadi β o'xshashlik o'lchovi ma'nosini yo'qotadi va qidiruv buziladi.
1-2 qadam: retrieve β kerakli chunklarni topish¶
Avval 15-bobda to'ldirgan Chroma kolleksiyasiga ulanamiz va savol bo'yicha eng yaqin chunklarni topamiz. Chroma so'rov matnini o'zi embeddingga aylantirib qidiradi (yoki siz tayyor vektorni berasiz).
import os
from dotenv import load_dotenv
import chromadb
from chromadb.utils import embedding_functions
load_dotenv()
# 15-bobda yaratilgan doimiy bazaga ulanamiz (aynan o'sha path va kolleksiya nomi)
client = chromadb.PersistentClient(path="./chroma_db")
# Embedding funksiyasi β indekslashdagi bilan AYNAN bir xil bo'lishi shart
embed_fn = embedding_functions.OpenAIEmbeddingFunction(
api_key=os.environ["OPENAI_API_KEY"],
model_name="text-embedding-3-small", # nomlar o'zgaradi β provayder ro'yxatini tekshiring
)
kolleksiya = client.get_collection(name="bilim_bazasi", embedding_function=embed_fn)
def chunklarni_top(savol: str, top_k: int = 4) -> list[dict]:
"""Savolga eng yaqin top_k chunkni matn va metadata bilan qaytaradi."""
natija = kolleksiya.query(
query_texts=[savol], # Chroma savolni o'zi embeddingga aylantiradi
n_results=top_k,
include=["documents", "metadatas", "distances"],
)
chunklar = []
# query [] ichida ro'yxat qaytaradi (ko'p savol bo'lishi mumkin); bizda bitta savol -> [0]
for matn, meta, masofa in zip(
natija["documents"][0],
natija["metadatas"][0],
natija["distances"][0],
):
chunklar.append({"matn": matn, "meta": meta, "masofa": masofa})
return chunklar
Sinab ko'ramiz:
for c in chunklarni_top("Ta'til necha kun?", top_k=3):
print(f"[masofa={c['masofa']:.3f}] manba={c['meta'].get('manba')}")
print(c["matn"][:120], "...\n")
Har bir chunk uchun bizda matn (modelga beradiganimiz), metadata (manba fayli, sahifa, chunk raqami β 15-bobda saqlagan) va masofa (qanchalik yaqin) bor. Masofa kichik bo'lsa β chunk savolga yaqinroq.
Hayotiy o'xshatish.
query(...)β kutubxonachiga "shu mavzudagi eng tegishli 4 ta sahifani topib ber" deyishdek. U javonlarni o'qib chiqmaydi (bu sekin bo'lardi), balki oldindan tuzilgan indeks orqali tez topadi β xuddi kitob oxiridagi alifbo ko'rsatkichi kabi.
Masofa va o'xshashlik
Chroma sukut bo'yicha masofa qaytaradi (kichik = yaqin), o'xshashlik emas. Ko'pincha o'xshashlik = 1 - masofa (cosine uchun) deb taxminlash mumkin. Aniq formula baza sozlamasiga bog'liq β keyingi bobda (17) qaytish chegarasi (threshold) qo'yishni ko'ramiz.
3-qadam: augment β promptni yig'ish¶
Endi eng nozik qism: topilgan chunklardan va savoldan bitta yaxlit prompt yasaymiz. Prompt uch qismdan iborat:
- Tizim ko'rsatmasi (system) β modelga qoidalarni aytadi: "FAQAT quyidagi kontekstga tayanib javob ber; kontekstda javob bo'lmasa β 'bilmayman' dey".
- Kontekst β top-k chunkning matnlari (manba belgisi bilan).
- Savol β foydalanuvchining asl savoli.
Avval kontekstni tartibli matn qilib yig'amiz. Har chunkni raqamlab qo'yamiz β keyin model javobida [1], [2] deb manbaga ishora qila olishi uchun:
def kontekst_yig(chunklar: list[dict]) -> str:
"""Top-k chunklarni raqamlangan, manbali matnga aylantiradi."""
qismlar = []
for i, c in enumerate(chunklar, start=1):
manba = c["meta"].get("manba", "noma'lum")
qismlar.append(f"[{i}] (manba: {manba})\n{c['matn']}")
return "\n\n".join(qismlar)
Endi to'liq prompt shablonini yasaymiz. Tizim ko'rsatmasi β RAG sifatining yarmi shu yerda hal bo'ladi:
TIZIM_KORSATMA = (
"Sen kompaniya hujjatlari bo'yicha yordamchi savol-javob tizimisan.\n"
"QOIDALAR:\n"
"1. FAQAT quyida berilgan KONTEKSTGA tayanib javob ber.\n"
"2. Agar javob kontekstda bo'lmasa, o'zingdan TO'QIMA β "
"'Bu savolga hujjatlarda javob topilmadi' deb ayt.\n"
"3. Javobing oxirida foydalangan manbalarni [1], [2] ko'rinishida ko'rsat.\n"
"4. Qisqa, aniq va o'zbek tilida javob ber."
)
def prompt_yig(savol: str, chunklar: list[dict]) -> list[dict]:
"""system + kontekst + savol dan messages ro'yxatini yig'adi."""
kontekst = kontekst_yig(chunklar)
foydalanuvchi_xabari = (
f"KONTEKST:\n{kontekst}\n\n"
f"SAVOL: {savol}\n\n"
f"Yuqoridagi kontekstga tayanib javob ber."
)
return [
{"role": "system", "content": TIZIM_KORSATMA},
{"role": "user", "content": foydalanuvchi_xabari},
]
Hayotiy o'xshatish. Promptni yig'ish β imtihonda talabaga ochiq kitob berib, "javobni faqat shu kitobdan top, yodingdan yozma" deyishdek. Tizim ko'rsatmasi β imtihon qoidasi; kontekst β ruxsat etilgan kitob; savol β imtihon savoli. Qoidasiz talaba esa yoddan to'qib yozishi (hallucination) mumkin.
Kontekstni savoldan ajrating
Kontekst va savolni aniq belgilar bilan (KONTEKST:, SAVOL:) ajrating. Bu modelga "qaysi qism ma'lumot, qaysi qism so'rov" ekanini tushuntiradi va prompt injectionni (kontekst ichidagi yashirin buyruqlarni) ham qiyinlashtiradi β bu haqda 24-bobda batafsil.
Hallucination'ni kamaytirish: "bilmasam β bilmayman"¶
LLM'ning eng katta xavfi (1-bobda aytganimiz) β hallucination: model bilmagan narsani ham ishonch bilan to'qib aytishi. RAG buni ikki tomonlama kamaytiradi:
- Haqiqiy manba beradi β model yoddan emas, sizning matningizga qarab javob yozadi.
- "Bilmayman" ruxsati β tizim ko'rsatmasida modelga ochiq aytamiz: javob kontekstda bo'lmasa, to'qima, "topilmadi" dey.
Ikkinchisi juda muhim. Sukut bo'yicha LLM "yordam berishga" intiladi va bo'sh javob o'rniga biror narsa to'qishni "afzal" ko'radi. Unga bilmaslik ham to'g'ri javob ekanini ochiq aytish kerak.
# Yomon ko'rsatma (model baribir to'qishi mumkin):
"Quyidagi ma'lumotga qarab savolga javob ber."
# Yaxshi ko'rsatma (bilmaslikka ruxsat):
"FAQAT kontekstga tayanib javob ber. Agar javob kontekstda yo'q bo'lsa, "
"'Bu savolga hujjatlarda javob topilmadi' deb ayt. Hech narsa to'qima."
Hayotiy o'xshatish. Tajribasiz yordamchi rahbarini xafa qilmaslik uchun "bilmadim" deyishdan qo'rqib, taxminiy javob beradi β bu yomon. Yaxshi yordamchiga "bilmasang, 'bilmayman' deyaver β bu uyat emas" deb aytib qo'yiladi. Tizim ko'rsatmasi modelga aynan shu ruxsatni beradi.
RAG hallucination'ni KAMAYTIRADI, yo'qotmaydi
RAG xavfni sezilarli kamaytiradi, lekin nolga tushirmaydi. Model topilgan kontekstni noto'g'ri talqin qilishi yoki ikki chunkni aralashtirib yuborishi mumkin. Shuning uchun citations (manba ko'rsatish) muhim β foydalanuvchi javobni asl manba bilan tekshira oladi. Sifatni o'lchashni 17-bobda ko'ramiz.
4-qadam: generate va manba ko'rsatish (citations)¶
Endi yig'ilgan promptni modelga yuboramiz. Bu β oddiy chat.completions.create(...) chaqiruvi, faqat messages ichida endi kontekst bor:
from openai import OpenAI
llm = OpenAI()
MODEL = "gpt-5.4-mini" # nomlar o'zgaradi β provayder ro'yxatini tekshiring
def javob_generatsiya(messages: list[dict]) -> str:
resp = llm.chat.completions.create(
model=MODEL,
messages=messages,
temperature=0.2, # past harorat β faktlarga sodiq, ijodkorlik kam
)
return resp.choices[0].message.content
temperature=0.2 β RAG uchun maslahat: javob faktlarga sodiq, "ixtirochilik" kam bo'lsin (parametrlar haqida 6-bobda).
Citations (manba ko'rsatish) β RAG'ni ishonchli qiladigan narsa. Foydalanuvchi "model qayerdan oldi?" deb tekshira olishi kerak. Biz buni ikki bosqichda ta'minlaymiz:
- Promptda chunklarni
[1],[2]deb raqamladik va modeldan javobida shu raqamlarni ishlatishni so'radik. - Javob bilan birga foydalanuvchiga manbalar ro'yxatini (qaysi
[n]qaysi faylga to'g'ri keladi) ko'rsatamiz β bu metadatadan keladi.
def manbalar_royxati(chunklar: list[dict]) -> str:
"""Foydalanuvchiga ko'rsatish uchun [n] -> manba jadvalini yasaydi."""
qatorlar = []
for i, c in enumerate(chunklar, start=1):
manba = c["meta"].get("manba", "noma'lum")
sahifa = c["meta"].get("sahifa")
belgi = f"[{i}] {manba}"
if sahifa is not None:
belgi += f", {sahifa}-sahifa"
qatorlar.append(belgi)
return "\n".join(qatorlar)
Citation β ishonchning kaliti
Korxona muhitida (huquq, tibbiyot, moliya) manbasiz javob qabul qilinmaydi. Citation foydalanuvchiga (a) javobni tasdiqlash, (b) chuqurroq o'qish va (c) modelni "ushlash" (agar javob manbaga mos kelmasa) imkonini beradi. Shu sababli citation ko'pincha RAG'ning eng qadrli qismi hisoblanadi.
top_kni tanlash: kam yaxshimi, ko'pmi?¶
top_k β bazadan nechta chunk olishimiz. Bu β RAG'ning eng muhim sozlamalaridan biri va u savdolashuv (trade-off):
top_k |
Afzalligi | Kamchiligi |
|---|---|---|
| Kam (1-2) | Aniq, kam shovqin, arzon (kam token) | Javob chunklarda bo'lsa-da, kerakli chunk tushmay qolishi mumkin |
| O'rtacha (3-6) | Ko'pincha eng yaxshi balans | β (odatda boshlash nuqtasi) |
| Ko'p (10+) | Kerakli ma'lumot tushib qolish ehtimoli kam | Ko'p shovqin (tegishsiz chunk), qimmat, model chalg'iydi |
Asosiy tushuncha: ko'p chunk = ko'p ma'lumot, lekin ko'p shovqin ham. Tegishsiz chunklar modelni chalg'itadi va javob sifatini pasaytirishi mumkin β "ko'p" har doim "yaxshi" emas.
Hayotiy o'xshatish.
top_kβ savolga tayyorlanayotganda nechta sahifani stolga qo'yishni tanlashdek. 1 sahifa β javob unda bo'lmasligi mumkin. 50 sahifa β kerakli jumlani topish qiyinlashadi, e'tibor tarqaladi. Tajribali talaba 3-5 ta eng tegishli sahifani tanlaydi.
Amaliy maslahat
top_k = 4 dan boshlang. Agar model "topilmadi" deb juda ko'p javob bersa β oshiring. Agar javoblar chalkash/noto'g'ri bo'lsa β kamaytiring yoki chunk hajmini qayta ko'ring (15-bob). Eng to'g'ri qiymatni faqat o'lchab topasiz (17-bob), taxmin bilan emas. Kontekst oynasiga sig'ishini ham hisobga oling: katta chunklar Γ katta top_k = ko'p token = qimmat va chegaraga urilishi mumkin.
Hammasini birlashtirish: rag_javob(savol)¶
Endi barcha qadamlarni bitta funksiyaga yig'amiz. Bu β RAG so'rov fazasining to'liq, ishlaydigan ko'rinishi: retrieve + augment + generate, citation bilan.
def rag_javob(savol: str, top_k: int = 4) -> dict:
"""Savolga RAG orqali javob beradi: top -> boyit -> generatsiya.
Qaytaradi: {"javob": str, "manbalar": str, "chunklar": list}.
"""
# 1-2. RETRIEVE β eng yaqin chunklarni topamiz
chunklar = chunklarni_top(savol, top_k=top_k)
# Hech narsa topilmasa (bo'sh baza yoki juda uzoq savol)
if not chunklar:
return {
"javob": "Bu savolga hujjatlarda javob topilmadi.",
"manbalar": "",
"chunklar": [],
}
# 3. AUGMENT β promptni yig'amiz
messages = prompt_yig(savol, chunklar)
# 4. GENERATE β modeldan javob olamiz
javob = javob_generatsiya(messages)
# 5. Manbalarni tayyorlaymiz (citation)
manbalar = manbalar_royxati(chunklar)
return {"javob": javob, "manbalar": manbalar, "chunklar": chunklar}
Ishlatish β oddiy:
natija = rag_javob("Ta'til kuni necha kun va qanday rasmiylashtiriladi?")
print(natija["javob"])
print("\nManbalar:")
print(natija["manbalar"])
Namuna chiqish:
Ta'til yiliga 21 ish kuni beriladi [1]. Rasmiylashtirish uchun
boshliqqa kamida 2 hafta oldin ariza topshiriladi [2].
Manbalar:
[1] qollanma.pdf, 12-sahifa
[2] qollanma.pdf, 13-sahifa
Mana shu β to'liq RAG tizimi: foydalanuvchi savol berdi, tizim kerakli chunklarni topdi, modelga kontekst qilib berdi, model faqat shu kontekstga tayanib javob yozdi va manbalarni ko'rsatdi. Endi sizning ilovangiz o'z hujjatlaringizni "biladi".
Hayotiy o'xshatish.
rag_javobβ bir butun referent xizmati: savol qabul qiladi, arxivdan kerakli hujjatlarni topadi, ekspertga beradi, ekspert javob yozadi va manbasini ko'rsatadi. Sizning ilovangizdagi har bir savol shu konveyer orqali o'tadi.
RAG = prompt + qidiruv
E'tibor bering: rag_javob ichida hech qanday "sehr" yo'q. U β sizga 2-10 boblarda tanish bo'lgan chat.completions.create(...) chaqiruvi, oldiga "savolga tegishli matnni topish" (13-15-boblardagi embedding + vektor baza) bosqichi qo'shilgani. RAG β yangi texnologiya emas, balki ikki tanish g'oyani (semantik qidiruv + LLM) birlashtirish.
Xulosa¶
- RAG ikki fazadan iborat: indekslash (15-bob, offlayn) va so'rov (shu bob, onlayn). So'rov fazasi oqimi: embed -> retrieve(top-k) -> augment -> generate -> javob.
- Retrieve β savolni embeddingga aylantirib, vektor bazadan eng yaqin
top_kchunkni olish. Savol embeddingi indekslashdagi aynan bir xil model bilan hisoblanishi shart. - Augment β promptni uch qismdan yig'ish: tizim ko'rsatmasi (qoidalar) + kontekst (raqamlangan chunklar) + savol.
- Hallucinationni kamaytirish: tizim ko'rsatmasida "FAQAT kontekstga tayanib javob ber; kontekstda yo'q bo'lsa β 'bilmayman' dey, to'qima". Bilmaslikka ruxsat berish β kalit qadam.
- Citations β chunklarni
[1],[2]deb raqamlab, modeldan javobida shularni ishlatishini so'rab, metadatadan manbalar ro'yxatini ko'rsatish. Bu javobni tekshiriladigan qiladi. top_kβ savdolashuv: kam = aniq lekin kontekst yetmasligi mumkin; ko'p = to'liqroq lekin shovqin va qimmat.top_k=4dan boshlab, o'lchab sozlang.rag_javob(savol)β retrieve + augment + generate'ni bitta funksiyaga yig'adi. RAG β yangi texnologiya emas, semantik qidiruv + LLM so'rovi birlashmasi.
Amaliy mashqlar¶
-
(Oson)
chunklarni_top("o'z savolingiz", top_k=3)ni 15-bobda qurgan bazangizda ishga tushiring. Qaytgan chunklarning matni vamasofasini chop eting. Masofa eng kichik chunk savolga eng tegishlimi β tekshiring. -
(Oson)
prompt_yig(...)qaytarganmessagesni modelga yubormasdan, shunchakiprintqiling. Tizim ko'rsatmasi, kontekst va savol qanday birlashganini ko'zingiz bilan ko'ring va to'liq prompt shablonini tushuning. -
(O'rtacha) Bir xil savol uchun
rag_javobnitop_k=1,top_k=4vatop_k=10bilan chaqiring. Javoblar va sifat qanday o'zgaradi? Qaysi biri eng yaxshi va nega β qisqa izoh yozing. -
(O'rtacha) Bazangizda mavjud bo'lmagan mavzuda savol bering (masalan, qo'llanmada yo'q narsa). Model "topilmadi" deb javob beradimi? Agar to'qib qo'ysa,
TIZIM_KORSATMAdagi "bilmaslik" ko'rsatmasini kuchaytiring va qayta sinang. -
(Qiyin)
rag_javobni kengaytiring: qaytgan chunklardan masofasi ma'lum chegaradan (masalan,masofa > 0.6) uzoqlarini olib tashlang (tegishsiz chunklarni filtrlash). Agar hech bir chunk chegaradan o'tmasa, modelga umuman murojaat qilmasdan to'g'ridan-to'g'ri "topilmadi" qaytaring. Bu shovqinni va xarajatni qanday kamaytirishini izohlang.
β¬ οΈ Oldingi: 15 β RAG: chunking va indekslash Β· π Kitob boshi Β· Keyingi: 17 β RAG'ni yaxshilash va baholash β‘οΈ