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.
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:
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:
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()
break7. Qachon streaming kerak EMAS#
- Foydalanuvchi javobni jonli o'qiydi (chat interfeysi)
- Javob uzun (bir necha jumladan ko'p)
- Tezkor javob berish taassuroti muhim
- 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#
| Belgi | Sabab | Yechim |
|---|---|---|
| Matn ekranga "sakrab-sakrab" chiqadi | Frontend har bo'lakni emas, katta bo'laklarda yangilamoqda | UI yangilanishini har delta uchun chaqiring, lekin DOM yangilanishini samarali qiling (masalan, requestAnimationFrame) |
| Streaming yarim yo'lda uziladi | Tarmoq muammosi yoki serverdagi timeout | Ulanish uzilishini alohida hodisa sifatida tutib, foydalanuvchiga xabar bering |
usage doim None chiqadi | Oraliq hodisalarda emas, faqat response.completed'da o'qilishi kerak | Yakuniy hodisagacha kutib, shu yerdan o'qing |
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.