CodeGym /Kurslar /ChatGPT Apps /Xətalar, idempotentlik və “təhlükəsiz” alətlərin dizaynı

Xətalar, idempotentlik və “təhlükəsiz” alətlərin dizaynı

ChatGPT Apps
Səviyyə, Dərs
Mövcuddur

1. ChatGPT App-də xətalar və idempotentlik

Klassik veb dünyasında çoxları hələ də “istifadəçi düyməni basdı → bir HTTP sorğusu → bir cavab” paradiqmasında yaşayır. LLM dünyasında isə artıq belə deyil. Model alətinizi bir neçə dəfə çağırmağa qərar verə bilər, istifadəçi Regenerate düyməsini basdıqdan sonra cavabı yenidən yarada bilər, sualı dəqiqləşdirə bilər, yolda şəbəkə xətası ilə üzləşə bilər. Nəticədə eyni alət çox bənzər arqumentlərlə iki və ya üç dəfə çağırıla bilər.

Eyni zamanda hər bir xətanın qəfil iki istehlakçısı yaranır. Bir tərəfdən — model, hansı ki, nə baş verdiyini maşın üçün oxunaqlı şəkildə başa düşməlidir ki, arqumentləri düzəldib yenidən yoxlaya bilsin. Digər tərəfdən — istifadəçi UI-si (vidjet və çatın özü), harada ki, “Error: 500 (see logs)” əvəzinə insana yönəlik mesaj və növbəti addımlar təklif olunmalıdır.

Daha bir vacib məqam: klassik arxitektura nadir hallarda kimsə “cavabı təkrarla” düyməsini kütləvi basacaq və bununla da təkrar sorğuların (retry-lərin) sayını artıracaq kimi nəzərdə tutulur. ChatGPT-də bu ssenari — normadır. Üstəlik, platforma müvəqqəti şəbəkə problemlərində özü də təkrar çağırış edə bilər. Buna görə idempotentlik anlayışı bu ekosistemdə əlavə seçim deyil, baza tələbatıdır, xüsusilə “gerçək” işlər görən alətlər üçün — sifariş yaratmaq, pul çıxarmaq, məktub göndərmək və s.

Bu mühazirə məhz alətin uğursuz bir çağırışının (tool call) istifadəçinin əhvalını və sizin prodaksiya mühitini korlamamasını necə təmin etməyiniz haqqındadır.

Insight

ChatGPT arqumentləri funksiyalarınıza ötürmür, daha çox sxemanı nəzərə alıb arqumentlər toplusunu təxmin edir. O, JSON Schema, dialoq konteksti əsasında statistik olaraq dəyərlər seçir — və kifayət qədər tez-tez yanılır. “Yanlış tip”, “məcburi sahə unudulub”, “ziddiyyətli parametrlər” kimi xətalar tool-call həyatının normal hissəsidir, fors-major deyil. Açıq məlumatlar və telemetriya göstərir ki, mürəkkəb sxemlərdə bu cür səhvlər asanlıqla təxminən ~30 % çağırışa qədər çatır.

Model üçün bu problem deyil: o, cavabınızı “arqumentlər yaxşı deyil” siqnalı kimi görür və sadəcə yenidən cəhd edir, bəzən ard-arda iki-üç dəfə, girişi bir az dəyişdirərək. Sizin üçün isə nəticə başqa olur: hər bir aləti elə layihələndirməlisiniz ki, sanki onu demək olar ki, mütləq bir neçə dəfə çox bənzər parametrlərlə çağıracaqlar.

Məhz buna görə idempotentlik bu qədər vacibdir. ChatGPT funksiyalarınızı hansı parametrlərlə çağırmalı olduğunu təkrar-təkrar təxmin edəcək. Hər çağırış üçün 2–3 cəhd — normadır.

2. Vidjetin təhlükəsiz tənzimlənməsi: text/html+skybridge və _meta

Təmamilə server mövzularına (xətalar, retry-lər, idempotentlik) keçməzdən əvvəl Apps SDK üçün UI təhlükəsizliyi ilə bağlı spesifik bir məqamı bağlayaq: vidjetinizin çatda “internetdən gəlmiş dəhşətli səhifə” kimi deyil, təhlükəsiz şəkildə render olmasını necə təmin etmək olar.

