aidoc.uz

3-qism · Murakkab tizimlar·10-bob

Agentdan mahsulot qurish

Skriptdan ishlaydigan mahsulotgacha. FastAPI backend, xatolarni boshqarish, inferens optimizatsiyasi, asinxronlik, kuzatuv va Docker bilan joylashtirish — ishlaydigan kod bilan.

Sizda ishlaydigan agent bor. Terminalda ajoyib natija beryapti.

Lekin bu mahsulot emas. Bu skript.

Mahsulot bo'lishi uchun: dasturlashni bilmagan odam ham ishlata olishi, bir vaqtda o'nlab foydalanuvchi kira olishi, API tushib qolganda qulamasligi, tez javob berishi va sizni xarajat bilan sindirmasligi kerak.

FastAPI: agentni veb-xizmatga aylantirish#

Streamlit prototip uchun yaxshi, lekin jiddiy mahsulot uchun FastAPI — kengayadigan, tez va standart yechim.

from fastapi import FastAPI, HTTPException
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
import anthropic
 
app = FastAPI(title="AI Agent API")
client = anthropic.Anthropic()
 
 
class Sorov(BaseModel):
    savol: str
    model: str = "claude-opus-5"
 
 
class Javob(BaseModel):
    javob: str
    kirish_token: int
    chiqish_token: int
    narx: float
 
 
NARXLAR = {"claude-opus-5": (5.0, 25.0), "claude-sonnet-5": (2.0, 10.0)}
 
 
def narx_hisobla(model, kirish, chiqish):
    k, ch = NARXLAR[model]
    return (kirish * k + chiqish * ch) / 1_000_000
 
 
@app.post("/sora", response_model=Javob)
def sora(sorov: Sorov):
    try:
        javob = client.messages.create(
            model=sorov.model,
            max_tokens=2048,
            messages=[{"role": "user", "content": sorov.savol}],
        )
    except anthropic.RateLimitError:
        raise HTTPException(status_code=429, detail="So'rovlar chegarasi oshdi")
    except anthropic.APIStatusError as e:
        raise HTTPException(status_code=502, detail=f"Model xatosi: {e}")
 
    matn = "".join(b.text for b in javob.content if b.type == "text")
 
    return Javob(
        javob=matn,
        kirish_token=javob.usage.input_tokens,
        chiqish_token=javob.usage.output_tokens,
        narx=narx_hisobla(sorov.model, javob.usage.input_tokens, javob.usage.output_tokens),
    )

Ishga tushirish:

terminal
pip install fastapi uvicorn
uvicorn app:app --reload
Uvicorn running on http://127.0.0.1:8000

http://127.0.0.1:8000/docs — avtomatik yaratilgan interaktiv hujjat. Bu FastAPI'ning katta afzalligi.

Oqim (streaming)#

Uzun javoblarda foydalanuvchi bo'sh ekranga qarab o'tirmasligi kerak:

@app.post("/sora-oqim")
def sora_oqim(sorov: Sorov):
    def generator():
        with client.messages.stream(
            model=sorov.model,
            max_tokens=4096,
            messages=[{"role": "user", "content": sorov.savol}],
        ) as stream:
            for matn in stream.text_stream:
                yield matn
 
    return StreamingResponse(generator(), media_type="text/plain")

Xatolarni boshqarish#

Ishlab chiqarishda hamma narsa buziladi. Uch eng ko'p uchraydigani.

Qayta urinish — to'g'ri usul#

import time
import random
import anthropic
import logging
 
logger = logging.getLogger(__name__)
 
 
def ishonchli_chaqiruv(prompt, urinishlar=4):
    """Eksponensial kutish + jitter bilan qayta urinish."""
    for i in range(urinishlar):
        try:
            return client.messages.create(
                model="claude-opus-5",
                max_tokens=4096,
                messages=[{"role": "user", "content": prompt}],
                timeout=30,                     # DOIM timeout qo'ying
            )
 
        except (anthropic.RateLimitError, anthropic.APIConnectionError):
            kutish = 2**i + random.random()     # 1s, 2s, 4s, 8s + tasodif
            logger.warning("Qayta urinish %d, %.1fs kutamiz", i + 1, kutish)
            time.sleep(kutish)
 
        except anthropic.APIStatusError as e:
            if e.status_code >= 500:            # server xatosi — qayta urinamiz
                time.sleep(2**i + random.random())
            else:                               # 400/404 — qayta urinish foydasiz
                logger.error("So'rov xatosi: %s", e)
                raise
 
    raise RuntimeError("Bir necha urinishdan keyin ham muvaffaqiyatsiz")

Kuzatuv (observability)#

print() — bu nosozlikni tuzatish uchun. Ishlab chiqarish uchun tizimli loglash:

