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() در آن جلسه پایه استفاده کنید تا سربار تجزیه مجدد دستورالعمل‌های سیستم را ذخیره کنید. این به شما امکان می‌دهد مکالمات موازی ایجاد کنید یا یک وظیفه را به خط پایه خود بازنشانی کنید.

نکن:

  • از همان جلسه برای کارهای غیرمرتبط دوباره استفاده نکنید و از شبیه‌سازی هر جلسه‌ای که از قبل حاوی سابقه تعامل غیرضروری است، خودداری کنید. هر دو الگو می‌توانند باعث شوند که زمینه قبلی نامرتبط با کار فعلی شما تداخل پیدا کند.
  • create() با دستورالعمل‌های سیستمی یکسان، مکرراً فراخوانی نکنید. در عوض، از الگوی شبیه‌سازی برای بهینه‌سازی عملکرد استفاده کنید.
// ✅ 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).

انجام دهید: تمام خروجی LLM را به عنوان محتوای غیرقابل اعتماد در نظر بگیرید. کل خروجی ترکیبی، نه فقط تکه‌ها را، پاکسازی کنید زیرا کد مخرب می‌تواند در به‌روزرسانی‌ها تقسیم شود. قبل از رندر کردن، در صورت پشتیبانی از API ضدعفونی‌کننده استفاده کنید. برای جلوگیری از کاهش عملکرد، از یک تجزیه‌کننده‌ی Streaming Markdown مانند 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);

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

مربوط به: API Prompt، زیرا تنها API است که از طرح‌های خروجی ساختاریافته پشتیبانی می‌کند.

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

نباید: با استفاده از طرح‌های خروجی ساختاریافته، محدودیت‌های کاراکتری سختگیرانه‌ای مانند maxLength: 125 اعمال کنید. وقتی پاسخ مدل طولانی‌تر از محدودیتی باشد که شما تعیین کرده‌اید، مدل ممکن است برای فشرده‌سازی معنا به توکن‌های با چگالی بالا مانند زبان‌های خارجی یا ایموجی‌ها روی آورد که منجر به خروجی بی‌معنی می‌شود.

/*  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)
دکمه‌های رد یا پذیرش پیشنهاد در گوگل داکس
الگوی رابط کاربری: Stepper (نمونه نمایشی متن جایگزین)

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

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

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

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

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

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

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

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

// ✅ 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;
}