registerResource və MIME tipi text/html+skybridge

ChatGPT nöqteyi-nəzərindən vidjetiniz — istifadəçinin brauzerinə birbaşa yox, ChatGPT müştərisinin sandbox-una düşən xüsusi HTML resursudur. Platformanın bunun sadə HTML deyil, məhz vidjet olduğunu anlaması üçün text/html+skybridge MIME tipi istifadə olunur.

MCP/server səviyyəsində resursu təxminən belə qeydiyyatdan keçirirsiniz (pseudo-TS):

// MCP server konfiqurasiyasında haradasa
registerResource({
  name: "giftgenius-widget",
  path: "/widget",
  mimeType: "text/html+skybridge", // vacibdir!
});

Bu mimeType — ChatGPT müştərisi üçün siqnaldır: “bu, sadəcə HTML deyil, daxili vidjet üçün komponent şablonudur və izolə olunmuş mühitdə işə salınmalıdır”. Əgər adi text/html göstərsəniz, platforma ya xam HTML göstərə bilər, ya da ümumiyyətlə renderdən imtina edə bilər.

_meta və təhlükəsizliyə nəzarət: CSP, domen və çərçivə

Daha sonra alətin və ya resursun cavabı ilə birlikdə ötürülən metadatalar — _meta işə düşür. Onlar vasitəsilə vidjetin hansı xarici resursları yükləyə biləcəyini, vizual olaraq necə davranacağını və hətta modeli onu necə təsvir edəcəyini idarə edirsiniz.

Tipik quruluş nümunəsi:

const toolResult = {
  content: "<!-- Vidjetin HTML-i -->",
  _meta: {
    "openai/widgetCSP": "default-src 'self'; img-src https://cdn.example.com",
    "openai/widgetDomain": "https://chatgpt.com",
    "openai/widgetPrefersBorder": true,
    "openai/widgetDescription": "GiftGenius hədiyyə tövsiyələrini kartlar şəklində göstərir."
  }
};

Açar sahələri nəzərdən keçirək.

  • openai/widgetCSP vidjet üçün Content Security Policy təyin edir. Bu, ChatGPT daxilindəki brauzer üçün kiçik firewall-ınız kimidir: hansı domenlərdən skript, stil, şəkil yükləmək, XHR etmək və s. olacağını açıq şəkildə sadalayırsınız. Platforma wildcard * olmadan ciddi siyasət gözləyir; istifadə edilən domenləri (çat, öz API-niz, CDN) aydın qeyd etməlisiniz.
  • openai/widgetDomain vidjetin işləyəcəyi origin-i təyin edir. Adətən bu, ChatGPT domenidir; siz onu öz saytınızla əvəz etmisiniz kimi yox, izolə olunmuş mühitdə necə görünməli olduğunu bildirirsiniz.
  • openai/widgetPrefersBorder — sırf vizual flaq: vidjetin ətrafında çərçivə çəkilsinmi. GiftGenius üçün tövsiyələr blokunu adi çat mesajlarından vizual ayırmaq üçün çərçivəni saxlamaq məntiqlidir.
  • openai/widgetDescription — model üçün mətn təsviri. Model öz-özünə “uydurmaq” əvəzinə, istifadəçiyə açılmış interfeysi izah edərkən bu sətri istifadə edə bilər. Bu, modelin qəribə və ya artıq şərh vermə riskini azaldır.

Praktiki nəticə: mimeType və _meta düzgün qurulduqda, lazımsız yerə “qaçmayan”, istifadəçi və platforma baxımından proqnozlaşdırılan şəkildə davranan, təhlükəsiz və izolə olunmuş UI əldə edirsiniz. Fronent təhlükəsizliyi hissəsini bağladıq: vidjet sandbox-da yaşayır və yalnız icazə verdiyiniz yerlərə müraciət edir. İndi server tərəfinə — xətaların növlərinə, onların necə təsvir olunmasına və alətləri necə idempotent etməyə fokuslanaq.

Insight: vidjetin keşlənməsi

