API های داخلی هوش مصنوعی: بایدها و نبایدها

منتشر شده: ۳۰ آوریل ۲۰۲۶

با هوش مصنوعی داخلی، وب‌سایت یا برنامه وب شما می‌تواند وظایف مبتنی بر هوش مصنوعی را بدون نیاز به استقرار، مدیریت یا میزبانی مستقل مدل‌ها انجام دهد. ممکن است انتقال از یک نسخه آزمایشی به یک ویژگی آماده تولید، چالش برانگیز باشد. این سند ملاحظات فنی و UX را پوشش می‌دهد تا به شما در جلوگیری از مشکلات رایج کمک کند.

مدل را زودتر آماده کنید

قابل اجرا در: همه APIها، برای مثال، Summarizer، Translator و Writer.

انجام دهید: به محض اینکه قصد کاربر را تشخیص دادید، جلسه را آغاز کنید. از آنجا که برای آغاز یک جلسه، فعال‌سازی کاربر لازم است، می‌توانید از هر تعاملی استفاده کنید، به عنوان مثال، یک کلیک در هر نقطه از صفحه که یک ویژگی مبتنی بر هوش مصنوعی را ارائه می‌دهد. این کار مدل و زمان اجرا را در حالی که کاربر با رابط کاربری تعامل دارد، آماده می‌کند. در صورت لزوم، به محض شروع رندر نتیجه، وظیفه هوش مصنوعی بعدی را که محتمل‌تر است، آغاز کنید.

نباید: صبر کنید تا کاربر روی «تولید» کلیک کند تا جلسه را مقداردهی اولیه کند. این منجر به تأخیر چند ثانیه‌ای در شروع سرد می‌شود، زیرا مدل ابتدا باید در حافظه بارگذاری شود و خط لوله اجرای خود را آماده کند.

تنظیم درخواست‌های اولیه در طول ایجاد

مربوط به: رابط برنامه‌نویسی کاربردی سریع.

انجام دهید: دستورالعمل‌های سیستم را در طول راه‌اندازی جلسه ارائه دهید تا سرعت اولین اعلان بهبود یابد.

انجام ندهید: با یک جلسه خالی شروع کنید و دستورالعمل‌های سیستم را به عنوان بخشی از اولین فراخوانی prompt() ارسال کنید. این کار باعث افزایش تأخیر می‌شود زیرا مدل را مجبور می‌کند آن دستورالعمل‌ها را در آخرین لحظه پردازش کند.

// ✅ DO: Create the session as early as possible (tip on warming up the model early) and use initialPrompts for system instructions in the create call
const session = await LanguageModel.create({
  initialPrompts: [
    { role: 'system', content: 'You are a helpful assistant specialized in code reviews.' }
  ]
});

// A few moments later, when the user triggers the AI feature
const review = await session.prompt(`Review the following code:\n\n${code}`);

// ❌ DON'T: Send instructions using prompt() after creation
// const slowerSession = await LanguageModel.create();
// await slowerSession.prompt(`You are a helpful assistant specialized in code reviews.\n\nReview the following code:\n\n${code}`); // Higher latency

جلسات کلون برای کارهای تکراری

مربوط به: رابط برنامه‌نویسی کاربردی سریع.

برای API مربوط به Prompt، هر جلسه، زمینه مکالمه را با در نظر گرفتن تمام تعاملات قبلی دنبال می‌کند . از آنجا که یک کلون همه چیز را از جلسه والد خود، از جمله اعلان‌های اولیه و تمام تاریخچه تعاملات تا زمان کلونینگ، به ارث می‌برد، استفاده خود را طوری ساختار دهید که فقط آنچه را که نیاز دارید به ارث ببرد.

انجام دهید:

  • ایجاد یک جلسه پایه: برای انجام کارآمد وظایف غیرمرتبط، یک جلسه پایه ایجاد کنید که فقط شامل دستورالعمل‌های سیستم شما باشد و هیچ زمینه مکالمه قبلی نداشته باشد.
  • شبیه‌سازی خط پایه: برای وظایف جدید، clone() در آن جلسه پایه استفاده کنید تا سربار تجزیه مجدد دستورالعمل‌های سنگین سیستم را ذخیره کنید. این به شما امکان می‌دهد مکالمات موازی ایجاد کنید یا یک وظیفه را به خط پایه خود بازنشانی کنید.

نکن:

  • از همان جلسه برای کارهای غیرمرتبط دوباره استفاده نکنید و از شبیه‌سازی هر جلسه‌ای که از قبل حاوی سابقه تعامل غیرضروری است، خودداری کنید. هر دو الگو می‌توانند باعث شوند که زمینه قبلی نامرتبط با کار فعلی شما تداخل پیدا کند.
  • Don't repeatedly call create() with identical, heavy system instructions. Use the cloning pattern instead to optimize performance.
