Задача: Карточки заведений в чате через кастомный тег агента

Карточки заведений в чате через кастомный тег агента

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

Цель

Сделать вывод заведений в AI-чате управляемым и структурированным: вместо произвольного текста/ссылок агент должен уметь явно ссылаться на заведение по его внутреннему ID, а интерфейс чата — заменять такую ссылку на полноценную карточку заведения.

Проблема

Сейчас агент при рекомендации или упоминании компаний/заведений отвечает в свободной форме: пишет название текстом, добавляет URL или оформляет ссылку по своему усмотрению. Фронтенд не может надежно определить, что конкретный фрагмент ответа соответствует конкретной записи заведения в базе, поэтому невозможно стабильно показывать карточки.

Предлагаемый контракт

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

<place id="PLACE_ID" />

Название тега можно выбрать в соответствии с текущими соглашениями проекта (place, company, venue и т.п.), но формат должен быть однозначным, машинно-парсируемым и документированным.

Ключевой принцип: LLM сообщает только id, а все отображаемые данные карточки (название, адрес, фото, рейтинг, ссылка, режим работы и т.п.) клиент/сервер получает из актуальных данных приложения. Не передавать эти поля внутри LLM-тега.

Backend / AI

  1. Дополнить системный prompt/инструкцию агента правилом: когда он рекомендует, перечисляет или предметно упоминает конкретное заведение из доступного ему каталога, использовать кастомный тег с ID этого заведения.
  2. Убедиться, что в контексте/tools агента ID заведения доступен вместе с данными, по которым модель выбирает заведение.
  3. Запретить модели придумывать ID. Тег допустим только для ID, полученного из результатов поиска/каталога/tools.
  4. Определить fallback: если заведение нельзя надежно сопоставить с внутренним ID, агент пишет обычный текст без кастомного тега.
  5. Сохранить нормальную возможность добавлять поясняющий текст вокруг карточки: например «Для семейного посещения подойдет …» + тег карточки.

Frontend чата

  1. Распознавать кастомный тег в assistant-message и не выводить его пользователю как сырой текст.
  2. По id загружать/получать данные заведения и рендерить существующую либо новую компактную карточку заведения непосредственно внутри сообщения.
  3. Поддержать несколько карточек в одном ответе, сохраняя порядок относительно текста.
  4. Карточка должна быть кликабельной и вести на страницу соответствующего заведения.
  5. Не интерпретировать произвольный HTML от модели: парсер должен поддерживать только явно разрешенный формат тега/атрибутов.
  6. Если ID не существует, запись удалена или загрузка карточки завершилась ошибкой — сообщение не должно ломаться. Использовать безопасный fallback (например, скрыть невалидный тег либо показать нейтральную текстовую заглушку — выбрать единообразное поведение).
  7. Учесть streaming: сырой незавершенный тег не должен мигать пользователю во время генерации. Парсить компонент после получения полного тега либо буферизовать потенциальный префикс тега.

Рекомендуемый формат рендера

Ответ модели:

Если хочется классическую городскую баню, я бы рассмотрел:

<place id="abc123" />

А если важнее современный SPA-формат:

<place id="def456" />

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

Критерии приемки

  • Определен и задокументирован единый синтаксис кастомного тега заведения с обязательным id.
  • Агент получает реальные ID заведений и использует тег при рекомендациях конкретных заведений.
  • Агент не генерирует тег для неизвестного/несопоставленного заведения.
  • Чат корректно преобразует тег в карточку соответствующего заведения.
  • В одном сообщении корректно работают несколько карточек вперемешку с обычным Markdown-текстом.
  • Служебный тег не виден пользователю, включая процесс streaming.
  • Невалидный/несуществующий ID не ломает рендер сообщения.
  • Карточка ведет на страницу правильного заведения.
  • Обычный Markdown и существующие ссылки в сообщениях продолжают работать без регрессий.
  • Добавлены тесты как минимум на: один тег, несколько тегов, невалидный ID, malformed tag, streaming/частичный тег.

Отдельно проверить перед реализацией

Стоит посмотреть текущий pipeline рендера Markdown/streaming и решить, где лучше выполнять преобразование: до Markdown-парсера через tokenizer/AST либо как разрешенное расширение рендера. Не использовать небезопасный dangerouslySetInnerHTML для обработки ответа модели.