Пошаговые инструкции beginner 13 мин

Как добавить чат-виджет ИИ-агента на сайт: пошаговая инструкция

Пошаговый гайд по AI-чат-виджету для сайта: frontend, backend endpoint, fetch, SSE, RAG, память, rate limit и fallback к человеку.

RAG AI-агенты Инструкция AI-чат чат-виджет SSE fetch

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

Короткая версия: сделайте виджет на frontend, отправляйте сообщения на свой backend endpoint через fetch, на backend вызывайте агента, возвращайте ответ, а для потоковой выдачи добавьте SSE. Никогда не кладите LLM API key в браузер.

Что мы собираем

Соберем базовую схему AI-чат-виджета для сайта. Она подходит для Laravel, Next.js, WordPress, статического сайта с отдельным backend, а также для агента, который живет в Flowise, n8n, LangGraph или отдельном API.

  • Кнопка открывает окно чата.
  • Пользователь вводит вопрос.
  • Frontend отправляет POST-запрос на backend.
  • Backend проверяет лимиты и сессию.
  • Backend вызывает agent_answer().
  • Ответ возвращается в виджет.
  • Для длинных ответов можно включить streaming через SSE.

Шаг 1. Определите задачу виджета

Не стоит начинать с кода. Сначала решите, что именно должен делать агент на сайте. Виджет “спросите что угодно” быстро превращается в дорогой и непредсказуемый чат. Лучше задать понятную область.

  • Ответы по базе знаний сайта.
  • Подбор статьи, инструкции или инструмента.
  • Первичная квалификация заявки.
  • Помощь с тарифами и условиями.
  • Сбор контактов после полезного ответа.
  • Передача сложного вопроса человеку.

Шаг 2. Выберите схему ответа

Для первого запуска чаще всего достаточно обычного HTTP-запроса через fetch: пользователь отправил сообщение, backend вернул готовый ответ. Streaming нужен позже, когда ответы длинные и пользователь должен видеть, что агент печатает.

  • fetch: проще всего, подходит для коротких ответов.
  • SSE: удобно для потоковой выдачи текста от сервера к браузеру.
  • WebSocket: нужен для полноценного двустороннего realtime, но для обычного AI-чата часто избыточен.

Шаг 3. Не вызывайте модель из браузера

Главное правило: браузер не должен напрямую обращаться к LLM-провайдеру с вашим API-ключом. Все секреты должны жить на backend. Frontend отправляет только текст пользователя на ваш endpoint, а backend уже решает, какую модель, RAG, память и инструменты использовать.

Browser widget -> /api/ai-chat -> agent backend -> LLM / RAG / tools

Так проще контролировать стоимость, лимиты, безопасность, логи и правила доступа.

Шаг 4. Сделайте простой HTML виджета

Для прототипа достаточно кнопки, окна сообщений и формы ввода. В production вы позже добавите состояния загрузки, историю, markdown, кнопки быстрых вопросов и передачу оператору.

<button class="ai-chat-button" type="button" aria-label="Открыть чат">
  AI
</button>

<section class="ai-chat-panel" hidden>
  <header class="ai-chat-header">
    <strong>AI-помощник</strong>
    <button type="button" class="ai-chat-close" aria-label="Закрыть">×</button>
  </header>

  <div class="ai-chat-messages" data-chat-messages></div>

  <form class="ai-chat-form" data-chat-form>
    <input name="message" autocomplete="off" placeholder="Напишите вопрос" />
    <button type="submit">Отправить</button>
  </form>
</section>

На этом этапе не перегружайте интерфейс. Важно проверить сквозной путь: сообщение ушло, backend получил, агент ответил, текст появился в чате.

Шаг 5. Добавьте отправку через fetch

Frontend должен отправлять запрос на ваш backend endpoint. Добавьте CSRF-защиту, если это Laravel или другой framework с защитой форм. В ответе backend возвращает JSON с полем answer.

const form = document.querySelector("[data-chat-form]");
const messages = document.querySelector("[data-chat-messages]");

function addMessage(role, text) {
  const item = document.createElement("div");
  item.className = `ai-chat-message ai-chat-message-${role}`;
  item.textContent = text;
  messages.append(item);
  messages.scrollTop = messages.scrollHeight;
}

form.addEventListener("submit", async (event) => {
  event.preventDefault();

  const input = form.elements.message;
  const text = input.value.trim();

  if (!text) {
    return;
  }

  addMessage("user", text);
  input.value = "";
  addMessage("assistant", "Думаю...");

  const response = await fetch("/api/ai-chat", {
    method: "POST",
    headers: {
      "Content-Type": "application/json",
      "Accept": "application/json",
    },
    body: JSON.stringify({ message: text }),
  });

  const data = await response.json();
  messages.lastElementChild.textContent = data.answer || "Не получилось получить ответ.";
});

Для production добавьте обработку ошибок сети, повторную отправку, disabled-состояние кнопки и ограничение длины ввода.

Шаг 6. Сделайте backend endpoint

Backend endpoint принимает сообщение, проверяет его и вызывает агентную функцию. Ниже упрощенный псевдокод на стиле Laravel: идея важнее конкретного framework.

Route::post('/api/ai-chat', function (Request $request) {
    $data = $request->validate([
        'message' => ['required', 'string', 'max:2000'],
    ]);

    $sessionId = $request->session()->getId();
    $answer = app(AiAgent::class)->answer(
        userMessage: $data['message'],
        threadId: 'site-chat-'.$sessionId,
    );

    return response()->json([
        'answer' => $answer,
    ]);
})->middleware(['web', 'throttle:20,1']);