ChatGPT vidjetin HTML-ni tətbiq qeydiyyatı anında keşləyir. ChatGPT HTML-vidjeti “canlı frontend” deyil, build-in sabitləşdirilmiş artefaktıdır. Tətbiqi dərc edərkən (Store və ya Dev Mode) platforma HTML resursunu (text/html+skybridge) oxuyur və sonra həmişə məhz həmin versiyanı istifadə edir. İstənilən dəyişiklik — hətta bir sətir mətn və ya kartdakı boşluq — faktiki olaraq yeni reliz deməkdir.

Buradan nəticə: HTML quruluşu, slotlar, data-* atributları və structuredContent → DOM müqaviləsi üzrə düzəlişlər “sürətli düzəliş” deyil, tam hüquqlu frontend miqrasiyasıdır. Əgər bu gün items[] siyahısını render edirsinizsə, sabah results[]-a keçsəniz, köhnə vidjet bunu bilməyəcək: o, əvvəlki JSON-u almağa davam edəcək və düzgün işləməyəcək.

3. Alətlərin işində xəta növləri

İndi konkret hissəyə keçək: ümumiyyətlə alətdə hansı xətalar olur və onlar UX və backend baxımından nə ilə fərqlənir. Dörd qatmanla düşünmək rahatdır.

Girişin validasiya xətaları

Ən baza səviyyə — giriş arqumentləri ümumiyyətlə kontrakta uyğun gəlmir.

Tədris tətbiqimiz GiftGenius və onun suggest_gifts aləti (maraqlara və büdcəyə görə hədiyyə seçimi) üçün nümunələr:

  • yaş sıfırdan kiçik və ya 120-dən böyükdür;
  • büdcə mənfidir;
  • məcburi relationship_type sahəsi yoxdur;
  • budget_min > budget_max.

Buraya, həmçinin, sxemaya uyğun olmayan banal JSON da daxildir. İdealda Apps SDK və JSON Schema “tamamilə pis” çağırışları kodunuza çatmazdan öncə filtrdən keçirəcək, amma biznes validasiyanı (məsələn, budget_min/budget_max nisbəti kimi) yenə də özünüz etməlisiniz.

Biznes məntiqi xətaları

Burada giriş guya düzgündür, lakin domen qaydalarına görə normal nəticə verə bilmirsiniz.

Tipik vəziyyətlər:

  • göstərilən maraqlar və büdcə üzrə bir dənə də hədiyyə tapılmadı;
  • istifadəçi gün ərzində seçim limitini aşdı;
  • modelin almağı xahiş etdiyi məhsul artıq satılmır.

Bu, “server sıradan çıxdı” deyil, normal, gözlənilən situasiyalardır; bunları istifadəçiyə və modelə 500 Internal Server Error kimi deyil, həzm olunan formada təqdim etmək lazımdır.

Xarici infrastruktur xətaları

Bu qat artıq “texniki cəhənnəm” kimidir: baza əlçatmazdır, xarici API timeout verir, kodunuzda emal olunmamış istisna (exception) baş verir.

Məsələn:

  • hədiyyə kataloquna sorğu 503 qaytarır və ya cavab vermir;
  • MongoDB qəfil pauza verir;
  • hədiyyə filtrləmə kodunda sıfıra bölürsünüz.

UX baxımından bu, çox vaxt “Xidmət müvəqqəti əlçatmazdır, bir az sonra yenidən cəhd edin” demək üçün səbəbdir, bəzən gizli retry cəhd etmək olar. Amma səssizcə “yoxa çıxmaq” və istifadəçiyə xam stack trace göstərmək olmaz.

Platforma/şəbəkə xətaları

Və nəhayət, ümumiyyətlə sizin kodunuzdan kənarda baş verə bilən qat var: tool-call çatmadı, cavabın ortasında əlaqə qırıldı, streaming-ssenarisi yarımçıq qaldı. Düşündüyünüzdən daha tez-tez olur. Məsələn, pulsuz tunel istifadə etsəniz, pik saatlarda sürət o qədər düşür ki, ChatGPT tool call-ları timeout-a düşüb qopur.

Bunu tam nəzarətdə saxlaya bilməzsiniz, amma alətləri və vidjeti elə layihələndirə bilərsiniz ki, təkrar çağırışlar və kəsilmələr sistemi xaosa çevirməsin. Məhz buna görə sadəcə “try/catch edib unutmaq” yox, idempotentlik və diqqətli xəta emalından danışırıq.

