Mundarija (22)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. Nega strukturali chiqish
- 2.2. Buzilgan JSON ni xavfsiz ajratib olish
- 2.3. Validatsiya: sxema, biznes qoidalari, dataclass
- 2.4. Moslashtirish (coercion): qaysi tuzatish xavfsiz
- 2.5. Xato bilan qayta so'rash (repair loop)
- 2.6. Tool use va structured outputs: API da sxemani majburlash
- 2.7. Extraction va maydon bo'yicha baholash
- 2.8. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Buzilgan JSON ni bosqichma-bosqich ajratish
- Misol 2 — Sxema validatori, biznes qoidasi va moslashtirish
- Misol 3 — Xato bilan qayta so'rash: temperatura, urinishlar va xarajat
- Misol 4 — Extraction: normallashtirish, manbaga bog'lash va maydon bo'yicha baho
- 5. To'g'ri va noto'g'ri tushunishlar
- 6. Keng tarqalgan xatolar va yechimlari
- 7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 8. Eng yaxshi amaliyotlar
- 9. Amaliy topshiriq
- Xulosa
25.5-dars: Strukturali chiqish va validatsiya
25-QISM — KATTA TIL MODELLARI · 5-dars
1. Kirish va motivatsiya
25.4-darsda promptni qanday yozishni ko'rdik: rol, ko'rsatma, misollar, chiqish formati. Endi keyingi savol: LLM javobini kim o'qiydi? Chat oynasida — odam. Lekin real tizimlarda javobni ko'pincha dastur o'qiydi: buyurtma bazaga yoziladi, murojaat navbatga qo'yiladi, maydonlar hisobotga tushadi. Odam "Albatta! Mana natija:" degan kirish so'zini, kod bloki belgilarini yoki ortiqcha vergulni ko'z bilan o'tkazib yuboradi. json.loads esa o'tkazib yubormaydi — birinchi ortiqcha belgida xato beradi.
Shuning uchun LLM ni dastur bilan bog'lashda uchta qatlam kerak. Birinchisi — ajratib olish: javob matnidan JSON ni xavfsiz topish. Ikkinchisi — validatsiya: topilgan JSON kutilgan shaklga (sxema) va ma'noga (biznes qoidalari) mosmi. Uchinchisi — tiklash: xato bo'lsa, modelga xatoni aytib qayta so'rash, lekin cheklangan marta, keyin esa zaxira yo'l (fallback).
Real vaziyat. Onlayn do'kon mijozlarning erkin yozilgan xabarlaridan buyurtma kartochkasini LLM yordamida to'ldirdi. Birinchi hafta hammasi yaxshi ko'rindi: xatolar kam, operatorlar mamnun. Oy oxirida hisob-kitob bo'limi mos kelmaslikni topdi: bir qator buyurtmalarda "jami" summa mahsulotlar narxlari yig'indisiga teng emas edi — model arifmetikada adashgan, lekin JSON to'g'ri shaklda bo'lgani uchun hech qanday tekshiruv uni ushlamagan. Boshqa buyurtmalarda mijoz telefon raqamini yozmagan bo'lsa ham, kartochkada "+998 90 123 45 67" turardi — model maydonni bo'sh qoldirish o'rniga uni "to'ldirib" qo'ygan. Bu darsning 2- va 4-misollari aynan shu ikki xatoni o'lchaydi.
Bu darsda LLM javobidan JSON ni xavfsiz ajratib olishni, JSON Schema ning kichik qismini noldan yozishni, xato bilan qayta so'rash siklini va ma'lumot ajratib olish (extraction) vazifasini maydon bo'yicha baholashni ko'ramiz.
Bu darsda:
- Nega strukturali chiqish: javobni dastur o'qiydi
- Buzilgan JSON ni xavfsiz ajratib olish
- Validatsiya: sxema, biznes qoidalari, dataclass
- Moslashtirish (coercion): qaysi tuzatish xavfsiz
- Xato bilan qayta so'rash va urinishlar chegarasi
- Tool use va structured outputs: API da sxemani majburlash
- Extraction va maydon bo'yicha baholash
- Tuzoqlar
ℹ Mashinada LLM API yo'q. Misollarda LLM o'rnida STUB —
SoxtaLLM/soxta_llmdeb nomlangan deterministik soxta mijoz ishlatiladi. U haqiqiy model emas: xatolarni biz tanlagan faraziy ehtimollar bilan chiqaradi. Stub natijalari "LLM shunday qiladi" degan da'vo emas — ular atrofdagi muhandislik kodining (ajratish, validatsiya, qayta so'rash, baholash) qanday ishlashini ko'rsatadi. Kod tuzilishi haqiqiy API bilan bir xil.
2. Nazariya — chuqur tushuntirish
2.1. Nega strukturali chiqish
ERKIN MATN:
"Mijoz Dilnoza Karimova, 2 ta kitob, Samarqandga, jami 90 ming so'm"
odam uchun tushunarli; dastur uchun - har safar boshqa shakl
STRUKTURALI CHIQISH (JSON):
{"mijoz": "Dilnoza Karimova", "soni": 2, "mahsulot": "kitob",
"shahar": "Samarqand", "summa": 90000}
kalitlar - oldindan kelishilgan; turlar - aniq; bazaga to'g'ridan-to'g'ri
KONTRAKT = SXEMA:
qaysi maydonlar majburiy, qaysi turda, qanday qiymatlar ruxsat
promptda ham, validatsiyada ham AYNAN bir xil sxema
JAVOB ZANJIRI:
LLM matni -> ajratish (JSON topish) -> json.loads -> sxema tekshiruvi
-> biznes qoidalari -> dataclass / baza
har bosqichda xato bo'lishi mumkin; har biriga alohida choraLLM — ehtimoliy generator (24.9-dars): u keyingi tokenni tanlaydi, "JSON grammatikasi" haqida kafolat bermaydi. Promptda "faqat JSON qaytaring" deyish ehtimolni oshiradi, lekin kafolat emas. Kafolat ikki yo'l bilan olinadi: (a) API darajasida sxema majburlash 2.6-bob, (b) o'z tomonimizda ajratish + validatsiya + qayta so'rash. Amalda ikkalasi birga ishlatiladi: majburlash shaklni kafolatlaydi, lekin ma'noni emas.
LLM javobini dastur o'qisa — kontrakt (sxema) kerak, va bu kontrakt dastur tomonida tekshiriladi, modelning "va'dasi" ga ishonilmaydi.
2.2. Buzilgan JSON ni xavfsiz ajratib olish
TIPIK BUZILISHLAR (1-misoldagi stub turlari):
kod bloki ```json { ... } ```
matn bilan "Albatta! Mana natija: {...} Yana savol bo'lsa..."
ortiqcha vergul {"a": 1, "b": 2,}
izoh "yosh": 21, // taxminiy
Python lug'ati {'talaba': True, 'izoh': None}
qo'shtirnoq xato "izoh": "u "a'lo" deb yozdi"
qirqilgan {"ism": "Ra'no", "yosh": 21, "sha (max_tokens)
BOSQICHLI AJRATISH (arzondan qimmatga):
1. json.loads(matn) - toza bo'lsa, shu yetadi
2. ```json ... ``` bloki - regex
3. qavs skaneri: birinchi '{' dan qavslarni sanash,
satr ichidagi '{', '}' ni hisobga olmay
4. yengil tuzatish, faqat SATRDAN TASHQARIDA:
izohlarni olib tashlash, ortiqcha vergul, True/False/None,
bitta qo'shtirnoqli satrlar -> JSON satr
5. bo'lmasa - xato; taxmin qilmaymiz
TUZATISH CHEGARASI:
bir ma'noli -> tuzatamiz ('True' -> true, ortiqcha vergul)
ko'p ma'noli -> RAD ETAMIZ va qayta so'raymiz:
ikki obyekt ("Misol: {...}. Javob: {...}") - qaysi biri javob?
ichki qo'shtirnoq - satr qayerda tugaydi?
qirqilgan - yetishmagan qism nima edi?
QIRQILISH BELGISI:
API javobida stop_reason == "max_tokens" -> javob to'liq emas
ajratishdan OLDIN tekshiriladiEng muhim o'lchov — jim xato: ajratish muvaffaqiyatli ko'rindi, lekin qiymat noto'g'ri. U "yiqilish" dan xavfliroq: yiqilishni ko'ramiz va qayta so'raymiz, jim xato esa bazaga kirib ketadi. 1-misolda 600 ta stub javobda naiv json.loads faqat toza javoblarni o'qidi (39.0%), bosqichli ajratish — 92.8%, jim xato esa 0. Tiklab bo'lmagan ikki tur — ichki qo'shtirnoq va qirqilgan javob — ataylab rad etildi.
Hech qachon eval() ishlatmang. Python lug'ati ko'rinishidagi javobni eval bilan o'qish — model (yoki model o'qigan hujjat, 25.13-dars) yozgan ixtiyoriy kodni bajarish demakdir. ast.literal_eval xavfsizroq (faqat literallar), lekin u ham JSON emas: true/null ni tanimaydi. 1-misoldagi kabi satrlarni hisobga oluvchi tuzatish — boshqariladigan yo'l.
Ajratish — bosqichli va konservativ: bir ma'noli buzilishni tuzating, shubhalisini rad eting; jim xato sonini alohida o'lchang.
2.3. Validatsiya: sxema, biznes qoidalari, dataclass
JSON SCHEMA (sanoat standarti; biz uning kichik qismini yozamiz):
type string / integer / number / boolean / array / object / null
required majburiy kalitlar
additionalProperties false -> noma'lum kalit xato
enum ruxsat etilgan qiymatlar ro'yxati
minimum / maximum son chegaralari
minLength / pattern satr uzunligi, regex (masalan, ^\+998\d{9}$)
items / minItems ro'yxat elementlari sxemasi
XATO XABARI - YO'L BILAN:
$.mahsulotlar[0].soni: kutilgan integer, keldi str ('2')
aniq xabar -> modelga qaytarsa bo'ladi 2.5-bob
PYTHON TUZOG'I:
isinstance(True, int) == True (bool - int ning quyi sinfi)
"soni": true sxemadan o'tib ketadi -> type(x) is int deb tekshiring
SHAKL va MA'NO:
sxema - SHAKL: turlar, kalitlar, chegaralar
biznes qoidalari - MA'NO: maydonlar orasidagi bog'liqlik
jami == sum(soni * narx); boshlanish < tugash; yosh va tug'ilgan yil
2-misol: jami 1000 so'mga xato - sxema xatolari 0, biznes qoidasi ushladi
DATACLASS:
tekshirilgan lug'at -> @dataclass(frozen=True) obyekt
qolgan kod lug'at kalitlari bilan emas, turi aniq maydonlar bilan ishlaydi
pydantic (o'rnatilgan bo'lsa) - sxema + tur + dataclass bir joydaSxema shaklni tekshiradi, ma'noni emas: 2-misolda moslashtirishdan keyin sxemadan o'tgan javoblar ichida 16 tasi noto'g'ri edi — hammasi maydonlararo qoida bilan ushlandi.
2.4. Moslashtirish (coercion): qaysi tuzatish xavfsiz
XAVFSIZ (bir ma'noli, qiymat o'zgarmaydi):
"3" -> 3
"Karta" -> "karta" (enum kichik harfda)
"90 123 45 67" -> "+998901234567" (9 raqam -> O'zbekiston kodi)
noma'lum kalit -> tashlash (agar siyosat shuni aytsa)
XAVFLI (taxmin):
"1,5" -> 1.5 yoki 15? (o'nlik vergul yoki minglik ajratuvchi)
"03.04" -> 3-aprel yoki 4-mart?
"ertaga" -> qaysi sana? (so'rov vaqtiga bog'liq)
bo'sh maydon -> standart qiymat? (uydirma bilan bir xil)
SIYOSATLAR (2-misol, 400 javob):
qat'iy qabul 74.0%, qabul qilingan noto'g'ri 11
moslashtirish qabul 98.8%, qabul qilingan noto'g'ri 16
moslash + biznes qabul 94.8%, qabul qilingan noto'g'ri 0Diqqat: qat'iy siyosat ham noto'g'ri javobni qabul qildi (11 ta). Qat'iylik shaklni kuchaytiradi, ma'noni emas — "jami" dagi arifmetik xato shaklan to'g'ri. Moslashtirish qabulni oshiradi, lekin shunda biznes qoidalari yanada muhim bo'ladi.
Moslashtirish faqat bir ma'noli holatlarda; har bir tuzatish qoidasi — "bu qiymatni o'zgartiradimi?" degan savolga "yo'q" javobi bilan.
2.5. Xato bilan qayta so'rash (repair loop)
SIKL:
prompt = asosiy
urinish 1..K:
javob = llm(prompt)
d, xato = ajrat_va_tekshir(javob)
agar xato yo'q -> qaytar d
prompt = asosiy + "Oldingi javobingiz: ..." + "XATO: <aniq xabar>"
K urinishdan keyin -> FALLBACK (None, navbat, operator, boshqa model)
NEGA XATO MATNI:
bir xil prompt + deterministik dekodlash (T = 0) -> BIR XIL javob
3-misol (stub): T = 0 da 1 va 5 urinish - 70.7% va 70.7%;
lekin 5 urinish 2.17 marta chaqiruv
xato matni promptni o'zgartiradi -> yangi javob
XARAJAT:
har urinish = yangi so'rov: kirish tokenlari qayta to'lanadi
xato bilan so'rashda prompt uzayadi (oldingi javob + xato)
3-misol: 'xato bilan' K 3 -> 5: muvaffaqiyat 96.7% -> 96.9%,
kirish tokenlari 61 -> 66 (so'rovga o'rtacha)
KIM YIQILADI:
3-misol: yiqilganlarning 76% i 'qiyin' yozuvlar (jami ~10%)
qayta so'rash ularni tuzatmaydi -> fallback kerakBu yerdagi raqamlar stubdan: "xato matni bilan qayta so'rash tuzatish ehtimolini oshiradi" — biz stubga kiritgan faraz (0.25 → 0.10), shuning uchun 3-misolning xato bilan - qayta, T=0.7 farqi dalil emas, farazning oqibati. Stubdan mustaqil haqiqatlar esa: (1) bir xil prompt va deterministik dekodlashda qayta so'rash foydasiz; (2) urinishlar soni oshgani sari foyda kamayadi, xarajat esa chiziqli o'sadi; (3) doimiy qiyin holatlar uchun fallback kerak. Haqiqiy modelda o'z ma'lumotingizda o'lchang: urinish raqami bo'yicha muvaffaqiyat ulushini loglang.
Eslatma: ba'zi yangi modellarda temperature parametri umuman qabul qilinmaydi — "bir xil prompt → aynan bir xil javob" kafolati yo'q, lekin xulosa o'zgarmaydi: promptni o'zgartirmaydigan qayta so'rash — tasodifga umid.
Qayta so'rash promptni o'zgartirishi kerak (aniq xato matni); urinishlarni 2-3 bilan cheklang va fallback ni oldindan loyihalang.
2.6. Tool use va structured outputs: API da sxemani majburlash
G'OYA (24.9 dagi generatsiya sikli bilan bog'liq):
har qadamda model logit beradi; server sxema grammatikasiga zid
tokenlarni -inf bilan niqoblaydi (constrained decoding)
-> chiqish sxemaga mos JSON bo'lishi KAFOLATLANADI
IKKI SHAKL (Anthropic API):
structured outputs: output_config={"format": {"type": "json_schema",
"schema": {...}}}
javob matni - sxemaga mos JSON
tool use: tools=[{"name": ..., "input_schema": {...}, "strict": True}]
model "asbobni chaqiradi": tool_use bloki, .input - lug'at
(asboblar va agent sikli - Agentlar va tool calling darsida)
KAFOLAT NIMANI QAMRAYDI:
+ kalitlar, turlar, enum, additionalProperties
- ma'no: jami summa, sana mantiqi, uydirma qiymat
- to'liqlik: max_tokens da qirqilsa - stop_reason tekshiring
-> biznes qoidalari va manbaga bog'lash BARIBIR kerak
QACHON O'Z ISHIMIZ KERAK:
provayder sxema majburlashni qo'llamasa yoki sxema juda murakkab bo'lsa
lokal / kichik modellar
majburlash bo'lsa ham: validatsiya ikkinchi himoya chizig'iSxema majburlash shaklni kafolatlaydi, ma'noni emas — validatsiya va biznes qoidalari baribir dastur tomonida qoladi.
2.7. Extraction va maydon bo'yicha baholash
VAZIFA: erkin matn -> maydonlar
"Men Bobur Aliyev, Xivadan yozyapman. 2 ta daftar kerak, Termizga
yetkazib bering. Telefonim: 905871484. 11-sentabr kuni kerak bo'ladi."
-> ism, shahar (yetkazish), mahsulot, soni, summa, telefon, sana
BAHOLASH DARAJALARI:
maydon bo'yicha aniqlik - har maydon alohida: qaysi maydon zaif?
yozuv bo'yicha aniq mos - 7 maydonning hammasi to'g'ri (qattiq)
uydirma (hallucination) - haqiqat None, model qiymat bergan
NORMALLASHTIRISH - BAHOLASHDA HAM:
"145 ming so'm", "145 000 so'm", "145000 so'm" -> 145000
"11-sentabr", "11.09" -> "09-11"
qat'iy satr taqqoslash format farqini xato deb sanaydi
4-misol: qat'iy 51.0%, normallangan 94.4% - farq 43.5 foiz punkt
MANBAGA BOG'LASH (grounding):
ajratilgan qiymat manba matnida uchraydimi? (telefon - raqamlar bo'yicha)
yo'q bo'lsa -> None (uydirma rad etiladi)
4-misol: uydirmalar 105 -> 0
CHEKLOV: matnda BOR, lekin noto'g'ri qiymatni ushlamaydi
(jo'natuvchi shahri va yetkazish shahri - ikkalasi ham matnda)Extraction ni maydon bo'yicha, normallangan qiymatlarda baholang; uydirmani alohida sanang va manbaga bog'lash bilan kamaytiring.
2.8. Tuzoqlar
Asosiy tuzoqlar: "faqat JSON qaytaring" deb yozib json.loads(javob) ni to'g'ridan-to'g'ri chaqirish; eval bilan Python lug'atini o'qish; ko'p ma'noli buzilishni "tuzatish" (ikki obyektdan birinchisini olish, qirqilgan JSON ni qavs bilan yopish); stop_reason ni tekshirmaslik; isinstance(x, int) bilan butun sonni tekshirish (True o'tib ketadi); faqat sxema bilan cheklanib, maydonlararo qoidalarni yozmaslik; noaniq qiymatlarni "moslashtirish" ("03.04", "1,5"); bo'sh maydonni standart qiymat bilan to'ldirish; T = 0 da bir xil prompt bilan qayta so'rash; urinishlar sonini cheklamaslik yoki fallback siz qoldirish; extraction ni qat'iy satr taqqoslash bilan baholash; uydirmani alohida o'lchamaslik; stub natijasini "model shunday qiladi" deb taqdim etish.
3. Tez ma'lumotnoma
import json
import anthropic
# ---- 1. Haqiqiy API: sxema majburlangan chiqish (structured outputs) ----
client = anthropic.Anthropic() # kalit ANTHROPIC_API_KEY muhit o'zgaruvchisidan
SXEMA = {
"type": "object",
"properties": {"ism": {"type": "string"}, "yosh": {"type": "integer"},
"shahar": {"type": "string"}},
"required": ["ism", "yosh", "shahar"],
"additionalProperties": False,
}
javob = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="Matndan ma'lumot ajratuvchisiz.",
messages=[{"role": "user", "content": "Men Malika, 34 yoshdaman, Xivada yashayman."}],
output_config={"format": {"type": "json_schema", "schema": SXEMA}},
)
if javob.stop_reason == "max_tokens":
raise RuntimeError("javob qirqilgan - max_tokens ni oshiring")
matn = next(b.text for b in javob.content if b.type == "text")
d = json.loads(matn) # shakl kafolatlangan; ma'noni baribir tekshiring
# ---- 2. Tool use: sxema asbob parametrlari sifatida (strict) ----
javob = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
tools=[{"name": "buyurtma_saqla", "description": "Buyurtmani saqlaydi",
"strict": True, "input_schema": SXEMA}],
messages=[{"role": "user", "content": "..."}],
)
for blok in javob.content:
if blok.type == "tool_use":
parametrlar = blok.input # lug'at
# ---- 3. pydantic (agar o'rnatilgan bo'lsa) ----
from pydantic import BaseModel, Field, ValidationError
class Shaxs(BaseModel):
ism: str = Field(min_length=2)
yosh: int = Field(ge=0, le=130)
shahar: str
try:
s = Shaxs.model_validate_json(matn)
except ValidationError as e:
xato_matni = str(e) # modelga qaytarish uchun
# ---- 4. O'z qo'limiz bilan (1-4 misollar) ----
# obj, bosqich = ajrat(matn) # to'g'ridan -> kod bloki -> qavs skaneri -> tuzatish
# xatolar = tekshir(obj, SXEMA) # "$.yosh: kutilgan integer, keldi str"
# xatolar += biznes_qoidalari(obj) # maydonlararo moslik
# for urinish in range(K): ... prompt += "XATO: " + xatolar[0]Strukturali chiqish xulosasi
javob matni -> ajrat (bosqichli, konservativ) -> sxema -> biznes qoidalari -> dataclass
jim xato - eng xavfli; alohida o'lchang
eval yo'q; stop_reason ni tekshiring
moslashtirish - faqat bir ma'noli
qayta so'rash: aniq xato matni, K = 2-3, keyin fallback
API da sxema majburlash - shakl; ma'no - sizning kodingizda
extraction: maydon bo'yicha, normallangan, uydirma alohida4. Batafsil misollar
Misollar Python 3.14 standart kutubxonasi va numpy bilan. LLM o'rnida — STUB (haqiqiy model emas); kod tuzilishi haqiqiy API bilan bir xil.
Misol 1 — Buzilgan JSON ni bosqichma-bosqich ajratish
"""LLM javobidan JSON ni xavfsiz ajratib olish: kod bloki, ortiqcha matn, izoh, vergul va qirqilish."""
import json
import random
import re
ISMLAR = ["Dilnoza", "Bobur", "Malika", "Sardor", "Nodira", "Jasur", "Ra'no", "O'tkir"]
SHAHARLAR = ["Toshkent", "Samarqand", "Buxoro", "Farg'ona", "Namangan", "Qo'qon"]
IZOHLAR = ["o'qishni yaxshi ko'radi", "sport bilan shug'ullanadi", None,
"ingliz tilini o'rganmoqda", None]
TURLAR = ["toza", "kod_bloki", "matn_bilan", "ortiqcha_vergul", "izohli",
"python_dict", "qoshtirnoq_xato", "qirqilgan"]
VAZNLAR = [40, 20, 12, 8, 6, 6, 4, 4]
UCH = "`" * 3 # kod bloki belgisi (uchta teskari tirnoq)
class SoxtaLLM:
"""STUB - haqiqiy model emas. Bir xil yozuvni turli 'buzilgan' formatlarda qaytaradi.
Format turlari va ularning ulushi (VAZNLAR) biz tanlagan faraz; haqiqiy modelda
ular boshqacha bo'ladi. Qaytaradi: (matn, stop_reason) - xuddi API dagi kabi.
"""
def __init__(self, urug):
self.rng = random.Random(urug)
def javob(self, yozuv):
tur = self.rng.choices(TURLAR, VAZNLAR)[0]
return tur, *self.shakllantir(yozuv, tur)
def shakllantir(self, yozuv, tur):
j = json.dumps(yozuv, ensure_ascii=False)
j2 = json.dumps(yozuv, ensure_ascii=False, indent=2)
if tur == "toza":
return j, "end_turn"
if tur == "kod_bloki":
return UCH + "json\n" + j2 + "\n" + UCH, "end_turn"
if tur == "matn_bilan":
return "Albatta! Mana natija:\n" + j + "\nYana savol bo'lsa, yozing.", "end_turn"
if tur == "ortiqcha_vergul":
return j2[:-2] + ",\n}", "end_turn"
if tur == "izohli":
q = j2.split("\n")
q[2] += " // yosh taxminiy"
return "\n".join(q), "end_turn"
if tur == "python_dict":
return str(yozuv), "end_turn"
if tur == "qoshtirnoq_xato":
j = json.dumps(dict(yozuv, izoh="__Q__"), ensure_ascii=False)
return j.replace('"__Q__"', '"u "a\'lo" deb yozdi"'), "end_turn"
return j[: int(len(j) * 0.7)], "max_tokens"
def kod_blokidan(matn):
m = re.search(UCH + r"(?:json)?[ \t]*\n(.*?)" + UCH, matn, re.S)
return m.group(1) if m else None
def muvozanatli_obyektlar(matn):
"""Yuqori darajadagi {...} larni qavs sanab topadi; satr ichidagi qavslar hisobga olinmaydi."""
topildi, chuqurlik, ichida, escape, bosh = [], 0, None, False, None
for i, c in enumerate(matn):
if ichida:
if escape:
escape = False
elif c == "\\":
escape = True
elif c == ichida:
ichida = None
elif chuqurlik and (c == '"' or (c == "'" and matn[i - 1] in " {[:,")):
ichida = c
elif c == "{":
if chuqurlik == 0:
bosh = i
chuqurlik += 1
elif c == "}" and chuqurlik:
chuqurlik -= 1
if chuqurlik == 0:
topildi.append(matn[bosh:i + 1])
return topildi # yopilmagan obyekt (qirqilgan) ro'yxatga kirmaydi
def yengil_tuzat(s):
"""Satrlardan TASHQARIDA: izohlar, ortiqcha vergul, True/False/None, bitta qo'shtirnoq."""
chiqish, i, n = [], 0, len(s)
while i < n:
c = s[i]
if c == '"': # oddiy JSON satr - o'zgarishsiz
j = i + 1
while j < n and s[j] != '"':
j += 2 if s[j] == "\\" else 1
chiqish.append(s[i:j + 1])
i = j + 1
elif c == "'" and "".join(chiqish).rstrip()[-1:] in ("{", "[", ":", ","):
j = i + 1
while j < n and not (s[j] == "'" and s[j + 1:j + 2].strip()[:1] in (",", "}", "]", ":")):
j += 2 if s[j] == "\\" else 1
chiqish.append(json.dumps(s[i + 1:j], ensure_ascii=False))
i = j + 1
elif s.startswith("//", i):
while i < n and s[i] != "\n":
i += 1
elif c == ",":
k = i + 1
while k < n and s[k] in " \t\r\n":
k += 1
if k < n and s[k] in "}]":
i += 1 # ortiqcha vergul tashlanadi
else:
chiqish.append(c)
i += 1
else:
m = re.match(r"True|False|None", s[i:])
if m and not (i and (s[i - 1].isalnum() or s[i - 1] == "_")):
chiqish.append({"True": "true", "False": "false", "None": "null"}[m.group()])
i += m.end()
else:
chiqish.append(c)
i += 1
return "".join(chiqish)
def ajrat(matn):
"""Bosqichma-bosqich: to'g'ridan -> kod bloki -> qavs skaneri -> yengil tuzatish."""
try:
return json.loads(matn), "to'g'ridan"
except json.JSONDecodeError:
pass
blok = kod_blokidan(matn)
if blok is not None:
nomzod = blok
else:
obyektlar = muvozanatli_obyektlar(matn)
if not obyektlar:
return None, "topilmadi"
if len(obyektlar) > 1:
return None, "bir nechta obyekt" # qaysi biri javob - noaniq
nomzod = obyektlar[0]
try:
return json.loads(nomzod), "kod bloki" if blok is not None else "qavs skaneri"
except json.JSONDecodeError:
pass
try:
return json.loads(yengil_tuzat(nomzod)), "tuzatish bilan"
except json.JSONDecodeError:
return None, "buzilgan"
def naiv(matn):
try:
return json.loads(matn)
except json.JSONDecodeError:
return None
def yozuv_yasa(rng):
return {"ism": rng.choice(ISMLAR), "yosh": rng.randint(17, 60),
"shahar": rng.choice(SHAHARLAR), "talaba": rng.random() < 0.4,
"izoh": rng.choice(IZOHLAR)}
def main() -> None:
rng = random.Random(0)
llm = SoxtaLLM(1)
print("=== 1. Buzilgan javoblar namunasi (stub) ===")
namuna = {"ism": "Ra'no", "yosh": 21, "shahar": "Qo'qon", "talaba": True, "izoh": None}
for tur in ["matn_bilan", "python_dict", "qirqilgan"]:
matn, sabab = llm.shakllantir(namuna, tur)
qisqa = matn.replace("\n", "\\n")
print(f" {tur:<13} {qisqa[:62]!s}")
print(f" {'':<13} -> naiv: {'OK' if naiv(matn) else 'xato'}, "
f"ajrat: {ajrat(matn)[1]}, stop_reason={sabab}")
print("\n=== 2. 600 ta javob: naiv json.loads va bosqichli ajratish ===")
natija = {t: [0, 0, 0, 0] for t in TURLAR} # soni, naiv OK, ajrat OK, jim xato
bosqich = {}
for _ in range(600):
yozuv = yozuv_yasa(rng)
tur, matn, sabab = llm.javob(yozuv)
obj, b = ajrat(matn)
s = natija[tur]
s[0] += 1
s[1] += naiv(matn) == yozuv
s[2] += obj == yozuv
s[3] += obj is not None and obj != yozuv
bosqich[b] = bosqich.get(b, 0) + 1
print(" tur soni naiv OK ajrat OK jim xato")
for t in TURLAR:
s = natija[t]
print(f" {t:<16} {s[0]:>5} {s[1] / s[0]:>8.0%} {s[2] / s[0]:>9.0%} {s[3]:>9}")
jami = [sum(natija[t][k] for t in TURLAR) for k in range(4)]
print(f" {'JAMI':<16} {jami[0]:>5} {jami[1] / jami[0]:>8.1%} {jami[2] / jami[0]:>9.1%} "
f"{jami[3]:>9}")
print("\n=== 3. Qaysi bosqich ishladi ===")
for b, n in sorted(bosqich.items(), key=lambda x: -x[1]):
print(f" {b:<15} {n:>4}")
print("\n=== 4. Chekka holatlar: satr ichidagi '}', '//' va apostrof ===")
holatlar = [
'Natija: {"sayt": "https://wisar.uz", "izoh": "qavs } ichida"} tamom',
'{"sayt": "https://wisar.uz", "teglar": ["a", "b",],}',
"{'ism': 'Ra'no', 'yosh': 21}",
'Misol: {"yosh": 0}. Javob: {"yosh": 34}',
]
for h in holatlar:
obj, b = ajrat(h)
print(f" {h[:44]:<45} -> {b}: {json.dumps(obj, ensure_ascii=False)[:40]}")
print("\n=== 5. Xulosa (natijadan) ===")
if jami[3] == 0:
print(" jim xato 0: tuzatish hech qachon noto'g'ri qiymat 'ixtiro' qilmadi")
else:
print(f" DIQQAT: {jami[3]} ta jim xato - tuzatish qiymatni buzmoqda")
yiqilgan = [t for t in TURLAR if natija[t][2] == 0]
print(f" tiklab bo'lmadi: {', '.join(yiqilgan)} - bular qayta so'rash kerak")
print(" qirqilganini stop_reason == 'max_tokens' dan oldindan bilish mumkin")
print(" \u2b50 Tuzatish - faqat bir ma'noli holatlar; shubhali javobni tuzatmang, qayta so'rang")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Buzilgan javoblar namunasi (stub) ===
matn_bilan Albatta! Mana natija:\n{"ism": "Ra'no", "yosh": 21, "shahar":
-> naiv: xato, ajrat: qavs skaneri, stop_reason=end_turn
python_dict {'ism': "Ra'no", 'yosh': 21, 'shahar': "Qo'qon", 'talaba': Tru
-> naiv: xato, ajrat: tuzatish bilan, stop_reason=end_turn
qirqilgan {"ism": "Ra'no", "yosh": 21, "shahar": "Qo'qon", "tala
-> naiv: xato, ajrat: topilmadi, stop_reason=max_tokens
=== 2. 600 ta javob: naiv json.loads va bosqichli ajratish ===
tur soni naiv OK ajrat OK jim xato
toza 234 100% 100% 0
kod_bloki 124 0% 100% 0
matn_bilan 70 0% 100% 0
ortiqcha_vergul 47 0% 100% 0
izohli 40 0% 100% 0
python_dict 42 0% 100% 0
qoshtirnoq_xato 16 0% 0% 0
qirqilgan 27 0% 0% 0
JAMI 600 39.0% 92.8% 0
=== 3. Qaysi bosqich ishladi ===
to'g'ridan 234
tuzatish bilan 129
kod bloki 124
qavs skaneri 70
topilmadi 27
buzilgan 16
=== 4. Chekka holatlar: satr ichidagi '}', '//' va apostrof ===
Natija: {"sayt": "https://wisar.uz", "izoh": -> qavs skaneri: {"sayt": "https://wisar.uz", "izoh": "qa
{"sayt": "https://wisar.uz", "teglar": ["a", -> tuzatish bilan: {"sayt": "https://wisar.uz", "teglar": [
{'ism': 'Ra'no', 'yosh': 21} -> tuzatish bilan: {"ism": "Ra'no", "yosh": 21}
Misol: {"yosh": 0}. Javob: {"yosh": 34} -> bir nechta obyekt: null
=== 5. Xulosa (natijadan) ===
jim xato 0: tuzatish hech qachon noto'g'ri qiymat 'ixtiro' qilmadi
tiklab bo'lmadi: qoshtirnoq_xato, qirqilgan - bular qayta so'rash kerak
qirqilganini stop_reason == 'max_tokens' dan oldindan bilish mumkin
⭐ Tuzatish - faqat bir ma'noli holatlar; shubhali javobni tuzatmang, qayta so'rangNima ko'rsatdi: stub bir xil yozuvni sakkiz xil ko'rinishda qaytardi — tur ulushlari (VAZNLAR) biz tanlagan faraz, haqiqiy modelda ular boshqacha bo'ladi. Naiv json.loads faqat toza javoblarni o'qidi: 600 tadan 39.0%. Bosqichli ajrat — 92.8%: kod bloki regex bilan, "Albatta! Mana natija:" kabi ortiqcha matn qavs skaneri bilan, ortiqcha vergul, // izoh va Python lug'ati (True, None, bitta qo'shtirnoq) yengil tuzatish bilan o'qildi. 3-bo'lim qaysi bosqich necha marta ishlaganini ko'rsatadi — amalda bu statistikani loglash foydali: masalan, "tuzatish bilan" ulushi to'satdan o'ssa, prompt yoki model versiyasi o'zgargan bo'lishi mumkin. Eng muhim ustun — jim xato: 0, ya'ni tuzatish biror marta ham noto'g'ri qiymat "ixtiro" qilmadi. Ichki qo'shtirnoqli va qirqilgan javoblar ataylab 0% — ularni ishonchli tiklab bo'lmaydi, qirqilganini esa stop_reason == "max_tokens" dan oldindan bilish mumkin. 4-bo'lim chekka holatlar: satr ichidagi } va https:// dagi // skanerni ham, tuzatishni ham chalg'itmadi; 'Ra'no' dagi apostrof to'g'ri o'qildi (satr tugashi — keyingi belgi , } : bo'lgan qo'shtirnoq). Oxirgi holat (Misol: {...}. Javob: {...}) — ikki obyekt: birinchisini olish jim xato bo'lardi (yosh: 0), shuning uchun ajrat uni rad etdi. Bog'liq bo'limlar: 2.2.
Misol 2 — Sxema validatori, biznes qoidasi va moslashtirish
"""JSON Schema ning kichik qismi noldan: validator, biznes qoidalari, dataclass va xavfsiz moslashtirish."""
import copy
import random
import re
from dataclasses import dataclass
SXEMA = {
"type": "object",
"required": ["mijoz", "telefon", "mahsulotlar", "tolov", "jami"],
"additionalProperties": False,
"properties": {
"mijoz": {"type": "string", "minLength": 2},
"telefon": {"type": "string", "pattern": r"^\+998\d{9}$"},
"mahsulotlar": {
"type": "array", "minItems": 1,
"items": {
"type": "object", "required": ["nomi", "soni", "narx"],
"additionalProperties": False,
"properties": {"nomi": {"type": "string", "minLength": 1},
"soni": {"type": "integer", "minimum": 1, "maximum": 100},
"narx": {"type": "number", "minimum": 0}}}},
"tolov": {"type": "string", "enum": ["naqd", "karta", "click", "payme"]},
"jami": {"type": "number", "minimum": 0},
"izoh": {"type": ["string", "null"]},
},
}
def tur_mosmi(x, tur):
if tur == "null":
return x is None
if tur == "boolean":
return type(x) is bool
if tur == "integer":
return type(x) is int # True/False int EMAS deb hisoblanadi
if tur == "number":
return type(x) in (int, float)
return isinstance(x, {"string": str, "array": list, "object": dict}[tur])
def tekshir(x, sx, yol="$"):
"""JSON Schema kichik qismi: type, required, additionalProperties, enum,
minimum/maximum, minLength, pattern, minItems, items. Xatolar ro'yxatini qaytaradi."""
turlar = sx["type"] if isinstance(sx["type"], list) else [sx["type"]]
if not any(tur_mosmi(x, t) for t in turlar):
return [f"{yol}: kutilgan {'/'.join(turlar)}, keldi {type(x).__name__} ({x!r})"]
xatolar = []
if "enum" in sx and x not in sx["enum"]:
xatolar.append(f"{yol}: {x!r} ruxsat etilganlar ichida yo'q {sx['enum']}")
if "minimum" in sx and x < sx["minimum"]:
xatolar.append(f"{yol}: {x} < minimum {sx['minimum']}")
if "maximum" in sx and x > sx["maximum"]:
xatolar.append(f"{yol}: {x} > maximum {sx['maximum']}")
if isinstance(x, str):
if len(x) < sx.get("minLength", 0):
xatolar.append(f"{yol}: satr juda qisqa")
if "pattern" in sx and not re.search(sx["pattern"], x):
xatolar.append(f"{yol}: {x!r} naqshga mos emas {sx['pattern']}")
if isinstance(x, list):
if len(x) < sx.get("minItems", 0):
xatolar.append(f"{yol}: kamida {sx['minItems']} element kerak")
for i, el in enumerate(x):
xatolar += tekshir(el, sx["items"], f"{yol}[{i}]")
if isinstance(x, dict):
for k in sx.get("required", []):
if k not in x:
xatolar.append(f"{yol}: majburiy maydon yo'q: {k!r}")
for k, v in x.items():
if k in sx["properties"]:
xatolar += tekshir(v, sx["properties"][k], f"{yol}.{k}")
elif sx.get("additionalProperties", True) is False:
xatolar.append(f"{yol}: ortiqcha maydon {k!r}")
return xatolar
def biznes_qoidalari(x):
"""Sxema ifodalay olmaydigan qoidalar: maydonlar orasidagi moslik."""
hisob = sum(m["soni"] * m["narx"] for m in x["mahsulotlar"])
return [] if abs(hisob - x["jami"]) < 0.5 else [f"$.jami: {x['jami']} != hisoblangan {hisob}"]
@dataclass(frozen=True)
class Qator:
nomi: str
soni: int
narx: float
@dataclass(frozen=True)
class Buyurtma:
mijoz: str
telefon: str
mahsulotlar: tuple
tolov: str
jami: float
izoh: str | None = None
@classmethod
def yarat(cls, d):
return cls(d["mijoz"], d["telefon"], tuple(Qator(**m) for m in d["mahsulotlar"]),
d["tolov"], d["jami"], d.get("izoh"))
def moslashtir(d):
"""Faqat BIR MA'NOLI tuzatishlar: '3' -> 3, 'Karta' -> 'karta',
'90 123 45 67' -> '+998901234567', noma'lum maydonlarni tashlash."""
d = copy.deepcopy(d)
for m in d.get("mahsulotlar", []):
if isinstance(m.get("soni"), str) and m["soni"].strip().isdigit():
m["soni"] = int(m["soni"])
if isinstance(d.get("tolov"), str):
d["tolov"] = d["tolov"].strip().lower()
if isinstance(d.get("telefon"), str):
raqam = re.sub(r"\D", "", d["telefon"])
if len(raqam) == 9:
raqam = "998" + raqam
if len(raqam) == 12 and raqam.startswith("998"):
d["telefon"] = "+" + raqam
for k in list(d):
if k not in SXEMA["properties"]:
del d[k]
return d
MAHSULOT = [("kitob", 45000), ("daftar", 6000), ("ruchka", 3500), ("sumka", 120000)]
BUZILISHLAR = [("soni_satr", 0.08), ("telefon_format", 0.10), ("tolov_katta", 0.06),
("ortiqcha_maydon", 0.05), ("jami_xato", 0.04), ("soni_nol", 0.02)]
def haqiqat(rng):
qatorlar = [{"nomi": n, "soni": rng.randint(1, 5), "narx": float(p)}
for n, p in rng.sample(MAHSULOT, rng.randint(1, 3))]
return {"mijoz": rng.choice(["Dilnoza Karimova", "Bobur Aliyev", "Ra'no Saidova"]),
"telefon": "+99890" + "".join(str(rng.randint(0, 9)) for _ in range(7)),
"mahsulotlar": qatorlar, "tolov": rng.choice(["naqd", "karta", "click", "payme"]),
"jami": sum(q["soni"] * q["narx"] for q in qatorlar), "izoh": None}
def soxta_llm(h, rng):
"""STUB - haqiqiy model emas: to'g'ri javobni olib, BUZILISHLAR ehtimollari bilan buzadi."""
d = copy.deepcopy(h)
for tur, p in BUZILISHLAR:
if rng.random() >= p:
continue
if tur == "soni_satr":
d["mahsulotlar"][0]["soni"] = str(d["mahsulotlar"][0]["soni"])
elif tur == "telefon_format":
r = d["telefon"][4:]
d["telefon"] = f"{r[:2]} {r[2:5]} {r[5:7]} {r[7:]}"
elif tur == "tolov_katta":
d["tolov"] = d["tolov"].capitalize()
elif tur == "ortiqcha_maydon":
d["valyuta"] = "so'm"
elif tur == "jami_xato":
d["jami"] = d["jami"] + 1000
elif tur == "soni_nol":
d["mahsulotlar"][-1]["soni"] = 0
return d
def main() -> None:
rng = random.Random(0)
print("=== 1. Validator xabarlari (yo'l bilan) ===")
yaxshi = haqiqat(rng)
holatlar = {
"to'g'ri": yaxshi,
"soni satr": {**yaxshi, "mahsulotlar": [{**yaxshi["mahsulotlar"][0], "soni": "2"}]},
"enum+ortiqcha": {**yaxshi, "tolov": "Karta", "valyuta": "so'm"},
"yo'q maydon": {k: v for k, v in yaxshi.items() if k != "telefon"},
"soni = True": {**yaxshi, "mahsulotlar": [{**yaxshi["mahsulotlar"][0], "soni": True}]},
}
for nom, d in holatlar.items():
x = tekshir(d, SXEMA)
print(f" {nom:<14} {'OK' if not x else x[0]}" + (f" (+{len(x) - 1})" if len(x) > 1 else ""))
print(f" isinstance(True, int) = {isinstance(True, int)} - shuning uchun type(x) is int")
print("\n=== 2. Sxema o'tdi, lekin biznes qoidasi buzildi ===")
d = {**yaxshi, "jami": yaxshi["jami"] + 1000}
print(f" sxema xatolari: {len(tekshir(d, SXEMA))}, biznes: {biznes_qoidalari(d)}")
print("\n=== 3. Tasdiqlangan lug'at -> dataclass ===")
b = Buyurtma.yarat(yaxshi)
print(f" {type(b).__name__}: mijoz={b.mijoz!r}, qatorlar={len(b.mahsulotlar)}, "
f"1-qator={b.mahsulotlar[0]}")
print("\n=== 4. 400 ta stub javob: qat'iy va moslashtirilgan qabul ===")
print(" siyosat qabul qabul, lekin noto'g'ri rad etildi")
juftlar = []
for _ in range(400):
h = haqiqat(rng)
juftlar.append((h, soxta_llm(h, rng)))
natija = {}
for siyosat in ["qat'iy", "moslashtirish", "moslash + biznes"]:
qabul = notogri = 0
for h, d in juftlar:
if siyosat != "qat'iy":
d = moslashtir(d)
xato = tekshir(d, SXEMA)
if not xato and siyosat == "moslash + biznes":
xato = biznes_qoidalari(d)
if not xato:
qabul += 1
notogri += d != h
natija[siyosat] = (qabul, notogri)
print(f" {siyosat:<17} {qabul / 400:>6.1%} {notogri:>18} ta {400 - qabul:>12}")
toza = sum(d == h for h, d in juftlar)
print(f" stub javoblari ichida aynan to'g'rilari: {toza / 400:.1%}")
print("\n=== 5. Xulosa (natijadan) ===")
q, m, mb = natija["qat'iy"], natija["moslashtirish"], natija["moslash + biznes"]
print(f" moslashtirish qabulni {q[0] / 400:.1%} -> {m[0] / 400:.1%} ga oshirdi")
if m[1] > 0 and mb[1] == 0:
print(f" lekin {m[1]} ta noto'g'ri javob o'tib ketdi; biznes qoidasi ularni to'xtatdi")
print(" \u2b50 Sxema - shakl; ma'no uchun maydonlararo qoidalar ham kerak")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Validator xabarlari (yo'l bilan) ===
to'g'ri OK
soni satr $.mahsulotlar[0].soni: kutilgan integer, keldi str ('2')
enum+ortiqcha $.tolov: 'Karta' ruxsat etilganlar ichida yo'q ['naqd', 'karta', 'click', 'payme'] (+1)
yo'q maydon $: majburiy maydon yo'q: 'telefon'
soni = True $.mahsulotlar[0].soni: kutilgan integer, keldi bool (True)
isinstance(True, int) = True - shuning uchun type(x) is int
=== 2. Sxema o'tdi, lekin biznes qoidasi buzildi ===
sxema xatolari: 0, biznes: ['$.jami: 586000.0 != hisoblangan 585000.0']
=== 3. Tasdiqlangan lug'at -> dataclass ===
Buyurtma: mijoz='Bobur Aliyev', qatorlar=2, 1-qator=Qator(nomi='sumka', soni=3, narx=120000.0)
=== 4. 400 ta stub javob: qat'iy va moslashtirilgan qabul ===
siyosat qabul qabul, lekin noto'g'ri rad etildi
qat'iy 74.0% 11 ta 104
moslashtirish 98.8% 16 ta 5
moslash + biznes 94.8% 0 ta 21
stub javoblari ichida aynan to'g'rilari: 71.2%
=== 5. Xulosa (natijadan) ===
moslashtirish qabulni 74.0% -> 98.8% ga oshirdi
lekin 16 ta noto'g'ri javob o'tib ketdi; biznes qoidasi ularni to'xtatdi
⭐ Sxema - shakl; ma'no uchun maydonlararo qoidalar ham kerakNima ko'rsatdi: 1-bo'limda validator har xatoni yo'l bilan qaytardi: $.mahsulotlar[0].soni: kutilgan integer, keldi str ('2') — bunday xabarni modelga qaytarsa, u aynan qaysi maydonni tuzatishni biladi. soni = True holati Python tuzog'ini ko'rsatadi: isinstance(True, int) — True, shuning uchun validator type(x) is int bilan tekshiradi va bool ni rad etadi. 2-bo'lim: jami summa 1000 so'mga oshirilgan buyurtma sxemadan xatosiz o'tdi (sxema xatolari: 0), uni faqat maydonlararo qoida ushladi. 3-bo'lim: tekshirilgan lug'at frozen dataclass ga aylandi. 4-bo'lim 400 ta stub javobda uch siyosatni solishtirdi (stub buzilish ehtimollari faraziy). Qat'iy siyosat 74.0% ni qabul qildi, lekin ular ichida ham 11 ta noto'g'ri bor — bular "jami" xatolari, shaklan to'g'ri. Moslashtirish ("3" → 3, "Karta" → "karta", telefon formati, noma'lum kalitni tashlash) qabulni 98.8% ga oshirdi, noto'g'ri qabullar esa 16 ga ko'paydi — endi ko'proq javob validatordan o'tadi, jumladan "jami" xatolilari ham. Biznes qoidasi qo'shilganda noto'g'ri qabul 0, qabul 94.8%: rad etilgan 21 ta — "jami" xatolari va soni = 0 holatlari. Stub javoblarining faqat 71.2% i aynan to'g'ri edi; moslashtirish + biznes qoidasi 94.8% ni xatosiz qabul qildi. Bog'liq bo'limlar: 2.3, 2.4.
Misol 3 — Xato bilan qayta so'rash: temperatura, urinishlar va xarajat
"""Xato bilan qayta so'rash (repair loop): urinishlar chegarasi, temperatura va xarajat."""
import hashlib
import json
import math
import random
import numpy as np
MAYDONLAR = ["ism", "shahar", "yosh"]
SHAHARLAR = ["Toshkent", "Samarqand", "Buxoro", "Xiva", "Termiz", "Nukus"]
ISMLAR = ["Dilnoza", "Bobur", "Malika", "Sardor", "Nodira", "Jasur", "Zarina", "Aziz"]
class SoxtaLLM:
"""STUB - haqiqiy model emas. Kod tuzilishi haqiqiy API bilan bir xil, xulqi esa FARAZ:
- oddiy yozuvda buzuq javob ehtimoli 0.25, 'qiyin' yozuvda 0.80;
- promptda oldingi xato matni bo'lsa: 0.10 va 0.60 (faraz, haqiqiy modelda o'lchang);
- temperature == 0 da tasodif faqat prompt matnidan (sha256) olinadi: bir xil prompt ->
aynan bir xil javob; temperature > 0 da har chaqiruv yangi tasodif.
"""
def __init__(self, urug):
self.rng = random.Random(urug)
self.chaqiruvlar = 0
def yarat(self, prompt, haqiqat, qiyin, temperature):
self.chaqiruvlar += 1
if temperature == 0:
r = random.Random(int(hashlib.sha256(prompt.encode()).hexdigest()[:12], 16))
else:
r = self.rng
p = (0.60 if qiyin else 0.10) if "XATO:" in prompt else (0.80 if qiyin else 0.25)
if r.random() >= p:
return json.dumps(haqiqat, ensure_ascii=False)
tur = r.choice(["maydon_yoq", "json_buzuq", "yosh_satr"])
if tur == "maydon_yoq":
return json.dumps({k: v for k, v in haqiqat.items() if k != "shahar"}, ensure_ascii=False)
if tur == "yosh_satr":
return json.dumps({**haqiqat, "yosh": f"{haqiqat['yosh']} yosh"}, ensure_ascii=False)
return json.dumps(haqiqat, ensure_ascii=False)[:-1]
def tekshir(matn):
"""None - hammasi joyida; aks holda modelga qaytariladigan aniq xato matni."""
try:
d = json.loads(matn)
except json.JSONDecodeError as e:
return None, f"JSON o'qilmadi ({e.msg}, {e.pos}-belgi)"
yoq = [k for k in MAYDONLAR if k not in d]
if yoq:
return None, f"majburiy maydon yo'q: {', '.join(yoq)}"
if type(d["yosh"]) is not int:
return None, f"'yosh' butun son bo'lishi kerak, keldi {d['yosh']!r}"
return d, None
def token(matn):
return math.ceil(len(matn) / 4) # taxminiy hisob (25.2-dars), faqat solishtirish uchun
def sorov(llm, matn, haqiqat, qiyin, strategiya, maks_urinish):
"""Qaytaradi: (natija yoki None, chaqiruvlar soni, jami kirish tokenlari)."""
asosiy = ("Matndan ism, shahar va yosh ni JSON ko'rinishida qaytaring. "
"Faqat JSON, boshqa matn yo'q.\nMatn: " + matn)
prompt, kirish = asosiy, 0
temperature = 0.7 if strategiya == "qayta, T=0.7" else 0.0
for urinish in range(1, maks_urinish + 1):
kirish += token(prompt)
javob = llm.yarat(prompt, haqiqat, qiyin, temperature)
d, xato = tekshir(javob)
if d is not None:
return d, urinish, kirish
if strategiya == "xato bilan":
prompt = (asosiy + "\n\nOldingi javobingiz: " + javob
+ "\nXATO: " + xato + "\nFaqat tuzatilgan JSON ni qaytaring.")
return None, maks_urinish, kirish
def tajriba(urug, strategiya, maks_urinish, n=300):
rng = random.Random(1000 + urug)
llm = SoxtaLLM(urug)
ok, chaqiruv, kirish, qiyin_yiqildi, yiqildi = 0, 0, 0, 0, 0
for _ in range(n):
h = {"ism": rng.choice(ISMLAR), "shahar": rng.choice(SHAHARLAR), "yosh": rng.randint(18, 70)}
qiyin = rng.random() < 0.10
matn = f"Men {h['ism']}, {h['yosh']} yoshdaman, {h['shahar']}da yashayman."
d, c, k = sorov(llm, matn, h, qiyin, strategiya, maks_urinish)
ok += d == h
chaqiruv += c
kirish += k
if d is None:
yiqildi += 1
qiyin_yiqildi += qiyin
return ok / n, chaqiruv / n, kirish / n, qiyin_yiqildi, yiqildi
def main() -> None:
print("=== 1. Bitta so'rov: xato matni modelga qaytariladi ===")
llm = SoxtaLLM(3)
h = {"ism": "Malika", "shahar": "Xiva", "yosh": 34}
asosiy = "Matndan ism, shahar va yosh ni JSON ko'rinishida qaytaring.\nMatn: ..."
prompt = asosiy
for urinish in range(1, 4):
javob = llm.yarat(prompt, h, True, 0.0)
d, xato = tekshir(javob)
print(f" urinish {urinish}: prompt {token(prompt):>3} token -> "
f"{'OK ' + str(d) if d else 'XATO: ' + xato}")
if d:
break
prompt = asosiy + "\nOldingi javobingiz: " + javob + "\nXATO: " + xato
print("\n=== 2. Strategiya va urinishlar chegarasi (5 urug' x 300 so'rov) ===")
print(" strategiya K muvaffaqiyat chaqiruv/so'rov kirish token/so'rov")
jadval = {}
for strategiya in ["qayta, T=0", "qayta, T=0.7", "xato bilan"]:
for K in (1, 2, 3, 5):
r = np.array([tajriba(s, strategiya, K)[:3] for s in range(5)])
jadval[strategiya, K] = r
if K in (1, 3, 5) or strategiya == "xato bilan":
print(f" {strategiya:<14} {K:>3} {r[:, 0].mean():>12.1%} {r[:, 1].mean():>15.2f} "
f"{r[:, 2].mean():>19.1f}")
print("\n=== 3. K = 3 da juftlashgan farq (bir xil urug'lar, SE) ===")
for a, b in [("qayta, T=0.7", "qayta, T=0"), ("xato bilan", "qayta, T=0.7")]:
f = jadval[a, 3][:, 0] - jadval[b, 3][:, 0]
se = f.std(ddof=1) / math.sqrt(len(f))
print(f" {a} - {b}: {f.mean():+.3f}, SE {se:.3f} -> "
f"{'sezilarli' if abs(f.mean()) > 2 * se else 'sezilarli emas'}")
print("\n=== 4. Kim yiqiladi: 'xato bilan', K = 3 ===")
q = [tajriba(s, "xato bilan", 3) for s in range(5)]
yiq, qyiq = sum(x[4] for x in q), sum(x[3] for x in q)
print(f" yiqilgan so'rovlar: {yiq} / 1500, ulardan 'qiyin': {qyiq} ({qyiq / max(1, yiq):.0%})")
print(" (qiyin yozuvlar jami ~10%) -> ularga qo'lda ko'rish yoki boshqa model kerak")
print("\n=== 5. Xulosa (natijadan) ===")
t0 = jadval["qayta, T=0", 5][:, 0].mean() - jadval["qayta, T=0", 1][:, 0].mean()
if abs(t0) < 1e-9:
print(" T=0 va bir xil prompt: 5 urinish 1 urinishdan yaxshi emas - javob aynan takrorlanadi")
x3, x5 = jadval["xato bilan", 3], jadval["xato bilan", 5]
print(f" 'xato bilan' K 3 -> 5: muvaffaqiyat {x3[:, 0].mean():.1%} -> {x5[:, 0].mean():.1%}, "
f"kirish token {x3[:, 2].mean():.0f} -> {x5[:, 2].mean():.0f}")
print(" (farqlar stubdagi FARAZIY ehtimollardan kelib chiqadi - haqiqiy modelda o'lchang)")
print(" \u2b50 Qayta so'rash promptni o'zgartirishi kerak; urinishlarni cheklang, keyin fallback")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bitta so'rov: xato matni modelga qaytariladi ===
urinish 1: prompt 18 token -> XATO: 'yosh' butun son bo'lishi kerak, keldi '34 yosh'
urinish 2: prompt 50 token -> OK {'ism': 'Malika', 'shahar': 'Xiva', 'yosh': 34}
=== 2. Strategiya va urinishlar chegarasi (5 urug' x 300 so'rov) ===
strategiya K muvaffaqiyat chaqiruv/so'rov kirish token/so'rov
qayta, T=0 1 70.7% 1.00 35.6
qayta, T=0 3 70.7% 1.59 56.4
qayta, T=0 5 70.7% 2.17 77.2
qayta, T=0.7 1 72.5% 1.00 35.6
qayta, T=0.7 3 94.8% 1.39 49.3
qayta, T=0.7 5 96.9% 1.47 52.3
xato bilan 1 70.7% 1.00 35.6
xato bilan 2 94.9% 1.29 57.1
xato bilan 3 96.7% 1.34 60.8
xato bilan 5 96.9% 1.41 65.6
=== 3. K = 3 da juftlashgan farq (bir xil urug'lar, SE) ===
qayta, T=0.7 - qayta, T=0: +0.241, SE 0.011 -> sezilarli
xato bilan - qayta, T=0.7: +0.019, SE 0.005 -> sezilarli
=== 4. Kim yiqiladi: 'xato bilan', K = 3 ===
yiqilgan so'rovlar: 50 / 1500, ulardan 'qiyin': 38 (76%)
(qiyin yozuvlar jami ~10%) -> ularga qo'lda ko'rish yoki boshqa model kerak
=== 5. Xulosa (natijadan) ===
T=0 va bir xil prompt: 5 urinish 1 urinishdan yaxshi emas - javob aynan takrorlanadi
'xato bilan' K 3 -> 5: muvaffaqiyat 96.7% -> 96.9%, kirish token 61 -> 66
(farqlar stubdagi FARAZIY ehtimollardan kelib chiqadi - haqiqiy modelda o'lchang)
⭐ Qayta so'rash promptni o'zgartirishi kerak; urinishlarni cheklang, keyin fallbackNima ko'rsatdi: avval ogohlantirish — stubning xulqi faraz: oddiy yozuvda buzuq javob ehtimoli 0.25, qiyin yozuvda 0.80, promptda xato matni bo'lsa 0.10 va 0.60. Demak, jadvaldagi mutlaq raqamlar va xato bilan - qayta, T=0.7 farqi (+0.019, SE 0.005) — farazning oqibati, haqiqiy model haqida da'vo emas. Stubdan mustaqil xulosalar esa bor. Birinchisi: T = 0 da bir xil prompt bilan qayta so'rash mutlaqo foydasiz — 1, 3 va 5 urinishda muvaffaqiyat bir xil 70.7%, lekin 5 urinish so'rovga o'rtacha 2.17 chaqiruv va 77.2 kirish tokeni sarfladi (1 urinishda 35.6). Sabab: deterministik dekodlashda bir xil kirish — aynan bir xil chiqish. T = 0.7 da urinishlar mustaqil bo'ldi (K = 1, 3, 5 da 72.5% → 94.8% → 96.9%), xato matni esa T = 0 da ham promptni o'zgartirdi (K = 1, 2, 3 da 70.7% → 94.9% → 96.7%). Ikkinchisi: foyda tez to'yinadi — xato bilan da K 3 dan 5 ga: 96.7% → 96.9%, kirish tokenlari esa 61 → 66; xato bilan so'rashda prompt uzayadi (oldingi javob + xato), shuning uchun u T=0.7 dan ko'proq token sarfladi (60.8 va 49.3, K = 3). Uchinchisi: yiqilgan 50 so'rovning 76% i "qiyin" yozuvlar (ular jami ~10%) — qayta so'rash bunday holatlarni hal qilmaydi, ular uchun fallback (operator, boshqa model, "aniqlanmadi") kerak. Bog'liq bo'limlar: 2.5.
Misol 4 — Extraction: normallashtirish, manbaga bog'lash va maydon bo'yicha baho
"""Ma'lumot ajratib olish (extraction): normallashtirish, manbaga bog'lash va maydon bo'yicha baholash."""
import math
import random
import re
import textwrap
import numpy as np
ISMLAR = ["Dilnoza Karimova", "Bobur Aliyev", "Malika Tosheva", "Sardor Rahimov",
"Nodira Yusupova", "Jasur Qodirov", "Zarina Ergasheva", "Aziz Nazarov"]
SHAHARLAR = ["Toshkent", "Samarqand", "Buxoro", "Xiva", "Termiz", "Nukus", "Andijon", "Qarshi"]
MAHSULOTLAR = ["kitob", "daftar", "ruchka", "sumka", "kalkulyator", "globus"]
OYLAR = ["yanvar", "fevral", "mart", "aprel", "may", "iyun", "iyul", "avgust",
"sentabr", "oktabr", "noyabr", "dekabr"]
SON_SOZ = {"bir": 1, "ikki": 2, "uch": 3, "to'rt": 4, "besh": 5, "olti": 6}
MAYDONLAR = ["ism", "shahar", "mahsulot", "soni", "summa", "telefon", "sana"]
def hujjat(rng):
"""Matn + haqiqiy (normallangan) qiymatlar + matndagi yuzaki ko'rinishi."""
h, y = {}, {}
h["ism"] = y["ism"] = rng.choice(ISMLAR)
manba, h["shahar"] = rng.sample(SHAHARLAR, 2)
y["shahar"] = h["shahar"]
h["mahsulot"] = y["mahsulot"] = rng.choice(MAHSULOTLAR)
h["soni"] = rng.randint(1, 6)
y["soni"] = rng.choice([str(h["soni"]), [k for k, v in SON_SOZ.items() if v == h["soni"]][0]])
matn = (f"Assalomu alaykum! Men {h['ism']}, {manba}dan yozyapman. {y['soni']} ta "
f"{h['mahsulot']} kerak, {h['shahar']}ga yetkazib bering.")
h["summa"] = h["telefon"] = h["sana"] = y["summa"] = y["telefon"] = y["sana"] = None
if rng.random() < 0.75:
ming = rng.randint(20, 900)
h["summa"] = ming * 1000
y["summa"] = rng.choice([f"{ming} 000 so'm", f"{ming} ming so'm", f"{ming * 1000} so'm"])
matn += " " + rng.choice(["Jami", "Narxi", "To'lov:"]) + " " + y["summa"] + "."
if rng.random() < 0.7:
r = "9" + str(rng.choice([0, 1, 3, 4, 7, 9])) + "".join(str(rng.randint(0, 9)) for _ in range(7))
h["telefon"] = "+998" + r
y["telefon"] = rng.choice([f"+998 {r[:2]} {r[2:5]} {r[5:7]} {r[7:]}",
f"{r[:2]}-{r[2:5]}-{r[5:7]}-{r[7:]}", r])
matn += f" Telefonim: {y['telefon']}."
if rng.random() < 0.6:
kun, oy = rng.randint(1, 28), rng.randint(1, 12)
h["sana"] = f"{oy:02d}-{kun:02d}"
y["sana"] = rng.choice([f"{kun}-{OYLAR[oy - 1]}", f"{kun:02d}.{oy:02d}"])
matn += f" {y['sana']} kuni kerak bo'ladi."
return matn, h, y, manba
def soxta_llm(yuzaki, manba, rng):
"""STUB - haqiqiy model emas: matndagi yuzaki qiymatlarni (formatini o'zgartirmay) qaytaradi
va biz tanlagan FARAZIY xatolarni qo'shadi: 12% - yetkazish shahri o'rniga
jo'natuvchi shahrini qaytarish, yo'q maydonni 30% ehtimol bilan 'o'ylab topish'."""
d = dict(yuzaki)
if rng.random() < 0.12:
d["shahar"] = manba
for k, uydirma in [("telefon", "+998 90 123 45 67"), ("sana", "1-yanvar"),
("summa", "100 000 so'm")]:
if d[k] is None and rng.random() < 0.30:
d[k] = uydirma
return d
def norm_son(x):
if x is None:
return None
x = str(x).strip().lower()
return int(x) if x.isdigit() else SON_SOZ.get(x)
def norm_summa(x):
if x is None:
return None
m = re.fullmatch(r"(\d+)\s*(ming|000)?\s*so'm", str(x).strip())
if not m:
return None
return int(m.group(1)) * (1000 if m.group(2) else 1)
def norm_telefon(x):
if x is None:
return None
r = re.sub(r"\D", "", str(x))
r = "998" + r if len(r) == 9 else r
return "+" + r if len(r) == 12 and r.startswith("998") else None
def norm_sana(x):
if x is None:
return None
x = str(x).strip().lower()
m = re.fullmatch(r"(\d{1,2})-([a-z]+)", x)
if m and m.group(2) in OYLAR:
return f"{OYLAR.index(m.group(2)) + 1:02d}-{int(m.group(1)):02d}"
m = re.fullmatch(r"(\d{1,2})\.(\d{1,2})", x)
return f"{int(m.group(2)):02d}-{int(m.group(1)):02d}" if m else None
NORM = {"ism": lambda x: x, "shahar": lambda x: x, "mahsulot": lambda x: x,
"soni": norm_son, "summa": norm_summa, "telefon": norm_telefon, "sana": norm_sana}
def manbada_bormi(xom, matn):
"""Manbaga bog'lash (grounding): ajratilgan qiymat matnda haqiqatan uchraydimi?"""
if xom is None:
return True
if re.sub(r"\D", "", str(xom)) and len(re.sub(r"\D", "", str(xom))) >= 7:
return re.sub(r"\D", "", str(xom))[-9:] in re.sub(r"\D", "", matn)
return str(xom).lower() in matn.lower()
def quvur(xom, matn):
"""B: normallashtirish + manbaga bog'lash; tekshiruvdan o'tmagan qiymat -> None."""
return {k: (NORM[k](xom[k]) if manbada_bormi(xom[k], matn) else None) for k in MAYDONLAR}
def main() -> None:
rng, rng_llm = random.Random(0), random.Random(1)
data = []
for _ in range(400):
matn, h, y, manba = hujjat(rng)
data.append((matn, h, soxta_llm(y, manba, rng_llm)))
print("=== 1. Bitta hujjat ===")
matn, h, xom = data[3]
for i, qator in enumerate(textwrap.wrap(matn, 80)):
print((" matn: " if i == 0 else " ") + qator)
print(f" stub (xom): soni={xom['soni']!r}, summa={xom['summa']!r}, telefon={xom['telefon']!r}")
b = quvur(xom, matn)
print(f" quvur (B): soni={b['soni']!r}, summa={b['summa']!r}, telefon={b['telefon']!r}")
print("\n=== 2. Maydon bo'yicha aniqlik (400 hujjat) ===")
print(" maydon A qat'iy A norm. B quvur uydirma A -> B")
toliq = {"A qat'iy": [], "A norm.": [], "B quvur": []}
for matn, h, xom in data:
a_norm = {k: NORM[k](xom[k]) for k in MAYDONLAR}
bq = quvur(xom, matn)
toliq["A qat'iy"].append([xom[k] == h[k] for k in MAYDONLAR])
toliq["A norm."].append([a_norm[k] == h[k] for k in MAYDONLAR])
toliq["B quvur"].append([bq[k] == h[k] for k in MAYDONLAR])
uyd = {}
for i, k in enumerate(MAYDONLAR):
yoq = [(matn, xom) for matn, h, xom in data if h[k] is None]
ua = sum(xom[k] is not None for _, xom in yoq)
ub = sum(quvur(xom, matn)[k] is not None for matn, xom in yoq)
uyd[k] = (ua, ub, len(yoq))
qatorlar = [np.mean([r[i] for r in toliq[s]]) for s in toliq]
print(f" {k:<9} {qatorlar[0]:>9.1%} {qatorlar[1]:>8.1%} {qatorlar[2]:>8.1%} "
+ (f" {ua:>3} -> {ub} (/{len(yoq)})" if yoq else ""))
print("\n=== 3. Yozuv darajasida va juftlashgan farq ===")
for s in toliq:
print(f" {s:<9} barcha 7 maydon to'g'ri: {np.mean([all(r) for r in toliq[s]]):.1%}, "
f"maydonlar o'rtachasi {np.mean(toliq[s]):.1%}")
f = np.mean(toliq["B quvur"], axis=1) - np.mean(toliq["A norm."], axis=1)
se = f.std(ddof=1) / math.sqrt(len(f))
print(f" B - A norm. (hujjat bo'yicha): {f.mean():+.4f}, SE {se:.4f} -> "
f"{'sezilarli' if abs(f.mean()) > 2 * se else 'sezilarli emas'}")
print("\n=== 4. B da qolgan xatolar ===")
qoldi = {}
for matn, h, xom in data:
bq = quvur(xom, matn)
for k in MAYDONLAR:
if bq[k] != h[k]:
qoldi[k] = qoldi.get(k, 0) + 1
for k, n in sorted(qoldi.items(), key=lambda x: -x[1]):
print(f" {k:<9} {n:>3} ta")
print("\n=== 5. Xulosa (natijadan) ===")
aq, an = np.mean(toliq["A qat'iy"]), np.mean(toliq["A norm."])
print(f" qat'iy satr taqqoslash aniqlikni {an - aq:.1%} ga past ko'rsatdi - format farqi xato emas")
ua, ub = sum(v[0] for v in uyd.values()), sum(v[1] for v in uyd.values())
print(f" manbaga bog'lash uydirmalarni {ua} -> {ub} ga tushirdi")
if qoldi.get("shahar", 0) == max(qoldi.values(), default=0):
print(" eng ko'p qolgan xato - shahar: noto'g'ri, lekin MATNDA BOR qiymatni tekshiruv ushlamaydi")
print(" \u2b50 Extraction ni maydon bo'yicha, normallangan qiymatlarda baholang")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Bitta hujjat ===
matn: Assalomu alaykum! Men Bobur Aliyev, Xivadan yozyapman. 2 ta daftar kerak,
Termizga yetkazib bering. Telefonim: 905871484. 11-sentabr kuni kerak bo'ladi.
stub (xom): soni='2', summa="100 000 so'm", telefon='905871484'
quvur (B): soni=2, summa=None, telefon='+998905871484'
=== 2. Maydon bo'yicha aniqlik (400 hujjat) ===
maydon A qat'iy A norm. B quvur uydirma A -> B
ism 100.0% 100.0% 100.0%
shahar 87.2% 87.2% 87.2%
mahsulot 100.0% 100.0% 100.0%
soni 0.0% 100.0% 100.0%
summa 19.2% 93.0% 100.0% 28 -> 0 (/105)
telefon 20.5% 92.0% 100.0% 32 -> 0 (/114)
sana 29.8% 88.8% 100.0% 45 -> 0 (/164)
=== 3. Yozuv darajasida va juftlashgan farq ===
A qat'iy barcha 7 maydon to'g'ri: 0.0%, maydonlar o'rtachasi 51.0%
A norm. barcha 7 maydon to'g'ri: 68.2%, maydonlar o'rtachasi 94.4%
B quvur barcha 7 maydon to'g'ri: 87.2%, maydonlar o'rtachasi 98.2%
B - A norm. (hujjat bo'yicha): +0.0375, SE 0.0035 -> sezilarli
=== 4. B da qolgan xatolar ===
shahar 51 ta
=== 5. Xulosa (natijadan) ===
qat'iy satr taqqoslash aniqlikni 43.5% ga past ko'rsatdi - format farqi xato emas
manbaga bog'lash uydirmalarni 105 -> 0 ga tushirdi
eng ko'p qolgan xato - shahar: noto'g'ri, lekin MATNDA BOR qiymatni tekshiruv ushlamaydi
⭐ Extraction ni maydon bo'yicha, normallangan qiymatlarda baholangNima ko'rsatdi: 400 ta o'zbekcha buyurtma xabari kod ichida yaratildi, har birining haqiqiy (normallangan) maydonlari ma'lum. Stub matnni tushunmaydi: u matndagi yuzaki qiymatlarni formatini o'zgartirmay qaytaradi va ikki xil faraziy xato qo'shadi — 12% holatda yetkazish shahri o'rniga jo'natuvchi shahrini beradi, matnda yo'q maydonni esa 30% ehtimol bilan "o'ylab topadi". 2-bo'lim uch baholash usulini ko'rsatadi. A qat'iy — xom qiymatni satr sifatida taqqoslash: soni 0.0% ('2' va 2 teng emas), summa 19.2% — faqat bo'sh maydonlar mos keldi. Bu model xatosi emas, baholash xatosi: bir xil javob A norm. da (baholashda normallashtirish) 100.0% va 93.0%. Qat'iy taqqoslash o'rtacha aniqlikni 43.5 foiz punkt past ko'rsatdi (51.0% va 94.4%). B quvur normallashtirishni quvurning o'ziga qo'shdi va manbaga bog'lashni (qiymat matnda uchraydimi) qo'shdi: uydirmalar 28 → 0, 32 → 0, 45 → 0 — jami 105 → 0, shuning uchun summa, telefon, sana 100.0% ga chiqdi. Diqqat: bu stubda uydirma qiymatlar hech qachon matnda uchramaydi, shuning uchun bog'lash ularning hammasini ushladi — haqiqiy modelda model matndagi boshqa raqamni noto'g'ri maydonga qo'yishi mumkin va bunday xato bog'lashdan o'tib ketadi. Buni shahar ko'rsatib turibdi: 87.2% — uchala usulda bir xil, B da qolgan yagona xato turi (51 ta). Jo'natuvchi shahri ham matnda bor, shuning uchun manbaga bog'lash uni ushlamaydi; bunga prompt aniqligi (qaysi shahar kerakligi) yoki kontekstli qoida kerak. 3-bo'lim: yozuv darajasida "hammasi to'g'ri" ulushi A qat'iyda 0.0%, A norm. da 68.2%, B da 87.2%; hujjat bo'yicha juftlashgan farq B - A norm. +0.0375, SE 0.0035 — sezilarli. Bog'liq bo'limlar: 2.7.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Promptda 'faqat JSON' desam, javob JSON bo'ladi" | Ehtimol oshadi, kafolat yo'q; 1-misoldagi stub turlari — tipik buzilishlar |
"json.loads o'qidimi — demak to'g'ri" |
Shakl to'g'ri; 2-misolda qat'iy siyosat ham 11 ta noto'g'ri javobni qabul qildi |
| "Buzilgan JSON ni har doim tuzatish mumkin" | Ichki qo'shtirnoq, qirqilish va ikki obyekt — ko'p ma'noli; rad etib qayta so'rang |
"eval tezroq va oson" |
Model yozgan kodni bajarish — xavfsizlik teshigi |
"isinstance(x, int) butun sonni tekshiradi" |
True ham o'tadi; type(x) is int |
| "API sxemani majburlasa, validatsiya kerak emas" | Shakl kafolatlanadi, ma'no va to'liqlik emas |
| "Qayta so'rash ko'p bo'lsa — yaxshi" | T = 0 da bir xil prompt — bir xil javob; foyda tez to'yinadi, xarajat o'sadi |
| "Bo'sh maydonni standart qiymat bilan to'ldirgan ma'qul" | Bu — uydirma; None va "aniqlanmadi" halolroq |
| "Extraction aniqligi = yozuvlarning qat'iy mosligi" | Maydon bo'yicha, normallangan qiymatlarda; qat'iy taqqoslash 43.5 punkt past ko'rsatdi |
| "Manbaga bog'lash hamma uydirmani ushlaydi" | Matnda bor, lekin noto'g'ri qiymatni (shahar) ushlamaydi |
6. Keng tarqalgan xatolar va yechimlari
1. To'g'ridan-to'g'ri json.loads
d = json.loads(javob.content[0].text) # ⚠️
d, bosqich = ajrat(matn) # ✅
if d is None:
... # qayta so'rash yoki fallback2. stop_reason tekshirilmagan
d = ajrat(matn)[0] # ⚠️
if javob.stop_reason == "max_tokens": # ✅
raise RuntimeError("qirqilgan javob")3. eval bilan o'qish
d = eval(matn) # ⚠️
d = json.loads(yengil_tuzat(nomzod)) # ✅4. bool butun son sifatida o'tib ketadi
if isinstance(x, int): ... # ⚠️
if type(x) is int: ... # ✅5. Faqat sxema
xatolar = tekshir(d, SXEMA) # ⚠️
xatolar = tekshir(d, SXEMA) or biznes_qoidalari(d) # ✅6. Bir xil prompt bilan qayta so'rash
for _ in range(5):
javob = llm(prompt, temperature=0) # ⚠️
for _ in range(3):
javob = llm(prompt, temperature=0)
d, xato = tekshir(javob)
if d is not None:
break
prompt = asosiy + "\nOldingi javobingiz: " + javob + "\nXATO: " + xato # ✅7. Qat'iy satr taqqoslash bilan baholash
aniq = np.mean([xom[k] == h[k] for k in MAYDONLAR]) # ⚠️
aniq = np.mean([NORM[k](xom[k]) == h[k] for k in MAYDONLAR]) # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 24.9-dars (o'tilgan): generatsiya sikli va logitlar — sxema bo'yicha niqoblash (constrained decoding) aynan shu siklga qo'shiladi; T = 0 da determinizm
- 25.4-dars (o'tilgan): prompt engineering — chiqish formatini promptda aniq tasvirlash, misollar bilan ko'rsatish
- 18-qism (o'tilgan): juftlashgan taqqoslash va SE — 3- va 4-misollarda
- Keyingi darslar: LLM API bilan ishlash — qayta urinishlar, xatolar turlari, xarajat hisobi; RAG — ajratilgan maydonlar metadata sifatida; LLM ni baholash — maydon bo'yicha baho va avtomatik baholovchilar; Agentlar va tool calling — asbob parametrlari aynan shu sxemalar bilan; LLM xavfsizligi — model chiqishiga ishonmaslik,
evalva injeksiya
8. Eng yaxshi amaliyotlar
Chiqish sxemasini bir joyda saqlang va uni ham promptda, ham validatsiyada ishlating.
Imkon bo'lsa API darajasidagi sxema majburlashni (structured outputs, strict tool use) yoqing — lekin validatsiyani olib tashlamang.
Ajratishni bosqichli va konservativ qiling; ko'p ma'noli buzilishni rad eting.
Jim xato sonini alohida o'lchang va loglang.
Sxemadan tashqari maydonlararo biznes qoidalarini yozing.
Moslashtirish qoidalarini faqat bir ma'noli holatlar uchun qo'shing.
Qayta so'rashda aniq xato matnini bering, urinishlarni 2-3 bilan cheklang, fallback ni loyihalang.
Extraction ni maydon bo'yicha, normallangan qiymatlarda baholang; uydirma ulushini alohida ko'rsating.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # json.loads(UCH + 'json\n{"a": 1}\n' + UCH) natijasi? (UCH - kod bloki belgisi)
2. # {'a': True, 'b': None} ni qaysi uch almashtirish JSON ga aylantiradi?
3. # isinstance(False, int) nima qaytaradi?
4. # "Misol: {"x": 0}. Javob: {"x": 5}" dan birinchi obyektni olish xavfi?
5. # stop_reason == "max_tokens" bo'lsa, qavsni yopib o'qish to'g'rimi?
6. # T = 0 da bir xil prompt bilan 5 marta qayta so'rash foydasi?
7. # "03.04" ni sanaga moslashtirish xavfsizmi?
8. # sxema majburlash "jami == sum(soni * narx)" ni kafolatlaydimi?
9. # qat'iy satr taqqoslashda "145 ming so'm" va 145000?
10. # manbaga bog'lash qaysi xatoni ushlamaydi?
11. # nega bo'sh maydonni standart qiymat bilan to'ldirmaslik kerak?
12. # qaysi holatda qayta so'rash ham yordam bermaydi?Javoblar
JSONDecodeError— kod bloki belgilari JSON emas; avval blok ichini ajratish kerak- Bitta qo'shtirnoq → qo'shtirnoq,
True→true,None→null(faqat satrlardan tashqarida) True—boolintning quyi sinfi- Jim xato: noto'g'ri obyekt (
x: 0) muvaffaqiyatli o'qilgandek ko'rinadi; rad etish kerak - Yo'q — yetishmagan qism noma'lum;
max_tokensni oshirib qayta so'rang - Yo'q — deterministik dekodlashda aynan bir xil javob (3-misol:
70.7%1 va 5 urinishda) - Yo'q — 3-aprel yoki 4-mart; noaniq
- Yo'q — faqat shakl; bu biznes qoidasi
- Teng emas deb sanaladi — format farqi xato bo'lib ko'rinadi
- Matnda bor, lekin noto'g'ri maydonga qo'yilgan qiymat (jo'natuvchi va yetkazish shahri)
- Bu uydirma: noma'lum qiymat bilan ma'lum qiymatni ajratib bo'lmay qoladi
- Doimiy qiyin holatlar (3-misol: yiqilganlarning
76%i) — fallback kerak
Vazifa 2: Xatolarni tuzating
1. d = json.loads(javob.content[0].text)
baza.saqla(d)
2. if isinstance(d["soni"], int) and d["soni"] >= 1:
qabul(d)
3. for _ in range(10):
javob = llm(prompt, temperature=0)
try:
return json.loads(javob)
except json.JSONDecodeError:
pass
4. d["telefon"] = d.get("telefon") or "+998000000000"
5. aniqlik = np.mean([javob[k] == haqiqat[k] for k in MAYDONLAR])Javoblar
1. if javob.stop_reason == "max_tokens":
raise RuntimeError("qirqilgan")
d, _ = ajrat(next(b.text for b in javob.content if b.type == "text"))
xatolar = tekshir(d, SXEMA) if d is not None else ["JSON topilmadi"]
xatolar = xatolar or biznes_qoidalari(d)
if not xatolar:
baza.saqla(Buyurtma.yarat(d))
2. if type(d["soni"]) is int and d["soni"] >= 1: # True/False o'tmaydi
qabul(d)
3. prompt_i = prompt
for _ in range(3): # chegarali
javob = llm(prompt_i, temperature=0)
d, xato = tekshir(javob)
if d is not None:
return d
prompt_i = prompt + "\nOldingi javobingiz: " + javob + "\nXATO: " + xato
return None # fallback
4. d["telefon"] = norm_telefon(d.get("telefon")) if manbada_bormi(d.get("telefon"), matn) else None
5. aniqlik = {k: np.mean([NORM[k](j[k]) == h[k] for j, h in juftlar]) for k in MAYDONLAR}
uydirma = {k: np.mean([j[k] is not None for j, h in juftlar if h[k] is None])
for k in MAYDONLAR}Vazifa 3: Ajratuvchi
Modellang:
- Stub bilan 8 xil buzilish turi
- Bosqichli
ajrat(to'g'ridan, kod bloki, qavs skaneri, tuzatish) - Tur bo'yicha muvaffaqiyat va jim xato soni
- Chekka holatlar: satr ichidagi
},//, apostrof, ikki obyekt; qo'shimcha: massiv ([...]) qaytaradigan javoblar
Vazifa 4: Validator
Modellang:
- JSON Schema kichik qismi:
type,required,enum,minimum,pattern,items - Yo'l bilan xato xabarlari
- Biznes qoidasi va dataclass
- Qat'iy / moslashtirish / moslash + biznes siyosatlari: qabul va noto'g'ri qabul
Vazifa 5: Qayta so'rash sikli
Modellang:
- Stub: T = 0 da prompt xeshidan deterministik javob
- Uch strategiya, K = 1, 2, 3, 5
- Muvaffaqiyat, chaqiruvlar, kirish tokenlari
- Yiqilganlar tahlili va fallback
Vazifa 6: Extraction bahosi
Modellang:
- O'zbekcha xabarlar generatori va haqiqiy maydonlar
- Normallashtirish funksiyalari (summa, telefon, sana, son so'zlari)
- Qat'iy / normallangan / quvur baholari maydon bo'yicha
- Uydirma ulushi va manbaga bog'lash; qolgan xatolar tahlili
Vazifa 7: O'ylash
Jamoadoshingiz aytdi: "Endi API da structured outputs bor — javob har doim sxemaga mos JSON. Demak, validator, biznes qoidalari va qayta so'rash kodini o'chirib tashlaymiz, ular ortiqcha."
Javob
Qisqa javob: sxema majburlash — katta yutuq va uni yoqish kerak, lekin u faqat bitta qatlamni (shakl) yopadi. Qolgan qatlamlarni o'chirish xavfli.
1. Kafolat nimani qamraydi. Kalitlar, turlar, enum, ortiqcha maydonlar yo'qligi — ha. 1-misoldagi buzilishlarning ko'pchiligi (kod bloki, izoh, ortiqcha vergul) haqiqatan yo'qoladi. Lekin qirqilish qoladi: max_tokens yetmasa, javob to'liq emas — stop_reason ni tekshirish baribir kerak.
2. Ma'no kafolatlanmaydi. 2-misoldagi "jami" xatosi shaklan mukammal edi: sxema xatolari 0, faqat biznes qoidasi ushladi. 4-misoldagi uydirma telefon raqami ham sxemaga mos (^\+998\d{9}$) — uni faqat manbaga bog'lash ushladi. Noto'g'ri shahar esa bog'lashdan ham o'tdi.
3. Qayta so'rash hali ham kerak — endi format xatolari uchun emas, balki biznes qoidalari xatolari uchun: "jami hisoblangan summaga teng emas, tuzating". Urinishlar chegarasi va fallback o'zgarmaydi.
4. Validator — himoyaning ikkinchi chizig'i. Provayder, model yoki sxema versiyasi o'zgarganda, yoki kelajakda boshqa (majburlashsiz) model ulanganda, validator jim xatolarni to'xtatadi. Uning narxi — mikrosekundlar.
Tavsiya:
# 1. structured outputs / strict tool use ni yoqing
# 2. stop_reason ni tekshiring
# 3. sxema validatori qoladi (arzon, ikkinchi himoya)
# 4. biznes qoidalari va manbaga bog'lash - albatta
# 5. qayta so'rash - biznes xatolari uchun, K = 2, keyin fallback
# 6. loglar: bosqich statistikasi, rad etilganlar, uydirma ulushiJamoadoshga javob: "Structured outputs ni yoqamiz — format xatolari yo'qoladi va ajratish kodi soddalashadi. Lekin validator va biznes qoidalari qoladi: sxemaga mos JSON ichida ham noto'g'ri summa va uydirma telefon bo'lishi mumkin, buni bizning test to'plamimiz ko'rsatdi."
Nimani mustahkamlaydi: 2.2, 2.3, 2.5, 2.6, 2.7-bo'limlar.
Xulosa
Bu darsda LLM javobini dastur o'qishi uchun kerak bo'ladigan qatlamlarni qurdik: JSON ni xavfsiz ajratish, sxema va biznes qoidalari bilan validatsiya, xato bilan qayta so'rash va extraction ni maydon bo'yicha baholash. Barcha misollarda LLM o'rnida stub ishlatildi — raqamlar farazlarning oqibati, lekin kod tuzilishi haqiqiy API bilan bir xil.
Eng muhim uch fikr:
Ajratish konservativ, validatsiya ikki qavatli. 1-misolda bosqichli
ajrat600 ta stub javobning92.8%ini o'qidi (naivjson.loads—39.0%), jim xato0; ko'p ma'noli holatlar (ichki qo'shtirnoq, qirqilish, ikki obyekt) ataylab rad etildi. 2-misolda sxema shaklni tekshirdi, lekin "jami" xatosini faqat biznes qoidasi ushladi: moslashtirish qabulni74.0%dan98.8%ga oshirdi va 16 ta noto'g'ri javobni o'tkazdi, biznes qoidasi bilan esa94.8%xatosiz qabul.Qayta so'rash promptni o'zgartirishi kerak va cheklangan bo'lishi kerak. 3-misolda T = 0 va bir xil prompt bilan 5 urinish 1 urinishdan yaxshi emas (
70.7%), lekin 2.17 barobar ko'p chaqiruv. Xato matni bilan qayta so'rash K = 3 da96.7%, K = 5 da96.9%— foyda to'yinadi, tokenlar o'sadi; yiqilganlarning76%i doimiy qiyin holatlar — ularga fallback kerak (stub farazlari ostida).Extraction ni maydon bo'yicha, normallangan qiymatlarda baholang. 4-misolda qat'iy satr taqqoslash aniqlikni
43.5foiz punkt past ko'rsatdi (51.0%va94.4%); manbaga bog'lash uydirmalarni105dan0ga tushirdi, lekin matnda bor noto'g'ri qiymatni (shahar,87.2%) ushlamadi.
Keyingi darsda LLM API bilan ishlash: SDK tuzilishi va xabarlar formati, xatolar turlari (429, 529, 400, timeout), eksponensial backoff va jitter, kesh va idempotentlik, streaming, batch va usage dan xarajat hisobini ko'ramiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!