import logging
import json
import time
from functools import wraps
 
logging.basicConfig(
    level=logging.INFO,
    format="%(asctime)s %(levelname)s: %(message)s",
)
logger = logging.getLogger("agent")
 
 
def kuzat(func):
    """Har chaqiruvni loglaydigan dekorator."""
    @wraps(func)
    def wrapper(*args, **kwargs):
        boshlandi = time.time()
        try:
            javob = func(*args, **kwargs)
            logger.info(json.dumps({
                "hodisa": "llm_chaqiruv",
                "kirish": javob.usage.input_tokens,
                "chiqish": javob.usage.output_tokens,
                "vaqt_ms": round((time.time() - boshlandi) * 1000),
            }))
            return javob
        except Exception as e:
            logger.exception("Chaqiruv muvaffaqiyatsiz: %s", e)
            raise
    return wrapper

Nimani loglash kerak: har chaqiruv, har xato, token sarfi, kechikish va agent qaysi asbobni tanlagani.


Inferensni tezlashtirish#

Agar o'z modelingizni ishlatayotgan bo'lsangiz (Llama, Mistral), inferensni tezlashtirish kerak. Uch jarayon bor:

1. Tokenizatsiya + embedding
parallellashadi ✓
2. Key va Value hisoblash
parallellashadi ✓
3. Generatsiya
KETMA-KET — 15-token 14-tokenga bog'liq ✗
Inferens: qaysi qism parallellashadi, qaysi bo'lmaydi

KV kesh — eng muhim optimizatsiya#

15-tokenni chiqarish uchun 1–14 tokenlarning Key va Value qiymatlari kerak. Ularni har safar qayta hisoblash — bu isrof.

# vLLM — KV kesh, paged attention va continuous batching'ni tayyor beradi
from vllm import LLM, SamplingParams
 
model = LLM(
    model="mistralai/Mistral-7B-Instruct-v0.3",
    gpu_memory_utilization=0.9,
    max_model_len=4096,
)
 
sozlama = SamplingParams(temperature=0.7, max_tokens=512)
 
# Ko'p so'rovni BIR VAQTDA — continuous batching avtomatik ishlaydi
sorovlar = ["Savol 1", "Savol 2", "Savol 3"]
javoblar = model.generate(sorovlar, sozlama)
TexnikaNima qiladiFoyda
KV keshK va V ni qayta ishlatadiO(n²) → O(n)
Continuous batchingSo'rovlarni dinamik guruhlaydiGPU to'liq band
Paged attentionXotirani sahifalarga bo'ladiKam fragmentatsiya
Kvantlashint8/int42-4× kam xotira

Asinxronlik#

Agent tizimida vaqtning katta qismi kutish bilan o'tadi: API javobi, baza so'rovi, fayl o'qish.

Sinxron kodda bu vaqt behuda ketadi:

import asyncio
import anthropic
 
async_client = anthropic.AsyncAnthropic()
 
 
async def parallel_sora(savollar: list[str]):
    """Barcha savollarni BIR VAQTDA yuboradi."""
    async def bitta(savol):
        javob = await async_client.messages.create(
            model="claude-opus-5",
            max_tokens=1024,
            messages=[{"role": "user", "content": savol}],
        )
        return "".join(b.text for b in javob.content if b.type == "text")
 
    return await asyncio.gather(*[bitta(s) for s in savollar])
 
 
# Sinxron: 3 savol × 1s = 3 soniya
# Asinxron: 3 savol parallel = ~1 soniya
javoblar = asyncio.run(parallel_sora(["Savol 1", "Savol 2", "Savol 3"]))

Bu ko'p agentli tizimda hal qiluvchi: 9-bobdagi sayohat tizimida mehmonxona va ob-havo agentlari bir-biriga bog'liq emas, demak ular parallel ishlashi mumkin.


Docker: «mening kompyuterimda ishlayapti»#

Har bir dasturchi bu jumlani eshitgan. Docker aynan shu muammoni hal qiladi.

Konteyner — ilova va uning barcha bog'liqliklari bitta paketda. Qayerda ishga tushirsangiz ham bir xil ishlaydi.

FROM python:3.12-slim
 
WORKDIR /app
 
# Bog'liqliklarni ALOHIDA qatlamda — kod o'zgarganda qayta o'rnatilmaydi
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
 
COPY . .
 
EXPOSE 8000
CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "8000"]

Bir necha xizmatni birga ishga tushirish uchun Docker Compose:

# docker-compose.yml
services:
  api:
    build: .
    ports:
      - "8000:8000"
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
    depends_on:
      - baza
 
  baza:
    image: postgres:16
    environment:
      - POSTGRES_PASSWORD=${DB_PAROL}
    volumes:
      - baza_data:/var/lib/postgresql/data
 