4. Xətaları model və UI üçün necə təsvir etmək və qaytarmaq

Düşüncə tərzində vacib dəyişiklik: xətanız — alət kontraktının bir hissəsidir, sadəcə console.error-də logladığınız şey deyil. Onunla həm model, həm də interfeys işləyəcək.

Xəta strukturu

Adətən sadə bir struktura sadiq qalmaq rahatdır:

type ToolError = {
  code: string;        // "VALIDATION_ERROR", "NO_RESULTS", "UPSTREAM_TIMEOUT"
  message: string;     // insan-oxunaqlı və ya model üçün yığcam
  retryable: boolean;  // yenidən cəhd etməyə dəyərmi
};

Və alət nəticəsini diskriminasiya olunan union-a bükmək olar:

type SuggestGiftsResult =
  | { ok: true; gifts: GiftCard[] }
  | { ok: false; error: ToolError };

MCP protokolunda “bu xəta” üçün ayrıca flaq olsa da, daxilən öz formatınıza sadiq qalmaq faydalıdır ki, həm UI, həm də model baş verəni eyni cür interpretasiya edə bilsin.

“Fail gracefully” strategiyası

Hər xoşagəlməz situasiyanı “sərt” xəta kimi rəsmiləşdirməyə ehtiyac yoxdur. Bəzən xəta qaytarmadan, boş nəticə və izahla cavab vermək daha faydalıdır.

Məsələn, hədiyyə tapılmadıqda, ok: true, boş gifts: [] və UI/model üçün noResultsReason kimi bir sahə qaytarmaq məntiqlidir, "NO_RESULTS" xəta əvəzinə. Onda model dialoqu davam etdirə bilər: “Bu büdcəyə uyğun heç nə tapmadım, büdcəni artırmaq və ya maraqları dəqiqləşdirmək istəyirsiniz?”

Əksinə, əgər xarici API tam yıxılıbsa, bu daha çox ok: false, code: "UPSTREAM_UNAVAILABLE" və retryable: true deməkdir ki, model sonra yenidən və ya başqa parametrlərlə cəhd edə bilsin.

Xatırladaq ki, 3-cü bölmədə dörd xəta qatı var. Validasiya xətaları adətən ok: false və retryable: false kimi gedir — model eyni arqumentlərlə təkrarlamamalıdır. “Heç nə tapılmadı” kimi biznes situasiyaları tez-tez ok: true boş nəticə və izahla verilir. Xarici xidmətlərin infrastruktur nasazlıqları — ok: false və retryable: true kimi, ki, model təhlükəsiz şəkildə yenidən yoxlaya bilsin. Platforma/şəbəkə xətaları isə ümumiyyətlə sizin kodunuzdan əvvəl və ya sonra baş verə bilər və praktikada tez-tez alətin təkrar çağırılması kimi görünür — məhz buna görə idempotentlik bu qədər vacibdir; indi bunun haqqında danışacağıq.

Daxili detalların sızmasına yol verməmək

Server kodunda error.toString()-i birbaşa cavaba ötürmək cazibədar görünə bilər. LLM alətləri üçün bu, yaxşı fikir deyil: dialoqa zibil düşəcək və həssas detallar (daxili servis URL-ləri, stack trace-lər, cədvəl adları) açıla bilər. Tövsiyə — istisnaları tutmaq və onları yığcam xəta kodları və səliqəli mesajlara çevirməkdir.

Minimal örtük nümunəsi:

try {
  const gifts = await loadGiftsFromCatalog(input);
  return { ok: true, gifts };
} catch (err) {
  console.error("suggest_gifts failed", err);
  return {
    ok: false,
    error: {
      code: "UPSTREAM_ERROR",
      message: "Catalog service is unavailable",
      retryable: true
    }
  };
}

Model səliqəli siqnal görür, UI — anlaşılan mətn görür, detallarsa loglarda qalır.

Xətanın vidjetdə göstərilməsi

React vidjeti baxımından vəzifə sadədir: ok-u yoxlamaq və əgər o false-dursa, dostyana mesaj və mümkün olduqda davam yolu göstərmək.