Даже если агент находится в Flowise или n8n, сайту лучше общаться не напрямую с ним, а через ваш backend. Так можно скрыть внутренние URL и ключи, добавить лимиты и нормальные логи.

Шаг 7. Подключите RAG и память

Для сайта особенно полезен RAG: агент отвечает по вашим страницам, инструкциям и базе знаний, а не придумывает общие ответы. Память лучше начинать с краткосрочной session-памяти, чтобы агент понимал текущий диалог, но не сохранял лишние персональные данные.

  • thread_id можно строить от session_id.
  • Для авторизованных пользователей можно использовать user_id.
  • RAG должен возвращать источники: URL статьи, название раздела, дату обновления.
  • Долгосрочную память включайте только после понятной политики хранения.
  • Для публичного сайта добавьте фразу “я не нашел точного ответа в базе”, если контекста нет.

Шаг 8. Добавьте streaming через SSE

Если ответы длинные, обычный fetch заставляет пользователя ждать весь ответ целиком. SSE позволяет backend отправлять текст частями, а браузер принимает события через EventSource. Сервер должен отвечать с MIME type text/event-stream.

const source = new EventSource("/api/ai-chat/stream?message=Как выбрать ИИ-агента?");

source.onmessage = (event) => {
  appendAssistantChunk(event.data);
};

source.addEventListener("done", () => {
  source.close();
});

SSE хорошо подходит для потока “сервер -> браузер”. Если вам нужно много двусторонних событий, совместное редактирование или сложный realtime, тогда уже смотрите в сторону WebSocket.

Шаг 9. Защитите публичный endpoint

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

  • Rate limit по IP, session_id и user_id.
  • Максимальная длина сообщения.
  • Ограничение числа сообщений в одной сессии.
  • Фильтр пустых, повторяющихся и явно мусорных запросов.
  • CAPTCHA или soft-check после подозрительной активности.
  • Лимит стоимости на день.
  • Логирование ошибок без секретов и лишних персональных данных.

Шаг 10. Добавьте передачу человеку

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

  • Кнопка “позвать специалиста”.
  • Форма имени, телефона или email.
  • Передача истории диалога в CRM или Helpdesk.
  • Отметка причины: не найден ответ, нужна цена, нужен человек, ошибка агента.
  • Отдельное согласие на обработку контакта, если это нужно по вашим правилам.

Шаг 11. Проверьте UX

Плохой виджет может раздражать сильнее, чем помогать. Он не должен перекрывать важные кнопки, открываться сам без причины и заставлять пользователя ждать без обратной связи.

  • Кнопка виджета не закрывает меню, корзину, форму и cookie banner.
  • На мобильном чат занимает экран аккуратно и имеет видимую кнопку закрытия.
  • Есть состояние “думаю”.
  • Ошибка объяснена человеческим языком.
  • Enter отправляет сообщение, Shift+Enter переносит строку, если поле многострочное.
  • История текущего диалога сохраняется при переходе между страницами, если это важно.

Шаг 12. Проверьте качество ответов

После технического запуска проверьте не только “работает ли кнопка”, но и качество ответов. Для сайта особенно важны точность, источники и отказ от выдумывания.

  • Агент правильно отвечает на 20-30 частых вопросов.
  • Агент показывает ссылки на статьи или документы, если использует RAG.
  • Агент не обещает несуществующие скидки, сроки и функции.
  • Агент не раскрывает внутренние инструкции.
  • Агент не повторяет системный промпт.
  • Агент корректно передает вопрос человеку.

Мини-чеклист запуска

  • Виджет открывается и закрывается на desktop и mobile.
  • Сообщение отправляется на ваш backend, а не напрямую в LLM API.
  • Backend валидирует длину и формат сообщения.
  • Есть rate limit.
  • Секретные ключи лежат только на сервере.
  • Для длинных ответов продуман streaming или понятное ожидание.
  • RAG-ответы содержат источники.
  • Ошибки агента не ломают интерфейс.
  • Есть fallback к человеку или форме заявки.

Частые вопросы

Что выбрать для первого виджета: fetch, SSE или WebSocket?

Для первого запуска выбирайте fetch. Это самый простой путь: отправили сообщение, получили JSON-ответ. SSE добавляйте, когда нужен потоковый текст. WebSocket имеет смысл, если нужен настоящий двусторонний realtime, а не просто чат с ответами агента.

Можно ли вставить ключ OpenAI или другого LLM-провайдера прямо в JavaScript?

Нет. Все ключи должны храниться на backend. Код в браузере виден пользователю, поэтому любой секрет в JavaScript можно вытащить и использовать за ваш счет.

Как связать чат-виджет с Flowise или n8n?

Сделайте backend endpoint на своем сайте, а внутри него вызывайте Flowise prediction API или n8n webhook. Так frontend не будет знать внутренние URL, токены и структуру агентной системы.

Нужна ли память в чат-виджете?

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

Что делать, если агент не нашел ответ?

Лучше честно сказать, что подтвержденного ответа нет, показать ближайшие статьи или предложить оставить контакт. Не стоит заставлять агента придумывать ответ, особенно если речь о ценах, договорах, медицине, финансах или юридических условиях.

Дальше по теме

Похожие материалы