aidoc.uz

2-qism · Matn generatsiyasi·04-bob

Streaming: javobni oqim sifatida olish

Nega ChatGPT javobi so'z-baso'z chiqadi, buni o'z ilovangizda qanday qurish, hodisa turlari va qachon kerak emasligi.

ChatGPT'da javob bir zumda emas, so'z-baso'z "yozilib" chiqadi — bu tasodifiy animatsiya emas, streaming deb ataladigan real texnik mexanizm. Model matnni token-batoken generatsiya qiladi, va server har bir token tayyor bo'lishi bilanoq uni sizga jo'natadi — butun javobni kutib turmasdan.

1. Nega bu kerak#

Oddiy (streaming'siz) so'rovda foydalanuvchi butun javob tayyor bo'lgunicha bo'sh ekranga tikiladi. Uzun javob uchun bu 10-15 soniya jimlik bo'lishi mumkin — foydalanuvchi ilova osilib qoldi deb o'ylashi mumkin.

So'rov yuborildi
Streaming'siz: kutish
10-15 soniya bo'sh ekran
Streaming bilan: birinchi so'z ~1 soniyada

Streaming foydalanuvchi tajribasini tubdan o'zgartiradi — javob darhol kela boshlaganday tuyuladi, garchi umumiy vaqt deyarli bir xil bo'lsa ham (ba'zan hatto biroz sekinroq — chunki uzatishning o'zi ozgina qo'shimcha narxi bor).

2. Qanday yoqiladi#

Bitta parametr: stream=True. Javob endi bitta obyekt emas, balki hodisalar (events) oqimi:

stream = client.responses.create(
    model="gpt-4o-mini",
    input="O'zbekiston tarixi haqida qisqa insho yoz.",
    stream=True,
)
 
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
const stream = await client.responses.create({
  model: "gpt-4o-mini",
  input: "O'zbekiston tarixi haqida qisqa insho yoz.",
  stream: true,
});
 
for await (const event of stream) {
  if (event.type === "response.output_text.delta") {
    process.stdout.write(event.delta);
  }
}

Har bir event — kichik voqea: "yangi matn bo'lagi keldi", "javob tugadi", "funksiya chaqiruvi boshlandi" va h.k. Siz faqat o'zingizga kerakli hodisa turlarini tinglaysiz.

3. Hodisa turlari — to'liq rasm#

Streaming faqat matn bo'laklarini emas, butun javob hayotiy tsiklini tasvirlaydi:

response.created
So'rov qabul qilindi, generatsiya boshlanmoqda
response.output_text.delta
Yangi matn bo'lagi keldi — asosiy, eng tez-tez keladigan hodisa
response.function_call_arguments.delta
Funksiya chaqiruvi argumentlari bo'lib-bo'lib kelmoqda (6-bobda ko'ramiz)
response.completed
Javob to'liq tayyor — endi response.usage kabi yakuniy ma'lumot mavjud
response.failed / error
Streaming davomida xato yuz berdi
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="", flush=True)
    elif event.type == "response.completed":
        print("\n\n--- Javob tugadi ---")
        print("Jami token:", event.response.usage.output_tokens)
    elif event.type == "response.error":
        print("\nXato yuz berdi:", event.error)

4. Kapotni ochib ko'ramiz: nima uzatiladi#

Texnik jihatdan bu — Server-Sent Events (SSE): server HTTP ulanishini ochiq qoldiradi va vaqti-vaqti bilan matn qatorlari jo'natadi. Xom ko'rinishi shunday:

event: response.output_text.delta
data: {"delta": "O'z"}
 
event: response.output_text.delta
data: {"delta": "bekiston"}
 
event: response.output_text.delta
data: {"delta": " —"}
 
event: response.completed
data: {"response": {...}}

SDK bu qatorlarni siz uchun avtomatik parslaydi va qulay event obyektlariga aylantiradi — lekin veb-ilova qurayotgan bo'lsangiz (masalan, brauzerga to'g'ridan-to'g'ri), aynan shu SSE formatini frontend'ga ham uzatishingiz kerak bo'ladi.

5. Veb-ilovada streaming: ikki qatlam#

Haqiqiy veb-ilovada ikkita alohida ulanish bor:

Brauzer ↔ sizning backend
Odatda WebSocket yoki SSE orqali
Sizning backend ↔ OpenAI
OpenAI'ning o'z streaming ulanishi

Backend OpenAI'dan kelayotgan har bir bo'lakni darhol brauzerga "quyib" yuboradi — bu ikki bosqichli quvur (pipeline):

# FastAPI misoli
from fastapi.responses import StreamingResponse
 
@app.post("/chat")
async def chat(prompt: str):
    def event_generator():
        stream = client.responses.create(
            model="gpt-4o-mini", input=prompt, stream=True
        )
        for event in stream:
            if event.type == "response.output_text.delta":
                yield event.delta
 
    return StreamingResponse(event_generator(), media_type="text/plain")

Frontend tomonida (brauzer) esa fetch'ning body.getReader()'i orqali kelayotgan matnni bo'lib-bo'lib o'qib, ekranga qo'shib boradi:

const res = await fetch("/chat", { method: "POST", body: JSON.stringify({ prompt }) });
const reader = res.body.getReader();
const decoder = new TextDecoder();
 
let natija = "";
while (true) {
  const { done, value } = await reader.read();
  if (done) break;
  natija += decoder.decode(value);
  ekranElementi.textContent = natija; // har bo'lakda ekranni yangilaymiz
}

6. Streaming'ni bekor qilish (cancel)#

Foydalanuvchi "to'xtatish" tugmasini bossa, oqimni yarim yo'lda to'xtatish kerak bo'ladi — bu ham xarajatni tejaydi (qolgan token generatsiya qilinmaydi), ham foydalanuvchi tajribasini yaxshilaydi:

stream = client.responses.create(model="gpt-4o-mini", input="Uzun insho yoz...", stream=True)
 
for event in stream:
    if event.type == "response.output_text.delta":
        print(event.delta, end="")
        if foydalanuvchi_bekor_qildimi():  # o'zingizning tekshiruvingiz
            stream.close()
            break

7. Qachon streaming kerak EMAS#

Streaming mos keladi
  • Foydalanuvchi javobni jonli o'qiydi (chat interfeysi)
  • Javob uzun (bir necha jumladan ko'p)
  • Tezkor javob berish taassuroti muhim
Streaming shart emas
  • Javob dasturiy tahlil qilinadi (structured output, function call)
  • Javob juda qisqa (bitta so'z, ha/yo'q)
  • Fon vazifasi — foydalanuvchi kutmayapti (masalan, tungi partiyaviy qayta ishlash)

Qoida oddiy: agar odam ekranga tikilib javobni kutayotgan bo'lsa — streaming yoqing. Agar javobni dastur qayta ishlaydigan bo'lsa — kerak emas, faqat murakkablik qo'shadi.

8. Nima noto'g'ri ketishi mumkin#

BelgiSababYechim
Matn ekranga "sakrab-sakrab" chiqadiFrontend har bo'lakni emas, katta bo'laklarda yangilamoqdaUI yangilanishini har delta uchun chaqiring, lekin DOM yangilanishini samarali qiling (masalan, requestAnimationFrame)
Streaming yarim yo'lda uziladiTarmoq muammosi yoki serverdagi timeoutUlanish uzilishini alohida hodisa sifatida tutib, foydalanuvchiga xabar bering
usage doim None chiqadiOraliq hodisalarda emas, faqat response.completed'da o'qilishi kerakYakuniy hodisagacha kutib, shu yerdan o'qing
Amaliy topshiriq

Streaming chat'ni cancel tugmasi bilan quring

2-bo'limdagi oddiy streaming kodini oling va unga "to'xtatish" imkoniyatini qo'shing: foydalanuvchi biror tugmani bossa (yoki belgilangan vaqtdan o'tsa), oqim to'xtatilishi va ekranda "to'xtatildi" deb chiqishi kerak.

Maslahatni ko'rsatish

Terminal ilovasida foydalanuvchi istalgan payt Enter bosishi bilan streaming to'xtashi kerak (masalan, alohida thread'da klaviatura kuzatuvi orqali). Muhim qism — stream.close() chaqirilishi va qolgan tokenlar generatsiya qilinmasligi.