Birinchi agent — noldan oxirigacha

Bu hujjatning oxirida sizda ishlaydigan agent bo'ladi: kod yozasiz, platformaga ulaysiz, o'rnatasiz va unga birinchi so'rovni yuborib javobini ko'rasiz.

Misol sifatida kalkulyator agentini quramiz — 12 + 5 yuboramiz, 17 qaytadi. Ataylab eng oddiy mantiq: bu yerdagi gap matematikada emas, agent platformaga qanday ulanishida.

Kerak: Python 3.11+, terminal, 20 daqiqa.

Bu hujjatdagi buyruqlar haqiqatan ishga tushirilib tekshirilgan.
Javoblar — o'sha ishning haqiqiy chiqishi, qo'lda yozilgan emas.

Bu yerdagi kod va manifest to'liq keltirilgan — nusxa olib ishlatsangiz bo'ladi. Boshqa hech qanday fayl kerak emas.


Avval — hozirgi holat haqida rostini aytamiz

Vaqtingizni behuda sarflamasligingiz uchun bilib qo'ying. Quyidagilar kamchilik sifatida yozilgan, bahona sifatida emas:

NimaHolati
Developer Portal (agentfather.uz/portal)Ishlaydi: hisob, agent yaratish, manifest, «Sinov» bo'limi (sinab ko'rish + sinov tokeni), statistika, otzivlar. «Explore» va vidjetlar yo'q — mijoz ekranlari botdagi ilovada.
Portaldagi «o'rnatish havolasi»Brauzerda o'rnatish sahifasini ochmaydi — JSON qaytaradi (client_id, authorize_url). O'rnatishni 7-bo'limdagidek o'zingiz boshlaysiz.
Bot va Mini App (@AgentFatherAI_bot)Mijoz tomoni shu yerda. Agentni yollash, unga vazifa berish, natijani ko'rish — botdagi ilovada. Portal — dasturchi uchun, bot — foydalanuvchi uchun. Ikkalasini adashtirmang.
Rozilik (consent) ekraniBrauzerda ham ochiladi: kirish Telegram hisobi bilan (Login Widget), parol yo'q. Telegram ichida esa ilovaning o'zida chiziladi (7.1).
Dasturchi uchun sinovBor: POST /apps/{id}/test-run (6.4) agentni bitta so'rov bilan sinaydi, POST /apps/{id}/sandbox-token (6.5) o'z kodingiz uchun token beradi. Ikkalasi portalda tugma sifatida ham bor. Mijoz o'rnatishi sinov uchun kerak emas.
Sinov ma'lumoti (demo kontaktlar va h.k.)POST /api/v1/dev/sandbox/seed sinov tashkilotingizga haqiqiy shakldagi vazifa, fayl va kontakt qo'yadi. Usiz tashkilot bo'sh keladi.
ModeratsiyaFaqat marketplace'ga chiqish uchun. O'z agentingizni o'zingiz sinash uchun moderatsiya kutish shart emas.

Xulosa: agent yozish va sinash — portalda yoki terminalda, ikkisi bir xil endpointlarni chaqiradi. Mijoz o'rnatishi (7-bo'lim) esa alohida ish va u sizning sinovingizga to'siq bo'lmaydi. Quyida to'liq yo'l, boshidan oxirigacha.


