Встроенные API для ИИ: что можно и чего нельзя делать

Опубликовано: 30 апреля 2026 г.

Благодаря встроенному ИИ ваш веб-сайт или веб-приложение может выполнять задачи, основанные на ИИ, без необходимости развертывания, управления или самостоятельного размещения моделей. Переход от демо-версии к готовой к использованию функции может оказаться непростым. В этом документе рассматриваются технические аспекты и вопросы пользовательского опыта, которые помогут вам избежать распространенных ошибок.

Подготовьте модель заранее.

Применимо ко всем API, например, к Summarizer, Translator и Writer.

Рекомендация: Инициализируйте сессию, как только вы распознаете намерение пользователя. Поскольку для инициализации сессии требуется активация пользователя , вы можете использовать любое взаимодействие, например, щелчок в любом месте страницы, предлагающей функцию, работающую на основе ИИ. Это подготовит модель и среду выполнения, пока пользователь взаимодействует с пользовательским интерфейсом. При необходимости, запустите следующую наиболее вероятную задачу ИИ, как только начнете отображать результат.

Не следует: Дожидаться, пока пользователь нажмет кнопку «Сгенерировать», для инициализации сессии. Это приводит к многосекундной задержке холодного запуска, поскольку модель должна сначала загрузиться в память и подготовить свой конвейер выполнения.

Задайте начальные подсказки во время создания.

Применимо к: API подсказок.

Рекомендация: Предоставьте системные инструкции во время инициализации сессии, чтобы повысить скорость появления первого приглашения к вводу.

Не следует: начинать с пустой сессии и отправлять системные инструкции в рамках первого вызова 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 подсказок.

В API подсказок каждая сессия отслеживает контекст разговора , учитывая все предыдущие взаимодействия. Поскольку клон наследует все от родительской сессии, включая первоначальные подсказки и всю историю взаимодействий до момента клонирования, структурируйте использование таким образом, чтобы наследовались только необходимые вам элементы.

Делать:

  • Создайте базовую сессию: для эффективной обработки несвязанных задач создайте базовую сессию, содержащую только инструкции вашей системы и не содержащую предыдущего контекста разговора.
  • Клонирование базовой версии: используйте 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() для сессий, которые вам больше не нужны, чтобы освободить память, когда функция больше не используется. Если вы используете шаблон клонирования, сохраните базовую сессию и уничтожьте клоны, которые вам больше не нужны.

Не следует: держать активными несколько больших сессий. Каждая сессия потребляет память, что приводит к ненужному использованию ресурсов и может стать проблемой. Сессии будут автоматически очищены сборщиком мусора, но вызов метода 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.

Рекомендация: Передавайте модели только то, что строго необходимо. Удаляйте всё, что не имеет отношения к решаемой задаче. Для больших наборов данных предоставляйте краткий обзор и небольшой набор релевантных элементов.

Don't: Send raw unprocessed text, unnecessary metadata, HTML tags, or large unfiltered lists to the APIs. Latency grows significantly with input size, which can make the AI feature seem broken on many devices.

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

Используйте структурированный вывод для получения предсказуемых результатов.

Применимо к: API подсказок.

Рекомендация: Если вам необходимо, чтобы модель возвращала данные в определенном формате, используйте структурированный вывод , указав поле responseConstraint , содержащее схему JSON. Это гарантирует предсказуемость вывода и избавляет от необходимости сложной постобработки или ручного анализа.

Не следует полагаться исключительно на инструкции на естественном языке (например, "выводить только 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 подсказок, поскольку это единственный 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. Несколько идей:

  • Потоковая передача длинного контента: для кратких обзоров или чата потоковая передача по умолчанию создает эффект набора текста на пишущей машинке для каждого токена. Это может выглядеть естественно и обеспечивать мгновенную обратную связь.
  • Непотоковая обработка для коротких задач (или длительных асинхронных задач): Для коротких выходных данных, например, alt-текста, непотоковая обработка может создать более отточенный пользовательский интерфейс. Она также дает время для предварительной подготовки следующей задачи ИИ, пока выполняется рендеринг текущей. Этот подход также работает для более длительных асинхронных или фоновых задач. Если пользователь не заблокирован на этапе вывода данных и не хочет продолжать работу, нет острой необходимости выводить данные по мере их поступления. Сигнализируйте о том, что процесс продолжается, в пользовательском интерфейсе.
  • Визуальные переходы для обновлений: при переводе или переписывании текста используйте анимацию, например, изменение формы слов.

Не следует: обновлять пользовательский интерфейс без визуальных подсказок.

Согласуйте с ментальной моделью времени и работы пользователя.

Применимо ко всем API.

Рекомендация: Рассмотрите возможность искусственной задержки в одну-две секунды, если ответ поступает практически мгновенно. Парадоксально, но пользователи могут считать результаты более достоверными, если процесс генерации соответствует их восприятию сложности задачи. Используйте анимацию, чтобы сигнализировать о завершении процесса обработки данных искусственным интеллектом.

Не следует: удивлять пользователей мгновенной заменой элементов пользовательского интерфейса.

Предоставьте пользователям возможность быстро перемещаться по страницам и отменять изменения, внесенные ИИ.

Применимо ко всем API.

Рекомендация: оснастите свой пользовательский интерфейс пошаговым меню или историей навигации, позволяющими пользователям уверенно просматривать различные результаты и быстро отменять изменения, внесенные ИИ. Это гарантирует доступность различных версий.

Не следует: перезаписывать предыдущий черновик пользователя или результат работы ИИ, который ему мог понравиться, без возможности вернуться назад, отменить изменения или сравнить версии.

Элемент пользовательского интерфейса Stepper, отображающий историю навигации.
Шаблон пользовательского интерфейса: Отклонить/Принять предложение (документация Google)
Кнопка «Отменить все изменения агента» в пользовательском интерфейсе Google Antigravity.
Шаблон пользовательского интерфейса: Отменить все изменения, внесенные агентом (Google Antigravity)
Кнопки «Отклонить» или «Принять предложение» в Google Docs.
Шаблон пользовательского интерфейса: 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;
}