function GiftResults({ result }: { result: SuggestGiftsResult }) {
  if (!result.ok) {
    return (
      <div>
        <p>Hədiyyələri seçmək mümkün olmadı: {result.error.message}</p>
        {result.error.retryable && <p>Parametrləri dəyişməyə və ya sorğunu təkrarlamağa cəhd edin.</p>}
      </div>
    );
  }

  if (result.gifts.length === 0) {
    return <p>Bu şərtlərlə hədiyyə tapılmadı. Büdcəni və ya maraqları dəyişməyə cəhd edin.</p>;
  }

  return <GiftCardsList gifts={result.gifts} />;
}

Bu, məhz elə hallardandır ki, sadə və dürüst mesaj “nə isə səhv getdi” deməkdən UX-i xeyli yaxşılaşdırır.

Artıq razılaşdıq ki, bəzi xətaları retryable: true kimi açıq şəkildə işarələmək və istifadəçiyə “yenidən cəhd et” təklif etmək olar. Sistemə belə retry-lər (UI-də açıq, platforma tərəfində gizli) əlavə olunan kimi növbəti sual yaranır: eyni alət eyni məlumatlarla iki dəfə çağırılsa nə baş verəcək? Bu artıq idempotentlik hekayəsidir.

5. Idempotentlik: “daha bir eyni çağırış”dan qorunma

İndi ən maraqlı hissəyə keçək. Formal olaraq idempotentlik — eyni giriş məlumatları ilə təkrar çağırışın sistemin vəziyyətini və nəticəni dəyişməməsi xüsusiyyətidir. Sərt mənada bu həm təkrar yan təsirlərin olmamasına, həm də eyni cavaba aiddir. ChatGPT Apps praktikasında bizi ilk növbədə birincisi maraqlandırır: təkrar çağırışlar məlumatı korlamasın və yeni obyektlər yaratmasın, hətta cavabın özü bir az fərqlənsə belə.

ChatGPT Apps kontekstində idempotentlik — retry-lər, Regenerate və LLM-in qeyri-proqnozlaşdırılan davranışlarının hamısından qorunmadır.

Idempotentliyin xüsusilə vacib olduğu yerlər

Sadəcə oxuma alətləri adətən təhlükəsizdir: suggest_gifts-i eyni parametrlərlə nə qədər çağırsaq da, sadəcə daha bir hədiyyə siyahısı alacağıq. Hətta bir az fərqli olsa belə, bu, sistemin vəziyyətini dəyişmir və yan təsir yaratmır.

Kritik olanlar xarici sistemlərin vəziyyətini dəyişən alətlərdir:

  • sifarişin yaradılması (create_order);
  • ödənişin aparılması (charge_card, submit_payment);
  • məktub və bildirişlərin göndərilməsi (send_email, send_sms);
  • yan təsirli obyektlərin yaradılması (məsələn, bronlar).

Belə bir alət eyniylə bənzər arqumentlərlə ard-arda iki dəfə çağırılsa, dublikat sifarişlər, ikiqat silinmələr və digər “mühasibat sevinc”ləri ortaya çıxa bilər.

idempotency_key pattern-i

Klassik yanaşma: alətə əlavə idempotency_key parametrini — əməliyyatın sətir identifikatorunu — əlavə etmək. Əgər həmin açarla sorğu artıq uğurla emal edilibsə, server hərəkəti bir daha etmir, saxlanmış nəticəni qaytarır.

GiftGenius-də hipotetik create_checkout_session aləti üçün genişləndirilmiş sxema nümunəsi:

const CreateCheckoutSchema = {
  type: "object",
  properties: {
    giftId: {
      type: "string",
      description: "Seçilmiş hədiyyənin ID-si"
    },
    idempotency_key: {
      type: "string",
      description: "Dublikatlardan qorunmaq üçün əməliyyatın unikal açarı"
    }
  },
  required: ["giftId", "idempotency_key"]
} as const;

Serverdə handler təxminən belə edir:

async function createCheckoutSession(input: CreateCheckoutInput) {
  const existing = await db.checkoutSessions.findOne({ idempotencyKey: input.idempotency_key });
  if (existing) {
    return existing; // köhnə nəticəni qaytarırıq
  }

  const session = await paymentProvider.createSession({ giftId: input.giftId });
  await db.checkoutSessions.insert({ idempotencyKey: input.idempotency_key, session });
  return session;
}

