Задача: Карточки заведений в чате через кастомный тег агента
Карточки заведений в чате через кастомный тег агента
Добавить структурированный тег с ID заведения в ответах агента и рендерить по нему карточку заведения в чате.
Цель
Сделать вывод заведений в AI-чате управляемым и структурированным: вместо произвольного текста/ссылок агент должен уметь явно ссылаться на заведение по его внутреннему ID, а интерфейс чата — заменять такую ссылку на полноценную карточку заведения.
Проблема
Сейчас агент при рекомендации или упоминании компаний/заведений отвечает в свободной форме: пишет название текстом, добавляет URL или оформляет ссылку по своему усмотрению. Фронтенд не может надежно определить, что конкретный фрагмент ответа соответствует конкретной записи заведения в базе, поэтому невозможно стабильно показывать карточки.
Предлагаемый контракт
Добавить в протокол ответа агента специальный кастомный тег, содержащий только идентификатор существующего заведения. Например:
<place id="PLACE_ID" />
Название тега можно выбрать в соответствии с текущими соглашениями проекта (place, company, venue и т.п.), но формат должен быть однозначным, машинно-парсируемым и документированным.
Ключевой принцип: LLM сообщает только id, а все отображаемые данные карточки (название, адрес, фото, рейтинг, ссылка, режим работы и т.п.) клиент/сервер получает из актуальных данных приложения. Не передавать эти поля внутри LLM-тега.
Backend / AI
- Дополнить системный prompt/инструкцию агента правилом: когда он рекомендует, перечисляет или предметно упоминает конкретное заведение из доступного ему каталога, использовать кастомный тег с ID этого заведения.
- Убедиться, что в контексте/tools агента ID заведения доступен вместе с данными, по которым модель выбирает заведение.
- Запретить модели придумывать ID. Тег допустим только для ID, полученного из результатов поиска/каталога/tools.
- Определить fallback: если заведение нельзя надежно сопоставить с внутренним ID, агент пишет обычный текст без кастомного тега.
- Сохранить нормальную возможность добавлять поясняющий текст вокруг карточки: например «Для семейного посещения подойдет …» + тег карточки.
Frontend чата
- Распознавать кастомный тег в assistant-message и не выводить его пользователю как сырой текст.
- По
idзагружать/получать данные заведения и рендерить существующую либо новую компактную карточку заведения непосредственно внутри сообщения. - Поддержать несколько карточек в одном ответе, сохраняя порядок относительно текста.
- Карточка должна быть кликабельной и вести на страницу соответствующего заведения.
- Не интерпретировать произвольный HTML от модели: парсер должен поддерживать только явно разрешенный формат тега/атрибутов.
- Если ID не существует, запись удалена или загрузка карточки завершилась ошибкой — сообщение не должно ломаться. Использовать безопасный fallback (например, скрыть невалидный тег либо показать нейтральную текстовую заглушку — выбрать единообразное поведение).
- Учесть 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 для обработки ответа модели.