03 β Chat formati: messages, rollar, suhbat¶
β¬ οΈ Oldingi: 02 β Muhitni sozlash va birinchi so'rov Β· π Kitob boshi Β· Keyingi: 04 β Modellar va provayderlar: bitta SDK, ko'p provayder β‘οΈ
Bu bobda: LLM bilan suhbatning asosiy "tili" β
messagesro'yxatini chuqur o'rganamiz. Uch rolni (system, user, assistant) va ularning vazifasini ko'ramiz;systemprompt modelning xulqini qanday o'zgartirishini sinab ko'ramiz; eng muhim tushunchani β API holatsiz (stateless), ya'ni model oldingi xabarlarni yodida tutmasligini β o'zlashtiramiz; va nihoyat ko'p navbatli (multi-turn) suhbatni o'zimiz qura olamiz. Yo'l-yo'lakay javobni to'g'ri o'qishni va boshqa provayderlarda (Claude, Gemini) formatdagi farqni bilib olamiz.
Muammodan boshlaymiz: bitta xabar yetarli emas¶
2-bobda biz birinchi so'rovni yubordik va u bitta xabardan iborat edi:
Bu ishladi. Lekin haqiqiy ilovada bizga ko'proq nazorat kerak bo'ladi. Masalan:
- Modelga qanday gapirishini aytmoqchimiz: "rasmiy bo'l", "faqat o'zbekcha javob ber", "qisqa yoz".
- Foydalanuvchi bilan suhbat qurmoqchimiz: u savol beradi, model javob beradi, u yana savol beradi va model avvalgi gapni hisobga olishi kerak.
Mana shu ikkala ehtiyojni ham bitta oddiy tuzilma hal qiladi: messages ro'yxati va undagi rollar. Bu bob β shu tuzilmani tushunishga bag'ishlangan. Uni yaxshi o'zlashtirsangiz, kitobning qolgan qismidagi deyarli barcha kod sizga tanish ko'rinadi.
Hayotiy o'xshatish.
messagesβ bu teatr ssenariysi kabi: har bir qator kim gapirayotganini (rol) va nima deyilayotganini (matn) ko'rsatadi. Model bu ssenariyni o'qib, keyingi qatorni β ya'ni o'z javobini yozadi.
messages β xabarlar ro'yxati¶
So'rovning yuragi β messages argumenti. U lug'atlar ro'yxati, va har bir lug'at bitta xabar:
messages = [
{"role": "system", "content": "Sen foydali yordamchisan."},
{"role": "user", "content": "Salom!"},
{"role": "assistant", "content": "Salom! Sizga qanday yordam bera olaman?"},
{"role": "user", "content": "Python nima?"},
]
Har bir xabarda ikkita kalit bor:
roleβ kim gapirayapti:"system","user"yoki"assistant".contentβ gap mazmuni (hozircha matn; 12-bobda rasm ham qo'shamiz).
Model bu ro'yxatni boshidan oxirigacha o'qiydi va eng oxirgi user xabariga (butun suhbatni hisobga olib) javob yozadi.
Tartib muhim
messages ichidagi tartib suhbatning vaqt oqimini bildiradi: birinchi xabar β eng eski, oxirgisi β eng yangi. system (agar bo'lsa) doim birinchi turadi; keyin user va assistant navbatma-navbat almashadi.
Uch rol va ularning vazifasi¶
Keling, har bir rolni alohida ko'rib chiqamiz. Bu β bobning markaziy g'oyasi.
system β modelga ko'rsatma beruvchi¶
system xabari modelga qanday xulq tutishini aytadi: shaxsiyati, ohangi, qoidalari, vazifasi. Bu foydalanuvchiga ko'rinmaydi β u "sahna ortidagi rejissyor" kabi. Misollar:
{"role": "system", "content": "Sen tajribali Python ustozsan. Sodda til bilan, misol bilan tushuntir. Faqat o'zbekcha javob ber."}
system ko'pincha bir marta, suhbat boshida beriladi va butun suhbat davomida amal qiladi.
user β foydalanuvchi xabari¶
user β bu foydalanuvchidan kelgan matn: savol, buyruq yoki ma'lumot. Aynan shunga javob berishni model "vazifa" deb biladi:
assistant β model javobi¶
assistant β bu modelning o'zi qaytargan javob. Yangi so'rovda buni siz qo'lda yozmaysiz β uni avvalgi javobdan olib, tarixga qo'shasiz (buni pastda multi-turn qismida ko'ramiz). U modelga "men avval shunday degandim" deb eslatadi:
Hayotiy o'xshatish. Tasavvur qiling, yangi xodimni ishga oldingiz.
systemβ unga bergan lavozim yo'riqnomangiz ("muloyim bo'l, faqat o'zbekcha gapir").userβ mijozning savoli.assistantβ xodimning javobi. Yo'riqnomani bir marta berasiz; mijoz bilan suhbat esa savol-javob bo'lib davom etadi.
system prompt xulqni qanday o'zgartiradi¶
system promptning kuchini eng yaxshi amalda ko'rasiz. Bir xil user savoliga, faqat systemni o'zgartirib, butunlay boshqa ohangdagi javob olamiz.
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
MODEL = "gpt-5.4-mini" # eslatma: nomlar o'zgaradi β provayder ro'yxatini tekshiring
def sora(system_matni: str, savol: str) -> str:
javob = client.chat.completions.create(
model=MODEL,
messages=[
{"role": "system", "content": system_matni},
{"role": "user", "content": savol},
],
)
return javob.choices[0].message.content
savol = "Internet nima?"
# 1) Rasmiy, qisqa ohang
print(sora("Sen rasmiy ensiklopediyasan. Bir jumlada, ilmiy uslubda javob ber.", savol))
# 2) Do'stona, bolaga tushuntiruvchi ohang
print(sora("Sen mehribon ustozsan. 7 yoshli bolaga o'xshatish bilan tushuntir.", savol))
Bir xil savol, lekin natijalar butunlay boshqacha bo'ladi: birinchisi quruq va rasmiy, ikkinchisi iliq va o'xshatishli. Foydalanuvchi savolini o'zgartirmasdan ham ilovangizning "shaxsiyatini" system orqali boshqarasiz.
system β eng arzon sozlash usuli
Modelni "to'g'rilash"ning eng oddiy va arzon yo'li β yaxshi system prompt yozish (fine-tuning emas!). "Faqat o'zbekcha javob ber", "noaniq bo'lsa, taxmin qilma β so'ra", "javobni JSON shaklida qaytar" kabi qoidalarni shu yerga yozasiz. Prompt yozishni 5-bobda chuqur o'rganamiz.
system β qonun emas, kuchli maslahat
system prompt modelga kuchli ta'sir qiladi, lekin u temir qonun emas β model ba'zan undan chetga chiqishi mumkin. Muhim cheklovlar (masalan, xavfsizlik) faqat systemga tayanmasligi kerak; ularni kod tomonida ham tekshiring (24-bob).
Eng muhim tushuncha: API holatsiz (stateless)¶
Endi butun bobning eng muhim g'oyasiga keldik. Diqqat bilan o'qing β ko'p boshlovchilar shu yerda adashadi.
LLM API holatsiz (stateless). Bu degani: model oldingi so'rovlaringizni yodida tutmaydi. Har bir create(...) chaqiruvi β model uchun mutlaqo yangi, mustaqil voqea. U sizning kim ekaningizni, oldin nima yozganingizni, bir necha soniya oldingi javobini ham bilmaydi.
Buni misolda ko'raylik. Noto'g'ri kutish:
# 1-so'rov
client.chat.completions.create(model=MODEL,
messages=[{"role": "user", "content": "Mening ismim Olim."}])
# 2-so'rov (ALOHIDA chaqiruv)
javob = client.chat.completions.create(model=MODEL,
messages=[{"role": "user", "content": "Mening ismim nima edi?"}])
print(javob.choices[0].message.content)
# -> "Kechirasiz, men sizning ismingizni bilmayman."
Model "Olim" deb javob bermaydi β chunki ikkinchi so'rovda biz birinchi suhbatni umuman yubormaganmiz. Model uchun ikkinchi so'rov "ko'kdan tushgan" yagona savol.
To'g'ri yo'l β tarixni o'zingiz yuborasiz:
javob = client.chat.completions.create(model=MODEL,
messages=[
{"role": "user", "content": "Mening ismim Olim."},
{"role": "assistant", "content": "Tanishganimdan xursandman, Olim!"},
{"role": "user", "content": "Mening ismim nima edi?"},
])
print(javob.choices[0].message.content)
# -> "Sizning ismingiz Olim."
Endi model javob beradi β chunki kerakli ma'lumot shu so'rovning ichida bor.
Hayotiy o'xshatish. Model β har safar xotirasi tozalanadigan mutaxassis kabi. Siz unga maslahat uchun kelganingizda, u sizni eslamaydi β shuning uchun har gal kelganingizda butun ish tarixini (papkani) qaytadan stolga qo'yasiz. Papkani qo'ymasangiz, u "siz kim?" deb so'raydi. "Xotira" sizning qo'lingizda (kodingizda), modelda emas.
Unda ChatGPT qanday eslaydi?
ChatGPT yoki Claude saytida suhbat davom etayotgandek tuyuladi, chunki interfeys har safar butun tarixni model'ga qayta yuboradi β siz buni ko'rmaysiz, xolos. API darajasida esa buni siz qilasiz. Bu mavzuni β uzun suhbatda tarix qanchalik o'sib ketishi va uni qisqartirish (xotira boshqaruvi) β 8-bobda chuqur ko'ramiz.
Javobni to'g'ri o'qish¶
Model javobi β oddiy matn emas, obyekt. Matnga yetib borish uchun to'g'ri maydonlardan o'tasiz:
javob = client.chat.completions.create(model=MODEL, messages=messages)
xabar = javob.choices[0].message # assistant xabari (obyekt)
print(xabar.role) # "assistant"
print(xabar.content) # modelning matnli javobi
javob.choicesβ javoblar ro'yxati (odatda bitta), shuning uchun[0]..messageβ o'sha javobning xabar obyekti..contentβ matn;.roleβ har doim"assistant".
Aynan shu message obyektini multi-turn suhbatda to'g'ridan-to'g'ri tarixga qo'shsa bo'ladi β endi shuni ko'ramiz.
Multi-turn suhbat: tarixni o'stirib borish¶
Endi hamma narsani birlashtiramiz. Multi-turn (ko'p navbatli) suhbat β bu sikl: foydalanuvchi yozadi, model javob beradi, javobni tarixga qo'shamiz, va takror. Suhbat o'sgani sayin messages ro'yxati ham uzayadi.
Mana to'liq, ishlaydigan mini-suhbat dasturi:
import os
from dotenv import load_dotenv
from openai import OpenAI
load_dotenv()
client = OpenAI()
MODEL = "gpt-5.4-mini" # nomlar o'zgaradi β provayder ro'yxatini tekshiring
# Tarix system xabaridan boshlanadi
messages = [
{"role": "system", "content": "Sen do'stona yordamchisan. Qisqa, o'zbekcha javob ber."}
]
print("Suhbat boshlandi. Chiqish uchun 'chiqish' deb yozing.\n")
while True:
savol = input("Siz: ")
if savol.strip().lower() in ("chiqish", "exit", "quit"):
break
# 1) Foydalanuvchi xabarini tarixga qo'shamiz
messages.append({"role": "user", "content": savol})
# 2) BUTUN tarixni yuboramiz (stateless β model o'zi eslamaydi!)
javob = client.chat.completions.create(model=MODEL, messages=messages)
matn = javob.choices[0].message.content
# 3) Model javobini ham tarixga qo'shamiz, keyingi navbat uchun
messages.append({"role": "assistant", "content": matn})
print("Bot:", matn, "\n")
Bu dasturning uchta muhim qadami bor (kod izohlarida raqamlangan):
- Foydalanuvchi gapini
messagesga qo'shamiz. - Butun
messagesni yuboramiz β chunki model o'zi eslamaydi. - Model javobini ham
messagesga qo'shamiz, toki keyingi navbatda u "eslansin".
Shu uch qadam β kitobdagi har qanday chatbotning yuragi. Qolgani β bezak.
message obyektini to'g'ridan-to'g'ri qo'shish
Yuqorida biz {"role": "assistant", "content": matn} lug'atini qo'lda yasadik. Buning o'rniga model qaytargan xabar obyektini ham qo'shsa bo'ladi: messages.append(javob.choices[0].message). Ikkalasi ham ishlaydi; lug'at varianti boshqa provayderlarga ko'chirishda barqarorroq.
Boshqa provayderlarda farq¶
Kitob ko'p-provayderli bo'lgani uchun bir narsani bilib qo'ying: yuqoridagi format β OpenAI-mos standart. U OpenAI, Groq, DeepSeek, OpenRouter, Ollama'da bir xil ishlaydi. Lekin ba'zi native SDK'larda kichik farq bor.
Anthropic (Claude): Bu yerda system messages ichida emas β alohida parametr. Qolgan user/assistant xabarlar messagesda qoladi:
import anthropic
client = anthropic.Anthropic() # ANTHROPIC_API_KEY
resp = client.messages.create(
model="claude-haiku-4-5", # nomlar o'zgaradi β tekshiring
max_tokens=1024, # Claude'da SHART (majburiy)
system="Sen do'stona yordamchisan. O'zbekcha javob ber.", # ALOHIDA parametr!
messages=[
{"role": "user", "content": "Salom! O'zingni tanishtir."},
],
)
print(resp.content[0].text) # javob: content bloklar ro'yxati
Google Gemini (native): Bu yerda umuman messages emas, contents ishlatiladi, system esa system_instruction sifatida konfiguratsiyada beriladi:
from google import genai
from google.genai import types
client = genai.Client() # GEMINI_API_KEY
resp = client.models.generate_content(
model="gemini-2.5-flash",
contents="Salom! O'zingni tanishtir.",
config=types.GenerateContentConfig(
system_instruction="Sen do'stona yordamchisan. O'zbekcha javob ber.",
),
)
print(resp.text)
Asosiy g'oya bir xil
Sintaksis turlicha bo'lsa-da, tushuncha bir xil: har joyda rollar bor, system modelga ko'rsatma beradi, va hamma joyda API holatsiz β multi-turn uchun tarixni siz yuborasiz. Bitta formatni (OpenAI-mos) yaxshi bilsangiz, qolganiga oson o'tasiz. Provayderlar farqini 4-bobda batafsil ko'ramiz.
Keng tarqalgan xatolar¶
Boshlovchilar shu uch xatoda eng ko'p qoqiladi:
- "Model esladimi?" deb kutish. Tarixni yubormay turib, modeldan avvalgi gapni eslashini kutish. Yechim: har so'rovda butun
messagesni yuboring (stateless!). - Javobni tarixga qo'shmaslik. Faqat
userxabarlarini to'plab,assistantjavoblarini qo'shmaslik. Unda model o'z avvalgi gapini "eslamaydi" β suhbat uzilib qoladi. - Rollar tartibini buzish. Masalan,
systemni o'rtaga qo'yish yoki ikkiuserxabarini ketma-ket yuborish. To'g'ri tartib:system(bitta, boshda), keyinuser/assistantnavbatma-navbat.
Hayotiy o'xshatish. Multi-turn suhbat β xat yozish tarixi kabi: yangi xat yozganingizda oldingilarini ham qog'ozda saqlab borasiz. Saqlamasangiz, suhbatchingiz "biz nimani gaplashayotgan edik?" deb adashadi. Tarixni saqlash β sizning vazifangiz.
Xulosa¶
messagesβ bu xabarlar ro'yxati; har biri{"role": ..., "content": ...}lug'ati. Tartib suhbatning vaqt oqimini bildiradi.- Uch rol:
system(modelga qoida/shaxsiyat/ohang beradi),user(foydalanuvchi xabari),assistant(model javobi). systemprompt xulqni kuchli o'zgartiradi: bir xil savolga rasmiy yoki do'stona ohangda javob olasiz β bu modelni sozlashning eng arzon usuli.- Eng muhim: LLM API holatsiz (stateless) β model oldingi xabarlarni yodida tutmaydi. Har so'rovda butun suhbat tarixini siz yuborasiz. "Xotira" sizning kodingizda.
- Multi-turn suhbat = sikl:
userni qo'shish -> butun tarixni yuborish ->assistantjavobini ham tarixga qo'shish -> takror. - Javobni o'qish:
resp.choices[0].message(.contentβ matn,.roleβ"assistant"). - Keng xatolar: tarixni yubormay "esladimi?" deb kutish, javobni tarixga qo'shmaslik, rollar tartibini buzish.
- Provayder farqi: Claude'da
systemalohida parametr (messages ichida emas), Gemini'dacontents+system_instruction. Asosiy g'oya β rollar va stateless β hamma joyda bir xil.
Amaliy mashqlar¶
-
(Oson)
messagesdasystemxabarini "Sen quvnoq robotsan, javob oxirida emoji qo'sh" deb yozing va bir savol bering. Keyinsystemni "Sen rasmiy hujjat uslubida yozasan" ga o'zgartirib, o'sha savolni qayta bering. Ikki javobni taqqoslang. -
(Oson) Ikkita alohida
create(...)chaqiruvi yozing: birinchisida "Mening sevimli rangim ko'k" deng, ikkinchisida (tarixsiz) "Sevimli rangim nima?" deb so'rang. Model nega bilmasligini o'z so'zingiz bilan tushuntiring. -
(O'rtacha) 2-mashqni to'g'rilang: ikkala xabarni bitta
messagesro'yxatiga joylang (orasigaassistantjavobini ham qo'shing) va bitta so'rovda yuboring. Endi model rangni eslaganini ko'ring. -
(O'rtacha) Bobdagi
while Truemini-chatbotni terib, ishga tushiring. Unga ketma-ket: "Ismim Dilnoza", keyin "Ismim nima?" deb yozing β model eslashini tasdiqlang. So'ng 3-qadamni (javobni tarixga qo'shish) o'chirib qo'ying va xuddi shu suhbatni qaytaring: nima o'zgarishini kuzating. -
(Qiyin) Chatbotni shunday kengaytiringki, har navbatdan keyin
len(messages)ni vajavob.usage.total_tokensni chop etsin. Bir necha navbat suhbatlashing va ikkala son ham o'sib borishini kuzating. Bu nima uchun stateless API'da xarajat suhbat davom etgani sayin oshib borishini tushuntiradimi? (Bu muammoni 8- va 22-bobda hal qilamiz.)
β¬ οΈ Oldingi: 02 β Muhitni sozlash va birinchi so'rov Β· π Kitob boshi Β· Keyingi: 04 β Modellar va provayderlar: bitta SDK, ko'p provayder β‘οΈ