volumes:
  baza_data:
terminal
docker compose up -d
✔ Container baza Started\n✔ Container api Started

Kubernetes — ko'p konteynerni boshqarish uchun: avtomatik kengaytirish, qulagan konteynerni qayta ishga tushirish, yuk taqsimlash. Lekin boshida kerak emas — bitta server va Docker Compose bilan ancha uzoqqa borasiz.


To'liq mahsulot ro'yxati#

Ishlab chiqarishga chiqarishdan oldin tekshiring:

✓
Xatolar boshqariladi
Qayta urinish + jitter, timeout, xatolarni ikkiga ajratish (qayta urinish foydali/foydasiz).
✓
Hamma narsa loglanadi
Har chaqiruv, token sarfi, kechikish, xatolar. Bularsiz nosozlikni topib bo'lmaydi.
✓
So'rov chegarasi bor
Har foydalanuvchi uchun limit. Aks holda bitta odam butun byudjetni yoqib yuboradi.
✓
Kalitlar xavfsiz
Muhit o'zgaruvchisida, kodda emas. .gitignore da. Production'da secrets boshqaruvi.
✓
Kirish tekshiriladi
Foydalanuvchi matnini ishonchli deb hisoblamang. Prompt injection'ni eslang.
Amaliy topshiriq

To'liq mahsulotni Docker bilan joylashtiring

4-bobdagi tadqiqot agentini to'liq mahsulotga aylantiring:

  1. FastAPI bilan o'rang — /sora va /sora-oqim endpointlari
  2. Xatolarni boshqaring — qayta urinish, timeout
  3. So'rov chegarasi qo'shing — har foydalanuvchi daqiqasiga 10 ta
  4. Token sarfini har javobda qaytaring
  5. Docker bilan o'rang va ishga tushiring
  6. curl bilan sinang

Talablar:

  • Kalit muhit o'zgaruvchisidan olinsin (Docker'ga yozilmasin)
  • Loglar tizimli bo'lsin (JSON format)
  • /docs orqali interaktiv hujjat ishlasin

Bonus: docker stats bilan konteyner qancha resurs ishlatayotganini kuzating.

Maslahatni ko'rsatish

Bosqichlar: 1. 4-bobdagi tadqiqot agentini oling 2. FastAPI bilan o'rang (yuqoridagi kod) 3. So'rov chegarasi qo'shing: from collections import defaultdict import time sorovlar = defaultdict(list) def chegara_tekshir(foydalanuvchi, limit=10, oyna=60): hozir = time.time() sorovlar[foydalanuvchi] = [t for t in sorovlar[foydalanuvchi] if hozir - t < oyna] if len(sorovlar[foydalanuvchi]) >= limit: raise HTTPException(429, 'Chegara oshdi') sorovlar[foydalanuvchi].append(hozir) 4. Dockerfile yozing 5. docker build va docker run 6. curl bilan sinang: curl -X POST localhost:8000/sora -H 'Content-Type: application/json' \ -d '{"savol": "salom"}' Kuzatuv uchun: har javobda X-Tokens va X-Cost header qo'shing (response.headers orqali). Bonus: docker stats bilan konteyner qancha xotira/protsessor ishlatayotganini kuzating.


Xulosa#

skript
terminalda ishlaydi
FastAPI
veb-xizmat, interaktiv hujjat
xato boshqaruvi + kuzatuv
qayta urinish, loglash, chegara
optimizatsiya
KV kesh, asinxronlik, kesh
Docker
hamma joyda bir xil ishlaydi
Skriptdan mahsulotgacha
Yodda saqlang
  • 01Token sarfini birinchi kundan loglang. Ishlab chiqarishdagi eng ko'p uchraydigan noxush kutilmagan holat — oy oxiridagi hisob.
  • 02Qayta urinish va timeout — majburiy. Tashqi API tushadi. Savol «tushadimi» emas, «qachon tushadi». Jitter'ni unutmang.
  • 03Asinxronlik kutishni yo'q qiladi, hisoblashni emas. I/O uchun asyncio, hisoblash uchun multiprocessing.
  • 04Optimallashtirishni o'lchagandan keyin qiling. KV kesh, Kubernetes — har biri murakkablik qo'shadi. Avval qayerda sekin ekanini o'lchang.
  • 05Docker «mening kompyuterimda ishlayapti» muammosini hal qiladi. Kalitni image ichiga yozmang — muhit orqali bering.
  • 06So'rov chegarasi qo'ying. Bitta foydalanuvchi butun byudjetingizni yoqib yuborishi mumkin.

Kitobning oxirgi bobida orqaga bir qadam chekinamiz va oldinga qaraymiz: agentlar qayerda ishlatilyapti, qanday savollar hali javobsiz va bu yo'l qayerga olib boryapti.