Smart Answer: ответ ИИ в поиске по сайту
Встраивание Smart Answer: скрипт suche.js, блок в разметке, публичный HTTP-интерфейс для поисковых страниц с серверным рендерингом, поток ответа через SSE, CSS-переменные и передача в чат.
Smart Answer показывает короткий ответ на вопрос посетителя над или под результатами поиска по сайту: потоком, с источниками из базы знаний и полем для следующего вопроса. Smart Answer — отдельный сервис; он использует ту же базу знаний, что и ассистент чата. Включается и оформляется в аккаунте в Проект → Smart Answer для поиска по сайту.
Встраивание через suche.js
Если загрузчик чата уже на странице, достаточно одной строки:
<script src="https://cdn.reactive.chat/suche.js" async></script>
Без виджета чата передайте ключ сайта:
<script src="https://cdn.reactive.chat/suche.js" data-site-key="SITE_KEY" async></script>
Скрипт подключается к полям поиска страницы (каким именно — задаётся CSS-селектором в аккаунте), ждёт отправки или паузы ввода и показывает ответ в блоке в указанном месте (перед целевым элементом, после него или внутри). По умолчанию отвечает только на ввод, похожий на вопрос.
Атрибуты тега скрипта:
- data-site-key (необязательно) — иначе берётся из загрузчика чата (
data-rc-site-key). - data-ws-url (необязательно) — адрес чата; иначе выводится как для чата.
- data-api (необязательно) — базовый адрес интерфейса, по умолчанию
https://ws.reactive.chatили собственный домен. - data-locale (необязательно) — язык; иначе
<html lang>, затем браузер.
Скрипт не устанавливает cookie и хранит данные только в sessionStorage. Для CSP нужны script-src https://cdn.reactive.chat и connect-src https://ws.reactive.chat (Безопасность и CSP).
Передача в чат
Если посетитель задаёт следующий вопрос в блоке, то в зависимости от настройки Уточняющий вопрос разговор продолжается в виджете чата (ReactiveChat('continueConversation', id, text) или ReactiveChat('startConversation', text)) либо ответ появляется в том же блоке. Без виджета чата на странице следующий вопрос всегда остаётся в блоке.
Оформление
Блок (.rc-ai) имеет собственный CSS и защищён от сбросов стилей страницы. Его можно настроить переменными с минимальной специфичностью:
.rc-ai {
--rc-ai-akzent: #0a7cff;
--rc-ai-hg: var(--karte, #fff);
--rc-ai-text: #1d1d1f;
--rc-ai-rand: #e3e3e8;
--rc-ai-radius: 10px;
--rc-ai-schrift: inherit;
--rc-ai-abstand: 20px;
--rc-ai-schriftgroesse: 16px;
--rc-ai-zeilenhoehe: 1.6;
}
Отдельные части меняются через .rc-ai .rc-ai__teil { … } в поле Собственный CSS аккаунта, например .rc-ai .rc-ai__hinweis { display: none; }. Защитный слой не защищает от правил страницы с #id или !important.
HTTP-интерфейс
Для поисковых страниц с серверным рендерингом или своих интерфейсов есть публичный интерфейс без аутентификации и cookie по адресу https://ws.reactive.chat/api/suche/SITE_KEY.
Общее для всех конечных точек:
- CORS только для источников из доменов проекта (включая поддомены); серверные вызовы без
Originработают всегда. - Ограничение частоты: 60 запросов в минуту на адрес посетителя по всем конечным точкам → 429 с
Retry-AfterиX-RateLimit-*. При вызове с сервера учитывается его адрес; поэтому для большого числа посетителей используйтеsuche.jsв браузере или собственный домен с ключом прокси. - Вопросы и ответы не записываются в журналы доступа.
GET /api/suche/SITE_KEY?locale=de — конфигурация, CSS и пустой шаблон блока (Cache-Control: public, max-age=60):
{"v":1,"aktiv":false}— отключено, заблокировано или неизвестно (404 для неизвестного ключа).{"v":1,"aktiv":true,"sprache":"de","konfig":{…},"css":"…","vorlage":"<div class=\"rc-ai\" …>","skript":"https://cdn.reactive.chat/suche.js"}
POST /api/suche/SITE_KEY — готовый HTML блока для вопроса, тело до 4 КБ:
curl -s -X POST "https://ws.reactive.chat/api/suche/SITE_KEY" \
-H "Content-Type: application/json" \
-d '{"q":"How long does shipping take?","locale":"en"}'
{"v":1,"aktiv":true,"frage":false,"html":""}— ввод не является вопросом.{"v":1,"aktiv":true,"frage":true,"html":"…","css":"<style id=\"rc-ai-css\">…</style>","skript":"…","einbettung":"…"}— поместитеeinbettungв поисковую страницу;suche.jsзатем заполнит блок ответом.- 400
{"v":1,"error":"bad_request","field":"q"}—qотсутствует, пусто или длиннее 300 символов. - 413
payload_too_large, 405 для любого другого метода.
POST /api/suche/SITE_KEY/antwort — сам ответ потоком Server-Sent Events, тело до 8 КБ:
{"q":"How long does shipping take?","locale":"en",
"visitor_id":"v_…",
"ctx":{"url":"https://shop.example/search","title":"Search"},
"folge":false,
"vorher":[{"q":"…","a":"…"}]}
- q (строка, обязательно, 1–300 символов)
- locale (строка, необязательно)
- visitor_id (строка, необязательно,
[A-Za-z0-9_-]{1,80}) — ID посетителя чата для корректного сопоставления при передаче. - ctx (объект, необязательно) —
url,path,referrer,title; строка запроса и якорь удаляются. - folge (
true, необязательно) — следующий вопрос; обходит фильтр вопросов. - vorher (список, необязательно) — до 3 предыдущих пар (
q≤ 300,a≤ 1500 символов).
Ответ text/event-stream:
event: status
data: {"stufe":"denkt"}
event: text
data: {"text":"Shipping usually takes"}
event: fertig
data: {"text":"Shipping usually takes 2–3 working days.","quellen":[{"titel":"Shipping","url":"https://shop.example/shipping"}],"fortsetzung":{"art":"faden","id":"01J…"}}
- status —
{stufe}:denkt,sucht,schreibt,wartet. Первое событие всегдаdenkt. - text —
{text}: весь видимый на данный момент текст (не приращение), не чаще раза в 150 мс. - fertig —
{text, quellen, fortsetzung};fortsetzung—{art:"faden", id}(сохранено как разговор),{art:"neu"}илиnull. - fehler —
{code, text?}сcode:aus,gesperrt,drossel,motor,zeit,einwilligung,ungueltig. Еслиfehlerприходит послеtext, оставьте текст на месте.
После fertig или fehler поток завершается. Ошибки до начала тоже поступают потоком ровно с одним fehler (HTTP 404, 429, 400, 413, 503 или 200). Если текста нет 45 секунд или завершения нет 90 секунд, следует fehler zeit. Если закрыть соединение, ответ прерывается. Промежуточный прокси должен передавать поток без буферизации.
// read the stream in the browser (EventSource cannot POST)
const r = await fetch('https://ws.reactive.chat/api/suche/SITE_KEY/antwort', {
method: 'POST', headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({ q: frage, locale: 'en' }), credentials: 'omit'
});
const leser = r.body.getReader(); const dec = new TextDecoder(); let puffer = '';
for (;;) {
const { value, done } = await leser.read(); if (done) break;
puffer += dec.decode(value, { stream: true });
let i; while ((i = puffer.indexOf('\n\n')) >= 0) {
const block = puffer.slice(0, i); puffer = puffer.slice(i + 2);
const ev = /^event: (.*)$/m.exec(block), da = /^data: (.*)$/m.exec(block);
if (ev && da) zeige(ev[1], JSON.parse(da[1]));
}
}
Обновлено: