Nota
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare ad accedere o modificare le directory.
L'accesso a questa pagina richiede l'autorizzazione. È possibile provare a modificare le directory.
Important
Gli elementi contrassegnati (anteprima) in questo articolo sono attualmente in anteprima pubblica. Questa anteprima viene fornita senza un contratto di servizio e non è consigliabile per i carichi di lavoro di produzione. Alcune funzionalità potrebbero non essere supportate o potrebbero avere funzionalità limitate. Per ulteriori informazioni, vedere Condizioni supplementari per l'uso delle versioni di anteprima di Microsoft Azure.
Per impostazione predefinita, ogni chiamante ottiene la propria sessione dell'agente ospitato, come descritto in Isolare le sessioni dell'agente ospitato per utente. Le applicazioni che servono molti utenti, ovvero un bot di Teams, un gateway ISV o una piattaforma di supporto clienti, non richiedono una sessione per utente. Un servizio di livello intermedio esegue invece il mapping di molti utenti a un pool delimitato di sessioni condivise e identifica ogni utente in ogni chiamata.
Questo articolo illustra come raggruppare le sessioni tra gli utenti del livello intermedio mantenendo isolati i dati di ogni utente all'interno di una sessione condivisa.
La piattaforma isola automaticamente lo stato della conversazione, anche quando gli utenti condividono una sessione: una catena di risposte creata da un utente non può essere continuata da un altro utente tramite previous_response_ide context.get_history() restituisce solo la cronologia che l'utente della richiesta corrente è autorizzato a visualizzare. Si è proprietari di due elementi: il mapping da utente a sessione nel livello intermedio e il partizionamento di tutti i dati archiviati dal contenitore stesso (file, righe o cache) oltre lo stato della conversazione gestita dalla piattaforma.
Un esempio completo e utilizzabile di multiplexing di sessioni mostra entrambe le parti, ovvero il pool di sessioni di livello intermedio e l'handler del contenitore, e questo articolo rimanda ai relativi file.
Prerequisites
- Agente ospitato che usa il protocollo contenitore versione 2.0.0. Per eseguire l'aggiornamento, vedere Eseguire la migrazione degli agenti ospitati.
- L'autorizzazione
Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/actionassegnata all'identità del servizio di livello intermedio. Questa autorizzazione non è inclusa nei ruoli predefiniti; concederla tramite un ruolo personalizzato: vedere Delegare l'identità dell'utente finale. Senza di esso, l'intestazionex-ms-user-identityviene rifiutata con un403. - La libreria client Azure Ai Projects per il livello intermedio e Azure AI AgentServer SDK per il contenitore (
azure-ai-agentserver-core2.0.0b7+ per Python oAzure.AI.AgentServer.Core1.0.0-beta.26+ per .NET). - Agente distribuito su cui eseguire il test. L'isolamento non è applicato nelle esecuzioni locali.
Isolare due utenti in una sessione condivisa
Iniziare con il comportamento principale: due utenti, chiamati Alice e Bob, gli utenti che hanno agito nell'esempio, possono condividere uno agent_session_ide la piattaforma mantiene comunque privata la conversazione di ogni utente. Il livello intermedio identifica, in ogni chiamata, l'utente per conto del quale viene eseguita l'azione con l'intestazione x-ms-user-identity (delega). Per continuare la conversazione di un utente, passa la risposta precedente dell'utente come previous_response_id.
L'esempio di chiamante invoke_previous_response_isolation.py minimo invia esattamente questo, utilizzando il client delle risposte associato all'agente dell'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 piattaforma collega ogni catena di risposte all'utente che lo ha creato. Se Bob invia il previous_response_id di Alice mentre si trova nella stessa sessione, la chiamata fallisce: Bob non può proseguire la conversazione di Alice. Questa garanzia si mantiene senza codice di isolamento aggiuntivo nel contenitore.
Ridimensionare a molti utenti con un pool di sessioni
Isolare due utenti all'interno di una sessione è l'elemento fondamentale. Per servire molti utenti, raggruppali in un pool distribuito tra un numero limitato di sessioni anziché aprire una sessione per ogni utente.
Ogni sessione viene conteggiata ai fini dei limiti regionali delle sessioni simultanee mentre elabora attivamente un turno, quindi una sessione per utente non è scalabile. Poiché gli utenti leggono, pensano e digitano tra turni, le richieste simultanee di picco sono in genere una piccola frazione del numero totale di utenti. Dimensionare un pool in base a quel picco, quindi associare ogni utente a una sessione al suo interno e passare l'identità di tale utente in ogni chiamata, esattamente come nella sezione precedente.
Decidi come associare gli utenti alle sessioni. Le strategie comuni includono:
- Persistente con il carico minore. Un utente di ritorno riutilizza la propria sessione; i nuovi utenti vengono indirizzati alla sessione meno carica. Questa strategia distribuisce il carico in modo uniforme e mantiene uniti gli turni di un utente. Aumentare il pool quando le sessioni raggiungono un limite per utente.
- Basato su hash. Assegna una sessione con
hash(user_id) % pool_size. Questa strategia è semplice e senza stato, ma il carico può essere distribuito in modo non uniforme e il ridimensionamento del pool ridistribuisce gli utenti. - Round robin. Distribuisci le richieste uniformemente all'interno del pool. Questa strategia è semplice, ma i turni di un utente possono arrivare in sessioni diverse.
- Basato sul gruppo. Instradare in base a tenant, team o area geografica in modo che gli utenti correlati condividono sessioni. Questa strategia è utile quando gli utenti di un gruppo condividono lo stesso contesto.
Il invoke_session_pool.py chiamante nell'esempio implementa l'assegnazione gestita dal chiamante con due strategie, sticky-fill e round-robin. Un utente di ritorno conserva sempre la propria sessione; un nuovo utente viene assegnato in base alla strategia selezionata. Il percorso sticky-fill riempie la sessione con il carico minore e apre una nuova sessione solo quando tutte le sessioni hanno raggiunto la capacità massima:
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
Inserire l'ID sessione restituito nella stessa chiamata delegata illustrata in precedenza: diventa agent_session_id in extra_bodye x-ms-user-identity rimane l'identificatore per utente.
Gestire la richiesta nel contenitore
Con il protocollo 2.0.0, la piattaforma identifica l’utente per conto del quale viene eseguita l’azione e lo espone alla routine di gestione tramite get_request_context(). Convalidare il contesto (chiudere in caso di errore se mancante, ad esempio nelle esecuzioni locali), quindi lasciare che la piattaforma restituisca la cronologia per utente con context.get_history(). Il gestore main.py nell'esempio non mantiene un proprio stato di conversazione:
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)
Poiché la piattaforma autorizza context.get_history() per richiesta, un utente in una sessione condivisa non riceve mai la cronologia della conversazione di un altro utente.
Partizionare i dati per utente archiviati nel contenitore
La piattaforma isola la cronologia delle conversazioni per te. Se il contenitore archivia anche i propri dati, ovvero file, righe di database o cache, tali dati non vengono partizionati automaticamente. Indicizzalo sia in base all'ID della sessione sia all'ID dell'utente, in modo che due utenti nella stessa sessione non possano vedere i rispettivi dati:
partition = (agent_session_id, user_id)
Avvertimento
Quando gli utenti condividono una sessione, la piattaforma non partiziona i dati archiviati dal contenitore stesso. Se il container indicizza questi dati unicamente in base all'ID di sessione, ogni utente del pool vede gli stessi dati. Includere sempre l'ID utente nella chiave di partizione.
Leggere l'ID utente dal contesto della piattaforma per richiesta:
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 piattaforma inserisce anche l'utente nell'intestazione della richiesta x-agent-user-id. Se il runtime non usa il contesto dell'SDK, legga direttamente questo header.
La piattaforma popola get_request_context().user_id nel protocollo 2.0.0. Non usare mai l'ID sessione solo per i dati di proprietà dell'utente quando più utenti possono accedere alla sessione.
Per un esempio pratico di archiviazione per sessione da usare come base, consulta l'esempio di agente per prendere appunti. Utilizzare una chiave per ogni file per sessione in $HOME. Per una sessione condivisa, estendere tale chiave con l'ID utente dal contesto della richiesta in modo che ogni utente ottenga la propria partizione.
Verificare l'isolamento
Conferma la garanzia tramite il test A-A-B del campione, invoke_previous_response_isolation.py. Eseguilo sul tuo agente già distribuito con due utenti distinti (l'esempio utilizza per impostazione predefinita Alice e Bob):
- Nei panni di Alice, crea una risposta in una sessione condivisa e acquisiscine il
id. - Nei panni di Alice, crea una seconda risposta nella stessa sessione con
previous_response_idimpostato suiddella prima risposta e acquisiscine il relativoid. - Nei panni di Bob, nella stessa sessione, inviare una richiesta con
previous_response_idimpostato sulla seconda risposta di Alice. La chiamata ha esito negativo: Bob non può continuare la catena di Alice.
Usa due utenti Entra diversi o due ID oggetto diversi. Due etichette che rimandano alla stessa identità non costituiscono un test valido tra utenti.
L'invio di un'intestazione di isolamento legacy in un percorso di protocollo 2.0.0 restituisce un errore, perché tale modello viene sostituito dal contesto utente della piattaforma.
Contenuti correlati
- Isolare le sessioni dell'agente ospitato per utente per il modello di isolamento predefinito per ogni chiamante.
- Esempio di multiplexing delle sessioni relativo al pool completo di sessioni di livello intermedio, al gestore del contenitore e al test di isolamento.
- Esempio di agente di acquisizione di note per un contenitore che rende persistenti i dati di proprietà dell'utente per sessione (Python e C#).
- Quote e limiti per il servizio Foundry Agent per i limiti di sessioni simultanee a livello regionale.
- Eseguire la migrazione degli agenti ospitati per spostare un contenitore nel protocollo 2.0.0.
- Contratto del runtime dell'agente ospitato per gli header della piattaforma e le variabili d'ambiente che un contenitore riceve.