1. Avval tushunchalar (2 daqiqa o'qing — keyin hammasi oson)

Beshta narsa bor va ular bir-biriga shunday ulanadi:


Integratsiya (app)  ──▶  Manifest (versiya)  ──▶  O'rnatish  ──▶  Ish (run)
   OAuth kalitlari        agent nima qila oladi    mijoz ruxsat berdi   agent ishladi
   redirect_uri           runtime.endpoint         access_token         natija
AtamaBu nimaQayerda tug'iladi
Integratsiya (app)OAuth kalitlari konteyneri: client_id, client_secret, signing_secret, redirect_uri. Kod emas.POST /api/v1/dev/apps
ManifestAgent nima qila olishini e'lon qiladi: qaysi manzilda turadi, qanday ruxsat kerak, qanday sozlama so'raydi. JSON fayl.POST /api/v1/dev/apps/{id}/versions
AgentSizning kodingiz. Bitta HTTP server. Platforma unga so'rov yuboradi.Sizning serveringiz
O'rnatish (installation)Mijoz «roziman» dedi → sizga access_token berildi.OAuth oqimi
Ish (run)Bitta bajarilish: kirish → agent → natija.POST /api/v1/runs

Eng muhim tushunish: platforma — chaqiruvchi, sizning agentingiz — chaqiriluvchi. Agentni ishga tushirib qo'yib kutish hech narsa bermaydi. Kimdir ish yaratmaguncha agentingizga hech kim murojaat qilmaydi. Agar «SDK'ni run qildim, ammo hech nima kelmadi» degan holatda bo'lsangiz — sabab shu, va bu xato emas.


2. Ikkita URL: nega ikkita va qaysi biri nima uchun

Bu — eng ko'p savol tug'diradigan joy, shuning uchun har birini alohida yozamiz.

2.1. redirect_uri — mijoz brauzeri qaytadigan manzil

Kim ochadi: foydalanuvchining brauzeri. Qachon: faqat o'rnatish paytida, bir marta. Qayerda beriladi: integratsiya yaratganda (redirect_uris) — 4-bo'lim.

Mijoz «Roziman» tugmasini bosgach, platforma uning brauzerini shu manzilga qaytaradi va URL'ga bir martalik code qo'shadi:


https://sizning-agent.uz/oauth/callback?code=XLZPuwd4hJm5N4mk…&state=…

Sizning saytingiz shu code ni oladi va uni access_token ga almashtiradi (7-bo'lim). Shundan keyin bu manzil boshqa ishlatilmaydi.

Qoidalar:

  • HTTPS bo'lishi shart. Istisno: http://localhost va http://127.0.0.1
  • — ishlab chiqish uchun ruxsat (app/api/schemas.py).

  • # (fragment) bo'lmasligi kerak.
  • Aniq mos kelishi shart — ro'yxatdagi satr bilan harfma-harf. Bitta
  • ortiqcha / ham invalid_redirect_uri beradi.

  • Bir nechta manzil berish mumkin (redirect_uris — ro'yxat), keyin
  • PATCH bilan o'zgartirasiz.

    2.2. runtime.endpoint — platforma so'rov yuboradigan manzil

    Kim ochadi: AFAI serveri (odam emas). Qachon: har bir ishda, doimiy. Qayerda beriladi: manifest ichida — 6-bo'lim.

    amoCRM yoki Telegram'dagi «webhook» tushunchasiga to'g'ri keladigani — aynan shu. Ish yaratilganda platforma shu manzilga POST yuboradi:

    
    {
      "type": "run.execute",
      "run": { "id": "run_…", "input": {"a": 12, "b": 5, "op": "+"} },
      "agent": { "id": "afai.calculator", "version": "1.0.0" },
      "installation": { "id": "ins_…", "workspace_id": "ws_…", "scopes": [], "settings": {} },
      "callback": { "base_url": "https://agentfather.uz/api/v1", "token": "…", "expires_in": 900 }
    }
    

    Sarlavhalar: X-AFAI-Signature: t=<unix>,v1=<hmac>, X-AFAI-Event, X-AFAI-Delivery, X-AFAI-Attempt.

    Qoidalar — bular redirect_uri dagidan butunlay boshqa:

  • agentfather.uz da HTTPS majburiy (REQUIRE_HTTPS_ENDPOINTS=true).
  • localhost ham, ichki IP ham qat'iyan mumkin emas — bizning
  • serverimiz ularga chiqa olmaydi va bu SSRF hujumi yo'li bo'lardi (app/security/ssrf.py). Urinsangiz aniq shu xatoni olasiz:

    ``json {"status": "failed", "error_code": "blocked_target", "error_message": "Bu manzilga so'rov yuborilmaydi: ichki manzil (::1)"} ``

  • Taqiqlangan portlar: 22, 23, 25, 445, 3306, 5432, 6379, 9200, 11211, 27017.
  • 2.3. callback.base_url — siz bermaysiz, o'zi keladi

    Yuqoridagi so'rovning ichida callback bloki bor. Bu — teskari yo'nalish: agent AFAI'ga natija qaytarish uchun ishlatadi. Tokeni vaqtinchalik (15 daqiqa) va faqat shu ish uchun. Hech qayerda ro'yxatdan o'tkazilmaydi. SDK uni o'zi ulaydi.

    2.4. Uchtasi bir jadvalda

    redirect_uriruntime.endpointcallback.base_url
    Yo'nalishAFAI → brauzer → sizAFAI → agentagent → AFAI
    Kim ochadiodamserversizning kodingiz
    Qachono'rnatishda, 1 martahar ishdauzoq ishlarda
    localhost mumkinmihayo'q—
    Kim beradisiz (app yaratishda)siz (manifestda)platforma (so'rov ichida)
    Nima bilan himoyalanganPKCE + aniq moslikHMAC imzovaqtinchalik token

    Nega birlashtirib bo'lmaydi: birinchisiga localhost mumkin, ikkinchisiga qat'iyan mumkin emas. Biri brauzer GET ini kutadi, ikkinchisi imzolangan server POST ini. Bitta maydon ikkala qoidaga ham bo'ysuna olmaydi. Buning ustiga runtime.type: "inline" agentlarda endpoint umuman bo'lmaydi, redirect_uri esa baribir kerak.


    3. Muhit: agentfather.uz

    Platformani o'zingizda ko'tarish shart emas — kerak ham emas. Sizga faqat HTTP API kerak: hisob, app, manifest, sinov — hammasi shu API orqali (yoki portaldagi tugmalar bilan). Platformani ishga tushiradigan buyruqlar sizga tegishli emas.

    Bitta shart bor: agentingiz internetdan ochiq HTTPS manzilda turishi kerak: noutbukingizdagi localhost:8200 ga bizning serverimiz hech qachon chiqa olmaydi.

    Ikki yo'l:

  • Agentni serverga qo'ying (docs/AGENT_DEPLOY.md).
  • Yoki lokal agentni tunnel bilan chiqaring:
  • ``bash cloudflared tunnel --url http://localhost:8200 # → https://random-words-1234.trycloudflare.com ` Shu https://… manzilni manifestdagi endpoint` ga yozasiz.

    
    export AFAI_BASE_URL=https://agentfather.uz
    
    API manzili haqida. API alohida subdomenda emas — barcha yo'llar
    saytning o'zida, https://agentfather.uz/api/v1/… ostida. «API'ning
    URL'i qaysi?» degan savolning javobi: sayt manzili + /api/v1.
    Windows'da ishlayotganlar uchun. export X=y — Linux/macOS (bash)
    yozuvi. PowerShell'da xuddi shu narsa
    $env:AFAI_BASE_URL = "https://agentfather.uz", buyruq ichida esa
    $AFAI_BASE_URL o'rniga $env:AFAI_BASE_URL turadi. Eng osoni —
    qiymatlarni buyruqqa to'g'ridan-to'g'ri yozib yuborish.
    Platformaning o'zi sizda bo'lsa (AFAI jamoasi ichida ishlayotganlar):
    ko'tarish yo'li platform/README.md da. O'shanda AFAI_BASE_URL
    http://localhost:8100 bo'ladi, tunnel kerak emas va endpoint ga
    http://localhost:8200 yoziladi. Qolgan hamma qadam bir xil.

    4. Integratsiya yarating

    Xohlasangiz shu bo'limni brauzerda ham qilsa bo'ladi:
    https://agentfather.uz/portal — hisob ochish, kalit olish, agent
    yaratish, manifest yuklash formalar bilan. Portal ham, curl ham bitta
    X-Developer-Key bilan ishlaydi, natijasi bir xil.

    4.1. Dasturchi hisobi

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/register \
      -H "Content-Type: application/json" \
      -d '{"email":"siz@example.uz","name":"Ismingiz","company":"Kompaniya"}'
    

    Javob:

    
    {
      "developer": { "id": "dev_3ad4606d8ec48d3a7bbca511" },
      "api_key": "dvk_3d72202de63c52883ef83eb7a6a72481574804cd27f0b388",
      "warning": "Kalitni saqlang — u boshqa ko'rsatilmaydi.",
      "sandbox_workspace_id": "ws_3a417b0961a340285243d550"
    }
    

    Ikkalasini ham saqlang:

    
    export AFAI_DEV_KEY=dvk_…
    export SANDBOX_WS=ws_…
    

    sandbox_workspace_id — bu sizning sinov tashkilotingiz. U shu zahoti, mijozsiz ochiladi. Bo'sh keladi: demo kontakt, vazifa yoki fayl bo'lmaydi.

    4.2. Integratsiya

    ⚠️ Dasturchi kaliti X-Developer-Key sarlavhasida yuboriladi,
    Authorization: Bearer da emas. Chalkashtirsangiz
    unauthorized: Sessiya tokeni o'qib bo'lmadi olasiz.
    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/apps \
      -H "X-Developer-Key: $AFAI_DEV_KEY" \
      -H "Content-Type: application/json" \
      -d '{
        "name": "Kalkulyator",
        "slug": "kalkulyator",
        "summary": "Ikki sonni hisoblaydi",
        "type": "public",
        "redirect_uris": ["https://sizning-agent.uz/oauth/callback"],
        "requested_scopes": ["runs:write"]
      }'
    

    slug va type — o'zgarmas: birinchisi havolalarda ishlatiladi, ikkinchisi integratsiyaning turi. Qolgan hammasini keyin PATCH bilan o'zgartirasiz — faqat o'zgartirmoqchi bo'lgan maydonni yuboring, qolganlari joyida qoladi:

    
    curl -s -X PATCH $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID \
      -H "X-Developer-Key: $AFAI_DEV_KEY" \
      -H "Content-Type: application/json" \
      -d '{"summary": "Yangi tavsif", "support_email": "siz@example.com"}'
    
    MaydonTuriIzoh
    namematnMarketplace'dagi nomi
    summarymatnBir qatorlik tavsif (kartada ko'rinadi)
    descriptionmatnTo'liq tavsif
    logo_urlURLMarketplace uchun majburiy
    privacy_urlURLMaxfiylik siyosati — majburiy
    support_emailemailQo'llab-quvvatlash — majburiy
    homepage_urlURLSayt
    install_urlURLBot-agentlar uchun: t.me/...
    redirect_urisro'yxatOAuth qaytish manzillari
    requested_scopesro'yxatSo'raladigan ruxsatlar (to'liq ro'yxat)

    Ro'yxat maydonlari (redirect_uris, requested_scopes) butunlay almashtiriladi, ustiga qo'shilmaydi — yangisini to'liq yuboring.

    requested_scopes ni o'zgartirish mavjud o'rnatishlarga ta'sir qilmaydi: mijozda agent eski ruxsatlar bilan ishlab turaveradi, yangisi uchun u qayta rozilik berishi kerak. Manifestda e'lon qilingan scope shu ro'yxatdan oshib ketsa, versiya qabul qilinmaydi.

    Javobda uchta sir keladi va ular boshqa hech qachon ko'rsatilmaydi:

    
    {
      "id": "app_248bc66cc7c2b71ff2f16564",
      "client_id": "afai_076a491f3367de262807c9e899c7f008",
      "client_secret": "afsec_IitDBFVdR6BNDeCAFiTIXx_udsic25t7IpQelrW5PDA",
      "signing_secret": "agsec_4PabK8vUlqqCC3ICdiZzBjCuVYYFJkTJq6aHzATH6DE",
      "status": "draft"
    }
    
    SirNima uchunKim ishlatadi
    client_idIntegratsiyaning ochiq kimligiOAuth authorize
    client_secretToken almashishSizning backend'ingiz
    signing_secretBizdan kelgan so'rov imzosini tekshirishSizning agentingiz (AFAI_AGENT_SECRET)
    
    export APP_ID=app_…
    export CLIENT_ID=afai_…
    export CLIENT_SECRET=afsec_…
    export AFAI_AGENT_SECRET=agsec_…
    
    status: "draft" — bu to'sqinlik emas. **Moderatsiya faqat marketplace
    uchun kerak** (10-bo'lim). Draft agentni o'zingizga hoziroq o'rnatib
    sinashingiz mumkin; o'rnatish ekranida shunchaki ogohlantirish chiqadi.

    5. Agent kodini yozing

    5.1. SDK

    
    pip install httpx uvicorn
    pip install https://agentfather.uz/sdk/afai-sdk.tar.gz
    
    AFAI jamoasi ichida ishlayotgan bo'lsangiz SDK repoda turadi:
    pip install -e platform/sdk/python.

    SDK sizning o'rningizga to'rt ishni qiladi: imzoni tekshiradi, callback tokenini ulaydi, qadamlarni yozadi, xatoni platforma tushunadigan formatga o'giradi. Siz faqat mantiqni yozasiz.

    5.2. main.py — to'liq kalkulyator

    
    from afai_sdk import Agent
    
    agent = Agent()          # AFAI_AGENT_SECRET ni muhitdan oladi
    
    AMALLAR = {
        "+": lambda a, b: a + b,
        "-": lambda a, b: a - b,
        "*": lambda a, b: a * b,
        "/": lambda a, b: a / b,
    }
    
    
    @agent.on_run
    async def handle(run):
        """Har bir `run.execute` shu funksiyaga tushadi."""
        a = run.input.get("a")
        b = run.input.get("b")
        op = run.input.get("op", "+")
    
        if a is None or b is None:
            raise ValueError("`a` va `b` majburiy")
        if op not in AMALLAR:
            raise ValueError(f"`{op}` — noma'lum amal")
        if op == "/" and b == 0:
            raise ValueError("Nolga bo'lib bo'lmaydi")
    
        await run.say(f"Hisoblayapman: {a} {op} {b}")
    
        natija = AMALLAR[op](a, b)
    
        await run.step("compute", name="calculator",
                       input={"a": a, "b": b, "op": op},
                       output={"result": natija})
    
        await run.say(f"Javob: {natija}")
    
        return {"result": natija, "expression": f"{a} {op} {b}"}
    
    
    app = agent.asgi()
    

    Nima bo'lyapti:

    QatorMa'nosi
    Agent()AFAI_AGENT_SECRET ni muhitdan oladi va har bir so'rov imzosini tekshiradi
    @agent.on_runHar bir ish shu funksiyaga tushadi
    run.inputPOST /api/v1/runs da yuborilgan input
    run.settingsMijoz kiritgan sozlamalar (manifestdagi config)
    run.scopesMijoz bergan ruxsatlar
    run.say(...)Odamga ko'rinadigan xabar
    run.step(...)Texnik qadam — ish tafsilotida ko'rinadi
    raise ...Ish failed bo'ladi, xato matni mijozga ko'rinadi
    return {...}Ishning output i
    agent.asgi()Toza ASGI ilova — uvicorn shuni ko'taradi
    Eng ko'p uchraydigan xato: Agent(client_secret) deb yozish. Agentga
    signing_secret (agsec_…) kerak, client_secret (afsec_…) emas.
    Chalkashtirsangiz har bir so'rov 401 invalid_signature bo'ladi.

    5.3. Ishga tushiring

    
    export AFAI_AGENT_SECRET=agsec_…          # 4.2 dan
    uvicorn main:app --port 8200              # `main.py` turgan katalogda
    
    
    INFO:     Uvicorn running on http://127.0.0.1:8200 (Press CTRL+C to quit)
    
    Diqqat: hozir hech nima bo'lmaydi va bo'lishi ham kerak emas. Agent —
    passiv server, u kutadi. Unga so'rov faqat 8-bo'limda keladi. Agar shu
    yerda «nega loglar bo'sh» deb to'xtab qolsangiz — hammasi to'g'ri ketyapti.

    agentfather.uz ga qarshi ishlayotgan bo'lsangiz, shu yerda tunnelni ham oching (3-bo'lim A) va chiqqan https://… manzilni yozib qo'ying.


    6. Manifestni e'lon qiling

    Manifest — agentingiz qayerda turishini va nima qila olishini aytadi. Aynan shu yerda ikkinchi URL (runtime.endpoint) beriladi.

    6.1. afai.agent.json

    Agentingiz yonida turadigan oddiy JSON fayl:

    
    {
      "manifest_version": 1,
      "id": "afai.calculator",
      "version": "1.0.0",
      "name": { "uz": "Kalkulyator", "en": "Calculator" },
      "summary": {
        "uz": "Ikki sonni qo'shadi, ayiradi, ko'paytiradi yoki bo'ladi",
        "en": "Adds, subtracts, multiplies or divides two numbers"
      },
      "categories": ["namuna"],
    
      "runtime": {
        "type": "webhook",
        "endpoint": "https://random-words-1234.trycloudflare.com",
        "timeout_ms": 30000
      },
    
      "scopes": ["runs:write"],
    
      "triggers": [{ "type": "manual" }]
    }
    
    endpoint — shu yerda. agentfather.uz da: tunnel yoki serveringiz
    bergan https://…. Lokal platformada: http://localhost:8200.
    To'liq maydonlar ro'yxati: MANIFEST.md.

    6.2. Yuborishdan oldin tekshiring

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/manifest/validate \
      -H "Content-Type: application/json" \
      -d @afai.agent.json
    
    
    { "valid": true, "errors": [], "summary": "Manifest yaroqli" }
    

    Bu endpoint hisobsiz ishlaydi — kalit ham kerak emas, shuning uchun CI'ga qo'yish oson. Xato bo'lsa har biri path, message va fix bilan qaytadi. Portalda esa «Versiyalar» bo'limidagi «Tekshirish» tugmasi shuni chaqiradi.

    6.3. E'lon qiling

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID/versions \
      -H "X-Developer-Key: $AFAI_DEV_KEY" \
      -H "Content-Type: application/json" \
      -d "{\"version\":\"1.0.0\",\"manifest\":$(cat afai.agent.json)}"
    
    
    {
      "id": "ver_59f4255797d2b8d4d394907b",
      "version": "1.0.0",
      "status": "draft",
      "is_current": true,
      "message": "Versiya joriy qilindi"
    }
    

    is_current: true — birinchi versiya avtomatik joriy bo'ladi. Keyingi versiyalarni POST /versions/{id}/promote bilan joriy qilasiz.

    6.4. Eng tez sinov — hoziroq

    Manifest e'lon qilingandan keyin agentni bitta so'rov bilan sinash mumkin. O'rnatish ham, token ham kerak emas:

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID/test-run \
      -H "X-Developer-Key: $AFAI_DEV_KEY" \
      -H "Content-Type: application/json" \
      -d '{"input": {"a": 12, "b": 5, "op": "+"}}'
    
    
    {
      "agent": { "version": "1.0.0", "runtime_type": "webhook",
                 "endpoint": "https://sizning-agent.uz" },
      "run": { "status": "succeeded", "duration_ms": 41,
               "output": { "result": 17, "expression": "12 + 5" } }
    }
    

    Platforma agentingizni o'z sandbox tashkilotingizga o'rnatib, bitta haqiqiy ish yuboradi: o'sha imzo, o'sha qadamlar, o'sha xatolar. Ya'ni bu yerda ishlagan narsa mijozda ham ishlaydi.

    Agent yiqilsa javob baribir 200 bo'ladi, ichida status: "failed" va error_code — «sinov yashil» bo'lib qolmaydi. agent.endpoint esa so'rov aynan qayerga ketganini ko'rsatadi.

    Kodni o'zgartirsangiz — agentni qayta ishga tushiring va shu
    buyruqni yana yuboring. Manifestni o'zgartirsangiz — avval yangi
    versiya e'lon qiling (6.3), sinov ishi doim eng yangi manifestni oladi.

    6.5. O'z kodingizdan ish yaratish — sinov tokeni

    test-run da ishni platforma yaratadi. O'z kodingizdan (SDK, navbat, qayta urinish) yaratishni sinash uchun access_token kerak:

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID/sandbox-token \
      -H "X-Developer-Key: $AFAI_DEV_KEY"
    
    
    { "access_token": "eyJ…", "expires_in": 7200, "installation_id": "ins_…",
      "workspace_id": "ws_…", "agent_version": "1.0.0" }
    
    
    export ACCESS_TOKEN=eyJ…
    curl -s -X POST $AFAI_BASE_URL/api/v1/runs \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"input": {"a": 12, "b": 5, "op": "+"}}'
    

    Token o'z sinov tashkilotingiz uchun, lekin oddiy token: o'sha imzo, o'sha scope'lar. Ya'ni shu bilan yozilgan kod mijoz tokenida ham o'zgarishsiz ishlaydi.

    Portalda tugma bor. agentfather.uz/portal/ → agent → «Sinov»:
    «Sinab ko'rish» (6.4) va «Sinov tokeni» (6.5) shu yerda, natija va
    qadamlar ekranda ko'rinadi. Buyruq qatori faqat CI uchun kerak.

    Quyidagi 7-bo'lim — mijoz qanday o'rnatishi. Uni agentingiz ishlab turganini ko'rganingizdan keyin o'qing: mijoz o'rnatishi sizning sinovingiz uchun kerak emas.


    7. Mijoz qanday o'rnatadi (OAuth)

    Bu bo'lim bitta oqim, ikki qadam bo'lib yozilgan. Ular ikki xil joyda bajariladi va shuning uchun alohida ko'rinadi:

    
    7.1  mijoz brauzerida          7.3  sizning serveringizda
         /oauth/authorize      →        /oauth/token
         (rozilik ekrani)      ←        code + code_verifier → access_token
                │
                └── rozilikdan keyin brauzer sizning `redirect_uri` ingizga
                    `?code=…` bilan qaytadi — 7.3 aynan shu yerdan boshlanadi
    

    Ya'ni 7.3 ni «alohida qilish» shart emas: u sizning /oauth/callback marshrutingizning ichida bo'ladi. Qo'lda sinaganda esa o'sha marshrutning o'rnida siz turasiz — kodni brauzer manzilidan nusxalab olasiz.

    redirect_uri ikkala qadamda ham bitta bo'lishi shart (yoki ikkalasida ham yozilmaydi — yuqoridagi izohga qarang). Kodda uni bitta konstanta qiling. Tayyor namuna — /install va /oauth/callback, 60 qator:

    
    curl -O https://agentfather.uz/sdk/examples/callback.py
    

    Barcha namunalar: GET /sdk/examples (main.py, callback.py, install.py, afai.agent.json).

    Bu bo'lim sizning sinovingiz uchun emas. O'z agentingizni sinash
    uchun 6.4 va 6.5 yetarli. Bu yerda yozilgani — boshqa odam sizning
    agentingizni o'z tashkilotiga qanday qo'shishi, ya'ni mahsulotga
    chiqqanda kerak bo'ladigan qism.

    Mijoz uchun bu qadamsiz hech qanday ish yaratib bo'lmaydi: POST /api/v1/runs o'rnatishga tegishli access_token talab qiladi.

    7.1. agentfather.uz da — brauzer orqali

    PKCE juftligini yarating:

    
    python - <<'PY'
    import base64, hashlib, secrets
    v = base64.urlsafe_b64encode(secrets.token_bytes(48)).rstrip(b"=").decode()
    c = base64.urlsafe_b64encode(hashlib.sha256(v.encode()).digest()).rstrip(b"=").decode()
    print("VERIFIER: ", v)
    print("CHALLENGE:", c)
    PY
    

    verifier ni saqlang — 7.3 da kerak.

    Kim nimani yasaydi (bu yerda eng ko'p chalkashlik bo'ladi):

    QiymatKim yasaydiKim ko'radi
    code_verifiersiz — tasodifiy satrfaqat siz. Hech qayerga chiqmaydi, /oauth/token da bir marta yuboriladi
    code_challengesiz — BASE64URL(SHA256(verifier))authorize havolasida ochiq ketadi
    codeplatforma — mijoz «Ruxsat berish» ni bosgandan keyinbrauzer sizning redirect_uri ingizga ?code=… bilan qaytaradi

    Ya'ni code ni o'zingiz o'ylab topmaysiz: u rozilikdan keyin tug'iladi, 10 daqiqa yashaydi va bir marta ishlatiladi. Nega bunday: challenge ochiq ketadi, verifier esa sizda qoladi — kodni yo'lda ushlab olgan odam uni tokenga almashtira olmaydi (PKCE, RFC 7636).

    Rozilik havolasini oching:

    redirect_uri ni yozmasangiz ham bo'ladi. Integratsiyada bitta
    manzil ro'yxatdan o'tgan bo'lsa, platforma o'shani oladi — authorize'da
    ham, token almashuvida ham. Shunda ikki joyda ikki xil satr yozib
    qo'yish holati umuman bo'lmaydi (RFC 6749 §3.1.2.3). Bir nechta manzil
    ro'yxatdan o'tgan bo'lsa — qaysi biri ekanini aytish kerak.

    Xohlasangiz aniq yozasiz:

    
    # App'dagi `redirect_uris` dan aynan bittasi.
    export REDIRECT_URI=https://sizning-agent.uz/oauth/callback
    
    
    https://agentfather.uz/oauth/authorize
      ?client_id=$CLIENT_ID
      &redirect_uri=$REDIRECT_URI
      &scope=runs:write
      &code_challenge=$CHALLENGE
      &code_challenge_method=S256
      &response_type=code
    

    Havolani brauzerda oching. Nima bo'ladi:

    1. brauzer rozilik sahifasiga o'tadi; sessiya bo'lmasa AFAI sizni kirish sahifasiga yuboradi va so'rovni next ichida saqlab qoladi; 2. kirish sahifasida Telegram tugmasi bor (Login Widget) — telefon raqami va Telegram tasdiqi orqali kirasiz. AFAI hisobi Telegram akkauntiga bog'langan, alohida parol yo'q; 3. kirgach brauzer avtomatik rozilik ekraniga qaytadi — scope'lar, ogohlantirishlar va «Ruxsat berish» shu yerda.

    Nega Telegram orqali. Mijoz hisobi Telegram akkauntiga bog'langan,
    shuning uchun sessiyani ham Telegram beradi: ilova ichida initData,
    brauzerda Login Widget (docs/BOT.md §3.1.1). Parol bilan kirish yo'q —
    ataylab.
    Telefonda osonroq. Kirish sahifasida ikkinchi havola ham bor —
    «Telegram ilovasida davom etish». U qurilmangizdagi Telegram'ni ochadi
    va bot rozilik ekranini ilovada ko'rsatadi (?start=oauth_<nonce>).
    redirect_uri sifatida o'zingiz boshqaradigan haqiqiy HTTPS manzil
    bering: telefondagi Telegram sizning noutbukingizdagi localhost ga
    chiqa olmaydi.

    Roziliktan keyin brauzer redirect_uri ga qaytadi va URL'da ?code=… bo'ladi. Mana shu yerda redirect_uri ishlatildi — 2.1-bo'limda aytilgani.

    Sinash uchun bu bo'lim kerak emas. O'z agentingizni sinashda 6.4 va
    6.5 dan foydalanasiz — u yerda na PKCE, na console token, na brauzer
    qatnashadi. Quyidagi qadamlar mahsulotga chiqqanda kerak: ularni
    sizning /install va /oauth/callback marshrutlaringiz bajaradi.

    7.2. Rozilikni qo'lda berish — faqat o'z platformangizda

    Bu yo'l faqat o'z platformangizda ishlaydi: prodda CONSOLE_AUTO_PROVISION=false va sessiya tashqaridan ochilmaydi (ataylab — mijoz sessiyasi faqat AFAI ilovasi orqali beriladi).

    
    curl -s -X POST $AFAI_BASE_URL/console/session \
      -H "Content-Type: application/json" \
      -d '{"email":"siz@example.uz","name":"Ismingiz","workspace":"Sinov MChJ"}'
    
    Emailni ro'yxatdan o'tgan email bilan bir xil qiling. Shunda sessiya
    to'g'ridan-to'g'ri sizning sinov tashkilotingizga tushadi — javobdagi
    workspace_id 4.1 dagi sandbox_workspace_id bilan bir xil bo'lishi
    kerak. Boshqa email bersangiz, boshqa tashkilot ochiladi va agentingiz
    u yerda ko'rinmaydi.
    
    { "token": "eyJhbGciOi…", "workspace_id": "ws_3a417b0961a340285243d550", "role": "owner" }
    
    
    export CONSOLE_TOKEN=eyJ…
    

    Rozilik:

    
    curl -s -X POST $AFAI_BASE_URL/oauth/consent \
      -H "Authorization: Bearer $CONSOLE_TOKEN" \
      -H "Content-Type: application/json" \
      -d "{\"client_id\":\"$CLIENT_ID\",
           \"redirect_uri\":\"http://localhost:8080/oauth/callback\",
           \"scope\":\"runs:write\",
           \"code_challenge\":\"$CHALLENGE\",
           \"approved\":true}"
    
    
    { "redirect_url": "http://localhost:8080/oauth/callback?code=XLZPuwd4hJm5N4mk…", "approved": true }
    

    code ni javobdan qo'lda olasiz — brauzer kerak emas.

    7.3. code → access_token

    Ikki manzil bor, ikkalasi bir xil ishlaydi — qaysi biri qulay bo'lsa:

    ManzilTana turiQachon
    POST /oauth/tokenapplication/x-www-form-urlencodedcurl, SDK, RFC 6749 talabi
    POST /oauth/token.jsonapplication/jsonPostman va JSON bilan ishlaydiganlar uchun

    Maydonlar — oltitasi ham majburiy va hech biri token emas (Authorization sarlavhasi bu so'rovda yo'q: access token aynan shu so'rovdan keyin paydo bo'ladi):

    MaydonNimaQayerdan olasiz
    grant_typedoim authorization_code—
    client_idintegratsiyaning ochiq kimligiGET /api/v1/dev/apps/{id}
    client_secretafsec_…app yaratilganda bir marta; yo'qolsa POST /apps/{id}/rotate-secret
    codebir martalik kodcallback manzilidagi ?code= dan keyin, & gacha
    code_verifierPKCE juftining maxfiy yarmi7.1 da yasagansiz (challenge emas!)
    redirect_uriauthorize'dagi aynan o'sha manzilapp'dagi redirect_uris dan
    
    curl -s -X POST $AFAI_BASE_URL/oauth/token \
      -d grant_type=authorization_code \
      -d client_id=$CLIENT_ID \
      -d client_secret=$CLIENT_SECRET \
      -d code=$CODE \
      -d code_verifier=$VERIFIER \
      -d redirect_uri=$REDIRECT_URI
    

    Xuddi shu narsa JSON bilan:

    
    curl -s -X POST $AFAI_BASE_URL/oauth/token.json \
      -H "Content-Type: application/json" \
      -d "{\"grant_type\":\"authorization_code\",
           \"client_id\":\"$CLIENT_ID\",
           \"client_secret\":\"$CLIENT_SECRET\",
           \"code\":\"$CODE\",
           \"code_verifier\":\"$VERIFIER\",
           \"redirect_uri\":\"$REDIRECT_URI\"}"
    
    **redirect_uri bu yerda ham kerak va authorize'dagi bilan
    belgi-belgisiga bir xil bo'lishi shart** (RFC 7636 talabi: kodni
    o'g'irlagan odam uni boshqa manzilga almashtira olmasin). Farq qilsa
    invalid_grant: redirect_uri avtorizatsiyadagidan farq qiladi keladi —
    javobning details maydonida ikkala qiymat ham turadi
    (expected va received), ya'ni farqni taxmin qilish shart emas.
    Eng ko'p uchraydigan to'rttasi:
    1. brauzer manzil qatoridan to'liq URL nusxalanadi — ?code=… qismi
    bilan. redirect_uri da so'rov qismi bo'lmaydi;
    2. oxiridagi / — …/callback/ boshqa satr hisoblanadi;
    3. maydonlar Postman'da Params (query) ga yoziladi — ular tanaga
    tushmaydi; Body → x-www-form-urlencoded yoki /oauth/token.json;
    4. ikki tunnel manzili almashib ketadi: redirect_uri — mijoz brauzeri
    qaytadigan manzil, runtime.endpoint — platforma ish yuboradigan
    manzil. Bular boshqa-boshqa.
    
    {
      "access_token": "eyJhbGciOi…",
      "token_type": "Bearer",
      "expires_in": 7200,
      "refresh_token": "vBaZ_xpx5ehGlfFuY3DH971bLZ6cFjSm…",
      "scope": "runs:write workspace:read",
      "installation_id": "ins_6352156e70a8179c5149f00d",
      "workspace_id": "ws_3a417b0961a340285243d550"
    }
    
    
    export ACCESS_TOKEN=eyJ…
    
    Har bir yangilashda yangi refresh token keladi. Eskisini ikkinchi marta
    ishlatsangiz, platforma buni o'g'irlik deb hisoblaydi va barcha tokenlarni
    bekor qiladi. SDK buni o'zi to'g'ri bajaradi.

    8. Birinchi ish

    Endi hamma bo'lak joyida. So'rov yuboramiz:

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/runs \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -H "Idempotency-Key: $(uuidgen)" \
      -d '{"input": {"a": 12, "b": 5, "op": "+"}}'
    
    
    {
      "id": "run_42595a3a0cbdd6c5dd4b3c5d",
      "status": "succeeded",
      "input": { "a": 12, "b": 5, "op": "+" },
      "output": { "result": 17, "expression": "12 + 5" },
      "duration_ms": 41
    }
    

    Shu daqiqada agentingiz terminalida POST ko'rinadi. Zanjir yopildi:

    
    curl → platforma → run yaratildi → run.execute (imzolangan) → agentingiz
         → natija → platforma → sizning javobingiz
    

    Qadamlarni ko'rish

    
    curl -s $AFAI_BASE_URL/api/v1/runs/$RUN_ID \
      -H "Authorization: Bearer $ACCESS_TOKEN" | jq '.steps'
    
    
    [
      { "seq": 1, "type": "message", "output": { "text": "Hisoblayapman: 12 + 5" } },
      { "seq": 2, "type": "compute", "name": "calculator",
        "input": { "a": 12, "b": 5, "op": "+" }, "output": { "result": 17 } },
      { "seq": 3, "type": "message", "output": { "text": "Javob: 17" } }
    ]
    

    run.say va run.step yozganlaringiz aynan shu yerda ko'rinadi.

    Xato holatini ham sinang

    
    curl -s -X POST $AFAI_BASE_URL/api/v1/runs \
      -H "Authorization: Bearer $ACCESS_TOKEN" \
      -H "Content-Type: application/json" \
      -d '{"input": {"a": 1, "b": 0, "op": "/"}}'
    
    
    { "status": "failed", "error_code": "ValueError", "error_message": "Nolga bo'lib bo'lmaydi" }
    

    Kodingizdagi raise shunday ko'rinadi. Xato matni mijozga yetadi — shuning uchun uni odam tushunadigan qilib yozing.


    9. Hech nima kelmasa — shu tartibda tekshiring

    Muammoni oxiridan boshiga qarab qidiring:

    1. Ish umuman yaratildimi? POST /api/v1/runs javobidagi status ni qarang. failed bo'lsa error_code hamma narsani aytadi.

    2. Platforma agentga chiqa oldimi?

    
    curl -s $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID/logs \
      -H "X-Developer-Key: $AFAI_DEV_KEY"
    

    3. Agent so'rovni oldimi? Agent terminalida POST qatorini qidiring. Bo'sh bo'lsa — so'rov unga yetmagan (jadvalning 1–3 qatorlari).

    BelgisiSababYechim
    blocked_target: ichki manzil (::1)endpoint da localhost, lekin platforma boshqa mashinadaagentfather.uz da: tunnel yoki server manzili. Lokalda: .env da ALLOW_PRIVATE_NETWORK_TARGETS=true
    blocked_target: faqat HTTPS qabul qilinadihttp:// endpointHTTPS bering (prodda majburiy)
    agent_unreachableAgent 30 soniyada javob bermadi yoki o'chiqAgentni ko'taring; uzoq ish bo'lsa run.defer()
    Agentda 401 invalid_signatureAFAI_AGENT_SECRET noto'g'ri yoki bo'sh4.2 dagi signing_secret (agsec_…). Yo'qotgan bo'lsangiz: POST /dev/apps/{id}/rotate-signing-secret
    Agent loglari butunlay bo'shHech kim ish yaratmaganBu xato emas. 6.4-bo'limdagi test-run ni yuboring
    unauthorized: Sessiya tokeni o'qib bo'lmadiDev kalitini Authorization ga qo'ygansizX-Developer-Key: dvk_…
    invalid_redirect_uriManzil ro'yxatdagidan farq qiladiHarfma-harf bir xil bo'lsin
    invalid_grant: PKCE tekshiruvidan o'tmadiBoshqa code_verifier7.1 dagi verifier ni saqlang
    insufficient_scopeScope so'ralmagan yoki berilmaganrequested_scopes ga qo'shing va qayta o'rnating
    Rozilik sahifasi «login» ga tashlaydiOddiy brauzerda ochgansizHavolani Telegram ichidan (@AgentFatherAI_bot → ilova) oching
    Manifest o'zgardi, lekin eskisi ishlayaptiYangi versiya joriy emasPOST /dev/apps/{id}/versions/{ver}/promote

    10. Marketplace'ga chiqarish (ixtiyoriy)

    Yuqoridagi hammasi o'zingiz uchun ishladi. Agentni boshqa mijozlar ham yollashi uchun uni moderatsiyaga topshirasiz. Uchta maydon to'ldirilmasa ariza qabul qilinmaydi:

    
    curl -s -X PATCH $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID \
      -H "X-Developer-Key: $AFAI_DEV_KEY" -H "Content-Type: application/json" \
      -d '{"logo_url":"https://…/logo.png",
           "privacy_url":"https://…/privacy",
           "support_email":"siz@example.com"}'
    
    curl -s -X POST $AFAI_BASE_URL/api/v1/dev/apps/$APP_ID/submit \
      -H "X-Developer-Key: $AFAI_DEV_KEY"
    # → {"status":"in_review","message":"Ariza qabul qilindi — moderatsiya 3 ish kunigacha."}
    

    Yetishmagan narsa bo'lsa javob aynan nimaligini aytadi (review_requirements_missing → details.missing).

    Moderatsiyadan o'tgach agent marketplace'da paydo bo'ladi va mijoz uni AgentFather ilovasidan yollaydi (Explore → «Install»).


    11. Agentingiz yolg'iz emas

    Bitta tashkilotda bir nechta agent turadi va ular bir-biriga murojaat qila oladi. Bu alohida protokol emas — o'sha platforma tool'lari:

    Nima kerakToolKerakli scope
    Boshqa agentga ish topshirish (natija keyin)delegate_tasktasks:write
    Boshqa agentdan javob so'rash (hozir)ask_agentruns:write
    Hodisa chiqarish, kim eshitsa o'sha ushlaydiemit_eventwebhooks:manage

    Ikki narsani bilib qo'ying:

  • Sizdan nima so'rash mumkinligini manifestingiz belgilaydi. ask_agent
  • faqat tools da e'lon qilingan tool'ni chaqira oladi — e'lon qilish «shu tashkilotdagi boshqa agentlar buni chaqirishi mumkin» degani.

  • Faqat bitta tashkilot ichida. Boshqa mijozning agentiga yetib
  • bo'lmaydi, zanjir chuqurligi esa 3 bilan chegaralangan.

    To'liq shartnoma, xavfsizlik qoidalari va misollar: agentfather.uz/docs/agents.


    12. Keyin nima

    XohlasangizQarang
    Uzoq ishlar (SMS, indekslash, hisobot)INTEGRATION.md → run.defer()
    Boshqa agentlar bilan ishlashINTEGRATION.md → delegate_task, ask_agent
    Mijozdan sozlama olish (API kalit va h.k.)MANIFEST.md → config
    Hodisalarga obunaWEBHOOKS.md
    AFAI ichida o'z UI'ingizSURFACES.md
    Kod yozmasdan agentMANIFEST.md → runtime.type: "inline"
    Agentni serverga qo'yishdocs/AGENT_DEPLOY.md
    Marketplace qoidalariMARKETPLACE.md
    Xato kodlari to'liq ro'yxatiERRORS.md