// ✅ DO: Create a baseline session and clone it for each new task
const baseSession = await LanguageModel.create({
  initialPrompts: [{
    role: 'system',
    content: 'You are a technical editor...',
  }],
});

// Clone the base session once for the first task
const task1 = await baseSession.clone();
const response1 = await task1.prompt("Review this first draft...");
// ... Repeat the cloning pattern for subsequent independent tasks
// Each task starts fresh from the baseline system instructions

// ❌ DON'T:
// Bad performance pattern: repeated create() calls for identical tasks.
// This forces the model to re-parse instructions every time, increasing latency.
// const sessionA = await LanguageModel.create({ initialPrompts: [...] });
// await sessionA.prompt("Task 1...");
// const sessionB = await LanguageModel.create({ initialPrompts: [...] });
// await sessionB.prompt("Task 2...");
// Bad quality pattern: reusing the same session for unrelated tasks.
// const session = await LanguageModel.create();
// await session.prompt("Analyze this financial report...");
// Unrelated task in the same session:
// await session.prompt("Now write a children's story...");

جلسات بلااستفاده را از بین ببرید

قابل اجرا برای: همه APIها.

انجام دهید: به طور صریح تابع destroy() را در sessionهایی که دیگر نیازی به آنها ندارید فراخوانی کنید تا وقتی یک ویژگی دیگر در حال استفاده نیست، حافظه آزاد شود. اگر از الگوی کلونینگ استفاده می‌کنید، session پایه را نگه دارید و کلون‌هایی را که دیگر نیازی به آنها ندارید، از بین ببرید.

نباید: چندین نشست بزرگ را فعال نگه دارید. هر نشست حافظه را مصرف می‌کند، که باعث استفاده غیرضروری از منابع می‌شود و ممکن است مشکل‌ساز شود. نشست‌ها به طور طبیعی توسط جمع‌کننده زباله پاک می‌شوند، اما فراخوانی destroy() حافظه را سریع‌تر آزاد می‌کند.

// ✅ DO: Use the clone and destroy it immediately after
const clone = await baseSession.clone();
const response = await clone.prompt("Quick task...");
// Free memory right away: destry the clone, keep the baseSession
clone.destroy();

پاسخ‌های استریمینگ را با خیال راحت و کارآمد رندر کنید

قابل اجرا در: همه APIهایی که از پخش جریانی پشتیبانی می‌کنند (Prompt، Summarizer، Writer، Rewriter و Translator).

Do: Treat all LLM output as untrusted content. Sanitize the full combined output, not just chunks, because malicious code could be split across updates. Before rendering, use the Sanitizer API where supported. To avoid a decrease in performance, use a streaming Markdown parser like streaming-markdown .

انجام ندهید: innerHTML مستقیماً روی هر به‌روزرسانی تکه تنظیم کنید. این کار کند است، مخصوصاً با قالب‌بندی پیچیده مانند هایلایت کردن سینتکس، و در برابر تزریق کد آسیب‌پذیر است.

import * as smd from "streaming-markdown";
// Set up virtual buffer and Sanitizer API
const sanitizer = new Sanitizer({
  allowElements: ['figure', 'figcaption', 'p', 'br', 'strong', 'em', 'img', 'a'],
  allowAttributes: {
    'loading': ['img'], 'decoding': ['img'], 'src': ['img'], 'href': ['a']
  }
});

// Create an off-screen fragment so the parser doesn't cause flicker
// or trigger XSS in the live DOM during the building process.
const buffer = new DocumentFragment();
const parser = smd.parser_new(buffer);

// Use sanitizer as a gatekeeper / cleaner function so we can combine it with the streaming Markdown parser
function syncSanitized(target, sourceFragment) {
  // .sanitize() returns a fresh, clean DocumentFragment
  const cleanFragment = sanitizer.sanitize(sourceFragment);
  // replaceChildren is the modern high-performance way to swap DOM content
  target.replaceChildren(cleanFragment);
}

// Streaming Logic
// `chunks` keeps track of the raw string (useful for logs/debug)
chunks += chunk;
// Let the parser build the DOM incrementally in the buffer.
// This is high-performance because the buffer is not live
smd.parser_write(parser, chunk);
// Use the Sanitizer API to port the content safely to the container.
syncSanitized(container, buffer);

بهینه‌سازی ورودی برای سرعت

قابل اجرا برای: همه APIها.