Model nədənsə aləti eyni idempotency_key ilə ikinci dəfə çağırsa, istifadəçi ikinci ödəniş görməyəcək, sadəcə eyni checkout-u görəcək.

prepare və commit bölünməsi

Xüsusilə həssas hərəkətlər (ödənişlər, geri dönüşü olmayan dəyişikliklər) üçün tez-tez iki mərhələli yanaşma istifadə olunur: hazırlıq üçün ayrıca alət (prepare_*), commit üçün ayrıca alət (commit_*).

Məsələn:

  • prepare_order — məhsulun mövcudluğunu yoxlayır, qiyməti hesablayır, “sifariş qaralaması” qaytarır;
  • commit_order — qaralamanın ID-si üzrə real sifariş yaradır və ödənişi başladır.

Belə dizayn bir neçə bonus verir. Birincisi, ilk addımı tam idempotent etmək olur: eyni parametrlərlə təkrar prepare_order eyni qaralamanı qaytaracaq. İkincisi, commit_order-u yalnız istifadəçinin açıq təsdiqindən sonra çağırmağa icazə vermək olur ki, bu da həm UX, həm təhlükəsizlik baxımından rahatdır.

6. Alətlərin təhlükəsiz dizaynı

Idempotentlik vacibdir, amma tək ingredient deyil. Modelə verdiyiniz alətlər dəstinin dizaynı çox şeyi həll edir.

Ən az səlahiyyət prinsipi

Fikir sadədir: hər alət ssenari üçün lazım olanı etməli, artıq heç nə etməməlidir. Məsələn, aşağıdakı kimi bir do_anything_with_user_account funksiyası lazım deyil ki:

  • hər şeyi oxuya, yeniləyə və silə bilir;
  • operation sətri və payload JSON-u kimi “bəxtə-bəxt” parametr qəbul edir.

Daha yaxşısı, ayrıca, aydın təsvir olunmuş alətlərə sahib olmaqdır:

  • get_user_profile;
  • update_user_preferences;
  • create_order;
  • cancel_order.

GiftGenius üçün də eyni məntiq: suggest_gifts yalnız variantları seçir; create_checkout_session sifarişi ləğv etməyi və ya istifadəçinin e‑mail ünvanını dəyişməyi “bilməməlidir”.

“read” və “write” alətlərinin ayrılması

Yaxşı pattern — yalnız oxuyan alətlərlə (read) nəyisə dəyişən alətləri (write) aydın ayırmaqdır. Hədiyyə kataloquna sorğu (search_products, suggest_gifts) model sui-istifadə etsə belə, özü-özlüyündə təhlükəsizdir. Amma create_order və ya charge_payment artıq daha diqqətli yanaşma tələb edir.

Belə alətlərin təsvirində nə etdiklərini və hansı kontekstdə çağırıla biləcəyini açıq yazmağa dəyər. Məsələn:

{
  "name": "create_checkout_session",
  "description": "Bir hədiyyə üçün yeni ödəniş sessiyası yaradır. Yalnız istifadəçi seçimini açıq şəkildə təsdiqlədikdən sonra çağır.",
  "parameters": { /* ... */ }
}

Bu, 100% müdafiə deyil (LLM yenə də səhv edə bilər), amma ən azından risklər barədə aydın siqnal verirsiniz.

Human-in-the-loop və təsdiqlər

Həqiqətən “təhlükəli” hərəkətlər üçün təsdiqli ssenari qurmaq rahatdır. Məsələn, model:

  1. Əvvəlcə alış üçün məlumatı hazırlayan və UI üçün əlverişli formada qaytaran aləti çağırır (hədiyyənin adı, qiyməti, çatdırılma ünvanı).
  2. Platforma istifadəçiyə “Təsdiqlə” düyməsi olan vidjet göstərir.
  3. Yalnız düymə klikindən sonra real ödənişi edən commit aləti çağırılır.

Beləcə, model qəfil “sakitcə” sifariş rəsmiləşdirə bilmir, hətta bunu “çox ağıllı çözüm” hesab etsə belə.

Risk semantikası təsvirlərdə və annotasiyalarda

