Notatka
Dostęp do tej strony wymaga autoryzacji. Może spróbować zalogować się lub zmienić katalogi.
Dostęp do tej strony wymaga autoryzacji. Możesz spróbować zmienić katalogi.
Domyślnie każdy wywołujący ma własną sesję agenta hostowanego, jak opisano w Izolowanie sesji agenta hostowanego dla każdego użytkownika. Aplikacje obsługujące wielu użytkowników — takie jak bot usługi Teams, brama ISV czy platforma obsługi klienta — nie wymagają oddzielnej sesji dla każdego użytkownika. Zamiast tego usługa warstwy pośredniej przypisuje wielu użytkowników do ograniczonej puli współdzielonych sesji i identyfikuje każdego użytkownika przy każdym wywołaniu.
W tym artykule pokazano, jak pulować sesje między użytkownikami z warstwy środkowej przy jednoczesnym zachowaniu izolacji danych każdego użytkownika w sesji udostępnionej.
Platforma izoluje stan konwersacji dla Ciebie, nawet jeśli użytkownicy współużytkowali sesję: łańcuch odpowiedzi tworzony przez jednego użytkownika nie może być kontynuowany przez innego użytkownika za pośrednictwem metody previous_response_idi context.get_history() zwraca tylko historię, którą użytkownik bieżącego żądania ma uprawnienia do wyświetlenia. Posiadasz dwie rzeczy: mapowanie użytkownika do sesji w warstwie pośredniej oraz partycjonowanie wszelkich danych, które kontener przechowuje sam (plików, wierszy lub pamięci podręcznej), poza zarządzanym przez platformę stanem konwersacji.
Kompletny, gotowy do uruchomienia przykład multipleksowania sesji przedstawia obie strony — pulę sesji warstwy pośredniej i moduł obsługi kontenera — a w tym artykule w miarę postępów znajdują się odnośniki do jego plików.
Prerequisites
- Hostowany agent korzystający z protokołu kontenera w wersji 2.0.0. Aby uaktualnić, zobacz Migrowanie hostowanych agentów.
- Uprawnienie
Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/action, przypisane do tożsamości usługi warstwy pośredniej. To uprawnienie nie jest uwzględnione we wbudowanych rolach; przyznaj je za pomocą roli niestandardowej — zobacz Delegowanie tożsamości użytkownika końcowego. Bez niego nagłówekx-ms-user-identityzostaje odrzucony z komunikatem403. - Biblioteka klienta Azure Projekty sztucznej inteligencji dla warstwy środkowej oraz zestaw SDK Azure AI AgentServer dla kontenera (
azure-ai-agentserver-core2.0.0b7+ dla Python lubAzure.AI.AgentServer.Core1.0.0-beta.26+ dla .NET). - Wdrożony agent do przeprowadzania testów. Izolacja nie jest egzekwowana podczas uruchomień lokalnych.
Izolowanie dwóch użytkowników w sesji udostępnionej
Zacznij od podstawowego mechanizmu działania: dwóch użytkowników — nazwijmy ich Alicją i Bobem, użytkownikami, w imieniu których działa się w tym przykładzie — może współdzielić jedno agent_session_id, a platforma nadal zachowuje prywatność rozmowy każdego z nich. Warstwa pośrednia identyfikuje użytkownika, w imieniu którego wykonywane jest działanie, w każdym wywołaniu za pomocą nagłówka x-ms-user-identity (delegacja). Aby kontynuować konwersację użytkownika, przekazuje poprzednią odpowiedź tego użytkownika jako previous_response_id.
Minimalny invoke_previous_response_isolation.py obiekt wywołujący w przykładzie wysyła dokładnie to przy użyciu klienta odpowiedzi powiązanych z agentem zestawu 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)
Platforma łączy każdy łańcuch odpowiedzi z użytkownikiem, który go utworzył. Jeśli Bob wyśle previous_response_id Alicji, będąc w tej samej sesji, wywołanie nie powiedzie się — Bob nie może kontynuować konwersacji Alicji. Ta gwarancja obowiązuje bez żadnego dodatkowego kodu izolującego w kontenerze.
Skalowanie do wielu użytkowników przy użyciu puli sesji
Izolowanie dwóch użytkowników w jednej sesji to blok konstrukcyjny. Aby obsłużyć wielu użytkowników, należy połączyć je w ograniczony zestaw sesji zamiast otwierać jedną sesję na użytkownika.
Każda sesja wlicza się do regionalnych limitów współbieżnych sesji podczas aktywnego przetwarzania tury, więc jedna sesja na użytkownika nie skaluje się. Ponieważ użytkownicy czytają, myślą i piszą między kolejnymi turami, szczytowa liczba jednoczesnych żądań jest zwykle niewielką częścią łącznej liczby użytkowników. Dobierz wielkość puli pod kątem tego szczytowego obciążenia, a następnie przypisz każdego użytkownika do sesji w tej puli i przekazuj tożsamość tego użytkownika przy każdym wywołaniu, dokładnie tak jak w poprzedniej sekcji.
Zdecyduj, jak mapować użytkowników na sesje. Typowe strategie obejmują:
- Przylepny, najmniej obciążony. Powracający użytkownik ponownie używa swojej sesji; nowi użytkownicy przechodzą do sesji z najmniejszym obciążeniem. Ta strategia równomiernie rozkłada obciążenie i utrzymuje kolejne tury jednego użytkownika razem. Zwiększ pulę, gdy sesje osiągną limit dla poszczególnych użytkowników.
- Oparte na skrótach. Przypisz sesję za pomocą polecenia
hash(user_id) % pool_size. Ta strategia jest prosta i bezstanowa, ale obciążenie może być nierównomierne, a zmiana rozmiaru puli powoduje ponowne przypisanie użytkowników. - Round-robin. Równomiernie rozdzielaj żądania w puli. Ta strategia jest prosta, ale wypowiedzi użytkownika mogą trafiać do różnych sesji.
- Oparte na grupach. Kieruj według dzierżawcy, zespołu lub regionu, aby powiązani ze sobą użytkownicy współdzielili sesje. Ta strategia jest przydatna, gdy użytkownicy w grupie dzielą wspólny kontekst.
Obiekt wywołujący invoke_session_pool.py w przykładzie implementuje przypisanie zarządzane przez obiekt wywołujący za pomocą dwóch strategii, sticky-fill i round-robin. Powracający użytkownik zawsze zachowuje swoją sesję; nowy użytkownik jest umieszczany przez wybraną strategię. Ścieżka sticky-fill wypełnia najmniej załadowaną sesję i otwiera nową tylko wtedy, gdy każda sesja jest w pojemności:
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
Przekaż zwrócony identyfikator sesji do tego samego wywołania delegowanego, które pokazano wcześniej: staje się on agent_session_id w extra_body, a x-ms-user-identity pozostaje identyfikatorem poszczególnego użytkownika.
Obsłuż żądanie w swoim kontenerze
W protokole 2.0.0 platforma ustala użytkownika, w imieniu którego wykonywane jest działanie, i udostępnia go procedurze obsługi za pośrednictwem elementu get_request_context(). Zweryfikuj, czy kontekst jest dostępny (jeśli go brakuje, np. podczas uruchomień lokalnych, zakończ działanie odmową), a następnie pozwól platformie zwrócić historię dla każdego użytkownika za pomocą context.get_history(). Program obsługi main.py w przykładzie nie przechowuje własnego stanu konwersacji:
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)
Ponieważ platforma autoryzuje context.get_history() dla każdego żądania, użytkownik w sesji współdzielonej nigdy nie otrzymuje historii konwersacji innego użytkownika.
Partycjonuj dane poszczególnych użytkowników przechowywane przez kontener
Platforma oddziela historię rozmów dla Ciebie. Jeśli kontener przechowuje również własne dane — pliki, wiersze bazy danych lub pamięć podręczną — te dane nie są automatycznie partycjonowane. Użyj jako klucza zarówno identyfikatora sesji, jak i identyfikatora użytkownika, aby dwóch użytkowników w tej samej sesji nie mogło widzieć nawzajem swoich danych:
partition = (agent_session_id, user_id)
Warning
Gdy użytkownicy udostępniają sesję, platforma nie partycjonuje danych, które przechowuje sam kontener. Jeśli kontener identyfikuje te dane wyłącznie na podstawie identyfikatora sesji, każdy użytkownik w puli widzi te same dane. Zawsze dołączaj identyfikator użytkownika do klucza partycji.
Odczytaj identyfikator użytkownika z kontekstu platformy dla każdego żądania:
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
Platforma dodaje również użytkownika jako nagłówek żądania x-agent-user-id. Jeśli środowisko uruchomieniowe nie używa kontekstu zestawu SDK, przeczytaj ten nagłówek bezpośrednio.
Platforma uzupełnia get_request_context().user_id w protokole 2.0.0. Nigdy nie używaj samego identyfikatora sesji dla danych należących do użytkownika, gdy więcej niż jeden użytkownik może wejść do sesji.
Praktyczny przykład użycia pamięci masowej przypisanej do sesji jako punktu wyjścia znajdziesz w przykładzie agenta do tworzenia notatek. Kluczem jest jeden plik na sesję w obszarze $HOME. W przypadku sesji współdzielonej rozszerz ten klucz o identyfikator użytkownika z kontekstu żądania, aby każdy użytkownik miał własną partycję.
Weryfikowanie izolacji
Potwierdź gwarancję za pomocą testu A-A-B próbki, invoke_previous_response_isolation.py. Uruchom to na wdrożonym agencie z dwoma różnymi użytkownikami (domyślnie w przykładzie są to Alice i Bob):
- Jako Alicja, utwórz odpowiedź we współdzielonej sesji i zarejestruj jej
id. - Jako Alicja, utwórz drugą odpowiedź w ramach tej samej sesji, z
previous_response_idustawionym naidpierwszej odpowiedzi, i przechwyć jejid. - Jako Bob, w tej samej sesji, wyślij żądanie z
previous_response_idustawionym na drugą odpowiedź Alicji. Połączenie kończy się niepowodzeniem — Bob nie może kontynuować łańcucha Alice.
Użyj dwóch różnych użytkowników entra lub identyfikatorów obiektów. Dwie etykiety, które wskazują na tę samą tożsamość, nie stanowią poprawnego testu między różnymi użytkownikami.
Wysłanie starszego nagłówka izolacji w ścieżce protokołu 2.0.0 zwraca błąd, ponieważ ten model jest zastępowany przez kontekst użytkownika platformy.
Treści powiązane
- Izoluj sesje hostowanych agentów dla każdego użytkownika dla domyślnego modelu izolacji dla każdego wywołującego.
- Przykład multipleksowania sesji dla kompletnej puli sesji warstwy pośredniej, obsługi kontenera i testu izolacji.
- Przykład agenta tworzenia notatek dla kontenera, który utrwala dane należące do użytkownika na sesję (Python i C#).
- Limity przydziału i limity dla usługi Foundry Agent Service dotyczące regionalnych limitów jednoczesnych sesji.
- Migrowanie hostowanych agentów w celu przeniesienia kontenera do protokołu 2.0.0.
- Specyfikacja środowiska uruchomieniowego hostowanego agenta określająca nagłówki platformy i zmienne środowiskowe otrzymywane przez kontener.