انجام دهید: فقط مواردی را که کاملاً مورد نیاز است به مدل منتقل کنید. هر چیزی را که به وظیفه مورد نظر مربوط نیست، حذف کنید. برای مجموعه داده‌های بزرگ، یک مرور کلی کوتاه و انتخاب کوچکی از موارد مرتبط ارائه دهید.

نباید: متن خام پردازش نشده، فراداده‌های غیرضروری، تگ‌های HTML یا لیست‌های بزرگ فیلتر نشده را به APIها ارسال کنید. تأخیر با اندازه ورودی به طور قابل توجهی افزایش می‌یابد، که می‌تواند باعث شود ویژگی هوش مصنوعی در بسیاری از دستگاه‌ها خراب به نظر برسد.

// ✅ DO: Send only relevant text
const cleanText = document.querySelector('#article').innerText;
const summary = await Summarizer.summarize(cleanText);

// ❌ DON'T: Send the entire DOM structure
// const dirtyText = document.querySelector('#article').innerHTML;

برای نتایج قابل پیش‌بینی از خروجی ساختاریافته استفاده کنید

مربوط به: رابط برنامه‌نویسی کاربردی سریع.

انجام دهید: وقتی نیاز دارید که مدل داده‌ها را با فرمت خاصی برگرداند، با ارائه یک فیلد responseConstraint برای ارائه یک JSON Schema از خروجی ساختاریافته استفاده کنید. این کار تضمین می‌کند که خروجی قابل پیش‌بینی است و از نیاز به پردازش‌های پیچیده پس از پردازش یا تجزیه دستی جلوگیری می‌کند.

انجام ندهید: فقط به دستورالعمل‌های زبان طبیعی (مانند "فقط خروجی JSON") تکیه نکنید. مدل‌ها ممکن است شامل پرکننده‌های مکالمه‌ای باشند که تجزیه‌گر شما را خراب می‌کنند.

// ✅ DO: Use a JSON Schema for predictable results
const schema = {
  type: "object",
  properties: {
    isTopicCats: { type: "boolean" }
  }
};

const result = await session.prompt(`Is this post about cats?\n\n${post}`, {
  responseConstraint: schema,
});
console.log(JSON.parse(result).isTopicCats);

جداسازی تولید از محدودیت‌های طول

Applies to: Prompt API, as it's the only API that supports structured output schemas .

انجام دهید: اجازه دهید مدل پاسخ خود را به طور طبیعی تولید کند، و سپس از منطق سمت کلاینت برای کوتاه کردن متن متناسب با رابط کاربری خود استفاده کنید.

Don't: Enforce strict character limits like maxLength: 125 using structured output schemas . When a model's response is longer than the limit you set, the model might switch to high-density tokens like foreign languages or emoji to compress meaning, resulting in nonsensical output.

/*  DO: Handle overflow using CSS */
.result {
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis; /* Displays '…' */
}
// ❌ DON'T: Force length in the prompt
const result = await session.prompt("Write a bio in exactly 50 characters.");

صبر کاربر را مدیریت کنید

قابل اجرا برای: همه APIها.

باید: از انیمیشن‌ها و تکنیک‌های رابط کاربری برای مدیریت صبر کاربر استفاده کنید. رویکرد بهینه به مورد استفاده شما و طول مورد انتظار خروجی API بستگی دارد. چند ایده:

  • پخش جریانی برای محتوای طولانی: برای خلاصه‌ها یا چت، پخش جریانی به طور پیش‌فرض یک اثر ماشین تحریر برای هر توکن ایجاد می‌کند. این می‌تواند طبیعی به نظر برسد و بازخورد فوری ارائه دهد.
  • عدم پخش برای وظایف کوتاه (یا وظایف طولانی ناهمزمان): برای خروجی‌های کوتاه، به عنوان مثال، متن جایگزین، عدم پخش می‌تواند رابط کاربری روان‌تری ایجاد کند. همچنین زمان لازم برای آماده‌سازی حدسی وظیفه هوش مصنوعی بعدی را در حین رندر شدن وظیفه فعلی فراهم می‌کند. این رویکرد همچنین برای وظایف طولانی‌تر ناهمزمان یا پس‌زمینه‌ای نیز کار می‌کند. اگر کاربر برای ادامه سفر خود روی خروجی مسدود نشده باشد، نیازی فوری به تولید خروجی در حین وقوع آن نیست. نشان دهید که فرآیند در رابط کاربری در حال انجام است.
  • انتقال‌های بصری برای به‌روزرسانی‌ها: هنگام ترجمه یا بازنویسی متن، از انیمیشن‌ها، مثلاً تغییر شکل کلمات، استفاده کنید.