Bəzi platforma versiyalarında destructiveHint kimi xüsusi annotasiyalar ortaya çıxır; onlar alətin geri dönüşü olmayan hərəkətlər edə biləcəyini siqnal verir. Hətta belə sahələr olmasa və ya stabil olmasa belə, bu semantikanı birbaşa description-a və parametr adlarına yerləşdirə bilərsiniz.

Məsələn, belə etməkdənsə:

{
  "name": "delete_user_data",
  "description": "İstifadəçi məlumatlarını silir."
}

belə edin:

{
  "name": "request_user_data_deletion",
  "description": "Xidmət siyasətinə uyğun olaraq istifadəçi hesabını şəxsi məlumatlarının silinməsi üçün işarələyir. Yalnız istifadəçi açıq şəkildə silməyi tələb etdikdən sonra istifadə et."
}

Və bunun ətrafında insan yönümlü təsdiqedici UX qurun.

7. GiftGenius üçün kiçik praktik təkmilləşdirmə

Bütün bunları hədiyyə seçimi üçün olan tədris tətbiqimiz GiftGenius ilə bağlayaq. Fərz edək ki, GiftGenius-ə daha bir alət — create_checkout_session — əlavə edirik ki, istifadəçi yalnız hədiyyə seçməsin, həm də rəsmiləşdirməyə keçə bilsin.

JSON Schema və təhlükəsizlik baxımından aşağıdakıları edirik.

Birincisi, idempotency_key əlavə edirik və səliqəli təsvir yazırıq:

const CreateCheckoutTool = {
  name: "create_checkout_session",
  description:
    "Seçilmiş bir hədiyyə üçün ödəniş sessiyası yaradır. " +
    "İstifadəçi bu hədiyyəni almaq istədiyini açıq şəkildə təsdiqlədikdən sonra çağır.",
  parameters: {
    type: "object",
    properties: {
      gift_id: {
        type: "string",
        description: "suggest_gifts nəticəsindən hədiyyənin identifikatoru."
      },
      idempotency_key: {
        type: "string",
        description: "Əməliyyatın unikal açarı. Təkrar çağırışda eyni açardan istifadə et."
      }
    },
    required: ["gift_id", "idempotency_key"]
  }
} as const;

İkincisi, serverdə idempotent handler reallaşdırırıq:

async function handleCreateCheckout(input: CreateCheckoutInput) {
  const existing = await db.checkout.findOne({ idempotencyKey: input.idempotency_key });
  if (existing) {
    return { ok: true, checkout: existing };
  }

  const checkout = await payments.createSession({ giftId: input.gift_id });
  await db.checkout.insert({ idempotencyKey: input.idempotency_key, ...checkout });

  return { ok: true, checkout };
}

Üçüncüsü, xətaları nəzərə alırıq:

try {
  return await handleCreateCheckout(input);
} catch (err) {
  console.error("create_checkout_session failed", err);
  return {
    ok: false,
    error: {
      code: "PAYMENT_PROVIDER_ERROR",
      message: "Ödəniş sessiyasını yaratmaq mümkün olmadı. Daha sonra cəhd edin.",
      retryable: true
    }
  };
}

Vidjetdə isə anlaşılan xəta vəziyyəti göstəririk və, bəlkə də, UI səviyyəsində “Təkrarla” düyməsi qoyuruq ki, model ilə yeni dialoqu başlatsın.

Beləliklə, addım-addım, bu sadə tədris layihəsi “demo oyuncağı” olmaqdan çıxıb, nəzəri cəhətdən prodaksiyaya buraxıla biləcək bir şeyə çevrilir.

8. Alətlərdə xətalar və idempotentliklə işləyərkən tipik səhvlər

Səhv №1: Xəta = sadəcə throw və 500.
Əgər istənilən nasazlıqda alətiniz sadəcə istisna atırsa və bu, “nə isə səhv oldu”ya çevrilirsə, model və UI məlumatdan məhrum qalır. Model eyni arqumentlərlə təkrar çağırıb-çağırmamağı anlamır, istifadəçi isə növbəti addımı bilmir. Daha yaxşısı — retryable əlaməti olan strukturlaşdırılmış xəta kodu və qısa mesaj qaytarmaq, detalları isə serverdə loglamaqdır.

