2-qism · Agentlar va bilim izlash·04-bob
Agent nima va u qanday quriladi
Asbob chaqiruvi protokoli ichidan. Sikl noldan yoziladi, asbob tavsifi qanday yozilishi kerak, xotira, rejalashtirish va to'liq ishlaydigan tadqiqot agenti.
Fransuz faylasufi Deni Didro shunday degan edi: "Agar har qanday savolga javob bera oladigan to'tiqush topilsa, men uni ikkilanmasdan aqlli mavjudot deb atardim."
Til modeli — juda mukammal to'tiqush. Lekin bir muhim farq bor:
Til modeli gapira oladi, lekin hech narsa qila olmaydi.
U internetga kira olmaydi. Kod ishga tushira olmaydi. Bazaga yoza olmaydi. Kechagi yangilikni bilmaydi.
Agent — aynan shu qutini ochish uchun.
Uch qismli tuzilma#
Agentni tushunishning eng oson yo'li — uni tirik mavjudotga o'xshatish:
- 1
- Miya — LLM va xotiraMa'lumotni birlashtiradi, qaror qabul qiladi, reja tuzadi. Bu LLM'ning o'zi, ustiga tashqi xotira qo'shilgan holda.
- 2
- Sezgi — kirish kanallariMatn, rasm, ovoz, sensor ma'lumoti. Modelning dunyoni ko'rish imkoniyatini kengaytiradi.
- 3
- Harakat — asboblarQidiruv, kod ishga tushirish, API chaqiruvi, bazaga yozish. Modelning dunyoga TA'SIR QILISH imkoniyati.
To'rt xususiyat#
Agentni oddiy chatbotdan ajratib turadigan narsalar:
| Xususiyat | Nima demak | Amaliy belgisi |
|---|---|---|
| Avtonomiya | Har qadamni tushuntirish shart emas | "Hisobot tayyorla" deysiz, qolganini o'zi hal qiladi |
| Reaktivlik | Muhit o'zgarsa javob o'zgaradi | Qidiruv bo'sh natija bersa — boshqa so'rov yozadi |
| Proaktivlik | Maqsadga yo'naltirilgan | Oldindan reja tuzadi, shunchaki javob bermaydi |
| Ijtimoiylik | Muloqot qila oladi | Odam va boshqa agentlar bilan ishlaydi |
Asbob chaqiruvi: protokol ichidan#
Ko'p qo'llanmalar to'g'ridan-to'g'ri LangChain'dan boshlaydi. Natijada odam agent yozadi, lekin nima bo'layotganini bilmaydi — va biror narsa buzilganda nima qilishni ham bilmaydi.
Shuning uchun avval protokolni ko'ramiz.
Asosiy tushuncha#
Model asbobni o'zi ishga tushirmaydi. U faqat "men shu asbobni shu argumentlar bilan chaqirmoqchiman" deb aytadi. Chaqirishni siz qilasiz.
Bu sikl. Model kerak bo'lsa ketma-ket bir necha asbob chaqiradi.
Asbobni e'lon qilish#
import anthropic
client = anthropic.Anthropic()
asboblar = [
{
"name": "ob_havo",
"description": (
"Berilgan shahardagi hozirgi ob-havoni qaytaradi. "
"Foydalanuvchi ob-havo, harorat yoki yomg'ir haqida so'raganda ishlating. "
"Kelajakdagi prognoz uchun EMAS — faqat hozirgi holat."
),
"input_schema": {
"type": "object",
"properties": {
"shahar": {
"type": "string",
"description": "Shahar nomi, masalan: Toshkent",
},
"birlik": {
"type": "string",
"enum": ["selsiy", "farengeyt"],
"description": "Harorat birligi. Standart: selsiy",
},
},
"required": ["shahar"],
},
}
]Yaxshi tavsif uch narsani aytadi:
- NIMA qiladi: «Berilgan shahardagi hozirgi ob-havoni qaytaradi»
- QACHON ishlatiladi: «Foydalanuvchi harorat yoki yomg'ir haqida so'raganda»
- QACHON ishlatilmaydi: «Kelajakdagi prognoz uchun EMAS»
- Har bir argumentda misol: «masalan: Toshkent»
- «Ob-havo asbobi» — model qachon ishlatishni bilmaydi
- «Ma'lumot qaytaradi» — qanday ma'lumot?
- Chegaralar ko'rsatilmagan — model uni prognoz uchun ham ishlatadi
- Argument tavsifi yo'q — model formatni taxmin qiladi va xato qiladi
Amaliy qoida: agar yangi jamoadoshingiz bu tavsifni o'qib, asbobni to'g'ri ishlata olmasa — model ham ishlata olmaydi.
Siklni noldan yozamiz#
Endi to'liq ishlaydigan agent. Freymvorksiz.
Asboblarni bajaradigan funksiya
import json def ob_havo(shahar: str, birlik: str = "selsiy") -> str: """Haqiqiy loyihada bu yerda API chaqiruvi bo'ladi.""" malumot = {"Toshkent": 32, "Samarqand": 29, "Nukus": 36} harorat = malumot.get(shahar) if harorat is None: return f"'{shahar}' shahri uchun ma'lumot topilmadi" if birlik == "farengeyt": harorat = harorat * 9 / 5 + 32 return f"{shahar}: {harorat}° {birlik}, ochiq havo" def kalkulyator(ifoda: str) -> str: """Oddiy matematik ifodani hisoblaydi.""" ruxsat = set("0123456789+-*/(). ") if not set(ifoda) <= ruxsat: return "Xato: faqat sonlar va + - * / ( ) belgilariga ruxsat" try: return str(eval(ifoda, {"__builtins__": {}}, {})) except Exception as e: return f"Hisoblab bo'lmadi: {e}" BAJARUVCHILAR = { "ob_havo": ob_havo, "kalkulyator": kalkulyator, }Asosiy sikl
def agent(sorov: str, asboblar: list, maks_qadam: int = 10) -> str: """Asbob chaqiruvi siklini boshqaradi.""" xabarlar = [{"role": "user", "content": sorov}] for qadam in range(maks_qadam): javob = client.messages.create( model="claude-opus-5", max_tokens=4096, tools=asboblar, messages=xabarlar, ) # Model tugatdimi? if javob.stop_reason == "end_turn": return "".join(b.text for b in javob.content if b.type == "text") # Model asbob so'radi if javob.stop_reason == "tool_use": # Modelning javobini tarixga qo'shamiz — TO'LIQ content bilan xabarlar.append({"role": "assistant", "content": javob.content}) natijalar = [] for blok in javob.content: if blok.type != "tool_use": continue print(f" [{qadam}] {blok.name}({blok.input})") try: natija = BAJARUVCHILAR[blok.name](**blok.input) xato = False except Exception as e: natija = f"Asbob xatosi: {e}" xato = True natijalar.append({ "type": "tool_result", "tool_use_id": blok.id, # ID MOS KELISHI SHART "content": natija, "is_error": xato, }) # Barcha natijalar BITTA user xabarida xabarlar.append({"role": "user", "content": natijalar}) continue return f"Kutilmagan holat: {javob.stop_reason}" return "Qadamlar chegarasiga yetildi" print(agent("Toshkentda havo qanday? Va 32 daraja farengeytda qancha bo'ladi?", asboblar))Natijani ko'ramiz
[0] ob_havo({'shahar': 'Toshkent'}) [1] kalkulyator({'ifoda': '32 * 9 / 5 + 32'}) Toshkentda hozir havo ochiq, harorat 32° selsiy. Farengeytda bu 89.6° ga to'g'ri keladi.Model ikkita asbobni ketma-ket chaqirdi va natijalarni birlashtirib javob berdi. Biz hech qanday mantiq yozmadik — faqat asboblarni berdik.
To'rtta muhim detal#
- 1
- tool_use_id mos kelishi shartHar bir tool_result dagi tool_use_id modelning tool_use blokidagi id bilan aynan bir xil bo'lishi kerak. Bu model qaysi natija qaysi so'rovga tegishli ekanini bilishi uchun.
- 2
- Barcha natijalar bitta xabardaModel bir vaqtda bir necha asbob so'rasa, ularning natijalarini BITTA user xabarida qaytaring. Alohida-alohida yuborsangiz, model parallel chaqiruvni asta-sekin to'xtatadi.
- 3
- Xatoni ham qaytaringAsbob ishlamasa, natijani tashlab yubormang. is_error=true bilan qaytaring — model xatoni ko'rib boshqa yo'l tanlashi mumkin.
- 4
- Assistant javobini TO'LIQ qo'shingjavob.content ni butunlay qo'shing, faqat matnini emas. Ichidagi tool_use bloklari keyingi so'rov uchun kerak.
Osonroq yo'l: tool runner#
Siklni tushundingiz. Endi uni SDK'ga topshirsa bo'ladi:
from anthropic import beta_tool
@beta_tool
def ob_havo(shahar: str, birlik: str = "selsiy") -> str:
"""Berilgan shahardagi hozirgi ob-havoni qaytaradi.
Args:
shahar: Shahar nomi, masalan Toshkent.
birlik: Harorat birligi — "selsiy" yoki "farengeyt".
"""
malumot = {"Toshkent": 32, "Samarqand": 29}
return f"{shahar}: {malumot.get(shahar, '?')}° {birlik}"
runner = client.beta.messages.tool_runner(
model="claude-opus-5",
max_tokens=4096,
tools=[ob_havo],
messages=[{"role": "user", "content": "Toshkentda havo qanday?"}],
)
for xabar in runner:
print(xabar)Sxema funksiya imzosidan avtomatik yasaladi, docstring esa tavsif bo'ladi. Sikl SDK ichida.
ReAct: o'yla, keyin harakat qil#
Yuqoridagi sikl ReAct naqshining amalga oshirilishi. Nomi ikki so'zdan: Reasoning va Acting.
Fikr: Foydalanuvchi bugungi valyuta kursini so'rayapti.
Mening bilimim eskirgan. Qidirish kerak.
Harakat: qidiruv("bugungi dollar kursi O'zbekiston")
Kuzatish: [natijalar...]
Fikr: Ma'lumot topildi, javob berish mumkin.
Javob: Bugungi kurs — ...
Model har qadamda o'z fikrini yozadi. Bu ikki foyda beradi:
- Model yo'ldan adashmaydi — o'z mantiqini ko'rib turadi
- Siz uning mantiqini o'qiy olasiz — nosozlikni tuzatish osonlashadi
Vazifani qadamlarga bo'lish#
Haqiqiy vazifalar bir qadamda hal bo'lmaydi. Ikki asosiy strategiya bor.
- Model butun rejani oldindan tuzadi, keyin bajaradi
- Vazifaning to'liq manzarasini ko'radi
- Gallyutsinatsiya va unutish kamayadi
- Loglash va tasdiqlash oson — reja oldindan ma'lum
- 3-qadamda xato chiqsa, rejani o'zgartira olmaydi
- Muhit o'zgarishiga moslashmaydi
- Noaniq vazifalarda yomon reja tuzadi
Aniq, oldindan ma'lum vazifalar uchun yaxshi: «bu hujjatni tarjima qil va xulosala».
- Bitta qadamni rejalashtiradi va bajaradi, keyin qayta o'ylaydi
- Xatoni yo'lda tuzatadi
- Muhit o'zgarishiga moslashadi
- Noaniq vazifalarda ancha yaxshi
- Murakkab masalada cheksiz zanjir hosil bo'lishi mumkin
- Qimmat — har qadamda model chaqiriladi
- Umumiy manzarani yo'qotishi mumkin
Tadqiqot, qidiruv, nosozlikni tuzatish kabi natijasi oldindan noma'lum vazifalar uchun. Yuqoridagi kodimiz aynan shu turdagi.
Boshqa strategiyalar:
- Ko'p reja tanlash — bir vazifaga bir nechta reja tuzib, ko'pchilik ovozi bilan eng yaxshisini tanlash. Qimmat, lekin murakkab holatlarda ishlaydi.
- Refleksiya — model o'z rejasiga o'zi (yoki ikkinchi model) fikr bildiradi. Kuchli, lekin kafolat yo'q — model tuzatish tsiklida qotib qolishi mumkin.
Xotira#
Model xotirasiz. Har so'rovda butun tarix qayta yuboriladi. Bu ikki muammo tug'diradi: kontekst to'ladi va narx o'sadi.
Uchinchi qavat ko'pincha unutiladi, lekin u eng qiymatlisi:
def refleksiya(suhbatlar, client):
"""Xom tajribadan foydalaniladigan bilim chiqaradi."""
prompt = f"""Quyida agent va foydalanuvchi o'rtasidagi {len(suhbatlar)} ta suhbat bor.
Ulardan kelajakda foydali bo'ladigan XULOSALAR chiqaring:
- Bu foydalanuvchi nimani afzal ko'radi?
- Qaysi yondashuvlar ishladi, qaysilari yo'q?
- Qanday xatolar takrorlandi?
Har bir xulosani bitta qatorda yozing.
{suhbatlar}"""
javob = client.messages.create(
model="claude-opus-5", max_tokens=1024,
messages=[{"role": "user", "content": prompt}],
)
return "".join(b.text for b in javob.content if b.type == "text")Natija keyingi suhbatlarda tizim ko'rsatmasiga qo'shiladi. Shunday qilib agent vaqt o'tishi bilan yaxshilanadi.
Kontekstni qisqartirish#
def kontekstni_qisqartir(xabarlar, client, chegara=20):
"""Eski xabarlarni xulosaga almashtiradi."""
if len(xabarlar) <= chegara:
return xabarlar
eski = xabarlar[:-10] # oxirgi 10 tasini saqlaymiz
yangi = xabarlar[-10:]
xulosa = client.messages.create(
model="claude-haiku-4-5", # xulosalash uchun arzon model yetarli
max_tokens=1024,
messages=[{
"role": "user",
"content": f"Quyidagi suhbatni qisqacha xulosalang:\n\n{eski}",
}],
)
matn = "".join(b.text for b in xulosa.content if b.type == "text")
return [{"role": "user", "content": f"[Oldingi suhbat xulosasi]\n{matn}"}] + yangiMiya uchun modelni tanlash#
| Mezon | Savol |
|---|---|
| Yopiq yoki ochiq | API orqali (sifat yuqori, har chaqiruv pullik) yoki o'zingizda (server kerak) |
| Tezlik | Foydalanuvchi 30 soniya kutmaydi. Sekundiga token sonini o'lchang |
| Narx | Agent bitta savolga 5-10 marta model chaqiradi — narx shunga ko'payadi |
| Soha bilimi | Kerak bo'lsa: maxsus model, fine-tuning yoki — eng arzoni — RAG |
Kutubxonalar#
Protokolni tushundingiz. Endi tayyor vositalarni ko'rish mumkin.
| Kutubxona | Kuchli tomoni | Zaif tomoni | Qachon |
|---|---|---|---|
| LangChain | Eng katta jamoa, ko'p integratsiya | O'rganish egri chizig'i tik, hujjatlar eskiradi | Murakkab agentlar, production |
| LlamaIndex | Indekslash va qidiruvda eng yaxshisi | Qidiruvdan tashqarida cheklangan | RAG loyihalari |
| Haystack | Osonroq o'rganiladi, yaxshi hujjatlar | Kichikroq jamoa, kamroq integratsiya | Qidiruv va savol-javob |
| Semantic Kernel | .NET muhiti, funksiya kompozitsiyasi | Python versiyasi yoshroq | Korporativ .NET tizimlar |
| AutoGen | Ko'p agentli suhbatlar | Tor ixtisoslashgan | Agentlar muhokamasi |
Loyiha: tadqiqot agenti#
Endi haqiqiy, foydali agent quramiz. U savol oladi, internetdan qidiradi, natijalarni tahlil qiladi va manbalari ko'rsatilgan javob yozadi.
Asboblarni tayyorlash
import json import anthropic from duckduckgo_search import DDGS client = anthropic.Anthropic() def internetdan_qidir(sorov: str, soni: int = 5) -> str: """DuckDuckGo orqali qidiradi va natijalarni matn qilib qaytaradi.""" try: with DDGS() as ddgs: natijalar = list(ddgs.text(sorov, max_results=soni)) except Exception as e: return f"Qidiruv xatosi: {e}" if not natijalar: return "Hech narsa topilmadi. Boshqa so'rov bilan urinib ko'ring." qatorlar = [] for i, n in enumerate(natijalar, 1): qatorlar.append(f"[{i}] {n['title']}\n {n['body'][:300]}\n Manba: {n['href']}") return "\n\n".join(qatorlar) def eslatib_qoy(fakt: str) -> str: """Muhim faktni agent xotirasiga yozadi.""" XOTIRA.append(fakt) return f"Saqlandi ({len(XOTIRA)} ta fakt)" XOTIRA: list[str] = [] BAJARUVCHILAR = { "internetdan_qidir": internetdan_qidir, "eslatib_qoy": eslatib_qoy, }Asbob sxemalari
ASBOBLAR = [ { "name": "internetdan_qidir", "description": ( "Internetdan ma'lumot qidiradi. Bugungi voqealar, statistika, " "yangi tadqiqotlar yoki 2025-yildan keyingi har qanday ma'lumot " "kerak bo'lganda ishlating. Umumiy bilim savollari uchun kerak emas." ), "input_schema": { "type": "object", "properties": { "sorov": { "type": "string", "description": "Qidiruv so'rovi. Aniq va qisqa bo'lsin, " "masalan: 'O'zbekiston aholisi 2026'", }, "soni": { "type": "integer", "description": "Nechta natija kerak. Standart 5, maksimum 10", }, }, "required": ["sorov"], }, }, { "name": "eslatib_qoy", "description": ( "Muhim faktni xotiraga yozadi. Yakuniy javobda ishlatiladigan " "har bir asosiy faktni topganingizda darhol saqlang." ), "input_schema": { "type": "object", "properties": { "fakt": { "type": "string", "description": "Fakt va uning manbasi", }, }, "required": ["fakt"], }, }, ]Agent sinfi
class TadqiqotAgenti: KORSATMA = """Siz sinchkov tadqiqotchisiz. Qoidalar: 1. Har bir da'vo uchun manba ko'rsating 2. Ma'lumot topilmasa — to'qib chiqarmang, "topilmadi" deng 3. Muhim faktlarni eslatib_qoy bilan saqlang 4. Bir nechta manbani solishtiring, ziddiyat bo'lsa aytib o'ting 5. Yakuniy javobda manbalar ro'yxatini bering""" def __init__(self, model="claude-opus-5", maks_qadam=12): self.client = anthropic.Anthropic() self.model = model self.maks_qadam = maks_qadam self.jami_kirish = 0 self.jami_chiqish = 0 def tadqiq_qil(self, savol: str) -> str: XOTIRA.clear() xabarlar = [{"role": "user", "content": savol}] korilgan = set() for qadam in range(self.maks_qadam): javob = self.client.messages.create( model=self.model, max_tokens=4096, system=self.KORSATMA, tools=ASBOBLAR, messages=xabarlar, ) self.jami_kirish += javob.usage.input_tokens self.jami_chiqish += javob.usage.output_tokens if javob.stop_reason == "end_turn": return "".join(b.text for b in javob.content if b.type == "text") xabarlar.append({"role": "assistant", "content": javob.content}) natijalar = [] for blok in javob.content: if blok.type != "tool_use": continue # Takrorlanuvchi chaqiruvni to'xtatamiz kalit = (blok.name, json.dumps(blok.input, sort_keys=True)) if kalit in korilgan: natija, xato = "Bu chaqiruv allaqachon bajarilgan. Boshqa yo'l tanlang.", True else: korilgan.add(kalit) print(f" [{qadam}] {blok.name}: {blok.input}") try: natija, xato = BAJARUVCHILAR[blok.name](**blok.input), False except Exception as e: natija, xato = f"Asbob xatosi: {e}", True natijalar.append({ "type": "tool_result", "tool_use_id": blok.id, "content": natija, "is_error": xato, }) xabarlar.append({"role": "user", "content": natijalar}) return "Qadamlar chegarasiga yetildi. Qisman natija:\n" + "\n".join(XOTIRA) def hisobot(self): narx = (self.jami_kirish * 5 + self.jami_chiqish * 25) / 1_000_000 return (f"tokenlar: {self.jami_kirish:,} → {self.jami_chiqish:,} " f"narx: ${narx:.4f}")Ishga tushirish
agent = TadqiqotAgenti() javob = agent.tadqiq_qil( "O'zbekistonda IT sohasidagi eksport 2025-yilda qancha bo'ldi? " "Manbalarni ko'rsating." ) print(javob) print("\n" + agent.hisobot())[0] internetdan_qidir: {'sorov': "O'zbekiston IT eksport 2025"} [1] eslatib_qoy: {'fakt': 'IT Park maʼlumotiga koʻra... (manba: ...)'} [2] internetdan_qidir: {'sorov': "IT Park Uzbekistan export statistics 2025"} [3] eslatib_qoy: {'fakt': '...'} [Yakuniy javob manbalar bilan] tokenlar: 18,432 → 2,104 narx: $0.1448
Agent ishlamasa: nosozliklarni topish#
| Belgi | Ehtimoliy sabab | Nima qilish |
|---|---|---|
| Asbobni umuman chaqirmaydi | description noaniq | Tavsifga "QACHON ishlatiladi" qo'shing |
| Noto'g'ri asbobni tanlaydi | Ikki tavsif o'xshash | Chegaralarni aniq yozing: "... uchun EMAS" |
| Argumentlarni xato beradi | input_schema da misol yo'q | Har maydonga description va misol qo'shing |
| Cheksiz sikl | Asbob foydasiz natija qaytaradi | Takrorni aniqlash + qadam chegarasi |
tool_use_id xatosi | ID mos kelmayapti | blok.id ni aynan ko'chiring |
| Parallel chaqiruv to'xtadi | Natijalar alohida xabarlarda | Hammasini bitta user xabarida qaytaring |
| Javob juda umumiy | Kontekst shovqinga to'lgan | Asbob natijasini qisqartiring |
Agentga uchta yangi asbob qo'shing
Yuqoridagi tadqiqot agentini kengaytiring:
bugungi_sana— model bugungi sanani bilmaydi. Bu eng oddiy, lekin eng ko'p foyda beradigan asbob.fayl_oqi— mahalliy papkadagi hujjatni o'qiydi. Xavfsizlikni unutmang: agent papkadan tashqariga chiqa olmasligi kerak.hisobla— matematik ifodani hisoblaydi.
Keyin tajriba o'tkazing:
- Bir xil murakkab savolni asboblar bilan va asboblarsiz bering
- Qadamlar sonini, narxni va javob sifatini solishtiring
- Qaysi asbob eng ko'p ishlatildi? Qaysi biri hech qachon ishlatilmadi? Nima uchun?
Ishlatilmagan asbobning tavsifini qayta yozib ko'ring — natija o'zgaradimi?
Maslahatni ko'rsatish
Fayl o'qish asbobi uchun XAVFSIZLIK muhim: from pathlib import Path RUXSAT_PAPKA = Path('./hujjatlar').resolve() def fayl_oqi(yol: str) -> str: toliq = (RUXSAT_PAPKA / yol).resolve() # Papkadan chiqib ketishni to'xtatamiz (../../etc/passwd) if not str(toliq).startswith(str(RUXSAT_PAPKA)): return 'Xato: bu papkaga ruxsat yo'q' if not toliq.exists(): return f'Fayl topilmadi: {yol}' return toliq.read_text()[:5000] Sana asbobi eng oddiy, lekin eng foydali — model bugungi sanani BILMAYDI: from datetime import datetime def bugungi_sana() -> str: return datetime.now().strftime('%Y-%m-%d, %A') Solishtirish uchun: bir xil savolni ikkala variantda bering va qadamlar sonini hamda narxni yozib boring.
Xulosa#
- 01Model asbobni o'zi ishga tushirmaydi — u faqat so'raydi. Chaqirishni sizning kodingiz qiladi. Bu butun protokolning asosi.
- 02Asbob tavsifi — bu promptning bir qismi. Model faqat shunga qarab tanlaydi. Ishlaydigan agentning yarmi shu matnda.
- 03Barcha tool_result larni BITTA user xabarida qaytaring va tool_use_id ni aynan ko'chiring. Bu ikki xato eng ko'p uchraydi.
- 04Cheksiz sikl — agentlarning asosiy nosozligi. Qadam chegarasi, takrorni aniqlash va byudjet chegarasi — uchtasi ham kerak.
- 05Agent bitta savolga 5-15 marta model chaqiradi va har safar butun tarixni yuboradi. Narxni shunga qarab hisoblang.
- 06Freymvorkni darhol olmang. Avval noldan yozing — nosozlikni faqat shunda tuzata olasiz.
Agentimiz endi harakat qila oladi. Lekin uning xotirasi yo'q — topgan narsasini saqlay olmaydi va o'tgan suhbatni eslay olmaydi.
Keyingi bobda aynan shu bo'shliqni to'ldiramiz: RAG.