انجام ندهید: رابط کاربری را بدون نشانه‌های بصری به‌روزرسانی نکنید.

با مدل ذهنی کاربر از زمان و کار هماهنگ شوید

قابل اجرا برای: همه APIها.

انجام دهید: اگر پاسخ تقریباً فوری است، یک تأخیر مصنوعی یک یا دو ثانیه‌ای را در نظر بگیرید. به طور متناقضی، کاربران ممکن است وقتی فرآیند تولید را با دشواری درک شده از کار همسو می‌دانند، نتایج را قابل اعتمادتر بدانند. از انیمیشن‌ها برای نشان دادن اینکه یک فرآیند هوش مصنوعی رخ داده است، استفاده کنید.

نباید: کاربران را با جایگزین‌های فوری رابط کاربری غافلگیر کنید.

به کاربران اجازه دهید به سرعت ویرایش‌های هوش مصنوعی را پیمایش و لغو کنند

قابل اجرا برای: همه APIها.

انجام دهید: رابط کاربری خود را به یک تاریخچه پیمایش یا پیمایش مجهز کنید که به کاربران امکان می‌دهد نتایج مختلف را با اطمینان بررسی کنند و به آنها اجازه دهد ویرایش‌های هوش مصنوعی را به سرعت لغو کنند. این تضمین می‌کند که نسخه‌های مختلف هنوز به راحتی در دسترس هستند.

انجام ندهید: پیش‌نویس قبلی کاربر یا نتیجه هوش مصنوعی که ممکن است مورد پسند او بوده باشد را بدون امکان بازگشت، بازگردانی یا مقایسه نسخه‌ها، بازنویسی نکنید.

عنصر رابط کاربری استپر که تاریخچه ناوبری را نشان می‌دهد.
الگوی رابط کاربری: رد/پذیرش پیشنهاد (اسناد گوگل)
دکمه لغو همه ویرایش‌های عامل در رابط کاربری Google Antigravity.
الگوی رابط کاربری: لغو تمام ویرایش‌های عامل (Google Antigravity)
دکمه‌های رد یا پذیرش پیشنهاد در گوگل داکس
UI pattern: Stepper (Alt text demo)

توانمندسازی کنترل و لغو دسترسی توسط کاربر

قابل اجرا برای: همه APIها.

انجام دهید: همیشه اجازه دهید کاربر حرف آخر را بزند. راهی برای لغو دستی پیشنهادات ارائه دهید. APIها ممکن است نتایج نادرستی تولید کنند.

انجام ندهید: نتیجه تولید شده توسط هوش مصنوعی را به عنوان تنها گزینه تحمیل نکنید.

نتایج کش برای وظایف تکراری

قابل اجرا برای: همه APIها.

Do: Implement a local result cache (for example, using sessionStorage or IndexedDB ) for repeated inputs or queries. Normalize the input by trimming whitespace and lowercasing to increase cache hits. For heavy inputs, for example, images, generate a hash to use as a cache key. Set a conservative time to live (TTL) for your cache (or serve cached results while updating them in the background). Let the user trigger a fresh inference if the result is unsatisfying.

انجام ندهید: برای یک جستجوی تکراری یا ورودی داده یکسان، مثلاً وقتی کاربر بین نتایج جستجو مدام در حال حرکت است، همان استنتاج را دوباره اجرا نکنید. اگرچه استنتاج روی دستگاه از نظر هزینه‌های ابری رایگان است، اما از نظر زمان کاربر و عمر باتری گران است.

// ✅ DO: Check a local cache before running inference
async function getAiResponse(userInput, forceRefresh = false) {
  // Normalize the query to increase cache hits
  const query = userInput.trim().toLowerCase();
  const cacheKey = `ai_results_${query}`;
  const TTL_MS = 3600000; // 1 hour conservative TTL

  if (!forceRefresh) {
    const itemStr = localStorage.getItem(cacheKey);
    if (itemStr) {
      const item = JSON.parse(itemStr);
      const now = Date.now();

      // Check if the item has expired
      if (now < item.expiry) {
        // Lightweight safety check before rendering
        if (isValid(item.value)) return item.value;
      } else {
        // Delete the stale entry if the TTL has passed
        localStorage.removeItem(cacheKey);
      }
    }
  }

  // Fallback: Run inference if no valid cache exists
  const session = await LanguageModel.create();
  const response = await session.prompt(userInput);

  // Store the result for future use (with an expiration)
  const cacheData = {
    value: response,
    expiry: Date.now() + TTL_MS
  };
  localStorage.setItem(cacheKey, JSON.stringify(cacheData));

  return response;
}