Примечание.
Для доступа к этой странице требуется авторизация. Вы можете попробовать войти или изменить каталоги.
Для доступа к этой странице требуется авторизация. Вы можете попробовать изменить каталоги.
По умолчанию каждая вызывающая сторона получает собственный сеанс размещённого агента, как описано в разделе Изолировать сеансы размещённого агента для каждого пользователя. Приложения, обслуживающие многих пользователей — бот Teams, шлюз isV или платформу поддержки клиентов, не требуют одного сеанса для каждого пользователя. Вместо этого служба среднего уровня сопоставляет многих пользователей с ограниченным пулом общих сеансов и определяет каждого пользователя по каждому вызову.
В этой статье показано, как организовать пул сеансов для пользователей на уровне среднего слоя приложения, при этом данные каждого пользователя остаются изолированными в рамках общего сеанса.
Платформа изолирует состояние беседы для каждого пользователя, даже если пользователи используют один и тот же сеанс: цепочку ответов, которую создал один пользователь, другой пользователь не может продолжить через previous_response_id, а context.get_history() возвращает только ту историю, которую пользователь текущего запроса имеет право видеть. В вашей зоне ответственности находятся две вещи: сопоставление пользователя с сеансом в вашем промежуточном слое и разбиение любых данных, которые контейнер хранит сам (файлы, записи или кэш), помимо этого управляемого платформой состояния сеанса.
Полностью работоспособный пример мультиплексирования сеансов демонстрирует обе стороны: пул сеансов промежуточного уровня и обработчик контейнера, а в этой статье по ходу изложения приводятся ссылки на его файлы.
Prerequisites
- Размещенный агент, использующий протокол контейнера версии 2.0.0. Чтобы выполнить обновление, см. раздел Миграция размещенных агентов.
- Разрешение
Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/action, назначенное удостоверению вашей службы промежуточного уровня. Это разрешение не входит во встроенные роли; предоставьте его через настраиваемую роль — см. раздел Делегирование идентичности конечного пользователя. Без него заголовокx-ms-user-identityотклоняется с сообщением403. - Клиентская библиотека Azure AI Projects для промежуточного уровня и Azure AI AgentServer SDK для контейнера (
azure-ai-agentserver-core2.0.0b7+ для Python илиAzure.AI.AgentServer.Core1.0.0-beta.26+ для .NET). - Развернутый агент для проведения тестирования. Изоляция не применяется для локальных запусков.
Изоляция двух пользователей в общем сеансе
Начните с основного принципа работы: два пользователя — назовём их Алисой и Бобом, пользователи, от имени которых выполняются действия в примере, — могут совместно использовать один agent_session_id, и при этом платформа по-прежнему сохраняет конфиденциальность разговора каждого пользователя. Промежуточный уровень определяет пользователя, от имени которого выполняется каждый вызов, с помощью заголовка x-ms-user-identity (делегирование). Чтобы продолжить разговор пользователя, его предыдущий ответ передаётся в качестве previous_response_id.
Чтобы отправить тот же заголовок удостоверения из интерфейса командной строки разработчика Azure, используйте azd ai agent invoke --user-identity. См. передачу удостоверения конечного пользователя, чтобы увидеть примеры и описание того, как выбрать состояние сеанса и беседы пользователя.
Минимальный клиент invoke_previous_response_isolation.py в примере отправляет именно это, используя клиент Responses пакета SDK, привязанный к агенту:
# Agent-bound Responses client from the Foundry SDK.
responses_client = project_client.get_openai_client(agent_name=agent_name).responses
# Target the shared session with agent_session_id, and identify the acted-for
# user with x-ms-user-identity (delegation). Pass previous_response_id to
# continue this user's own chain. Don't send x-agent-user-id; Foundry sets the
# container-side request context after it resolves the user.
kwargs = {
"input": user_message,
"stream": False,
"store": True,
"extra_body": {"agent_session_id": session_id},
"extra_headers": {"x-ms-user-identity": user_id},
}
if previous_response_id:
kwargs["previous_response_id"] = previous_response_id
response = responses_client.create(**kwargs)
Платформа связывает каждую цепочку ответов с пользователем, создавшим его. Если Боб отправляет previous_response_id Алисы, находясь в том же сеансе, вызов завершается ошибкой — Боб не может продолжить разговор, начатый Алисой. Это гарантирует отсутствие дополнительного кода изоляции в контейнере.
Масштабирование до многих пользователей с пулом сеансов
Изоляция двух пользователей в рамках одного сеанса — это базовый элемент. Чтобы обслуживать многих пользователей, объедините их через ограниченный набор сеансов вместо открытия одного сеанса на пользователя.
Каждый сеанс засчитывается в региональные лимиты на количество одновременных сеансов, пока активно обрабатывает запрос, поэтому модель «один сеанс на пользователя» не масштабируется. Поскольку пользователи читают, думают и вводят текст между сообщениями, пиковое число одновременных запросов обычно составляет лишь небольшую долю от общего числа пользователей. Настройте размер пула в соответствии с этим пиковым значением, затем сопоставьте каждого пользователя с сеансом в этом пуле и передавайте идентификатор пользователя при каждом вызове, точно как в предыдущем разделе.
Решите, как сопоставить пользователей с сеансами. К общим стратегиям относятся:
- Липкий, наименее загруженный. Возвращающий пользователь повторно использует сеанс; новые пользователи отправляются в наименее загруженный сеанс. Эта стратегия равномерно распределяет нагрузку и сохраняет все реплики пользователя вместе. Увеличивайте пул, когда число сеансов достигает лимита на пользователя.
- На основе хеша. Назначьте сеанс с
hash(user_id) % pool_size. Эта стратегия проста и не хранит состояние, но нагрузка может распределяться неравномерно, а при изменении размера пула пользователи перераспределяются. - По кругу. Равномерно распределяйте запросы по пулу. Эта стратегия проста, но реплики пользователя могут попадать в разные сеансы.
- На базе групп. Маршрутизация по клиенту, команде или региону, чтобы связанные пользователи поделились сеансами. Эта стратегия полезна, когда пользователи в группе имеют общий контекст.
Компонент invoke_session_pool.py caller в примере реализует назначение, управляемое вызывающей стороной, с двумя стратегиями: sticky-fill и round-robin. Возвращающийся пользователь всегда сохраняет свой сеанс; Новый пользователь помещается выбранной стратегией. Алгоритм sticky-fill заполняет наименее загруженный сеанс и открывает новый только тогда, когда каждый сеанс заполнен до предела:
def get_session_for_user(self, user_id: str) -> str:
if user_id in self.user_to_session:
return self.user_to_session[user_id] # returning user is sticky
session_id = self._next_fill_session() # new user: place by strategy
self.user_to_session[user_id] = session_id
self.session_user_counts[session_id] += 1
return session_id
def _next_fill_session(self) -> str:
# Reuse a session with capacity; open a new one only when all are full.
session_id = next(
(s for s, count in self.session_user_counts.items()
if count < self.max_users_per_session),
None,
)
if session_id is None:
session_id = self._session_name(len(self.session_user_counts))
self.session_user_counts[session_id] = 0
return session_id
Передайте возвращённый идентификатор сеанса в тот же делегированный вызов, показанный ранее: он становится agent_session_id в extra_body, а x-ms-user-identity остаётся идентификатором для каждого пользователя.
Обработайте запрос в своем контейнере
В протоколе 2.0.0 платформа определяет пользователя, от имени которого выполняется действие, и передаёт его вашему обработчику через get_request_context(). Проверьте наличие контекста (если он отсутствует, например при локальных запусках, должен применяться безопасный отказ), а затем позвольте платформе вернуть историю для конкретного пользователя с context.get_history(). Обработчик main.py в примере не хранит собственное состояние диалога:
from azure.ai.agentserver.core import get_request_context
@app.response_handler
async def handler(request, context, _cancellation_signal):
ctx = get_request_context()
if not (ctx.user_id and ctx.call_id):
# Hosted protocol 2.0.0 populates this context; off-platform it's absent.
raise ValueError("A user context is required on protocol 2.0.0.")
user_input = await context.get_input_text() or "Hello!"
history = await context.get_history() # platform-authorized for this user
input_items = _build_input(user_input, history)
response = _responses_client.create(model=_model, input=input_items, store=False)
return TextResponse(context, request, text=response.output_text)
Поскольку платформа выполняет авторизацию context.get_history() для каждого запроса, пользователь в совместно используемом сеансе никогда не получает историю переписки другого пользователя.
Разделяйте данные отдельных пользователей, которые хранит ваш контейнер
Платформа изолированно хранит вашу историю разговоров. Если контейнер также хранит собственные данные — файлы, строки базы данных или кэш, то эти данные не секционируются автоматически. Используйте в качестве ключа одновременно идентификатор сессии и идентификатор пользователя, чтобы два пользователя в одной сессии не могли видеть данные друг друга:
partition = (agent_session_id, user_id)
Предупреждение
Когда пользователи совместно используют сеанс, платформа не секционирует данные, которые хранятся в контейнере. Если в вашем контейнере для этих данных в качестве ключа используется только идентификатор сеанса, каждый пользователь в пуле будет видеть одни и те же данные. Всегда включайте идентификатор пользователя в ключ секционирования.
Считывает идентификатор пользователя из контекста платформы каждого запроса:
from azure.ai.agentserver.core import get_request_context
def partition_key() -> tuple[str, str]:
ctx = get_request_context()
if not ctx or not ctx.user_id:
raise PermissionError("A user context is required on protocol 2.0.0.")
return (ctx.session_id, ctx.user_id) # key all user-owned data by this
Платформа также добавляет пользователя в заголовок запроса x-agent-user-id. Если среда выполнения не использует контекст пакета SDK, прочитайте этот заголовок напрямую.
Платформа заполняет get_request_context().user_id по протоколу 2.0.0. Никогда не используйте идентификатор сеанса только для данных, принадлежащих пользователю, когда несколько пользователей могут входить в сеанс.
Готовый пример реализации посеансового хранения, который можно взять за основу, см. в примере агента для ведения заметок. Он ключит один файл на сеанс в разделе $HOME. Для общего сеанса расширьте этот ключ с идентификатором пользователя из контекста запроса, чтобы каждый пользователь получил свою собственную секцию.
Проверка изоляции
Подтвердите гарантию с помощью A-A-B-теста образца, invoke_previous_response_isolation.py. Запустите это на вашем развернутом агенте с двумя разными пользователями (по умолчанию в примере используются Alice и Bob):
- От имени Алисы создайте ответ в совместном сеансе и зафиксируйте его
id. - От имени Алисы создайте второй ответ в том же сеансе, установив
previous_response_idравнымidпервого ответа, и сохраните егоid. - От имени Боба в том же сеансе отправьте запрос, установив значение
previous_response_idравным второму ответу Алисы. Вызов не удаётся — Боб не может продолжить цепочку вызовов Алисы.
Используйте два разных пользователя Entra или идентификаторы объектов. Две метки, которые указывают на одну и ту же учетную запись, не являются корректным тестом для разных пользователей.
Отправка устаревшего заголовка изоляции по пути протокола 2.0.0 приводит к ошибке, поскольку эта модель была заменена контекстом пользователя платформы.
Связанный контент
- Изолируйте сеансы размещённых агентов для каждого пользователя для модели изоляции по умолчанию — отдельно для каждого вызывающего абонента.
- Пример мультиплексирования сеансов для полного пула сеансов среднего уровня, обработчика контейнеров и теста изоляции.
- Пример агента для ведения заметок для контейнера, который сохраняет пользовательские данные в рамках каждого сеанса (Python и C#).
- Квоты и ограничения для Foundry Agent Service о региональных ограничениях на количество параллельных сеансов.
- Перенесите размещённые агенты, чтобы перевести контейнер на протокол 2.0.0.
- Спецификация среды выполнения размещённого агента, определяющая заголовки платформы и переменные среды, которые получает контейнер.