CodeGym /Kurslar /ChatGPT Apps /Qum qutusunda iş: məhdudiyyətlər, nüanslar və window.open...

Qum qutusunda iş: məhdudiyyətlər, nüanslar və window.openai

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

1. Qum qutusu nədir və niyə vidjetiniz «qəfəsdədir»

ChatGPT sizin vidjetinizi göstərərkən onu heç də adi <iframe src="https://sizin-saytiniz"> kimi render etmir. Vidjet idarə olunan «qum qutusunda» — ayrıca origin-ə və sərt təhlükəsizlik parametrlərinə malik izolyasiya olunmuş iframe-də işə düşür.

Texniki olaraq bu təxminən belə görünür:

flowchart TD
    User["İstifadəçi ChatGPT-də"]
    Chat["ChatGPT UI + model"]
    Iframe["Sizin vidjetiniz
sandboxed iframe"] MCP["Sizin MCP / backend"] User --> Chat Chat -->|tool çağırışı| MCP MCP -->|structuredContent + _meta| Chat Chat -->|window.openai.*| Iframe Iframe -->|callTool / follow-up| Chat Chat --> MCP

Sizin kod yalnız bu iframe daxilində icra olunur, qalan dünyaya çıxış isə hostun (ChatGPT) təqdim etdiyi dar nəzarət olunan API vasitəsilə gedir. Vidjet etməməlidir:

  • ChatGPT-nin özünü sındırmaq (DOM, stillər, performans);
  • istifadəçinin məxfiliyini pozmaq;
  • şəbəkəyə nəzarətsiz çıxmaq.

Buradan qum qutusunun əsas məhdudiyyətləri çıxır.

DOM və origin izolyasiyası

Vidjet qum qutusu üçün xüsusi domenə (məsələn, https://sandbox-apps.oaiusercontent.com) yerləşdirilir və iframe-də sandbox atributu olur. Bu o deməkdir ki:

  • window.parent və ya ChatGPT-nin document-inə girməyiniz mümkün deyil — SecurityError alacaqsınız;
  • postMessage kimi kross-domen mexanizmlər host tərəfindən idarə olunur;
  • «ChatGPT interfeysini CSS-lə düzəldək» cəhdləri uğursuz olacaq.

Şəbəkə və CSP məhdudiyyətləri

Brauzer və hostun CSP siyasəti vidjetiniz üçün şəbəkə çıxışını məhdudlaşdırır:

  • fetch metodu yalnız review-dan keçmiş whitelist-dəki domenlərə çıxış əldə edə bilər;
  • vidjetdən müraciət edə biləcəyiniz domenləri MCP cavablarında openai/widgetCSP vasitəsilə açıq şəkildə bəyan edirsiniz; əks halda sorğular keçməyəcək;
  • tövsiyə edilən yol: ciddi işlər üçün ümumiyyətlə vidjetdən şəbəkəyə çıxmayın, backend-ə MCP alətləri və callTool vasitəsilə gedin (buna Modul 4-də daha ətraflı baxacağıq).

Praktik olaraq: vidjeti nazik UI qatı kimi düşünün. O, ChatGPT və serverinizlə sərt müəyyən edilmiş kanallar üzərindən danışır, internetdə sərbəst yaşayan adi SPA kimi yox.

Yaddaş və resurslar

Yerli saxlanclar (localStorage, sessionStorage) sizin üçün əlçatandır, amma cookie — yox. Tətbiqi hazırlayarkən bunu nəzərə alın. Yaddaş və CPU məhduddur: əgər vidjet daxilində bir milyarda qədər sadə ədədləri hesablamaq qərarına gəlsəniz, host iframe-i sadəcə dayandıra bilər.

Vacib nəticə: vidjetdə ağır hesablamalar və uzunömürlü «keşlər» olmaz. Mürəkkəb məntiq — server tərəfində, React komponentində deyil.

2. window.openai: vidjetlə ChatGPT arasında körpü

Vidjetin nəsə öyrənməsi (alət nəticələri, görüntü rejimi, lokal, vəziyyət) üçün ChatGPT inisializasiya zamanı iframe pəncərəsinə bir qlobal obyekt yerləşdirir — window.openai.

Bu nə sizin kodunuz, nə də npm paketidir, bu host object-dir, onu İİ platformasının özü təqdim edir. Qapalı tərəfdə host ilə iframe arasında hadisələr və mesajlaşma əsasında işləyir, amma bunun haqqında demək olar ki, düşünməyiniz lazım deyil. Vacib bir neçə məqam var.

window.openai kim və nə zaman yaradılır

window.openai yalnız:

  • ChatGPT sizin vidjetiniz üçün yaratdığı həmin iframe daxilində olur;
  • HTML şablonu düzgün mimeType ilə (text/html+skybridge) verilib və bütün yoxlamalardan keçib.

Bu növü HelloWorld App modulu haqqında danışanda görmüşdünüz — məhz bu, vidjet səhifəsinin adi text/html əvəzinə qaytardığı dəyərdir.

Əgər vidjet səhifəsini brauzerdə birbaşa açırsınızsa:

console.log(window.openai); // undefined

— bu normaldır. Odur ki, lokal inkişaf və ya storybook üçün «standalone» rejimə bel bağlayırsınızsa, vidjet kodunda obyektin mövcudluğunu həmişə yoxlamağa dəyər.

Sadə nümunə (final deyil, sadəcə illüstrasiya):

if (typeof window !== "undefined" && (window as any).openai) {
  console.log("We are inside ChatGPT sandbox!");
}

İnsi­alizasiya asinxronluğu

Pərdə arxasında ChatGPT yeni məlumatlar gəldikcə (toolOutput yenilənəndə, displayMode dəyişəndə və s.) window.openai-i openai:set_globals adlı daxili hadisə ilə yeniləyir.

Yəni oradakı «dəyərlər» statik deyil: İİ modeli MCP alətini çağıra bilər, backend yeni structuredContent qaytarar və window.openai.toolOutput React komponentiniz işləyən anda dəyişə bilər.

Buradan iki tövsiyə:

  1. Aşağıdakı kimi «kor» snapshot-lar etməyin: const toolOutput = window.openai.toolOutput bir dəfə başlanğıcda götürüb onun əbədi qalacağını düşünmək olmaz. Eyni vidjet ChatGPT tərəfindən təkrar istifadə oluna bilər.
  2. Dəyişikliklərə abunə ola bilən hook qatından (bir azdan) istifadə edin.

3. window.openai anatomiyası: məlumatlar, API və kontekst

Rəsmi sənədlərdə window.openai üçün sahələr və metodlar barədə kifayət qədər kompakt cədvəl var. Gəlin bunu daha «insani» formada yığaq.

Əsas sahələr və metodlar

window.openai = {
  // Vəziyyət & məlumatlar
  toolInput,               // JSON: İİ sizin MCP-tool-a ötürdüyü parametrlər
  toolOutput,              // JSON: MCP-tool-un İİ-yə qaytardığı parametrlər
  toolResponseMetadata,    // MCP-tool cavabının bir hissəsi: _meta: {...}
  widgetState,             // Vidjetin yadda saxlanmış vəziyyətini oxumaq olar
  setWidgetState,          // Vidjetinizin vəziyyətini burada saxlamaq olar

  // İcra zamanı API-lər
  callTool,                // MCP-tool çağırmaq olar
  sendFollowUpMessage,     // Çatda İİ-yə gizli mesaj yazmaq: cavab verməyə başlayacaq
  requestDisplayMode,      // Vidjeti başqa bir rejimə keçirməyi xahiş et: fullscreen, pip, inline
  requestModal,            // Vidjeti modal pəncərəyə çevirmək.
  requestClose,            // Vidjeti bağlayır. Modal bağlananda — yenidən vidjet olur.
  requestCheckout,         // Ödəniş üçün modal pəncərə açır. Server ACP-ni reallaşdırmalıdır
  notifyIntrinsicHeight,   // Vidjetin hündürlüyünün dəyişdiyini bildirin
  openExternal,            // Keçidi yeni pəncərədə açmaq

  // Kontekst
  theme,                   // Tünd və ya açıq mövzu
  displayMode,             // Vidjetin cari görüntü rejimi, requestDisplayMode-dan fərqli ola bilər
  maxHeight,               // Vidjet üçün icazə verilən maksimal hündürlük
  safeArea,                // "Təhlükəsiz render sahəsi" — çentiqli telefonlar üçün aktualdır
  view,
  userAgent,               // Brauzerin userAgent-i
  locale                   // Brauzerin locale-i
}

Eyni məlumatı cədvəldə:

Kateqoriya Xüsusiyyət / metod Nəyə lazımdır
State & data
toolInput
Alətin çağırıldığı arqumentlər. Yalnız oxunur.
State & data
toolOutput
Sizin MCP cavabından gələn structuredContent. Vidjet və modelin gördüyü şey.
State & data
toolResponseMetadata
Cavabdan _meta. Yalnız vidjetə görünür, model bunu oxumur.
State & data
widgetState
ChatGPT-nin vidjet renderləri arasında saxladığı UI vəziyyətinin snapshot-ı.
State & data
setWidgetState(state)
widgetState-in yeni snapshot-ını sinxron saxlayır.
Function
callTool(name, args)
Vidjetdən MCP alətini çağırmaq.
Function
sendFollowUpMessage({prompt})
ChatGPT-dən vidjet adından çata mesaj göndərməsini xahiş etmək. O, cavab verməyə başlayacaq.
Function
requestDisplayMode(...)
Hostdan inline / fullscreen / pip xahiş etmək.
Function
requestModal({title})
Modal pəncərə açmağı xahiş etmək.
Function
notifyIntrinsicHeight()
Məzmunun hündürlüyü dəyişdiyini bildirmək.
Function
requestCheckout(...)
ACP protokolu üzrə ödəniş dialoqunu açır.
Function
openExternal({href})
Xarici linki istifadəçinin brauzerində açmaq.
Context
theme, displayMode, maxHeight, safeArea, view, userAgent, locale
Mühit siqnalları: mövzu, rejim, əlçatan hündürlük, lokal və s.

Hamısını dərhal əzbərləmək lazım deyil — bunu «xəritə» kimi qəbul edin. İndi bunu «arayış» kimi yox, normal insan kimi başa düşək.

toolInput və toolOutput: məlumatlar haradan gəlir

Model alətinizi çağırmaq qərarına gələndə JSON arqumentləri formalaşdırır. Bu arqumentlər:

  • MCP serverinə handler-də input kimi gəlir;
  • eyni zamanda vidjetdə window.openai.toolInput-a düşür.

Alət yerinə yetirildikdən sonra server qaytarır:

  • structuredContent — UI üçün strukturlaşdırılmış məlumatlar;
  • _meta — yalnız vidjet üçün özəl məlumatlar;
  • content — modelin özünün istifadəçiyə «nə baş verdiyini danışması» üçün mətn.

structuredContent olur window.openai.toolOutput, _meta isə olur window.openai.toolResponseMetadata.

Mini nümunə (vanilla JS, React-siz):

const root = document.getElementById("root");

// Nullish operatorundan təhlükəsiz istifadə edə bilərsiniz
const gifts = window.openai.toolOutput?.gifts ?? [];

root.textContent = `Tapılan hədiyyələrin sayı: ${gifts.length}`;

widgetState və setWidgetState: vidjetin yaddaşı

widgetState — platformanın renderlər və hətta dialoqun ayrı gedişləri arasında sizin UI barədə yadda saxlamağa hazır olduğu şeydir.

widgetState üçün uyğun nümunələr:

  • seçilmiş hədiyyə;
  • cari çeşidləmə (qiymətə görə / populyarlığa görə);
  • listdəki səhifə nömrəsi.

Uyğunsuz nümunələr:

  • kənar API-nin xam cavabı;
  • base64 şəklində təsvir;
  • gizli tokenlər.

İki şeyi yadda saxlayın:

  1. widgetState kontekstlə birlikdə modelə də ötürülür, ona görə oraya həssas heç nə qoymuruq.
  2. Həcm məhduddur (təxminən 4 min token), odur ki, onu mini-bazaya çevirmirik.

Ən sadə istifadə nümunəsi (hook-suz, vanilla JS):

const current = window.openai.widgetState ?? { selectedGiftId: null };

function selectGift(id) {
  window.openai.setWidgetState({ ...current, selectedGiftId: id });
}

Real kodda bunu React hook-ları ilə qablaşdıracağıq.

Runtime API: callTool, sendFollowUpMessage və digərləri

Bu metodlar vidjetin təkcə «çəkilməsinə» deyil, həm də dialoq və serverlə qarşılıqlı əlaqəsinə imkan verir.

Tipik ssenarilər:

  • callTool("search_gifts", { budget: 50 }) — istifadəçi «Büdcəni dəyiş» düyməsini basır, siz serveri çağırıb UI-ni yeniləyirsiniz;
  • sendFollowUpMessage({ prompt: "Daha bahalı ideyalar göstər" }) — istifadəçidən mətn yazmasını xahiş etmək əvəzinə follow-up düyməsi əlavə edirsiniz, o da çatda yeni mesaj yaradır;
  • requestDisplayMode({ mode: "fullscreen" }) — inline rejim dar gəlibsə, vidjet nəzakətlə ChatGPT-dən tam ekrana keçməyi xahiş edə bilər;
  • openExternal({ href: "https://myshop.com/checkout?giftId=123" }) — istifadəçini təsdiqlənmiş kanalla xarici sayta (checkout, profil və s.) yönləndirmək.

Bunların hamısı birbaşa internetə yox, ChatGPT üzərindən «xət»lə gedir.

Mühit konteksti: mövzu, rejim, hündürlük, lokal

theme, displayMode, maxHeight, locale kimi sahələr vidjetin hansı mühitdə yaşadığını anlamağa kömək edir.

Nümunə:

const theme = window.openai.theme;          // "light" və ya "dark"
const mode = window.openai.displayMode;     // "inline" | "fullscreen" | "pip"
const maxH = window.openai.maxHeight;       // əlçatan hündürlük
const locale = window.openai.locale;        // "en-US", "de-DE", ...

Bu siqnallarla siz:

  • rəngləri və boşluqları mövzuya görə tənzimləyə bilərsiniz;
  • rejimdən asılı olaraq layout-u dəyişə bilərsiniz (inline vs fullscreen);
  • UI yazılarını istifadəçinin dilinə uyğunlaşdırabilirsiniz (bu mövzuya ayrıca modulda baxacağıq).

Platforma sizə nə qədər yer olduğu, hansı mövzu və lokalın aktiv olduğu barədə siqnallar verir. Onları useOpenAIGlobal, useDisplayMode, useMaxHeight və digər hook-lar vasitəsilə istifadə etmək məntiqlidir ki, vidjet ChatGPT-də «doğma» görünsün.

4. window.openai üzərində hook-lar: qlobal obyektə əllə toxunmuruq

window.openai-ə birbaşa çıxış prototip üçün rahatdır, amma tezliklə kodu xaosa çevirir: hadisələrə abunələr, undefined yoxlamaları, təkrarlanan qablaşdırmalar. Məhz buna görə Apps SDK üçün Next.js şablonunda detalları gizlədən və hər şeyi reaktiv edən hazır React hook dəsti var.

Tipik hook indeks faylı belə görünür:

// app/hooks/openai/index.ts
export { useCallTool } from "./use-call-tool";
export { useSendMessage } from "./use-send-message";
export { useOpenExternal } from "./use-open-external";
export { useRequestDisplayMode, useRequestModal, useRequestClose } from "./use-request-display-mode";
export { useRequestCheckout } from "./use-request-checkout";

// State hooks
export { useDisplayMode } from "./use-display-mode";
export { useWidgetProps } from "./use-widget-props";
export { useWidgetState } from "./use-widget-state";
export { useOpenAIGlobal } from "./use-openai-global";

export { useMaxHeight } from "./use-max-height";
export { useIsChatGptApp } from "./use-is-chatgpt-app";

Adlar və dəqiq yol sizin şablonda bir qədər fərqlənə bilər, amma fikir eynidir: komponentdə window.openai.* yerinə hook-lardan istifadə edirsiniz. Gələn əsaslarını baxaq.

useWidgetProps: alətin girişi və çıxışı

useWidgetProps adətən vidjetə lazım olan məlumatlarla obyekt qaytarır: toolInput, toolOutput, toolResponseMetadata və bəzən isLoading kimi əlavə flag-lər.

Nümunə:

import { useWidgetProps } from "../hooks/openai";

type Gift = { id: string; title: string; price: number };

export function GiftList() {
  const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
  const gifts = toolOutput?.gifts ?? [];

  if (!gifts.length) {
    return <div>Hələ hədiyyə variantı yoxdur.</div>;
  }

  return (
    <ul>
      {gifts.map((g) => (
        <li key={g.id}>{g.title} — ${g.price}</li>
      ))}
    </ul>
  );
}

Komponent kodunda heç bir window.openai yoxdur — və bu yaxşıdır.

useWidgetState: widgetState üzərində «reaktiv qablaşdırma»

useWidgetState widgetState ilə adi React state-i kimi işləməyə imkan verir: [state, setState] alırsınız, hook isə pərdə arxasında bunu window.openai.widgetStatesetWidgetState ilə sinxronlaşdırır.

Nümunə:

import { useWidgetState } from "../hooks/openai";

type UiState = { selectedGiftId: string | null };

export function SelectedGiftIndicator() {
  const [uiState, setUiState] = useWidgetState<UiState>(() => ({
    selectedGiftId: null,
  }));

  if (!uiState?.selectedGiftId) {
    return <div>Hələ hədiyyə seçilməyib.</div>;
  }

  return (
    <div>
      Siz id={uiState.selectedGiftId} olan hədiyyəni seçdiniz
      <button onClick={() => setUiState({ selectedGiftId: null })}>
        Sıfırla
      </button>
    </div>
  );
}

Klikdən sonra setUiState təkcə React state-i yeniləməyəcək, həm də yeni vəziyyəti ChatGPT tərəfində saxlayacaq.

useOpenAIGlobal: window.openai-dən istənilən sahəyə çıxış

Əgər bir qlobal sahəyə (məsələn, mövzuya və ya rejimə) çıxış lazımdırsa, universal useOpenAIGlobal(key) hook-u var. O, openai:set_globals hadisəsinə abunə olur və həmişə aktual dəyəri qaytarır.

Nümunə:

import { useOpenAIGlobal } from "../hooks/openai";

export function ThemeAwareBlock() {
  const theme = useOpenAIGlobal<"light" | "dark">("theme");

  const background = theme === "dark" ? "#222" : "#fff";
  const color = theme === "dark" ? "#fff" : "#000";

  return <div style={{ background, color }}>Mən ChatGPT mövzusuna uyğunlaşıram</div>;
}

useCallTool, useSendMessage, useOpenExternal və digərləri

  • useCallTool(name) — göstərilən adlı MCP alətini çağıran funksiya qaytarır. Bu, callTool üzərində qablaşdırmadır.
  • useSendMessage() — vidjetin mesajları təşəbbüs etməsi üçün sendFollowUpMessage üzərində qablaşdırma.
  • useOpenExternal() — openExternal({ href }) ətrafında rahat köməkçi.
  • useRequestDisplayMode() və useRequestModal() — rejim dəyişikliyi / modal açılması üçün qablaşdırmalar.

Demək olar ki, hər şeyi birdən istifadə edən kiçik GiftGenius vidjeti üçün baza nümunəsi:

import {
  useWidgetProps,
  useWidgetState,
  useCallTool,
  useSendMessage,
  useOpenExternal,
} from "../hooks/openai";

type Gift = { id: string; title: string; url: string; price: number };

export function GiftWidget() {
  const { toolOutput } = useWidgetProps<{ gifts: Gift[] }>();
  const gifts = toolOutput?.gifts ?? [];

  const [ui, setUi] = useWidgetState<{ selectedId: string | null }>(() => ({
    selectedId: null,
  }));

  const callSearch = useCallTool("search_gifts");
  const sendMessage = useSendMessage();
  const openExternal = useOpenExternal();

  if (!gifts.length) {
    return <div>Hələ ideya yoxdur. Nəticələri yeniləməsi üçün GPT-dən xahiş edin.</div>;
  }

  return (
    <div>
      {gifts.map((g) => (
        <button
          key={g.id}
          style={{
            display: "block",
            fontWeight: ui?.selectedId === g.id ? "bold" : "normal",
          }}
          onClick={() => setUi({ selectedId: g.id })}
        >
          {g.title} — ${g.price}
        </button>
      ))}

      <div style={{ marginTop: 12 }}>
        <button
          onClick={() =>
            sendMessage({ prompt: "Mövcudlardan daha bahalı hədiyyələr göstərin." })
          }
        >
          Daha çox ideya istə
        </button>

        <button
          onClick={async () => {
            await callSearch({ budget: 200 });
          }}
        >
          Büdcə $200 ilə yenilə
        </button>

        {ui?.selectedId && (
          <button
            onClick={() =>
              openExternal({
                href: `https://giftgenius.example.com/checkout?id=${ui.selectedId}`,
              })
            }
          >
            Alışa keç
          </button>
        )}
      </div>
    </div>
  );
}

Bu səhifə hələ xamdır (növbəti modullarda UX, xəta emalı və s. əlavə edəcəyik), amma yanaşmanı artıq göstərir: heç bir birbaşa window.openai müraciəti yoxdur, yalnız hook-lar var.

5. Praktika: qum qutusunu və window.openai-ni araşdırırıq

«Vidjet adi sayt deyil» hissini qavramaq üçün bir neçə məşqi etmək faydalıdır.

Tapşırıq: «Mühiti yoxla»

Vidjetinizdə cari app/page.tsx-ə ilk renderdə sadə effekt əlavə edin:

import { useEffect } from "react";
import { useIsChatGptApp } from "../hooks/openai";

export default function Root() {
  const isChatGpt = useIsChatGptApp();

  useEffect(() => {
    if (typeof window !== "undefined") {
      console.log("window.origin =", window.origin);
      console.log("window.openai =", (window as any).openai);
    }
  }, []);

  return (
    <main>
      <h1>GiftGenius widget</h1>
      <p>ChatGPT daxilində işə salınıb: {String(isChatGpt)}</p>
    </main>
  );
}

DevTools-u açın: ya birbaşa ChatGPT pəncərəsində (əgər tunel viewer buna icazə verirsə), ya da səhifəni birbaşa açarkən lokal brauzerdə. Hər iki halda müqayisə edin:

  • adi brauzerdə işə salınanda isChatGptApp false olacaq, window.openai isə çox güman undefined;
  • ChatGPT vasitəsilə işə salınanda toolInput, toolOutput, theme və s. sahələri olan obyekt görəcəksiniz.

Bu, yaxşı bir intuitiv hissdir: eyni React kodu mühitdən asılı olaraq fərqli davranır, və məhz bunun üçün hook-lar düşünüb tapılıb.

Tapşırıq: «Platformanın verdiyi hər şeyi çıxar»

Müvəqqəti debug komponenti əlavə edin:

import { useWidgetProps, useOpenAIGlobal } from "../hooks/openai";

export function DebugPanel() {
  const { toolInput, toolOutput, toolResponseMetadata } = useWidgetProps();
  const theme = useOpenAIGlobal("theme");
  const displayMode = useOpenAIGlobal("displayMode");

  return (
    <pre style={{ fontSize: 10, maxHeight: 200, overflow: "auto" }}>
      {JSON.stringify(
        { toolInput, toolOutput, toolResponseMetadata, theme, displayMode },
        null,
        2
      )}
    </pre>
  );
}

Və müvəqqəti olaraq əsas UI-nin altına <DebugPanel /> daxil edin. Beləliklə, açıq-aydın görəcəksiniz:

  • toolOutput-a MCP-dən konkret hansı sahələrin gəldiyini;
  • _meta-da nə olduğunu (məsələn, locale, userLocation və s.);
  • vidjeti böyüdəndə displayMode-un necə dəyişdiyini.

Sonradan bu komponenti silə və ya, məsələn, DEBUG_WIDGET kimi flag ilə idarə oluna bilən saxlaya bilərsiniz.

6. Münasibətlər: ChatGPT ↔ vidjet ↔ MCP/server

Vidjetə «sistemin baş iştirakçısı» kimi yanaşmamaq üçün rolları bir daha dəqiq qeyd etmək faydalıdır.

  1. İstifadəçi mesaj yazır: «Qız üçün hədiyyə seç, büdcə 50$».
  2. ChatGPT modeli sizin MCP alətiniz search_gifts-i { recipient: "girlfriend", budget: 50 } arqumentləri ilə çağırmağa qərar verir.
  3. MCP server biznes məntiqini yerinə yetirir, qaytarır:
    • model üçün qısa təsvirlə content;
    • hədiyyələr massivini əhatə edən structuredContent;
    • texniki detallarla _meta (məs., source və valyuta).
  4. ChatGPT:
    • istifadəçiyə mətn mesajını göstərir («Bir neçə variant tapdım...»);
    • vidjet-iframe yaradır və ora structuredContent_meta-nı window.openai.toolOutputtoolResponseMetadata vasitəsilə ötürür.
  5. Sizin vidjet:
    • toolOutput-a əsasən UI-ni render edir;
    • interaksiyalar zamanı callTool çağırır və ya follow-up göndərir;
  6. Model bundan sonra bu hərəkətlərin nəticələri ilə nə edəcəyinə qərar verir.

Bütün bunlar vacib fikrə gətirir: vidjet prosesi heç vaxt tək idarə etmir. O — model və MCP serverdən ibarət ekosistemdə yaşayan UI qatıdır. Mürəkkəb işlər (autentifikasiya, məxfi məlumatlara çıxış, ciddi biznes məntiqi) server tərəfində qalmalıdır. Vidjet isə rahat interfeys və istifadəçi ilə səliqəli ünsiyyət üçün cavabdehdir.

7. Qum qutusunda qaydalar və siyasətlər

İzolyasiya olunmuş iframe və window.openai sadəcə «belə» mövcud deyil, təhlükəsizlik və məxfilik tələblərinə görə var. OpenAI-nin rəsmi qaydaları bir neçə prinsipi vurğulayır.

Birincisi, məlumatların minimallaşdırılması. Vidjet vasitəsilə istifadəçidən mümkün qədər çox PII (personally identifiable information) toplamağa çalışmamalısınız. Həqiqətən vacib olan hər şey alətlərdə dəqiq təsvir olunmalıdır və həm model, həm də təhlükəsizlik qatı belə çağırışları diqqətlə izləyəcək.

İkincisi, gizli izləmə və fingerprinting qadağandır. İstifadəçinin cihazını «gizlicə izləyən», brauzerin barmaq izlərini toplayan və məhdudiyyətləri dolanan sistemlər qurmaq olmaz. userAgent, userLocation və s. parametrlər — UX üçün ipucudur, autentifikasiya və ya identifikasiya üçün yox.

Üçüncüsü, structuredContent, _meta, widgetState-ə qoyduğunuz hər şey hansısa formada ya istifadəçiyə görünür, ya da Store-un reviwer-i tərəfindən görülə bilər. Buna görə:

  • heç bir API açarı, token, parol və admin sirləri ora qoymaq olmaz;
  • vidjet vəziyyətini elə dizayn edin ki, istifadəçi onu loglarda və ya debugda görəndə təəccüblənməsin.

Dördüncüsü, şəbəkə çağırışları. Vidjetdən kənar API-lərə birbaşa sorğular yalnız ciddi şəkildə məhdud siyahıdakı domenlərə və həssas olmayan ssenarilərdə mümkündür. Söhbət pul, hesablar, məxfi məlumatlardan gedirsə — hamısı MCP/backend üzərindən olmalıdır.

8. Qum qutusunda və window.openai ilə işləyərkən tipik səhvlər

Səhv №1: vidjeti «iframe-də adi sayt» hesab etmək.
Yeni başlayanlar vərdişlə window.parent-ə girməyə, ChatGPT-nin stillərini dəyişməyə və ya localStorage-dan həmişəki kimi istifadə etməyə çalışırlar. Qum qutusunda bu ya işləmir, ya da qeyri-sabit işləyir: origin fərqlidir, storage izolyasiya olunub, DOM-a çıxış bloklanır. İdarə olunan mühitdə yaşadığınızı qəbul edin və hostla yalnız window.openai və hook-lar vasitəsilə ünsiyyət qurun.

Səhv №2: window.openai-ə hər yerdə birbaşa toxunmaq.
Onlarla komponentdə window.openai.toolOutput oxumaq — çətin debug olunan tətbiq yoludur. Bu halda hadisələrə, asinxronluğa və undefined yoxlamalarına özünüz nəzarət etməlisiniz. Daha etibarlısı, artıq openai:set_globals-ı qablaşdıran və vəziyyəti sinxronlaşdıran useWidgetProps, useWidgetState, useOpenAIGlobal və digər hook-lardan dərhal istifadə etməkdir.

Səhv №3: widgetState-də hər şeyi (xüsusən də sirləri) saxlamaq.
Bəzən «birdən lazım olar» deyə oraya nəhəng API nəticələrini və ya hətta giriş tokenini qoymaq istəyirsiniz. Nəticədə kontekst böyüyür, modelin işi pisləşir və siz təhlükəsizlik tələblərini pozursunuz. widgetState kiçik olmalıdır, yalnız UI siqnallarını saxlamalıdır və heç vaxt məxfi məlumatları yox.

Səhv №4: internetə vidjetdən birbaşa çıxmağa çalışmaq.
Qum qutusundan fetch("https://api.superbank.com/...") çağırışları demək olar ki, həmişə CORS-a ilişəcək, hətta hər şeyi ideal sazlasanız belə, bu nə təhlükəsizdir, nə də idarəolunandır. Hesablar, pullar və şəxsi məlumatlarla bağlı hər şey MCP alətləri kimi reallaşdırılmalı və callTool və ya server tərəfi vasitəsilə çağırılmalıdır.

Səhv №5: ChatGPT-dən kənarda window.openai-ın sabit olmasına güvənmək.
Bəzən inkişaf etdiricilər vidjeti ayrıca SPA kimi işə salırlar və window.openai-ın undefined ola biləcəyini yoxlamırlar. Dev mühitində bu, «Cannot read properties of undefined» ilə crash-lə bitir. useIsChatGptApp, typeof window !== "undefined" yoxlamalarından və vidjet olmayan hallar üçün fallback UI-dan istifadə edin.

Səhv №6: mühit kontekstini (theme, displayMode, maxHeight, locale) nəzərə almamaq.
Əlbəttə, 2000px sabit hündürlük, həmişə tünd mövzu və yalnız desktop üçün veriliş təyin edə bilərsiniz — amma bu, istifadəçi təcrübəsini qəribə edəcək. Platforma sizə nə qədər yer olduğu, hansı mövzu və lokalın aktiv olduğu barədə siqnallar verir — useOpenAIGlobal, useDisplayMode, useMaxHeight və s. vasitəsilə onlardan istifadə edin ki, vidjet ChatGPT-də «doğma» görünsün.

Səhv №7: siyasəti üçüncü tərəf skriptləri ilə «dolamağa» çalışmaq.
Bəzən hansısa tracker, üçüncü tərəf JS bundle-ı qoşmaq və ya «səssizcə» yad domenlərdən kod icra etmək istəyi yaranır. Qum qutusu və CSP siyasətləri məhz bunun qarşısını almaq üçün var: üçüncü tərəf skriptləri bloklanır və sistemi dolamaq cəhdləri App-ınızın Store-da rədd edilməsinə aparır.

Şərhlər
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION