Mundarija (24)
- 1. Kirish va motivatsiya
- 2. Nazariya — chuqur tushuntirish
- 2.1. SDK tuzilishi: mijoz, so'rov, javob
- 2.2. Xabarlar formati va suhbat tarixi
- 2.3. Xatolar turlari
- 2.4. Eksponensial backoff, jitter va mijoz tomonidagi cheklovchi
- 2.5. Kesh va idempotentlik
- 2.6. Streaming
- 2.7. Batch ishlov berish
- 2.8. usage dan xarajat hisobi va byudjet
- 2.9. Kalitni xavfsiz saqlash
- 2.10. Tuzoqlar
- 3. Tez ma'lumotnoma
- 4. Batafsil misollar
- Misol 1 — Xabarlar formati va suhbat tarixi strategiyalari
- Misol 2 — Xatolar, eksponensial backoff, jitter va cheklovchi (virtual vaqt)
- Misol 3 — Javoblar keshi va idempotentlik
- Misol 4 — usage dan xarajat, byudjet, streaming, batch va kalit xavfsizligi
- 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.6-dars: LLM API bilan ishlash
25-QISM — KATTA TIL MODELLARI · 6-dars
1. Kirish va motivatsiya
25.5-darsda LLM javobini dastur qanday o'qishini ko'rdik. Endi javobning o'zini qanday olishni — ya'ni API bilan muhandislik darajasida ishlashni o'rganamiz. Birinchi qarashda bu oddiy: bitta funksiya chaqiruvi, client.messages.create(...), javob keldi. Lekin prototip ishlab chiqarishga chiqqan kuni boshqa manzara paydo bo'ladi: bir vaqtda yuzlab so'rov, tezlik chegarasi (429), server band (529), uzilgan ulanish, ikki marta yuborilgan buyurtma, oy oxirida kutilmagan hisob va GitHub ga tushib qolgan kalit.
API atrofidagi kod — qayta urinish, kesh, xarajat hisobi, streaming, batch — model sifatiga bog'liq emas, lekin tizimning ishonchliligi va narxi aynan shunga bog'liq. Yaxshi xabar: bu kod oddiy dasturlash, uni lokal ravishda to'liq sinash mumkin. Buning uchun stub — xatolarni, kechikishni va usage ni haqiqiy API kabi qaytaradigan soxta mijoz yetarli.
Real vaziyat. Kichik jamoa 20 ta ishchi jarayon bilan 100 ta hujjatni tunda qayta ishlaydigan skript yozdi. Birinchi kechada skript 5 soniyada "tugadi": hujjatlarning to'rtdan uch qismidan ko'prog'i 429 xatosi bilan tashlab ketilgan edi. Keyin "xato bo'lsa darhol qayta yubor" qo'shildi — endi server 800 ga yaqin so'rov qabul qildi, ularning deyarli hammasi yana 429, va hujjatlarning ko'pi baribir qayta ishlanmadi. Uchinchi urinishda eksponensial backoff qo'shildi — hammasi ishladi, lekin oyna 50 soniyaga cho'zildi. Oxirida eng oddiy yechim eng yaxshisi bo'lib chiqdi: so'rovlarni serverning tezlik chegarasidan sekinroq tartib bilan yuborish. Bu darsning 2-misoli aynan shu to'rt bosqichni virtual vaqtda o'lchaydi.
Bu darsda Anthropic SDK tuzilishini, xabarlar formati va suhbat tarixini, xatolar turlari va qayta urinish strategiyalarini, kesh va idempotentlikni, streaming, batch va usage dan xarajat hisobini hamda kalitni xavfsiz saqlashni ko'ramiz.
Bu darsda:
- SDK tuzilishi: mijoz, so'rov, javob obyekti
- Xabarlar formati va suhbat tarixi
- Xatolar turlari: qaysi biriga qayta urinish kerak
- Eksponensial backoff, jitter va mijoz tomonidagi cheklovchi
- Kesh va idempotentlik
- Streaming
- Batch ishlov berish
-
usagedan xarajat hisobi va byudjet - Kalitni xavfsiz saqlash
- Tuzoqlar
ℹ Mashinada LLM API va internet yo'q. Haqiqiy SDK kodi (
import anthropic) faqat nazariya va ma'lumotnoma bloklarida. Misollarda STUB —SoxtaLLM,SoxtaServer,SoxtaMijozdeb nomlangan deterministik soxta mijozlar ishlatiladi: ular haqiqiy model emas, xatolar ehtimoli va narxlar — faraz. Lekin qayta urinish, backoff, kesh, idempotentlik va xarajat hisobi kodi haqiqiy — aynan shu kod haqiqiy API bilan ham ishlaydi. Vaqt — virtual hisoblagich (time.sleepyo'q).
2. Nazariya — chuqur tushuntirish
2.1. SDK tuzilishi: mijoz, so'rov, javob
MIJOZ:
client = anthropic.Anthropic()
kalit: ANTHROPIC_API_KEY muhit o'zgaruvchisidan (kodga YOZILMAYDI)
timeout (standart 10 daqiqa), max_retries (standart 2) - sozlanadi
SO'ROV (Messages API):
client.messages.create(
model="claude-sonnet-5", # model nomi
max_tokens=1024, # chiqish uchun YUQORI chegara
system="...", # ko'rsatma - alohida parametr
messages=[{"role": "user", "content": "..."}],
)
JAVOB OBYEKTI:
response.id - so'rov identifikatori (loglash uchun)
response.model - qaysi model javob berdi
response.content - bloklar ro'yxati: text, tool_use, ...
response.content[0].text - eng oddiy holatda matn
response.stop_reason - end_turn / max_tokens / stop_sequence / tool_use / refusal
response.usage - input_tokens, output_tokens,
cache_read_input_tokens, cache_creation_input_tokens
BOSHQA PROVAYDERLAR:
OpenAI, Google va boshqalarda nomlar farq qiladi, g'oya bir xil:
model + xabarlar -> matn + stop sababi + token hisobiIkki narsaga alohida e'tibor bering. Birinchisi — stop_reason: max_tokens bo'lsa, javob qirqilgan (25.5-dars), refusal bo'lsa — model rad etgan; content ni o'qishdan oldin tekshiring. Ikkinchisi — usage: bu hisob-kitob manbai. Tokenlarni o'zingiz taxmin qilmang (bu darsdagi len(matn) / 4 — faqat stub uchun); haqiqiy hisob usage da yoki count_tokens endpointida.
Javob — matn emas, obyekt: stop_reason va usage ni har doim o'qing va loglang.
2.2. Xabarlar formati va suhbat tarixi
ROLLAR:
system - alohida parametr (messages ichida emas)
messages - user / assistant navbatma-navbat, birinchisi user
ketma-ket bir xil rol - API ularni bitta navbatga birlashtiradi
API HOLATSIZ (stateless):
model oldingi so'rovni "eslamaydi"
har navbatda BUTUN tarix qayta yuboriladi
1-misol: 1-navbat 374 token, 30-navbat 2794 token (kirish)
30 navbatli suhbatda jami kirish ~ n^2 ga proporsional o'sadi
TARIX STRATEGIYALARI (1-misol, 30 navbat, jami kirish tokenlari):
to'liq tarix 47 671 - hamma narsa esda, eng qimmat
sirpanuvchi oyna 22 812 - arzon, lekin eski faktlar YO'QOLADI
xulosa + oyna 23 694 - eski qism qisqa xulosa bilan almashtiriladi
to'liq + kesh 47 671, shundan 46 530 keshdan (prefiks kesh)
PREFIKS KESH (prompt caching):
so'rov boshi (system + eski tarix) o'zgarmasa, server uni keshdan o'qiydi
keshdan o'qilgan token arzonroq (aniq koeffitsient - provayder sahifasida)
shart: prefiks BAYTMA-BAYT bir xil; system ichida sana/vaqt - keshni buzadi1-misolda faraziy narxlar bilan 1000 ta suhbat: to'liq tarix $143.01, oyna $68.44, xulosa $71.08, to'liq + kesh — $17.38. Muhim kuzatuv: oyna eng arzon bo'lsa ham, 2-navbatda aytilgan ism 30-navbatda yo'qoldi; xulosa uni saqladi (chunki stub xulosasi muhim faktni o'z ichiga oldi — haqiqiy xulosa buni kafolatlamaydi). Keshlangan to'liq tarix esa ham arzon, ham to'liq — shuning uchun amalda birinchi qadam — prefiksni barqaror qilish va keshni yoqish, keyin kerak bo'lsa xulosa.
Model hech narsani eslamaydi — kontekstda nima bo'lsa, faqat shu bor; tarix strategiyasi narx va "xotira" orasidagi murosa.
2.3. Xatolar turlari
HTTP SDK sinfi (Python) SABAB QAROR
400 BadRequestError so'rov noto'g'ri TUZATING, qayta emas
401 AuthenticationError kalit noto'g'ri TUZATING
403 PermissionDeniedError ruxsat yo'q TUZATING
404 NotFoundError model nomi xato TUZATING
429 RateLimitError tezlik chegarasi KUTIB qayta urinish
(retry-after sarlavhasi)
500 InternalServerError server ichki xatosi qayta urinish
529 InternalServerError server band (overloaded) qayta urinish
timeout APITimeoutError javob kelmadi qayta urinish (!)
tarmoq APIConnectionError ulanish uzildi qayta urinish
UMUMIY: APIStatusError - barcha HTTP xatolarining asosi (.status_code, .message)
TIMEOUT TUZOG'I:
javob kelmadi != so'rov bajarilmadi
server ishni bajargan, faqat javob yo'lda yo'qolgan bo'lishi mumkin
-> yon ta'sirli amalda idempotentlik kaliti kerak (2.5)SDK o'zi ham qayta urinadi (standart max_retries=2, 429 va 5xx uchun). Lekin tungi skript yoki katta yuklama uchun bu yetmaydi: o'z siyosatingiz (urinishlar soni, maksimal kutish, mijoz tomonidagi cheklovchi) kerak bo'ladi. Muhimi — xatolarni turi bo'yicha ajratish: bitta except Exception 400 ni ham qayta yuboradi (befoyda) va 401 ni ham (kalit xato bo'lsa, 10 marta yuborish kalitni tuzatmaydi).
400-lar — kodingizdagi xato, 429/5xx/timeout — vaqtinchalik: faqat ikkinchisiga qayta urining.
2.4. Eksponensial backoff, jitter va mijoz tomonidagi cheklovchi
EKSPONENSIAL BACKOFF:
kutish(u) = min(shift, asos * 2^(u-1)) u - urinish raqami
asos 0.5 s: 0.5, 1, 2, 4, 8, 16, ...
FULL JITTER:
kutish(u) = uniform(0, min(shift, asos * 2^(u-1)))
maqsad: bir vaqtda xato olgan mijozlar bir vaqtda qaytmasin
("thundering herd" - hammasi bir lahzada qaytib, yana 429)
RETRY-AFTER:
server "shuncha soniyadan keyin" deydi -> kamida shuncha kuting
MIJOZ TOMONIDAGI CHEKLOVCHI (rate limiter):
so'rovlarni serverning chegarasidan biroz sekinroq tartib bilan yuborish
429 ni "davolash" emas, OLDINI OLISH
2-MISOL (20 mijoz x 5 so'rov, server 5 so'rov/s, 5 urug'):
strategiya muvaff. urinish 429 lar tugash
retry yo'q 18.9% 100 79 5.0 s
darhol 28.7% 799 767 6.8 s
eksponensial 100.0% 189 84 51.7 s
full jitter 100.0% 214 108 39.7 s
jitter + retry-after 100.0% 220 114 38.2 s
cheklovchi + jitter 100.0% 105 0 30.0 sNatijani halol o'qiylik. Kutilganidek, qayta urinishsiz va "darhol" qayta urinish bilan so'rovlarning ko'pi yo'qoldi, "darhol" esa serverni 767 ta befoyda so'rov bilan to'ldirdi. Backoff muvaffaqiyatni 100% ga chiqardi. Lekin jitter haqidagi "darslik" da'vosi bu tajribada tasdiqlanmadi: full jitter 429 larni kamaytirmadi, balki ko'paytirdi (+24.6, SE 6.0 — sezilarli), chunki uning o'rtacha kutishi ikki marta qisqa; tugash vaqti esa qisqardi, lekin farq sezilarli emas (-12.0 s, SE 7.0). Retry-after qo'shish ham sezilarli farq bermadi. Jitterning haqiqiy foydasi mijozlar ko'p va sinxron bo'lganda (masalan, hammasi bir vaqtda qayta ulanganda) ko'rinadi — buni o'z yuklamangizda o'lchang. Aniq g'olib esa mijoz tomonidagi cheklovchi: 429 lar 0, urinishlar 105 (100 so'rov + 5 ta 5xx/timeout), tugash 30.0 s.
Avval tezlikni o'zingiz cheklang; qayta urinish — zaxira chora: eksponensial, yuqori chegara bilan, urinishlar soni cheklangan.
2.5. Kesh va idempotentlik
JAVOB KESHI (bizning tomonda, prefiks keshdan boshqa):
bir xil so'rov -> bir xil javob (T = 0 da) -> API ni qayta chaqirmaslik
KALIT = javobga ta'sir qiluvchi HAMMA narsa:
model, system, messages, max_tokens, temperature, tools, ...
kanonik JSON: sort_keys=True, bir xil ajratuvchilar -> sha256
3-MISOL (3000 so'rov, 300 savol, Zipf, 2 do'kon):
kalit API chaqiruv hit ulushi noto'g'ri
kesh yo'q 3000 0.0% 0
naiv: faqat savol matni 411 86.3% 1283 <- boshqa do'kon javobi!
to'liq so'rov 613 79.6% 0
to'liq + normallash 434 85.5% 0
to'liq + norm. + LRU 100 989 67.0% 0
NORMALLASHTIRISH:
" Yetkazish haqida..." va "yetkazish haqida..." - bir xil savol
ehtiyot: faqat MA'NONI o'zgartirmaydigan normallashtirish
QACHON KESHLAMASLIK:
T > 0 va xilma-xillik kerak bo'lsa; shaxsiy ma'lumotli javoblar;
vaqtga bog'liq savollar ("bugun kurs qancha?") - TTL bilan
IDEMPOTENTLIK:
yon ta'sirli amal (buyurtma yaratish, SMS yuborish, to'lov)
timeout dan keyin qayta urinish -> amal IKKI marta bajarilishi mumkin
yechim: mijoz amal uchun BIR MARTA kalit yaratadi, server kalitni eslaydi
3-misol: 300 amal, 10% timeout -> kalitsiz 35 ta ortiqcha bajarilish, kalit bilan 0Naiv kalit eng yuqori hit ulushini berdi — va eng xavfli bo'ldi: 1283 ta javob boshqa do'kon uchun yaratilgan edi, chunki kalitda system yo'q. Bu "tez va arzon" ko'rinadigan, lekin jim xato beradigan klassik tuzoq. LRU 100 — xotira cheklangan kesh: Zipf taqsimotida eng mashhur savollar keshda qoladi, hit ulushi 67.0% ga tushdi, lekin narx baribir kesh yo'q holatdan uch barobar kam ($3.41 va $10.35, faraziy narxlar).
Kesh kaliti — javobga ta'sir qiluvchi hamma narsaning kanonik xeshi; yon ta'sirli amalga — idempotentlik kaliti.
2.6. Streaming
ODDIY SO'ROV: foydalanuvchi butun javob tayyor bo'lguncha kutadi
STREAMING: javob bo'laklari (delta) kelishi bilan ko'rsatiladi
HODISALAR KETMA-KETLIGI:
message_start - usage.input_tokens
content_block_delta - matn bo'lagi (ko'p marta)
message_delta - stop_reason, usage.output_tokens
message_stop - tugadi
4-MISOL (stub, virtual vaqt):
birinchi so'z 0.83 s da, oxirgisi 4.43 s da
streamsiz foydalanuvchi 4.43 s bo'sh ekranga qaraydi
UZILISH:
message_stop kelmadi -> javob TO'LIQ EMAS
output_tokens ham yo'q -> xarajatni taxmin qilish kerak
qisman javobni "tayyor" deb saqlamang
QACHON KERAK:
chat interfeysi (sezilgan kechikish), uzun javoblar (HTTP timeout dan saqlanish)
fon ishlarida (batch) - shart emas Streaming haqiqiy kechikishni emas, sezilgan kechikishni kamaytiradi; message_stop siz oqim — to'liq emas.
2.7. Batch ishlov berish
G'OYA:
ko'p so'rovni bitta paketda yuborish -> provayder o'z vaqtida bajaradi
afzallik: arzonroq (odatda chegirma bilan), tezlik chegarasiga urilmaydi
kamchilik: natija darhol emas (daqiqalar - soatlar)
QACHON:
tungi hisobotlar, katta arxivni tasniflash, baholash to'plamlari (LLM ni baholash darsi)
foydalanuvchi kutmaydigan har qanday ish
NATIJALAR TARTIBI:
natijalar IXTIYORIY tartibda keladi
har so'rovga custom_id -> natijani custom_id bo'yicha bog'lash
4-misol: pozitsiya bo'yicha bog'lash 998 / 1000 xato, custom_id bo'yicha 0 Kutish mumkin bo'lgan ish — batch ga; natijalarni doim custom_id bo'yicha bog'lang.
2.8. usage dan xarajat hisobi va byudjet
FORMULA (har so'rov):
narx = input_tokens * narx_kirish
+ cache_read_tokens * narx_kirish * kesh_koeffitsienti
+ output_tokens * narx_chiqish
narxlar $/1M token; chiqish odatda kirishdan bir necha barobar qimmat
ANIQ NARXLAR O'ZGARADI - provayderning rasmiy sahifasidan oling
HISOBOT KESIMLARI:
vazifa (chat, xulosa, tasnif), model, foydalanuvchi, kun
4-misol (faraziy narxlar): xarajatning 86% i 'xulosa' da
-> optimallashtirishni eng katta ulushdan boshlang (kesh, kichikroq model, qisqa kontekst)
BYUDJET QO'RIQCHISI:
so'rovdan OLDIN: joriy sarf + taxminiy yuqori chegara > byudjet -> to'xtash
taxmin: kirish tokenlari (count_tokens) + max_tokens * narx_chiqish
4-misol: $0.50 byudjetda 9 so'rov, sarf $0.457 - oshmadi Har so'rovning usage ini loglang va vazifa bo'yicha yig'ing — "bizning LLM hisobimiz qayerga ketyapti?" savoliga javob faqat shu loglardan.
2.9. Kalitni xavfsiz saqlash
QOIDALAR:
kalit - muhit o'zgaruvchisida (ANTHROPIC_API_KEY) yoki sirlar menejerida
kodda, notebook da, git da, loglarda - HECH QACHON
.env fayli - .gitignore ga; namuna uchun .env.example (kalitsiz)
logda - niqoblangan: sk-ant-...Ab12
KALIT OSHKOR BO'LSA:
o'chirish yetmaydi - git tarixida qoladi
darhol kalitni bekor qilish va yangisini yaratish (rotate)
AVTOMATIK TEKSHIRUV:
commit dan oldin kalit naqshini qidirish (4-misol: sk-ant-... regex)
CI da secret scanning
BRAUZER / MOBIL ILOVA:
kalit mijoz qurilmasiga HECH QACHON bermang
so'rov o'z serveringiz orqali (proksi) - u yerda kalit, cheklov, logKalit — muhitda, koddan tashqarida; oshkor bo'lgan kalit — bekor qilingan kalit.
2.10. Tuzoqlar
Asosiy tuzoqlar: stop_reason ni tekshirmasdan content[0].text ni o'qish; tokenlarni usage o'rniga taxmin bilan hisoblash; system ni messages ichiga qo'yish; API "eslaydi" deb tarixni yubormaslik yoki aksincha — cheksiz tarix yuborish; system prompt ichiga sana/vaqt qo'yib prefiks keshni buzish; bitta except Exception bilan 400 va 401 ni ham qayta yuborish; kutmasdan darhol qayta urinish; urinishlar soni va maksimal kutish chegarasiz; tezlik chegarasini faqat 429 dan keyin "bilib olish"; timeout ni "bajarilmadi" deb hisoblab yon ta'sirli amalni qayta yuborish; kesh kalitida system yoki model yo'qligi; json.dumps ni sort_keys siz kalit uchun ishlatish; qisman stream javobini to'liq deb saqlash; batch natijalarini pozitsiya bo'yicha bog'lash; kalitni kodga yozish, logga chiqarish yoki brauzerga berish; aniq narxlarni kodga "haqiqiy" deb qotirib qo'yish.
3. Tez ma'lumotnoma
import os
import anthropic
# ---- mijoz: kalit muhitdan, timeout va SDK ning o'z qayta urinishlari ----
assert "ANTHROPIC_API_KEY" in os.environ, "kalitni muhit o'zgaruvchisiga qo'ying"
client = anthropic.Anthropic(timeout=60.0, max_retries=3)
# ---- oddiy so'rov ----
javob = client.messages.create(
model="claude-sonnet-5",
max_tokens=1024,
system="Siz do'kon yordamchisisiz. Qisqa javob bering.",
messages=[
{"role": "user", "content": "Yetkazish qancha vaqt oladi?"},
{"role": "assistant", "content": "Odatda 2 kun."},
{"role": "user", "content": "Samarqandga ham shundaymi?"},
],
)
if javob.stop_reason == "max_tokens":
print("javob qirqilgan")
matn = "".join(b.text for b in javob.content if b.type == "text")
print(javob.id, javob.usage.input_tokens, javob.usage.output_tokens)
# ---- xatolarni turi bo'yicha ushlash ----
try:
javob = client.messages.create(model="claude-sonnet-5", max_tokens=256,
messages=[{"role": "user", "content": "Salom"}])
except anthropic.BadRequestError as e: # 400 - so'rovni tuzating
raise
except anthropic.RateLimitError as e: # 429 - kutib qayta urinish
kutish = e.response.headers.get("retry-after")
except anthropic.APITimeoutError: # javob kelmadi - bajarilgan bo'lishi mumkin
pass
except anthropic.APIStatusError as e: # boshqa HTTP xatolar (5xx, 529 ...)
print(e.status_code, e.message)
except anthropic.APIConnectionError: # tarmoq
pass
# ---- prefiks kesh: barqaror system ni keshlash ----
javob = client.messages.create(
model="claude-sonnet-5", max_tokens=1024,
system=[{"type": "text", "text": "UZUN VA O'ZGARMAS KO'RSATMA ...",
"cache_control": {"type": "ephemeral"}}],
messages=[{"role": "user", "content": "Savol"}],
)
print(javob.usage.cache_read_input_tokens, javob.usage.cache_creation_input_tokens)
# ---- streaming ----
with client.messages.stream(model="claude-sonnet-5", max_tokens=2048,
messages=[{"role": "user", "content": "Uzun javob yozing"}]) as oqim:
for bolak in oqim.text_stream:
print(bolak, end="", flush=True)
yakuniy = oqim.get_final_message() # to'liq javob va usage
# ---- tokenlarni oldindan sanash ----
sanoq = client.messages.count_tokens(model="claude-sonnet-5",
messages=[{"role": "user", "content": "Salom"}])
print(sanoq.input_tokens)
# ---- batch ----
from anthropic.types.message_create_params import MessageCreateParamsNonStreaming
from anthropic.types.messages.batch_create_params import Request
paket = client.messages.batches.create(requests=[
Request(custom_id=f"hujjat-{i}",
params=MessageCreateParamsNonStreaming(
model="claude-sonnet-5", max_tokens=512,
messages=[{"role": "user", "content": f"{i}-hujjatni tasniflang"}]))
for i in range(1000)
])
# keyinroq: client.messages.batches.retrieve(paket.id).processing_status == "ended"
for natija in client.messages.batches.results(paket.id):
if natija.result.type == "succeeded":
saqla(natija.custom_id, natija.result.message) # custom_id bo'yicha!LLM API xulosasi
javob = obyekt: content + stop_reason + usage; ikkalasini ham tekshiring
API holatsiz: tarix har safar yuboriladi; prefiks kesh + barqaror system
400/401/403/404 - tuzating; 429/5xx/529/timeout - qayta urining
avval mijoz tomonida cheklovchi, keyin eksponensial backoff (+ jitter), chegara bilan
kesh kaliti = kanonik JSON xeshi (model, system, messages, parametrlar)
yon ta'sir + timeout -> idempotentlik kaliti
streaming - sezilgan kechikish; message_stop siz - to'liq emas
batch - arzon, sekin; custom_id bo'yicha bog'lash
narx = usage x narx jadvali (rasmiy sahifadan); byudjetni so'rovdan oldin tekshirish
kalit - muhitda; oshkor bo'lsa - rotate4. Batafsil misollar
Misollar Python 3.14 standart kutubxonasi va numpy bilan. API o'rnida — STUB (haqiqiy model emas); retry, backoff, kesh, idempotentlik va xarajat kodi haqiqiy. Vaqt — virtual.
Misol 1 — Xabarlar formati va suhbat tarixi strategiyalari
"""Xabarlar formati va suhbat tarixi: tekshiruv, tarix strategiyalari va kirish tokenlari narxi."""
import math
SYSTEM = ("Siz 'Wisar' onlayn do'konining yordamchisisiz. Faqat do'kon mahsulotlari, "
"yetkazib berish va to'lov haqida javob bering. ") * 12 # ~ uzun ko'rsatma
def token(matn):
return math.ceil(len(matn) / 4) # taxminiy (25.2-dars); haqiqiysi: count_tokens
def xabarlarni_tekshir(system, xabarlar):
"""API qoidalariga o'xshash tekshiruv: rollar, birinchi xabar, bo'sh matn."""
xato = []
if not isinstance(system, str):
xato.append("system - alohida parametr, satr bo'lishi kerak")
for i, x in enumerate(xabarlar):
if x["role"] not in ("user", "assistant"):
xato.append(f"[{i}] noma'lum rol {x['role']!r} (system - messages ichida emas)")
if not str(x.get("content", "")).strip():
xato.append(f"[{i}] bo'sh content")
if i and x["role"] == xabarlar[i - 1]["role"]:
xato.append(f"[{i}] ketma-ket ikki {x['role']!r} - bitta navbatga birlashadi")
if not xabarlar or xabarlar[0]["role"] != "user":
xato.append("birinchi xabar 'user' bo'lishi kerak")
return xato
class SoxtaLLM:
"""STUB - haqiqiy model emas. Faqat kontekstda bor ma'lumotga 'javob beradi':
'ismim nima' savoliga ism kontekstda bo'lsa - to'g'ri, bo'lmasa - 'bilmayman'."""
def yarat(self, system, xabarlar):
kontekst = system + " ".join(x["content"] for x in xabarlar)
savol = xabarlar[-1]["content"]
if "ismim nima" in savol:
javob = "Ismingiz Dilnoza." if "Dilnoza" in kontekst else "Kechirasiz, bilmayman."
else:
javob = "Tushundim. " + "Buyurtmangiz bo'yicha ma'lumot: ... " * 6
kirish = token(system) + sum(token(x["content"]) + 4 for x in xabarlar)
return javob, {"input_tokens": kirish, "output_tokens": token(javob)}
def ha_yoq(b):
return "ha" if b else "yo'q"
def tarix_tayyorla(strategiya, tarix, xulosa, oyna=6):
if strategiya == "to'liq":
return tarix
if strategiya == "oyna":
return tarix[-(2 * oyna - 1):] # oxirgi navbatlar, user dan boshlanadi
bosh = [{"role": "user", "content": "Oldingi suhbat xulosasi: " + xulosa},
{"role": "assistant", "content": "Tushundim, davom etamiz."}]
return bosh + tarix[-(2 * oyna - 1):]
def suhbat(strategiya, navbatlar=30, kesh=False):
llm = SoxtaLLM()
tarix, xulosa, jami_kirish, jami_kesh, eslab = [], "", 0, 0, None
oldingi_prefiks = 0
for n in range(1, navbatlar + 1):
if n == 2:
matn = "Ismim Dilnoza, buyurtmam Samarqandga boradi."
elif n == navbatlar:
matn = "Aytgancha, ismim nima edi?"
else:
matn = f"{n}-savol: yetkazib berish muddati va to'lov usullari haqida aytib bering."
tarix.append({"role": "user", "content": matn})
yuborish = tarix_tayyorla(strategiya, tarix, xulosa)
assert not xabarlarni_tekshir(SYSTEM, yuborish)
javob, usage = llm.yarat(SYSTEM, yuborish)
if kesh: # prefiks kesh: oldingi so'rovning boshi qayta o'qiladi
jami_kesh += min(oldingi_prefiks, usage["input_tokens"])
oldingi_prefiks = usage["input_tokens"] + usage["output_tokens"]
jami_kirish += usage["input_tokens"]
tarix.append({"role": "assistant", "content": javob})
if strategiya == "xulosa" and "Ismim" in matn:
xulosa += "Mijoz ismi Dilnoza, manzil Samarqand. " # stub xulosa: muhim faktlar
if n == navbatlar:
eslab = javob.startswith("Ismingiz")
return jami_kirish, jami_kesh, eslab, usage["input_tokens"]
def main() -> None:
print("=== 1. Xabarlar formatini tekshirish ===")
holatlar = {
"to'g'ri": [{"role": "user", "content": "Salom"},
{"role": "assistant", "content": "Assalomu alaykum!"},
{"role": "user", "content": "Yetkazish qancha?"}],
"system ichida": [{"role": "system", "content": "Siz yordamchisiz"},
{"role": "user", "content": "Salom"}],
"assistant boshida": [{"role": "assistant", "content": "Salom"}],
"ketma-ket user": [{"role": "user", "content": "Salom"},
{"role": "user", "content": "Yetkazish qancha?"}],
}
for nom, x in holatlar.items():
xato = xabarlarni_tekshir(SYSTEM, x)
print(f" {nom:<18} {'OK' if not xato else xato[0]}")
print("\n=== 2. API holatsiz: har navbatda butun tarix qayta yuboriladi ===")
print(f" system ~{token(SYSTEM)} token; 30 navbatli suhbat")
for n in (1, 10, 20, 30):
kirish = suhbat("to'liq", navbatlar=n)[3]
print(f" {n:>2}-navbat so'rovi: kirish {kirish:>5} token")
print("\n=== 3. Tarix strategiyalari (30 navbat) ===")
print(" strategiya jami kirish shundan keshdan 2-navbatdagi ism esda")
natija = {}
for strategiya, kesh in [("to'liq", False), ("to'liq", True), ("oyna", False), ("xulosa", False)]:
jami, keshda, eslab, _ = suhbat(strategiya, kesh=kesh)
nom = strategiya + (" + kesh" if kesh else "")
natija[nom] = (jami, keshda, eslab)
print(f" {nom:<14} {jami:>11} {keshda:>16} {ha_yoq(eslab):>22}")
print("\n=== 4. Faraziy narx bilan (kirish tokenlari uchun) ===")
NARX_KIRISH, KESH_KOEF = 3.0, 0.1 # FARAZIY: $ / 1M token; kesh o'qish - 10%
for nom, (jami, keshda, _) in natija.items():
dollar = ((jami - keshda) + keshda * KESH_KOEF) * NARX_KIRISH / 1e6
print(f" {nom:<14} ${dollar * 1000:.2f} / 1000 suhbat")
print(" (haqiqiy narxni provayderning rasmiy sahifasidan oling)")
print("\n=== 5. Xulosa (natijadan) ===")
t, o, x = natija["to'liq"], natija["oyna"], natija["xulosa"]
print(f" to'liq tarix oynadan {t[0] / o[0]:.1f} barobar qimmat, lekin fakt esda: "
f"{ha_yoq(t[2])} / {ha_yoq(o[2])}")
if x[2] and not o[2]:
print(" xulosa: oyna narxiga yaqin va faktni saqladi (agar xulosa uni o'z ichiga olsa)")
print(" \u2b50 Model hech narsani 'eslamaydi' - kontekstda nima bo'lsa, faqat shu bor")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Xabarlar formatini tekshirish ===
to'g'ri OK
system ichida [0] noma'lum rol 'system' (system - messages ichida emas)
assistant boshida birinchi xabar 'user' bo'lishi kerak
ketma-ket user [1] ketma-ket ikki 'user' - bitta navbatga birlashadi
=== 2. API holatsiz: har navbatda butun tarix qayta yuboriladi ===
system ~363 token; 30 navbatli suhbat
1-navbat so'rovi: kirish 374 token
10-navbat so'rovi: kirish 1114 token
20-navbat so'rovi: kirish 1954 token
30-navbat so'rovi: kirish 2794 token
=== 3. Tarix strategiyalari (30 navbat) ===
strategiya jami kirish shundan keshdan 2-navbatdagi ism esda
to'liq 47671 0 ha
to'liq + kesh 47671 46530 ha
oyna 22812 0 yo'q
xulosa 23694 0 ha
=== 4. Faraziy narx bilan (kirish tokenlari uchun) ===
to'liq $143.01 / 1000 suhbat
to'liq + kesh $17.38 / 1000 suhbat
oyna $68.44 / 1000 suhbat
xulosa $71.08 / 1000 suhbat
(haqiqiy narxni provayderning rasmiy sahifasidan oling)
=== 5. Xulosa (natijadan) ===
to'liq tarix oynadan 2.1 barobar qimmat, lekin fakt esda: ha / yo'q
xulosa: oyna narxiga yaqin va faktni saqladi (agar xulosa uni o'z ichiga olsa)
⭐ Model hech narsani 'eslamaydi' - kontekstda nima bo'lsa, faqat shu borNima ko'rsatdi: 1-bo'limda tekshiruvchi uchta tipik xatoni topdi: system ni messages ichiga qo'yish, suhbatni assistant bilan boshlash va ketma-ket ikki user (API ularni birlashtiradi — xato emas, lekin ko'pincha kodda tarixni noto'g'ri yig'ish belgisi). 2-bo'lim API holatsizligining narxini ko'rsatadi: system prompt ~363 token, 1-navbatda kirish 374 token, 30-navbatda esa 2794 — har navbatda butun tarix qayta yuboriladi. 3-bo'limda 30 navbatli suhbat uchun jami kirish tokenlari: to'liq tarix 47 671, sirpanuvchi oyna (oxirgi 6 navbat) 22 812, xulosa + oyna 23 694. To'liq tarix oynadan 2.1 barobar qimmat — lekin faqat to'liq tarix va xulosa 2-navbatda aytilgan ismni 30-navbatda "esladi"; oyna uni yo'qotdi. Stub xulosasi muhim faktni saqlashi — biz yozgan qoida; haqiqiy modelning xulosasi nimani tashlab yuborishini oldindan bilmaysiz. 4-bo'limda faraziy narxlar (kirish $3 / 1M token, keshdan o'qish 10%) bilan: to'liq tarix $143.01 / 1000 suhbat, prefiks kesh bilan $17.38 — bu stubda har so'rov oldingi so'rovning butun prefiksini keshdan o'qiydi deb soddalashtirilgan (haqiqiy keshda minimal prefiks uzunligi va yashash muddati bor). Xulosa: tarixni qisqartirishdan oldin prefiksni barqaror qilib keshni yoqing — u "xotira" ni saqlagan holda narxni eng ko'p kamaytirdi. Bog'liq bo'limlar: 2.1, 2.2.
Misol 2 — Xatolar, eksponensial backoff, jitter va cheklovchi (virtual vaqt)
"""Xatolar turlari va qayta urinish: eksponensial backoff, jitter va retry-after (virtual vaqtda)."""
import heapq
import math
import random
import numpy as np
QAYTA_URINILADI = {429, 500, 529, "timeout"} # 400 - so'rovning o'zi xato, qayta urinilmaydi
class SoxtaServer:
"""STUB server - haqiqiy API emas. Token-bucket tezlik chegarasi (sekundiga TEZLIK so'rov),
tasodifiy 529 (band), 500 va timeout, buzuq so'rovga doim 400. Ehtimollar - FARAZ."""
TEZLIK, SIGIM = 5.0, 5.0
def __init__(self, rng):
self.rng = rng
self.tokenlar, self.oxirgi = self.SIGIM, 0.0
def sorov(self, t, buzuq):
self.tokenlar = min(self.SIGIM, self.tokenlar + (t - self.oxirgi) * self.TEZLIK)
self.oxirgi = t
if self.tokenlar < 1:
return 429, (1 - self.tokenlar) / self.TEZLIK, 0.05 # retry-after, javob vaqti
self.tokenlar -= 1
if buzuq:
return 400, None, 0.05
r = self.rng.random()
if r < 0.03:
return 529, None, 0.3
if r < 0.04:
return 500, None, 0.3
if r < 0.05:
return "timeout", None, 10.0 # mijoz 10 s kutib, o'zi uzadi
return 200, None, 1.0
def kutish(strategiya, urinish, retry_after, rng, asos=0.5, shift=60.0):
"""urinish - nechanchi muvaffaqiyatsiz urinishdan keyin (1, 2, ...)."""
if strategiya == "darhol":
return 0.05
chegara = min(shift, asos * 2 ** (urinish - 1))
if strategiya == "eksponensial":
return chegara
kut = rng.uniform(0, chegara) # full jitter
if strategiya == "jitter + retry-after" and retry_after is not None:
kut = max(kut, retry_after)
return kut
def simulyatsiya(strategiya, urug, mijozlar=20, har_biri=5, maks_urinish=10):
rng_srv, rng_kl = random.Random(urug), random.Random(10_000 + urug)
server = SoxtaServer(rng_srv)
buzuq = {(m, k): rng_kl.random() < 0.02 for m in range(mijozlar) for k in range(har_biri)}
slot = [0.0] # mijoz tomonidagi umumiy cheklovchi
def rejalashtir(t):
"""Cheklovchi bo'lsa: keyingi bo'sh slotni band qiladi (TEZLIK dan 5% sekinroq)."""
if strategiya != "cheklovchi + jitter":
return t
s = max(t, slot[0])
slot[0] = s + 1.05 / SoxtaServer.TEZLIK
return s
navbat = [(rejalashtir(0.0), m, m, 0, 1) for m in range(mijozlar)] # (vaqt, tartib, mijoz, so'rov, urinish)
heapq.heapify(navbat)
tartib = mijozlar
oldin_400 = set()
st = {"urinish": 0, "429": 0, "ok": 0, "400 qayta": 0, "yiqildi": 0, "tugash": 0.0}
while navbat:
t, _, m, k, u = heapq.heappop(navbat)
st["urinish"] += 1
kod, retry_after, dt = server.sorov(t, buzuq[m, k])
t_javob = t + dt
keyingi = None
if kod == 200:
st["ok"] += 1
keyingi = (t_javob, k + 1, 1)
else:
st["429"] += kod == 429
qayta = kod in QAYTA_URINILADI or strategiya == "darhol" # 'darhol' - 400 ni ham
if kod == 400:
st["400 qayta"] += (m, k) in oldin_400 # aynan shu so'rov yana 400 oldi
oldin_400.add((m, k))
if strategiya != "retry yo'q" and qayta and u < maks_urinish:
keyingi = (t_javob + kutish(strategiya, u, retry_after, rng_kl), k, u + 1)
else:
st["yiqildi"] += kod != 400
keyingi = (t_javob, k + 1, 1)
st["tugash"] = max(st["tugash"], t_javob)
if keyingi[1] < har_biri:
tartib += 1
heapq.heappush(navbat, (rejalashtir(keyingi[0]), tartib, m, keyingi[1], keyingi[2]))
st["togri"] = sum(not b for b in buzuq.values())
return st
def main() -> None:
print("=== 1. Xatolar turlari va qaror ===")
for kod, izoh in [(400, "so'rov xato (validatsiya)"), (429, "tezlik chegarasi"),
(500, "server ichki xatosi"), (529, "server band (overloaded)"),
("timeout", "javob kelmadi")]:
qaror = "qayta urinish" if kod in QAYTA_URINILADI else "TUZATISH kerak, qayta emas"
print(f" {str(kod):<8} {izoh:<28} -> {qaror}")
print("\n=== 2. 20 mijoz x 5 so'rov bir vaqtda (server: 5 so'rov/s), 5 urug' ===")
print(" strategiya muvaff. urinish 429 lar 400 qayta tugash (s)")
strategiyalar = ["retry yo'q", "darhol", "eksponensial", "full jitter", "jitter + retry-after",
"cheklovchi + jitter"]
natija = {}
for s in strategiyalar:
r = [simulyatsiya(s, u) for u in range(5)]
natija[s] = r
ok = np.mean([x["ok"] / x["togri"] for x in r])
print(f" {s:<22} {ok:>7.1%} {np.mean([x['urinish'] for x in r]):>8.0f} "
f"{np.mean([x['429'] for x in r]):>9.0f} {np.mean([x['400 qayta'] for x in r]):>10.0f} "
f"{np.mean([x['tugash'] for x in r]):>11.1f}")
print(" (tugash - oxirgi javob kelgan virtual vaqt; time.sleep ishlatilmadi)")
print("\n=== 3. Jitter ta'siri: juftlashgan farq (bir xil urug'lar) ===")
for a, b, kalit in [("full jitter", "eksponensial", "429"),
("full jitter", "eksponensial", "tugash"),
("jitter + retry-after", "full jitter", "429")]:
f = np.array([x[kalit] - y[kalit] for x, y in zip(natija[a], natija[b])])
se = f.std(ddof=1) / math.sqrt(len(f))
print(f" {kalit:<6} {a} - {b}: {f.mean():+.1f}, SE {se:.1f} -> "
f"{'sezilarli' if abs(f.mean()) > 2 * se else 'sezilarli emas'}")
print("\n=== 4. Kutish vaqtlari (urinish 1..6, asos 0.5 s, shift 60 s) ===")
rng = random.Random(0)
print(" eksponensial: " + ", ".join(f"{kutish('eksponensial', u, None, rng):.1f}"
for u in range(1, 7)))
print(" full jitter: " + ", ".join(f"{kutish('full jitter', u, None, rng):.1f}"
for u in range(1, 7)))
print("\n=== 5. Xulosa (natijadan) ===")
yoq, jit = natija["retry yo'q"], natija["full jitter"]
print(f" qayta urinishsiz muvaffaqiyat {np.mean([x['ok'] / x['togri'] for x in yoq]):.1%} - "
f"429 lar birinchi soniyalarda to'planadi")
d = np.mean([x["400 qayta"] for x in natija["darhol"]])
if d > 0:
print(f" 'darhol' 400 ni ham qayta yubordi: {d:.0f} ta befoyda so'rov")
c = natija["cheklovchi + jitter"]
print(f" mijoz tomonida cheklovchi: 429 lar {np.mean([x['429'] for x in c]):.0f}, "
f"urinishlar {np.mean([x['urinish'] for x in c]):.0f} (so'rovlar 100)")
print(" \u2b50 Avval tezlikni o'zingiz cheklang; qayta urinish - 429/5xx/timeout, backoff + jitter")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Xatolar turlari va qaror ===
400 so'rov xato (validatsiya) -> TUZATISH kerak, qayta emas
429 tezlik chegarasi -> qayta urinish
500 server ichki xatosi -> qayta urinish
529 server band (overloaded) -> qayta urinish
timeout javob kelmadi -> qayta urinish
=== 2. 20 mijoz x 5 so'rov bir vaqtda (server: 5 so'rov/s), 5 urug' ===
strategiya muvaff. urinish 429 lar 400 qayta tugash (s)
retry yo'q 18.9% 100 79 0 5.0
darhol 28.7% 799 767 1 6.8
eksponensial 100.0% 189 84 0 51.7
full jitter 100.0% 214 108 0 39.7
jitter + retry-after 100.0% 220 114 0 38.2
cheklovchi + jitter 100.0% 105 0 0 30.0
(tugash - oxirgi javob kelgan virtual vaqt; time.sleep ishlatilmadi)
=== 3. Jitter ta'siri: juftlashgan farq (bir xil urug'lar) ===
429 full jitter - eksponensial: +24.6, SE 6.0 -> sezilarli
tugash full jitter - eksponensial: -12.0, SE 7.0 -> sezilarli emas
429 jitter + retry-after - full jitter: +5.8, SE 7.4 -> sezilarli emas
=== 4. Kutish vaqtlari (urinish 1..6, asos 0.5 s, shift 60 s) ===
eksponensial: 0.5, 1.0, 2.0, 4.0, 8.0, 16.0
full jitter: 0.4, 0.8, 0.8, 1.0, 4.1, 6.5
=== 5. Xulosa (natijadan) ===
qayta urinishsiz muvaffaqiyat 18.9% - 429 lar birinchi soniyalarda to'planadi
'darhol' 400 ni ham qayta yubordi: 1 ta befoyda so'rov
mijoz tomonida cheklovchi: 429 lar 0, urinishlar 105 (so'rovlar 100)
⭐ Avval tezlikni o'zingiz cheklang; qayta urinish - 429/5xx/timeout, backoff + jitterNima ko'rsatdi: stub server sekundiga 5 so'rov o'tkazadi (token-bucket), qolganiga 429 va retry-after qaytaradi; o'tgan so'rovlarning 3% i 529, 1% i 500, 1% i timeout (ehtimollar — faraz), 2% so'rov buzuq (doim 400). 20 mijoz 5 tadan so'rovni bir vaqtda boshlaydi. Qayta urinishsiz faqat 18.9% muvaffaqiyat — 79 ta so'rov birinchi soniyalarda 429 olib tashlab ketildi. "Darhol" qayta urinish (va 400 ni ham qayta yuborish) serverni 767 ta 429 bilan to'ldirdi va baribir 28.7% da qoldi: 10 urinish yarim soniyada tugaydi, tezlik chegarasi esa tiklanishga ulgurmaydi. Eksponensial backoff 100% ga chiqardi (189 urinish, 51.7 s). Kutilmagan natija: full jitter 429 larni ko'paytirdi (214 urinish, 108 ta 429; juftlashgan farq +24.6, SE 6.0 — sezilarli), chunki uning o'rtacha kutishi ikki barobar qisqa; tugash vaqti qisqardi (39.7 s), lekin farq -12.0, SE 7.0 — sezilarli emas. retry-after ni hisobga olish ham sezilarli farq bermadi (+5.8, SE 7.4). Bu jitterni "foydasiz" demaydi — bu yuklamada mijozlar backoff tufayli allaqachon yetarli darajada tarqalib ketadi; jitter mijozlar bir lahzada sinxron qaytganda (masalan, server qayta ishga tushgandan keyin) muhim. Aniq g'olib — mijoz tomonidagi cheklovchi: 0 ta 429, 105 urinish (100 so'rov + 5 ta 5xx/timeout qayta urinishi) va eng qisqa tugash 30.0 s. 100 so'rovni 5 so'rov/s da yuborishning nazariy minimumi ~21 s — qolgani qayta urinishlar va oxirgi javoblar. Bog'liq bo'limlar: 2.3, 2.4.
Misol 3 — Javoblar keshi va idempotentlik
"""Javoblar keshi va idempotentlik: kesh kaliti, normallashtirish, LRU va takroriy yon ta'sirlar."""
import hashlib
import json
import random
import re
from collections import OrderedDict
import numpy as np
DOKONLAR = {"A": "Siz 'Bozor24' do'konining yordamchisisiz. Yetkazish 2 kun.",
"B": "Siz 'TezSavdo' do'konining yordamchisisiz. Yetkazish 1 kun."}
MAVZULAR = ["yetkazish", "to'lov", "qaytarish", "kafolat", "chegirma", "buyurtma holati"]
def savollar_havzasi():
return [f"{m} haqida {i}-savol: qanday ishlaydi?" for i in range(50) for m in MAVZULAR]
class SoxtaLLM:
"""STUB - haqiqiy model emas: javob system va savolga deterministik bog'liq."""
def __init__(self):
self.chaqiruv = 0
def yarat(self, sorov):
self.chaqiruv += 1
dokon = re.search(r"'(\w+)'", sorov["system"]).group(1)
savol = " ".join(sorov["messages"][-1]["content"].split()).lower()
return f"[{dokon}] {savol} -> javob"
def kalit_naiv(sorov):
return sorov["messages"][-1]["content"] # faqat savol matni
def kanonik(sorov, normallash):
s = json.loads(json.dumps(sorov))
if normallash:
for x in s["messages"]:
x["content"] = " ".join(x["content"].split()).lower()
return json.dumps(s, sort_keys=True, ensure_ascii=False, separators=(",", ":"))
def kalit_togri(sorov, normallash=False):
"""Javobga ta'sir qiladigan HAMMA narsa: model, system, messages, parametrlar."""
return hashlib.sha256(kanonik(sorov, normallash).encode()).hexdigest()
class LRUKesh:
def __init__(self, sigim=None):
self.d, self.sigim = OrderedDict(), sigim
def ol(self, k):
if k in self.d:
self.d.move_to_end(k)
return self.d[k]
return None
def qoy(self, k, v):
self.d[k] = v
self.d.move_to_end(k)
if self.sigim is not None and len(self.d) > self.sigim:
self.d.popitem(last=False) # eng eski ishlatilganni chiqarish
def yuklama(n, urug):
rng = np.random.default_rng(urug)
havza = savollar_havzasi()
vazn = 1.0 / np.arange(1, len(havza) + 1) ** 1.1 # Zipf: bir necha savol juda ko'p
idx = rng.choice(len(havza), n, p=vazn / vazn.sum())
sorovlar = []
for i in idx:
savol = havza[i]
if rng.random() < 0.25: # foydalanuvchi yozish uslubi
savol = " " + savol.capitalize().replace(" ", " ", 1) + " "
dokon = "A" if rng.random() < 0.5 else "B"
sorovlar.append({"model": "claude-sonnet-5", "max_tokens": 300, "temperature": 0.0,
"system": DOKONLAR[dokon], "messages": [{"role": "user", "content": savol}]})
return sorovlar
def ishga_tushir(sorovlar, kalit_fn, sigim=None):
llm, kesh, notogri = SoxtaLLM(), LRUKesh(sigim), 0
for s in sorovlar:
haqiqiy = SoxtaLLM().yarat(s) # tekshiruv uchun "to'g'ri javob"
if kalit_fn is None:
javob = llm.yarat(s)
else:
k = kalit_fn(s)
javob = kesh.ol(k)
if javob is None:
javob = llm.yarat(s)
kesh.qoy(k, javob)
notogri += javob != haqiqiy
return llm.chaqiruv, notogri
def main() -> None:
print("=== 1. Kesh kaliti: tartib va bo'shliqlar ===")
a = {"model": "m", "system": "s", "messages": [{"role": "user", "content": "Salom"}]}
b = {"messages": [{"content": "Salom", "role": "user"}], "system": "s", "model": "m"}
print(f" json.dumps (sort_keys yo'q) teng: {json.dumps(a) == json.dumps(b)}")
print(f" kanonik (sort_keys=True) teng: {kanonik(a, False) == kanonik(b, False)}")
c = {**a, "messages": [{"role": "user", "content": " salom "}]}
print(f" ' salom ' va 'Salom': normallashsiz {kalit_togri(a) == kalit_togri(c)}, "
f"normallash bilan {kalit_togri(a, True) == kalit_togri(c, True)}")
print("\n=== 2. 3000 so'rov (300 savol, Zipf, 2 do'kon), 5 urug' ===")
print(" kalit API chaqiruv hit ulushi noto'g'ri javob")
variantlar = [("kesh yo'q", None, None), ("naiv: faqat savol", kalit_naiv, None),
("to'liq so'rov", kalit_togri, None),
("to'liq + normallash", lambda s: kalit_togri(s, True), None),
("to'liq + norm. + LRU 100", lambda s: kalit_togri(s, True), 100)]
natija = {}
for nom, fn, sigim in variantlar:
r = np.array([ishga_tushir(yuklama(3000, u), fn, sigim) for u in range(5)])
natija[nom] = r
print(f" {nom:<27} {r[:, 0].mean():>12.0f} {1 - r[:, 0].mean() / 3000:>12.1%} "
f"{r[:, 1].mean():>16.0f}")
print("\n=== 3. Faraziy narx (1 chaqiruv ~ 400 kirish + 150 chiqish token) ===")
NARX_KIR, NARX_CHIQ = 3.0, 15.0 # FARAZIY $ / 1M token - rasmiy sahifadan oling
bir = (400 * NARX_KIR + 150 * NARX_CHIQ) / 1e6
for nom in ["kesh yo'q", "to'liq + normallash", "to'liq + norm. + LRU 100"]:
print(f" {nom:<27} ${natija[nom][:, 0].mean() * bir:.2f} / 3000 so'rov")
print("\n=== 4. Idempotentlik: yon ta'sirli amal va timeout dan keyin qayta urinish ===")
for kalit_bilan in (False, True):
rng, bajarilgan, kalitlar = random.Random(7), [], set()
for op in range(300): # 300 ta "buyurtma yaratish" amali
ikalit = f"buyurtma-{op}" # mijoz amal uchun BIR MARTA yaratadi
for _ in range(4):
if not (kalit_bilan and ikalit in kalitlar):
bajarilgan.append(op) # server amalni bajardi
kalitlar.add(ikalit)
if rng.random() >= 0.10: # 10% - javob yo'lda yo'qoldi (timeout)
break
takror = len(bajarilgan) - len(set(bajarilgan))
holat = "bor" if kalit_bilan else "yo'q"
print(f" idempotentlik kaliti {holat:<4}: 300 amal -> bajarildi {len(bajarilgan)}, "
f"ortiqcha (takroriy) bajarilish {takror}")
print("\n=== 5. Xulosa (natijadan) ===")
nv = natija["naiv: faqat savol"]
if nv[:, 1].mean() > 0:
print(f" naiv kalit: hit ko'p, lekin {nv[:, 1].mean():.0f} ta javob BOSHQA do'kon uchun edi")
t, n = natija["to'liq so'rov"], natija["to'liq + normallash"]
print(f" normallash hit ulushini {1 - t[:, 0].mean() / 3000:.1%} -> "
f"{1 - n[:, 0].mean() / 3000:.1%} ga oshirdi, xato 0")
print(" \u2b50 Kalit = javobga ta'sir qiluvchi hamma narsa; yon ta'sirli amalga idempotentlik kaliti")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. Kesh kaliti: tartib va bo'shliqlar ===
json.dumps (sort_keys yo'q) teng: False
kanonik (sort_keys=True) teng: True
' salom ' va 'Salom': normallashsiz False, normallash bilan True
=== 2. 3000 so'rov (300 savol, Zipf, 2 do'kon), 5 urug' ===
kalit API chaqiruv hit ulushi noto'g'ri javob
kesh yo'q 3000 0.0% 0
naiv: faqat savol 411 86.3% 1283
to'liq so'rov 613 79.6% 0
to'liq + normallash 434 85.5% 0
to'liq + norm. + LRU 100 989 67.0% 0
=== 3. Faraziy narx (1 chaqiruv ~ 400 kirish + 150 chiqish token) ===
kesh yo'q $10.35 / 3000 so'rov
to'liq + normallash $1.50 / 3000 so'rov
to'liq + norm. + LRU 100 $3.41 / 3000 so'rov
=== 4. Idempotentlik: yon ta'sirli amal va timeout dan keyin qayta urinish ===
idempotentlik kaliti yo'q: 300 amal -> bajarildi 335, ortiqcha (takroriy) bajarilish 35
idempotentlik kaliti bor : 300 amal -> bajarildi 300, ortiqcha (takroriy) bajarilish 0
=== 5. Xulosa (natijadan) ===
naiv kalit: hit ko'p, lekin 1283 ta javob BOSHQA do'kon uchun edi
normallash hit ulushini 79.6% -> 85.5% ga oshirdi, xato 0
⭐ Kalit = javobga ta'sir qiluvchi hamma narsa; yon ta'sirli amalga idempotentlik kalitiNima ko'rsatdi: 1-bo'lim kalit qurishning ikki tuzog'ini ko'rsatadi: kalitlari boshqa tartibda yozilgan bir xil lug'atlar json.dumps da farq qiladi (False), sort_keys=True bilan — teng (True); bo'sh joy va katta harf farqi normallashtirishsiz alohida kalit beradi. 2-bo'limda 3000 so'rov (300 savol, Zipf taqsimoti — bir necha savol juda ko'p so'raladi; 25% so'rov ortiqcha bo'sh joy va katta harf bilan; ikki do'kon — ikki xil system). Naiv kalit (faqat savol matni) eng yuqori hit ulushini berdi (86.3%), lekin 1283 ta javob boshqa do'kon uchun edi — jim xato. To'liq so'rov kaliti 79.6% hit va 0 xato; ma'noni o'zgartirmaydigan normallashtirish hit ni 85.5% ga ko'tardi, xato baribir 0. LRU 100 — xotira 100 javob bilan cheklangan: hit 67.0%, lekin faraziy narx kesh yo'q holatdagi $10.35 o'rniga $3.41 (cheksiz keshda $1.50). 4-bo'lim idempotentlik: 300 ta yon ta'sirli amal, har urinishda 10% ehtimol bilan javob yo'lda yo'qoladi (server esa amalni bajargan). Kalitsiz qayta urinishlar 35 ta ortiqcha bajarilish berdi — real hayotda bu 35 ta ikki marta yaratilgan buyurtma yoki ikki marta yechilgan to'lov. Mijoz har amal uchun bir marta yaratgan kalit bilan server takrorni tanidi — 0. Bog'liq bo'limlar: 2.5.
Misol 4 — usage dan xarajat, byudjet, streaming, batch va kalit xavfsizligi
"""Xarajat usage dan, byudjet qo'riqchisi, streaming hodisalari, batch va kalitni xavfsiz saqlash."""
import heapq
import math
import random
import re
# FARAZIY narx jadvali ($ / 1M token): haqiqiy narxni provayderning rasmiy sahifasidan oling
NARX = {"katta": {"kirish": 6.0, "chiqish": 30.0},
"orta": {"kirish": 2.0, "chiqish": 10.0},
"kichik": {"kirish": 0.5, "chiqish": 2.5}}
KESH_OQISH_KOEF = 0.1 # faraz: keshdan o'qilgan kirish tokeni 10% narxda
BATCH_CHEGIRMA = 0.5 # faraz: batch so'rovlar yarim narxda
def narx(model, usage):
n = NARX[model]
return (usage["input_tokens"] * n["kirish"]
+ usage.get("cache_read_input_tokens", 0) * n["kirish"] * KESH_OQISH_KOEF
+ usage["output_tokens"] * n["chiqish"]) / 1e6
class SoxtaMijoz:
"""STUB - haqiqiy API emas. Javobda haqiqiy API dagi kabi usage maydonlari bor."""
def __init__(self, urug):
self.rng = random.Random(urug)
def create(self, model, vazifa):
kir = {"chat": 1200, "xulosa": 6000, "tasnif": 300}[vazifa]
chiq = {"chat": 250, "xulosa": 400, "tasnif": 5}[vazifa]
kesh = int(kir * 0.8) if vazifa == "chat" and self.rng.random() < 0.7 else 0
return {"usage": {"input_tokens": int(kir * self.rng.uniform(0.8, 1.2)) - kesh,
"cache_read_input_tokens": kesh,
"output_tokens": int(chiq * self.rng.uniform(0.5, 1.5))}}
def stream(self, matn, uzilish=None):
"""Hodisalar ketma-ketligi (virtual vaqt bilan), haqiqiy streaming API ga o'xshash."""
t = 0.8 # birinchi tokengacha
yield t, {"type": "message_start", "usage": {"input_tokens": 900}}
sozlar = matn.split(" ")
for i, s in enumerate(sozlar):
if uzilish is not None and i == uzilish:
return # ulanish uzildi
t += 0.03
yield t, {"type": "content_block_delta", "text": s + " "}
yield t, {"type": "message_delta", "stop_reason": "end_turn",
"usage": {"output_tokens": math.ceil(len(matn) / 4)}}
yield t, {"type": "message_stop"}
def stream_yigish(hodisalar):
matn, usage, tugadi, birinchi = [], {}, False, None
for t, h in hodisalar:
if h["type"] == "message_start":
usage.update(h["usage"])
elif h["type"] == "content_block_delta":
birinchi = t if birinchi is None else birinchi
matn.append(h["text"])
elif h["type"] == "message_delta":
usage.update(h["usage"])
elif h["type"] == "message_stop":
tugadi = True
oxirgi = t
return "".join(matn), usage, tugadi, birinchi, oxirgi
def niqobla(kalit):
return kalit[:7] + "..." + kalit[-4:] if kalit and len(kalit) > 12 else "***"
def main() -> None:
mijoz = SoxtaMijoz(0)
print("=== 1. usage dan xarajat: vazifa va model bo'yicha (600 so'rov) ===")
marshrut = {"chat": "orta", "xulosa": "katta", "tasnif": "kichik"}
jami = {}
for i in range(600):
vazifa = ["chat", "chat", "xulosa", "tasnif", "tasnif", "tasnif"][i % 6]
u = mijoz.create(marshrut[vazifa], vazifa)["usage"]
j = jami.setdefault(vazifa, [0, 0, 0, 0.0])
j[0] += u["input_tokens"]
j[1] += u["cache_read_input_tokens"]
j[2] += u["output_tokens"]
j[3] += narx(marshrut[vazifa], u)
print(" vazifa model kirish keshdan chiqish narx ($)")
for v, (a, b, c, d) in jami.items():
print(f" {v:<7} {marshrut[v]:<7} {a:>8} {b:>9} {c:>9} {d:>10.3f}")
eng = max(jami, key=lambda v: jami[v][3])
print(f" eng qimmat: {eng} ({jami[eng][3] / sum(x[3] for x in jami.values()):.0%})")
print("\n=== 2. Byudjet qo'riqchisi: 'xulosa' vazifasi, byudjet $0.50 ===")
sarf, bajarildi = 0.0, 0
taxmin = narx("katta", {"input_tokens": 7200, "output_tokens": 600}) # yuqori chegara
for _ in range(100):
if sarf + taxmin > 0.50:
break # so'rovdan OLDIN tekshirish
sarf += narx("katta", mijoz.create("katta", "xulosa")["usage"])
bajarildi += 1
print(f" bajarildi {bajarildi} / 100, sarflandi ${sarf:.3f}, byudjetdan oshmadi: {sarf <= 0.50}")
print("\n=== 3. Streaming: birinchi so'z va to'liq javob (virtual vaqt) ===")
javob = " ".join(["Buyurtmangiz"] + ["ertaga yetkaziladi"] * 60)
matn, usage, tugadi, bir, oxir = stream_yigish(mijoz.stream(javob))
print(f" birinchi so'z {bir:.2f} s da, oxirgisi {oxir:.2f} s da; "
f"so'z {len(matn.split())}, usage {usage}, to'liq: {tugadi}")
matn2, usage2, tugadi2, _, _ = stream_yigish(mijoz.stream(javob, uzilish=40))
print(f" uzilgan oqim: so'z {len(matn2.split())}, output_tokens bormi: "
f"{'output_tokens' in usage2}, to'liq: {tugadi2} -> qayta so'rash kerak")
print("\n=== 4. 1000 so'rov: parallel (8 oqim) va batch ===")
rng = random.Random(1)
kechikish = [rng.uniform(2, 8) for _ in range(1000)]
oqimlar = [0.0] * 8
for k in kechikish: # har so'rov eng tez bo'shagan oqimga
heapq.heapreplace(oqimlar, oqimlar[0] + k)
print(f" parallel 8 oqim: tugash {max(oqimlar) / 60:.1f} daqiqa, narx 1.00 x")
print(f" batch: tugash - soatlargacha (provayder kafolati ichida), narx {BATCH_CHEGIRMA:.2f} x (faraz)")
natijalar = [(f"sorov-{i}", f"javob-{i}") for i in range(1000)]
rng.shuffle(natijalar) # batch natijalari ixtiyoriy tartibda
pozitsiya = sum(n[1] != f"javob-{i}" for i, n in enumerate(natijalar))
lugat = dict(natijalar)
custom = sum(lugat[f"sorov-{i}"] != f"javob-{i}" for i in range(1000))
print(f" natijani pozitsiya bo'yicha bog'lash: {pozitsiya} xato; custom_id bo'yicha: {custom} xato")
print("\n=== 5. Kalit: muhitdan olish, niqoblash va koddan izlash ===")
muhit = {"ANTHROPIC_API_KEY": "sk-ant-api03-" + "x" * 40 + "Ab12"} # soxta kalit
kalit = muhit.get("ANTHROPIC_API_KEY")
print(f" kalit topildi: {kalit is not None}, logda: {niqobla(kalit)}")
fayllar = {"app.py": 'client = anthropic.Anthropic()',
"eski.py": 'client = anthropic.Anthropic(api_key="sk-ant-api03-' + "y" * 40 + '")',
"README.md": "Kalitni ANTHROPIC_API_KEY ga yozing"}
naqsh = re.compile(r"sk-ant-[A-Za-z0-9_\-]{20,}")
for nom, matn in fayllar.items():
if naqsh.search(matn):
print(f" DIQQAT: {nom} ichida kalit bor -> o'chiring va kalitni almashtiring (rotate)")
print("\n=== 6. Xulosa (natijadan) ===")
print(f" xarajatning {jami[eng][3] / sum(x[3] for x in jami.values()):.0%} i '{eng}' da - "
f"optimallashtirishni shundan boshlang")
print(" \u2b50 Har so'rovning usage ini loglang; natijani custom_id bilan bog'lang; kalit - muhitda")
if __name__ == "__main__":
main()Natijaning muhim qismi:
=== 1. usage dan xarajat: vazifa va model bo'yicha (600 so'rov) ===
vazifa model kirish keshdan chiqish narx ($)
chat orta 114863 123840 49175 0.746
xulosa katta 600894 0 40694 4.826
tasnif kichik 90413 0 1304 0.048
eng qimmat: xulosa (86%)
=== 2. Byudjet qo'riqchisi: 'xulosa' vazifasi, byudjet $0.50 ===
bajarildi 9 / 100, sarflandi $0.457, byudjetdan oshmadi: True
=== 3. Streaming: birinchi so'z va to'liq javob (virtual vaqt) ===
birinchi so'z 0.83 s da, oxirgisi 4.43 s da; so'z 121, usage {'input_tokens': 900, 'output_tokens': 288}, to'liq: True
uzilgan oqim: so'z 40, output_tokens bormi: False, to'liq: False -> qayta so'rash kerak
=== 4. 1000 so'rov: parallel (8 oqim) va batch ===
parallel 8 oqim: tugash 10.7 daqiqa, narx 1.00 x
batch: tugash - soatlargacha (provayder kafolati ichida), narx 0.50 x (faraz)
natijani pozitsiya bo'yicha bog'lash: 998 xato; custom_id bo'yicha: 0 xato
=== 5. Kalit: muhitdan olish, niqoblash va koddan izlash ===
kalit topildi: True, logda: sk-ant-...Ab12
DIQQAT: eski.py ichida kalit bor -> o'chiring va kalitni almashtiring (rotate)
=== 6. Xulosa (natijadan) ===
xarajatning 86% i 'xulosa' da - optimallashtirishni shundan boshlang
⭐ Har so'rovning usage ini loglang; natijani custom_id bilan bog'lang; kalit - muhitdaNima ko'rsatdi: narx jadvali va kesh/batch koeffitsientlari — faraziy (model nomlari o'rniga "katta/o'rta/kichik" darajalari); haqiqiy narxlarni provayderning rasmiy sahifasidan oling. 1-bo'limda 600 so'rovning usage i vazifa bo'yicha yig'ildi: so'rovlarning faqat oltidan biri bo'lgan xulosa (uzun kirish, katta model) xarajatning 86% ini tashkil qildi, tasnif esa so'rovlarning yarmi bo'lsa ham $0.048 — kichik model va 5 tokenli javob. chat da kirish tokenlarining katta qismi keshdan o'qildi (123 840 va 114 863). Bunday jadval optimallashtirishni qayerdan boshlashni aytadi. 2-bo'lim: byudjet qo'riqchisi har so'rovdan oldin "joriy sarf + yuqori chegara" ni tekshirdi — $0.50 byudjetda 9 so'rov, sarf $0.457, oshib ketish yo'q. 3-bo'lim streaming hodisalarini yig'di: birinchi so'z 0.83 s da, oxirgisi 4.43 s da — streamsiz foydalanuvchi 4.43 s bo'sh ekranga qarardi; usage message_start va message_delta dan yig'ildi. Uzilgan oqimda 40 so'z keldi, message_stop va output_tokens yo'q — javob to'liq emas deb belgilanadi. 4-bo'lim: 1000 so'rov 8 parallel oqimda 10.7 daqiqa; batch arzonroq, lekin soatlab kutish mumkin. Batch natijalari aralash tartibda keldi: pozitsiya bo'yicha bog'lash 998 xato, custom_id bo'yicha — 0. 5-bo'lim: kalit muhitdan olindi va logga niqoblangan holda (sk-ant-...Ab12) yozildi; oddiy regex skaner eski.py ichidagi yozib qo'yilgan kalitni topdi. Bog'liq bo'limlar: 2.6, 2.7, 2.8, 2.9.
5. To'g'ri va noto'g'ri tushunishlar
| Noto'g'ri fikr | To'g'risi |
|---|---|
| "Model suhbatni eslaydi" | API holatsiz: har navbatda butun tarix yuboriladi (1-misol: 374 → 2794 token) |
| "Tarixni qisqartirish — eng yaxshi tejash" | Oyna faktni yo'qotdi; prefiks kesh to'liq tarixni $143.01 dan $17.38 ga tushirdi (faraziy narx) |
| "Har qanday xatoga qayta urinish kerak" | 400/401/404 — kodingizdagi xato; qayta yuborish befoyda |
| "Xato bo'lsa darhol qayta yuborish tezroq" | 2-misol: 767 ta 429, muvaffaqiyat 28.7% |
| "Jitter har doim 429 ni kamaytiradi" | 2-misolda ko'paytirdi (+24.6, SE 6.0); foydasi yuklamaga bog'liq |
| "429 — qayta urinish bilan hal bo'ladi" | Eng yaxshisi — oldini olish: cheklovchi bilan 0 ta 429 |
| "Timeout — so'rov bajarilmadi" | Bajarilgan bo'lishi mumkin: 35 ta ortiqcha bajarilish |
| "Kesh kaliti uchun savol matni yetarli" | 1283 ta boshqa do'kon javobi; kalit — butun so'rov |
| "Streaming javobni tezlashtiradi" | Sezilgan kechikishni: birinchi so'z 0.83 s da, oxirgisi baribir 4.43 s |
| "Batch natijalari yuborilgan tartibda" | Ixtiyoriy tartib; custom_id bo'yicha bog'lang |
| "Kalitni private repoga qo'ysa bo'ladi" | Kalit — muhitda; oshkor bo'lsa — rotate |
6. Keng tarqalgan xatolar va yechimlari
1. stop_reason tekshirilmagan
matn = javob.content[0].text # ⚠️
if javob.stop_reason == "max_tokens": # ✅
raise RuntimeError("javob qirqilgan")
matn = "".join(b.text for b in javob.content if b.type == "text")2. system messages ichida
messages = [{"role": "system", "content": "..."}, {"role": "user", "content": q}] # ⚠️
client.messages.create(model=m, max_tokens=512, system="...",
messages=[{"role": "user", "content": q}]) # ✅3. Hamma xatoga bir xil qayta urinish
except Exception:
continue # ⚠️
except (anthropic.RateLimitError, anthropic.InternalServerError,
anthropic.APITimeoutError, anthropic.APIConnectionError):
time_kut(kutish("full jitter", urinish, None, rng)) # ✅ 400 - tuzatiladi4. Backoff siz, chegarasiz
while True:
try: return yubor(s)
except RateLimitError: pass # ⚠️
for urinish in range(1, 7):
try: return yubor(s)
except RateLimitError: kut(min(60, 0.5 * 2 ** (urinish - 1))) # ✅5. Kesh kaliti to'liq emas
kalit = savol # ⚠️
kalit = hashlib.sha256(json.dumps(sorov, sort_keys=True).encode()).hexdigest() # ✅6. Yon ta'sirli amalni kalitsiz qayta yuborish
for _ in range(3): buyurtma_yarat(d) # ⚠️
ikalit = f"buyurtma-{d['id']}" # ✅ bir marta
for _ in range(3): buyurtma_yarat(d, idempotency_key=ikalit)7. Kalit kodda
client = anthropic.Anthropic(api_key="sk-ant-...") # ⚠️
client = anthropic.Anthropic() # ANTHROPIC_API_KEY muhitdan # ✅7. Integratsiya — bu bilim qayerda kerak bo'ladi
- 25.2-dars (o'tilgan): tokenlar, kontekst oynasi va xarajat — bu yerda
usagebilan haqiqiy hisob - 25.5-dars (o'tilgan): strukturali chiqish — qayta so'rash sikli aynan shu API xatolari siyosati ichida ishlaydi
- 18-qism (o'tilgan): juftlashgan taqqoslash va SE — 2-misoldagi strategiyalar
- Keyingi darslar: Embeddinglar — embedding API ham xuddi shu retry/batch/kesh qoidalari bilan; RAG — kontekstni prompt ga sig'dirish va prefiks kesh; LLM ni baholash — batch bilan katta baholash to'plamlari; Agentlar va tool calling — ko'p chaqiruvli sikllarda byudjet va idempotentlik; LLM xavfsizligi — kalitlar, proksi server; MLOps va deploy qismida — monitoring, loglar, xarajat panellari
8. Eng yaxshi amaliyotlar
Har so'rovda
stop_reasonvausageni o'qing varesponse.idbilan birga loglang.Xatolarni turi bo'yicha ajrating: 400-larni tuzating, 429/5xx/timeout ga qayta urining.
Yuklamani mijoz tomonida cheklang; qayta urinish — eksponensial, jitter bilan, urinishlar va kutish chegaralangan.
System prompt va eski tarixni barqaror saqlang — prefiks kesh ishlasin; unga sana va tasodifiy ID qo'ymang.
Javob keshi kalitini butun so'rovning kanonik xeshidan quring.
Yon ta'sirli har amalga idempotentlik kaliti bering.
Foydalanuvchi kutmaydigan ishni batch ga yuboring; natijani
custom_idbo'yicha bog'lang.Xarajatni vazifa va model bo'yicha yig'ing; byudjetni so'rovdan oldin tekshiring.
Kalitni muhitda saqlang, commit dan oldin skanerlang, oshkor bo'lsa — darhol almashtiring.
9. Amaliy topshiriq
Vazifa 1: Bashorat qiling
1. # 10-navbatda model 2-navbatdagi faktni qayerdan "biladi"?
2. # system prompt ichiga joriy vaqt qo'yilsa, prefiks keshga nima bo'ladi?
3. # BadRequestError (400) ga qayta urinish kerakmi?
4. # 529 nimani anglatadi va qayta urinish kerakmi?
5. # asos 0.5 s, shift 60 s: 8-urinishdan oldingi eksponensial kutish?
6. # full jitter da 3-urinishdan oldingi kutish oralig'i?
7. # timeout bo'ldi - buyurtma yaratildimi?
8. # json.dumps({"b": 1, "a": 2}) == json.dumps({"a": 2, "b": 1}) ?
9. # kesh kalitida system bo'lmasa, ikki do'kon bitta keshni ishlatsa?
10. # stream da message_stop kelmadi - javobni saqlaymizmi?
11. # batch natijalari qaysi tartibda keladi?
12. # kalit git ga tushdi va faylni o'chirdik - yetarlimi?Javoblar
- Faqat biz yuborgan tarixdan — API holatsiz; tarix yuborilmasa, fakt yo'q
- Har so'rovda prefiks o'zgaradi — kesh hech qachon ishlamaydi
- Yo'q — so'rovni tuzatish kerak
- Server band (overloaded) — vaqtinchalik, backoff bilan qayta urinish
min(60, 0.5 * 2^6) = 32suniform(0, 0.5 * 2^2) = uniform(0, 2)s- Noma'lum — bajarilgan bo'lishi mumkin; idempotentlik kaliti bilan qayta yuboring
False—sort_keys=Truekerak- Bir do'kon mijoziga boshqa do'kon javobi (3-misol: 1283 ta)
- Yo'q — to'liq emas; qayta so'rang yoki "qisman" deb belgilang
- Ixtiyoriy —
custom_idbo'yicha bog'lanadi - Yo'q — git tarixida qoladi; kalitni bekor qilib yangisini yarating
Vazifa 2: Xatolarni tuzating
1. client = anthropic.Anthropic(api_key="sk-ant-api03-...")
print("kalit:", client.api_key)
2. for urinish in range(100):
try:
return client.messages.create(**sorov)
except Exception:
pass
3. kesh_kaliti = sorov["messages"][-1]["content"]
4. for i, natija in enumerate(client.messages.batches.results(paket.id)):
saqla(hujjatlar[i], natija)
5. def tolov_yech(summa):
for _ in range(3):
try:
return tolov_api.yech(summa)
except TimeoutError:
continueJavoblar
1. client = anthropic.Anthropic() # ANTHROPIC_API_KEY muhitdan
print("kalit:", niqobla(os.environ["ANTHROPIC_API_KEY"]))
2. rng = random.Random(sorov_id)
for urinish in range(1, 7): # chegarali
try:
return client.messages.create(**sorov)
except anthropic.BadRequestError:
raise # tuzatiladi, qayta emas
except (anthropic.RateLimitError, anthropic.InternalServerError,
anthropic.APITimeoutError, anthropic.APIConnectionError):
kut(rng.uniform(0, min(60, 0.5 * 2 ** (urinish - 1))))
raise RuntimeError("urinishlar tugadi")
3. kesh_kaliti = hashlib.sha256(
json.dumps(sorov, sort_keys=True, ensure_ascii=False).encode()).hexdigest()
4. for natija in client.messages.batches.results(paket.id):
saqla(hujjat_idsi[natija.custom_id], natija) # custom_id bo'yicha
5. def tolov_yech(summa, amal_id):
ikalit = f"tolov-{amal_id}" # amal uchun bir marta
for urinish in range(1, 4):
try:
return tolov_api.yech(summa, idempotency_key=ikalit)
except TimeoutError:
kut(min(10, 0.5 * 2 ** (urinish - 1)))Vazifa 3: Suhbat tarixi
Modellang:
- Xabarlar tekshiruvchisi (rollar, birinchi xabar, bo'sh matn)
- To'liq / oyna / xulosa + oyna strategiyalari
- Navbat bo'yicha kirish tokenlari va jami
- Prefiks kesh bilan faraziy narx; eski faktni "eslash" testi
Vazifa 4: Qayta urinish simulyatsiyasi
Modellang:
- Token-bucket server va xatolar turlari (virtual vaqt)
- Strategiyalar: yo'q, darhol, eksponensial, jitter, retry-after, cheklovchi
- Muvaffaqiyat, urinishlar, 429 lar, tugash vaqti — 5 urug'
- Juftlashgan farqlar va SE; mijozlar sonini 5 va 50 ga o'zgartirib jitter ta'sirini qayta o'lchang
Vazifa 5: Kesh va idempotentlik
Modellang:
- Kanonik kalit va normallashtirish
- Zipf yuklama, ikki system
- Hit ulushi va noto'g'ri javoblar, LRU sig'imi bo'yicha egri chiziq (50, 100, 200, 400)
- Idempotentlik kaliti bilan va kalitsiz
Vazifa 6: Xarajat va batch
Modellang:
usagedan narx (faraziy jadval), vazifa kesimida- Byudjet qo'riqchisi
- Streaming hodisalarini yig'ish va uzilishni aniqlash
- Batch natijalarini
custom_idbo'yicha bog'lash
Vazifa 7: O'ylash
Hamkasbingiz aytdi: "SDK o'zi 2 marta qayta urinadi, demak xatolar haqida o'ylash shart emas. Bundan tashqari, jitter — ortiqcha murakkablik: 2-misolda u 429 larni ko'paytirdi. Oddiy eksponensial backoff qoldiramiz va tamom."
Javob
Qisqa javob: SDK ning o'rnatilgan qayta urinishlari — yaxshi boshlang'ich himoya, lekin katta yuklama uchun siyosat emas. Jitter haqidagi xulosa esa o'lchovdan kengroq.
1. SDK qayta urinishlari nimani qamraydi. Bitta so'rovning vaqtinchalik xatosini — ha. Lekin 20 ta jarayon bir vaqtda ishlasa, 2 ta qayta urinish tezlik chegarasini "yengib o'tmaydi": 2-misolda qayta urinishsiz 18.9%, backoff bilan ham 10 urinish kerak bo'ldi. Tungi skript uchun mijoz tomonidagi cheklovchi hal qiluvchi: 0 ta 429, eng qisqa tugash vaqti (30.0 s).
2. Xatolar turi baribir muhim. 400 va 401 ni SDK ham qayta yubormaydi — lekin sizning kodingiz ularni qanday qayta ishlaydi? Loglanadimi, navbatga qaytariladimi, ogohlantirish chiqadimi? Timeout dan keyin yon ta'sirli amal — idempotentlik kalitisiz ikki marta bajarilishi mumkin (3-misol: 35 ta).
3. Jitter haqida. 2-misolda full jitter 429 larni ko'paytirdi (+24.6, SE 6.0), chunki o'rtacha kutish qisqaroq; tugash vaqtidagi farq esa sezilarli emas. Bu — shu yuklama uchun natija. Jitterning maqsadi — sinxron qaytishning oldini olish; mijozlar ko'p va bir lahzada xato olganda (server qayta ishga tushishi) sinxron to'lqinlar paydo bo'ladi. Uni olib tashlashdan oldin o'sha stsenariyni ham o'lchang. Jitter bitta qator kod — murakkablik emas.
Tavsiya:
# 1. mijoz tomonida cheklovchi (so'rov/s va parallel oqimlar soni)
# 2. SDK max_retries + o'z siyosatingiz: 429/5xx/timeout, eksponensial + jitter, chegara
# 3. 400/401 - alohida qayta ishlash va ogohlantirish
# 4. yon ta'sirli amallar - idempotentlik kaliti
# 5. loglar: urinishlar soni, 429 ulushi, tugash vaqti - haftalik ko'rib chiqishHamkasbga javob: "SDK qayta urinishlari qolsin, lekin tungi yuklama uchun asosiy himoya — so'rovlarni tezlik chegarasidan sekinroq yuborish; 2-misolda bu 429 larni nolga tushirdi. Jitterni esa qoldiraylik — u arzon, va uning foydasi aynan biz o'lchamagan holatda (sinxron qayta ulanish) ko'rinadi."
Nimani mustahkamlaydi: 2.2, 2.3, 2.4, 2.5, 2.8-bo'limlar.
Xulosa
Bu darsda LLM API atrofidagi muhandislik kodini qurdik va stub yordamida lokal sinadik: xabarlar formati va suhbat tarixi, xatolar turlari va qayta urinish, kesh va idempotentlik, streaming, batch, usage dan xarajat va kalit xavfsizligi. Stub ehtimollari va narxlari faraziy, lekin kodning o'zi haqiqiy API bilan ham xuddi shunday ishlaydi.
Eng muhim uch fikr:
API holatsiz: tarix, kesh va xarajat bir-biriga bog'liq. 1-misolda 30 navbatli suhbatda kirish 374 dan 2794 tokenga o'sdi, jami 47 671 token. Sirpanuvchi oyna narxni 2.1 barobar kamaytirdi, lekin eski faktni yo'qotdi; prefiks kesh esa to'liq tarixni saqlagan holda faraziy narxni
$143.01dan$17.38ga tushirdi.Xatolar turi bo'yicha, yuklama esa mijoz tomonida boshqariladi. 2-misolda qayta urinishsiz
18.9%, "darhol" qayta urinish bilan28.7%va 767 ta 429; eksponensial backoff100%, lekin51.7 s. Full jitter bu yuklamada 429 larni kamaytirmadi (+24.6,SE 6.0), tugash vaqtidagi farq sezilarli emas. Aniq g'olib — mijoz tomonidagi cheklovchi:0ta 429,105urinish,30.0 s.Kesh kaliti to'liq bo'lsin, yon ta'sirli amal idempotent bo'lsin, xarajat
usagedan hisoblansin. 3-misolda faqat savol matnidan qurilgan kalit 1283 ta boshqa do'kon javobini qaytardi; to'liq kanonik kalit + normallashtirish85.5%hit va 0 xato berdi. Idempotentlik kalitisiz 300 amalda 35 ta ortiqcha bajarilish. 4-misolda xarajatning86%i bitta vazifada edi —usageloglari optimallashtirishni qayerdan boshlashni ko'rsatadi.
Keyingi darsda embeddinglar va semantik qidiruv: matnni vektorga aylantirish, kosinus o'xshashligi va normallashtirish, semantik va kalit so'z qidiruvini halol taqqoslash, embedding o'lchami va narxi, hamda taxminiy yaqin qo'shni qidiruvi (IVF) ni noldan yozamiz.
Izohlar (0)
Izoh yozish uchun kiring.
- Hozircha izoh yo'q. Birinchi bo'ling!