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.