Remarque
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de vous connecter ou de modifier des répertoires.
L’accès à cette page nécessite une autorisation. Vous pouvez essayer de modifier des répertoires.
Par défaut, chaque appelant obtient sa propre session d’agent hébergé, comme décrit dans Isoler les sessions d’agent hébergées par utilisateur. Les applications qui servent de nombreux utilisateurs - un bot Teams, une passerelle ISV ou une plateforme de support client - n’ont pas besoin d’une session par utilisateur. Au lieu de cela, un service de niveau intermédiaire mappe de nombreux utilisateurs à un pool limité de sessions partagées et identifie chaque utilisateur sur chaque appel.
Cet article explique comment regrouper des sessions entre les utilisateurs de votre niveau intermédiaire tout en conservant les données de chaque utilisateur isolées à l’intérieur d’une session partagée.
La plateforme isole l’état de conversation pour vous, même lorsque les utilisateurs partagent une session : une chaîne de réponse qu’un utilisateur crée ne peut pas être poursuivie par un autre utilisateur via previous_response_id, et context.get_history() retourne uniquement l’historique que l’utilisateur de la demande actuelle est autorisé à voir. Vous possédez deux choses : le mappage utilisateur-à-session dans votre niveau intermédiaire et le partitionnement de toutes les données que votre conteneur stocke lui-même (fichiers, lignes ou cache) au-delà de cet état de conversation géré par la plateforme.
Un exemple complet et entièrement fonctionnel de multiplexage de session montre les deux aspects — le pool de sessions de la couche intermédiaire et le gestionnaire de conteneur — et cet article propose des liens vers ses fichiers au fil de la lecture.
Prerequisites
- Agent hébergé qui utilise le protocole conteneur version 2.0.0. Pour effectuer la mise à niveau, consultez Migrer les agents hébergés.
- Autorisation
Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/actionaffectée à l'identité de votre service de niveau intermédiaire. Cette autorisation n’est pas incluse dans les rôles intégrés ; accordez-le via un rôle personnalisé : consultez Déléguer l’identité de l’utilisateur final. En son absence, l’en-têtex-ms-user-identityest rejeté avec un403. - La bibliothèque cliente Azure AI Projects pour le niveau intermédiaire, ainsi que le KIT SDK AZURE AI AgentServer pour le conteneur (
azure-ai-agentserver-core2.0.0b7+ pour Python, ouAzure.AI.AgentServer.Core1.0.0-beta.26+ pour .NET). - Un agent déployé sur lequel effectuer des tests. L’isolation n’est pas appliquée lors des exécutions en local.
Isoler deux utilisateurs dans une session partagée
Commencez par le comportement principal : deux utilisateurs - les appellent Alice et Bob, les utilisateurs ayant agi dans l’exemple - peuvent partager un agent_session_id, et la plateforme conserve toujours la conversation privée de chaque utilisateur. Votre niveau intermédiaire identifie, dans chaque appel, l’utilisateur pour le compte duquel l’action est effectuée à l’aide de l’en-tête x-ms-user-identity (délégation). Pour poursuivre la conversation d’un utilisateur, la réponse précédente de cet utilisateur est transmise sous la forme de previous_response_id.
L'appelant invoke_previous_response_isolation.py minimal de l'exemple envoie exactement cela à l'aide du client Responses lié à l'agent du 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)
La plateforme lie chaque chaîne de réponse à l’utilisateur qui l’a créée. Si Bob envoie celui d’Alice previous_response_id tout en étant dans la même session, l’appel échoue : Bob ne peut pas poursuivre la conversation d’Alice. Cette garantie s’applique sans aucun code d’isolation supplémentaire dans votre conteneur.
Passez à l'échelle pour de nombreux utilisateurs grâce à un pool de sessions.
L’isolation de deux utilisateurs dans une session est le bloc de construction. Pour servir de nombreux utilisateurs, poolez-les sur un ensemble limité de sessions au lieu d’ouvrir une session par utilisateur.
Chaque session est comptabilisée dans les limites régionales de sessions simultanées tant qu'elle traite activement un tour ; une session par utilisateur ne passe donc pas à l'échelle. Étant donné que les utilisateurs lisent, pensent et tapent entre deux tours d’échange, votre pic de requêtes simultanées ne représente généralement qu’une faible fraction de votre nombre total d’utilisateurs. Dimensionner un pool à ce pic, puis mapper chaque utilisateur à une session dans celle-ci et transmettre l’identité de cet utilisateur à chaque appel, exactement comme dans la section précédente.
Décidez comment associer les utilisateurs à des sessions. Les stratégies courantes sont les suivantes :
- Persistante, avec la session la moins chargée. Un utilisateur retourné réutilise sa session ; les nouveaux utilisateurs accèdent à la session la moins chargée. Cette stratégie répartit uniformément la charge tout en conservant les interactions d’un même utilisateur sur la même session. Augmentez le pool lorsque les sessions atteignent une limite par utilisateur.
- Basée sur un hachage. Affecter une session avec
hash(user_id) % pool_size. Cette stratégie est simple et sans état, mais la charge peut être répartie de manière inégale et le redimensionnement du pool réaffecte les utilisateurs. - Tourniquet (round robin). Répartissez uniformément les requêtes dans le groupe. Cette stratégie est simple, mais les interactions d’un même utilisateur peuvent être traitées par des sessions différentes.
- Basé sur les groupes. Effectuez le routage par locataire, équipe ou région afin que les utilisateurs associés partagent les mêmes sessions. Cette stratégie est utile lorsque les utilisateurs d’un groupe partagent un contexte.
L'appelant invoke_session_pool.py de l'exemple implémente une affectation contrôlée par l'appelant avec deux stratégies, sticky-fill et round-robin. Un utilisateur retourné conserve toujours sa session ; un nouvel utilisateur est placé par la stratégie sélectionnée. La stratégie sticky-fill remplit d’abord la session la moins chargée et n’ouvre une nouvelle session que lorsque chaque session a atteint sa capacité maximale :
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
Injectez l’ID de session renvoyé dans le même appel avec délégation mentionné plus haut : il devient agent_session_id dans extra_body, et x-ms-user-identity reste l’identifiant propre à chaque utilisateur.
Gérer la requête dans votre conteneur
Avec le protocole 2.0.0, la plateforme résout l’utilisateur pour le compte duquel l’action est effectuée et l’expose à votre routine de traitement par l’intermédiaire de get_request_context(). Vérifiez la présence de ce contexte (en appliquant un refus par défaut lorsqu’il est absent, par exemple lors des exécutions locales), puis laissez la plateforme renvoyer l’historique propre à chaque utilisateur avec context.get_history(). Le gestionnaire main.py de l'exemple ne conserve aucun état de conversation :
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)
Étant donné que la plateforme autorise context.get_history() pour chaque requête, un utilisateur dans une session partagée ne reçoit jamais l’historique de conversation d’un autre utilisateur.
Partitionnez les données de chaque utilisateur stockées dans vos conteneurs de stockage
La plateforme isole l’historique des conversations pour vous. Si votre conteneur stocke également ses propres données ( fichiers, lignes de base de données ou cache), ces données ne sont pas partitionnée automatiquement. Clé-la à la fois par l’ID de session et l’ID utilisateur afin que deux utilisateurs de la même session ne puissent pas voir les données des autres :
partition = (agent_session_id, user_id)
Warning
Lorsque les utilisateurs partagent une session, la plateforme ne partitionne pas les données que votre conteneur stocke elle-même. Si votre conteneur indexe ces données uniquement par ID de session, chaque utilisateur du groupe voit les mêmes données. Incluez toujours l’ID utilisateur dans la clé de partition.
Lisez l’identifiant utilisateur depuis le contexte de plateforme propre à la requête :
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
La plateforme injecte également l’utilisateur dans l’en-tête de requête x-agent-user-id. Si votre runtime n’utilise pas le contexte du Kit de développement logiciel (SDK), lisez cet en-tête directement.
La plateforme renseigne get_request_context().user_id avec le protocole 2.0.0. N’utilisez jamais l’ID de session seul pour les données appartenant à l’utilisateur lorsque plusieurs utilisateurs peuvent entrer dans la session.
Pour un exemple concret de stockage par session sur lequel vous appuyer, consultez l’exemple d’agent de prise de notes. Elle crée un fichier par session sous $HOME. Pour une session partagée, étendez cette clé avec l’ID utilisateur du contexte de requête afin que chaque utilisateur obtienne sa propre partition.
Vérifier l’isolation
Confirmez la garantie avec le test A-A-B de l’échantillon. invoke_previous_response_isolation.py Exécutez-le sur votre agent déployé avec deux utilisateurs distincts (l’exemple par défaut est Alice et Bob) :
- En tant qu’Alice, créez une réponse dans une session partagée et capturez son
id. - En tant qu'Alice, créez une deuxième réponse dans la même session avec
previous_response_iddéfini sur leidde la première réponse, puis récupérez sonid. - En tant que Bob, dans la même session, envoyez une requête avec
previous_response_iddéfini sur la deuxième réponse d'Alice. L'appel échoue : Bob ne peut pas poursuivre la chaîne d'Alice.
Utilisez deux ID d’objet ou d’utilisateurs Entra différents. Deux étiquettes qui renvoient à la même identité ne constituent pas un test croisé entre utilisateurs valide.
L’envoi d’un ancien en-tête d’isolation sur un point de terminaison utilisant le protocole 2.0.0 renvoie une erreur, car ce modèle est remplacé par le contexte utilisateur fourni par la plateforme.
Contenu connexe
- Isoler les sessions d’agent hébergées par utilisateur pour le modèle d’isolation par appel par défaut.
- Exemple de multiplexage de sessions pour le pool de sessions complet du niveau intermédiaire, le gestionnaire de conteneur et le test d'isolation.
- Exemple d’agent de prise de notes pour un conteneur qui conserve les données détenues par l’utilisateur par session (Python et C#).
- Quotas et limites du service de l’agent Foundry pour connaître les limites régionales de sessions simultanées.
- Migrez les agents hébergés pour déplacer un conteneur vers le protocole 2.0.0.
- Contrat d’exécution de l’agent hébergé pour les en-têtes de plateforme et les variables d’environnement qu’un conteneur reçoit.