Как сохранять историю клиента в онлайн-чате
Разбираем, как идентифицировать посетителя, продолжать старый диалог и не создавать новое обращение при каждом возвращении на сайт.
Редакция Kanyman · об издателе

Коротко
Клиент уже общался с поддержкой, вернулся через несколько дней и ожидает продолжения. Если виджет открывает пустой чат, а первое новое сообщение создаёт отдельное обращение, команда теряет контекст, а клиент вынужден повторяться. Это не косметическая проблема интерфейса, а разрыв в модели идентификации и хранения диалога.
Почему история пропадает
Веб-чат часто начинает работу с анонимной сессии. Браузер получает локальный идентификатор, сервер создаёт обращение, а сообщения связываются с этой парой. Если идентификатор очищается, меняется формат ключа или сервер не умеет восстановить прежнюю сессию, следующий визит выглядит как новый клиент.
Ещё одна распространённая ошибка — связывать историю только с открытой вкладкой. После перезагрузки или истечения короткой сессии интерфейс теряет ссылку на обращение, хотя сами сообщения продолжают храниться в базе данных.
- локальный идентификатор посетителя не переживает повторный визит;
- сессия чата существует отдельно от устойчивой личности клиента;
- виджет создаёт обращение до попытки восстановить прежнее;
- история загружается только после первого нового сообщения;
Что нужно сохранять
Минимальная модель состоит из устойчивого идентификатора посетителя, активного обращения и истории сообщений. При открытии виджета клиент передаёт идентификатор, сервер ищет доступный диалог и возвращает сообщения до того, как пользователь что-либо написал.
Для авторизованного клиента лучше использовать внешний идентификатор из продукта: ID пользователя, аккаунта или организации. Для анонимного посетителя подходит случайный токен в cookie или localStorage. Его нельзя превращать в открытый последовательный ID обращения: токен должен быть непредсказуемым и проверяться сервером.
- visitor_id — устойчивый идентификатор браузера или пользователя;
- conversation_id — конкретный диалог в рамках канала;
- contact_id — единый профиль клиента для нескольких каналов;
- последовательность сообщений и служебных событий;
Как должен выглядеть повторный визит
Сначала виджет восстанавливает локальный токен, затем запрашивает состояние диалога. Если прежнее обращение доступно, интерфейс сразу показывает историю. Если оно закрыто, продукт может открыть историю в режиме чтения и явно предложить создать новое обращение — это лучше, чем молча смешивать разные вопросы.
При отправке сообщения сервер повторно проверяет связь клиента и диалога. Такая проверка защищает от ситуации, когда старый токен существует, но обращение уже перенесено, удалено или недоступно текущему проекту.
Как это устроено в Канимане
Каниман рассматривает веб-чат как один из каналов проекта. Диалог принадлежит клиентскому контуру, а не отдельной загрузке страницы. Поэтому агент может продолжать разговор, использовать накопленную историю и при необходимости передать обращение оператору вместе с контекстом.
Для команды результат простой: меньше дублей, меньше повторных вопросов и понятная история контакта. Для клиента — ощущение, что поддержка действительно помнит предыдущий разговор.
Сценарий восстановления по шагам
При первом открытии виджет создаёт случайный идентификатор посетителя и сохраняет его в браузере. Сервер связывает идентификатор с контактом и обращением, но не раскрывает внутренний номер диалога клиенту. При следующем открытии виджет сначала запрашивает доступное состояние, а уже затем показывает форму отправки сообщения.
Для авторизованного пользователя внешний ID лучше передавать с сервера продукта в подписанном контексте. Тогда человек сможет увидеть тот же разговор на другом устройстве после входа в аккаунт, а случайный посетитель останется привязан только к своему браузеру.
- восстановить идентификатор до создания нового обращения;
- проверить принадлежность диалога текущему проекту и клиенту;
- загрузить сообщения и статус до первого нового ввода;
- явно отделить закрытый вопрос от нового обращения;
Что проверить перед запуском
Пройдите один и тот же путь в обычном и приватном окне, после перезагрузки страницы и повторного входа в аккаунт. Отдельно проверьте истечение сессии, закрытое обращение и переход диалога к сотруднику. В каждом случае интерфейс должен объяснять состояние, а не молча создавать дубль.
Для контроля результата полезно следить за долей повторных обращений, которые удалось связать с существующим контактом, количеством дублей и случаями, когда оператору пришлось заново спрашивать уже известные данные. Эти показатели показывают качество восстановления лучше, чем общее число сообщений.
- история появляется до отправки нового сообщения;
- чужой токен не открывает чужой диалог;
- закрытый диалог имеет понятный режим продолжения;
- оператор видит тот же контекст, что и AI-агент;
Типовые ошибки реализации
Не храните единственную связь с обращением только в React-состоянии, открытой вкладке или короткой серверной сессии. Эти уровни полезны для интерфейса, но не заменяют устойчивую модель контакта и проверку доступа на сервере. Иначе история исчезнет при обновлении, а попытка быстро восстановить её создаст риск открыть чужой диалог.
Другая ошибка — считать любое новое сообщение продолжением последнего обращения. Между визитами может измениться проект, тема или статус вопроса. Сервер должен решать, можно ли продолжить диалог, а интерфейс — явно показать пользователю, когда создаётся новое обращение.
- не использовать последовательный conversation_id как секрет доступа;
- не создавать новый диалог до завершения попытки восстановления;
- не смешивать историю разных проектов и каналов без проверки;
- не скрывать от клиента статус закрытого или переданного обращения;
