Перейти к содержимому
DedicatedPHP Контакт

Диалоговая память в PHP: как изолировать контекст между пользователями и организациями

Спроектируйте память PHP-ассистента так, чтобы каждый диалог учитывал личность пользователя, организацию и актуальные разрешения — без смешения контекста.

Концептуальная схема диалогов, разделённых по пользователям и организациям в PHP-приложении

Если ассистент отвечает информацией о другом человеке или компании, проблема не ограничивается качеством ответа: это может быть нарушением конфиденциальности и контроля доступа. В многопользовательском PHP-приложении к диалоговой памяти следует относиться как к данным с определёнными владельцем, областью действия и жизненным циклом. Недостаточно просто сохранять сообщения и извлекать их по идентификатору сессии.

Изоляция диалогового контекста в PHP-приложениях означает, что материалы, используемые для генерации ответа, относятся к текущему диалогу и по-прежнему разрешены к использованию. К ним относятся предыдущие сообщения, сводки и любые извлечённые данные. Поиск по документам может дополнять контекст, но у него другие правила доступа и цели.

Разделяйте диалоговую память, историю и документы

Разделяйте диалоговую память, историю и документы — guía visual de DedicatedPHP

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

Такое разграничение упрощает принятие решений, которые можно проверить. Историю можно хранить для аудита или нужд продукта, одновременно ограничивая объём памяти, передаваемой модели. Кроме того, для каждого фрагмента документа можно сохранять сведения об источнике и контроле доступа. Если смешать источники, не обозначив их, будет трудно объяснить, какие данные повлияли на ответ, и применить политику хранения.

Определите владельца каждого диалога

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

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

Веб-сессия может использоваться для аутентификации запроса, но не должна быть единственным ключом для постоянного определения владельца. Пользователь может выйти из системы, сменить организацию или потерять разрешение, пока диалог всё ещё хранится. При каждом запросе приложение должно проверять, может ли текущий пользователь получить доступ к этому диалогу в активной организации.

Изменения контекста следует определить явно. Если пользователь меняет организацию, может ли он продолжить предыдущий диалог, должен ли открыть новый или ему разрешено сохранить только утверждённую сводку? Универсального ответа нет: всё зависит от продукта и требований к конфиденциальности. Решение должно быть закреплено в проверяемых правилах, а не основываться на неявном предположении интерфейса.

Применяйте ограничения доступа до извлечения контекста

Авторизация должна быть частью запроса, извлекающего данные, и выполняться до их добавления в prompt. Сначала извлечь широкий набор данных, а затем отфильтровать его в PHP — значит повысить риск того, что из-за ошибки, альтернативного пути выполнения или записи в журнал будут раскрыты чужие данные.

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

SELECT message_id, role, content, created_at
FROM conversation_messages
WHERE conversation_id = :conversation_id
  AND organization_id = :organization_id
  AND conversation_id IN (
      SELECT conversation_id
      FROM conversations
      WHERE owner_user_id = :user_id
         OR EXISTS (/* проверить действующее разрешение */)
  )
ORDER BY created_at DESC
LIMIT :message_limit

Реальная схема может отличаться, но важно, чтобы область доступа ограничивала извлекаемые записи. Используйте связанные параметры и единый слой авторизации для операций чтения, записи, экспорта и удаления. Если используется векторная база данных или поисковый сервис, ограничения по организации и разрешениям нужно применять непосредственно в запросе на извлечение; недостаточно попросить модель игнорировать неразрешённые результаты.

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

Предотвращайте смешение данных в кэшах, очередях и общих компонентах

Корректный доступ к базе данных сам по себе не гарантирует изоляцию, если другие уровни используют общее состояние. Ключа кэша вроде conversation:123 может быть недостаточно, если один идентификатор используется в нескольких областях, повторно применяется или настройки кэша допускают коллизии. Включайте в ключ идентификаторы организации и диалога, а при необходимости — версию разрешений или данных. Не включайте конфиденциальное содержимое в ключ.

Передавайте фоновой задаче минимальный и явно заданный контекст: организацию, диалог, инициатора и цель задачи. При выполнении повторно проверяйте, сохраняет ли задача право доступа и не использует ли она идентичность из общего глобального состояния. Избегайте статических переменных, singleton-сервисов с изменяемыми пользовательскими данными и повторно используемых между запросами объектов сессии — особенно в долгоживущих PHP-процессах.

Заранее определите также поведение при ошибках и повторных попытках. При повторном запуске задачи её организация не должна меняться из-за состояния другого процесса, а ответ в кэше не должен оставаться доступным после отзыва разрешения, если политика требует его инвалидировать. Поведение должно быть преднамеренным и наблюдаемым.

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

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

Если используются сводки, сохраняйте сведения об их области действия и происхождении и обновляйте их по понятным правилам. Сводка не является источником истины и не заменяет проверку разрешений. Для критически важных данных обращайтесь к авторизованной системе учёта, а не полагайтесь на утверждение, сохранённое в предыдущем диалоге. Небольшое окно контекста снижает объём раскрываемых данных и расходы, но не компенсирует ошибочную авторизацию.

Проверяйте, что один диалог не влияет на другой

Тесты должны охватывать как разрешённый, так и запрещённый доступ. Создайте сценарии с разными пользователями и организациями и проверьте, что запрос не извлекает сообщения, сводки или документы, относящиеся к другой личности. Учтите крайние случаи:

  • Пользователь меняет организацию и снова открывает старый идентификатор диалога.
  • Разрешение отзывают после создания диалога, но до извлечения его памяти.
  • Два пользователя одновременно обращаются к системе и используют общие процессы, кэш или инфраструктуру очередей.
  • Для несуществующего или чужого идентификатора возвращается контролируемый ответ, не раскрывающий, что он принадлежит другому пользователю.
  • Задача запускается повторно после изменения разрешений или организации.

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

Расследуйте инциденты, не записывая конфиденциальное содержимое

Расследуйте инциденты, не записывая конфиденциальное содержимое — guía visual de DedicatedPHP

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

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

Безопасная архитектура явно определяет владельца, область действия и актуальность каждого фрагмента контекста. Применение этих правил в запросах, кэшах и асинхронных задачах, а также проверка сценариев с отказом доступа превращают изоляцию в проверяемое свойство PHP-приложения, а не в ожидание, возлагаемое на ассистента.

Хотите применить эти идеи в своем проекте?Давайте обсудим вашу PHP-платформу.
Посмотреть связанные услуги