Səhv №2: Xəta tipləri arasında fərq qoymamaq.
Validasiya, biznes və infrastruktur xətalarını bir qazana atmaq pis fikirdir. Nəticədə “heç nə tapılmadı” situasiyası model və istifadəçi üçün “baza yıxıldı” kimi görünür. Bu, UX-i korlayır və modelin adekvat reaksiyasına mane olur: sorğunu dəyişdirməyi təklif etmək əvəzinə “bağışlayın, servis xarabdır” rejiminə düşür. Bu, xüsusilə 3-cü bölmədəki kimi biznes xətaları ilə infrastruktur xətalarını qarışdıranda ağrılıdır.

Səhv №3: Retry dünyasında idempotent olmayan əməliyyatlar.
create_order alətini guya həmişə dəqiq bir dəfə çağıracaqlarmış kimi layihələndirmək dublikat sifarişlərə birbaşa yoldur, xüsusən istifadəçi aktiv şəkildə Regenerate-ə basanda və ya əlaqə yarı yolda qırılanda. Əgər alətdə yan təsirlər varsa, demək olar ki, həmişə idempotency_key əlavə edib nəticələri saxlamaq lazımdır ki, təkrar çağırış yeni obyektlər yaratmasın.

Səhv №4: Bir monstr “universal” alət.
Bəzən tərtibatçılar action parametri ilə hər şeyi bacaran super alət yazmağa çalışırlar: axtarır, yaradır, dəyişir və silir. LLM üçün bu, davranışı demək olar ki, zəmanətli şəkildə qeyri-proqnozlaşdırılan edir: model nə zaman nəyi çağırmağı çətin öyrənir və xətaların nəticələri daha ağır olur. Düzgünü — mümkün qədər kiçik, aydın təsvir olunmuş, read-only alətlərə bölmək və ayrıca — təsdiqlərlə səliqəli tərtib edilmiş mutating alətlər yaratmaqdır.

Səhv №5: Daxili detalların cavablara sızması.
Modelə və UI-yə xam stack trace və ya istisnaların tam mətnini atmaq — tipik mühəndis tənbəlliyidir. Bu, istifadəçi üçün narahatdır, sistemin daxili quruluşunu aça bilər və modelə düzəlməyə kömək etmir. İstisnaları tutub yığcam kodlara və sadə mesajlara mapləyin, bütün detallar isə loglarda və monitorinq sistemində qalsın.

Səhv №6: Xətaları vidjet UX-i ilə bağlamamaq.
Çox vaxt server tərəfi səliqəli xəta kodları qaytarır, amma UI vidjeti sonsuz spinner və ya boş blok vəziyyətində ilişib qalır. İstifadəçi “heç nə olmadı” görür, model isə tool-call tamamlanıb deyə dialoqa sanki heç nə olmamış kimi davam edir. Bunun əvəzinə ayrıca error və empty vəziyyətləri düşünün, insana anlaşılan mesajlar göstərin və mümkün olduqda hərəkət variantları (parametrləri dəyişmək, sonra cəhd etmək) təklif edin.

Səhv №7: Ən az səlahiyyət prinsipini görməzdən gəlmək.
İdempotentlik və yaxşı xəta emalını etmiş olsanız belə, execute_sql_anywhere kimi hər şeyi bacaran alət təsviri saxlamısınızsa, risk çox böyükdür. LLM onu səhv kontekstdə və ya yanlış parametrlərlə çağıra bilər. Hər bir alət maksimum dərəcədə dar olmalı və bir aydın əməliyyatı yerinə yetirməlidir — xüsusən pul və ya istifadəçinin şəxsi məlumatları barədə danışırıqsa.

1
Tapşırıq
ChatGPT Apps, səviyyə, dərs
Bağlanıb
İdemponent add_favorite_jet aləti idempotency_key ilə
İdemponent add_favorite_jet aləti idempotency_key ilə
1
Tapşırıq
ChatGPT Apps, səviyyə, dərs
Bağlanıb
İki mərhələli “Request quote”: prepare + commit, xətalar + idemponentlik + təsdiq
İki mərhələli “Request quote”: prepare + commit, xətalar + idemponentlik + təsdiq
1
Sorğu/viktorina
, səviyyə, dərs
Əlçatan deyil
App alətləri və callTool
App alətləri və callTool: UI ↔ backend əlaqəsi
Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION