Sdružit více uživatelů v jedné relaci hostovaného agenta

Ve výchozím nastavení každý volající získá vlastní relaci hostovaného agenta, jak je popsáno v části Izolace relací hostovaného agenta pro jednotlivé uživatele. Aplikace, které obsluhují mnoho uživatelů – například bot Teams, brána ISV nebo platforma zákaznické podpory – nepotřebují samostatnou relaci pro každého uživatele. Místo toho služba ve střední vrstvě přiřazuje mnoho uživatelů k omezenému fondu sdílených relací a při každém volání identifikuje každého uživatele.

V tomto článku se dozvíte, jak sdílet relace mezi uživateli z vaší střední vrstvy a zároveň zachovat data jednotlivých uživatelů izolovaná uvnitř sdílené relace.

Platforma za vás izoluje stav konverzace, i když uživatelé sdílejí relaci: v řetězci odpovědí, který vytvoří jeden uživatel, nemůže jiný uživatel pokračovat prostřednictvím previous_response_id a context.get_history() vrací pouze historii, kterou je uživatel aktuálního požadavku oprávněn zobrazit. Vlastníte dvě věci: mapování uživatele na relaci ve střední vrstvě a rozdělení všech dat, která váš kontejner ukládá sám (soubory, řádky nebo mezipaměť), nad rámec tohoto stavu konverzace spravovaného platformou.

Kompletní, spustitelná ukázka multiplexování relací demonstruje obě strany – fond relací ve střední vrstvě i obslužnou rutinu kontejneru – a tento článek na její soubory průběžně odkazuje.

Předpoklady

  • Hostovaný agent, který používá protokol kontejneru verze 2.0.0. Pokud chcete upgradovat, přečtěte si téma Migrace hostovaných agentů.
  • Oprávnění Microsoft.CognitiveServices/accounts/AIServices/agents/endpoints/UserIdentityImpersonation/action přiřazená identitě služby střední vrstvy. Toto oprávnění není součástí předdefinovaných rolí; udělte ji prostřednictvím vlastní role – viz Delegování identity koncového uživatele. Bez ní je hlavička x-ms-user-identity odmítnuta s 403.
  • Klientská knihovna Azure AI Projects pro střední vrstvu a Azure AI AgentServer SDK pro kontejner (azure-ai-agentserver-core 2.0.0b7+ pro Python nebo Azure.AI.AgentServer.Core 1.0.0-beta.26+ pro .NET).
  • Nasazený agent k otestování. Izolace se pro místní spuštění nevynucuje.

Izolujte dva uživatele ve sdílené relaci

Začněte základním principem: dva uživatelé – říkejme jim Alice a Bob, zastupovaní uživatelé v ukázce – mohou sdílet jednu agent_session_id a platforma přitom stále zachovává soukromí konverzace každého z nich. Střední vrstva při každém volání identifikuje uživatele, v jehož zastoupení jedná, pomocí hlavičky x-ms-user-identity (zastupování). Chcete-li pokračovat v konverzaci daného uživatele, předchozí odpověď tohoto uživatele se předá jako previous_response_id.

Pokud chcete odeslat stejnou hlavičku identity z rozhraní příkazového řádku pro vývojáře Azure, použijte azd ai agent invoke --user-identity. Příklady a způsob výběru stavu relace a konverzace uživatele najdete v tématu Předání identity koncového uživatele .

invoke_previous_response_isolation.py Minimální volající v ukázce odešle přesně to pomocí klienta odpovědí vázaného na agenta sady 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 prováže každý řetěz odpovědí na uživatele, který ho vytvořil. Pokud Bob odešle Alicino previous_response_id v rámci stejné relace, hovor selže – Bob nemůže pokračovat v Alicině konverzaci. Tato záruka se uchovává bez dalšího izolovaného kódu ve vašem kontejneru.

Škálujte pro velký počet uživatelů pomocí fondu relací

Izolovat dva uživatele v jedné relaci je základní stavební kámen. Pokud chcete obsloužit mnoho uživatelů, sdružte je v rámci omezené sady relací místo otevírání samostatné relace pro každého uživatele.

Každá relace se započítává do regionálních limitů souběžných relací po dobu aktivního zpracování interakce, takže jedna relace na uživatele není škálovatelná. Protože uživatelé mezi jednotlivými tahy čtou, přemýšlejí a píšou, maximální počet souběžných požadavků obvykle tvoří jen malou část celkového počtu uživatelů. Dimenzujte fond podle této špičky, potom každého uživatele namapujte na relaci v něm a při každém volání předávejte identitu tohoto uživatele, přesně jako v předchozí části.

Rozhodněte se, jak přiřadit uživatele k relacím. Mezi běžné strategie patří:

  • Perzistentní, nejméně zatížená. Vrátivší se uživatel znovu použije svou stávající relaci; noví uživatelé jsou přiřazeni k nejméně vytížené relaci. Tato strategie rovnoměrně rozloží zatížení a udržuje uživatele ve spojení. Zvětšte fond, když relace dosáhnou limitu pro jednotlivé uživatele.
  • Založené na hodnotě hash. Přiřaďte relaci pomocí hash(user_id) % pool_size. Tato strategie je jednoduchá a bezstavová, ale zátěž může být nerovnoměrná a změna velikosti fondu způsobí nové přerozdělení uživatelů.
  • Kruhové dotazování. Distribuujte žádosti rovnoměrně napříč fondem. Tato strategie je jednoduchá, ale jednotlivé vstupy uživatele mohou skončit v různých relacích.
  • Na základě skupin. Směrujte podle tenanta, týmu nebo regionu, aby související uživatelé sdíleli relace. Tato strategie je užitečná, když uživatelé ve skupině sdílejí kontext.

Volající invoke_session_pool.py v ukázce implementuje přiřazení vlastněné volajícím se dvěma strategiemi sticky-fill a round-robin. Navracející se uživatel si vždy zachová svou relaci; nový uživatel je umístěn podle vybrané strategie. Strategie sticky-fill zaplní nejméně vytíženou relaci a novou otevře teprve tehdy, když všechny relace dosáhnou své kapacity:

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

Předejte vrácené ID relace do stejného delegovaného volání zobrazeného dříve: stane se agent_session_id v extra_bodya x-ms-user-identity zůstane identifikátorem pro jednotlivé uživatele.

Zpracujte požadavek ve svém kontejneru

V protokolu 2.0.0 platforma určí uživatele, jehož jménem se jedná, a zpřístupní ho obslužné rutině prostřednictvím get_request_context(). Ověřte, že je k dispozici kontext (pokud chybí, například při místním spuštění, operace bezpečně selže), a pak nechte platformu vrátit historii pro jednotlivé uživatele pomocí context.get_history(). Obslužná procedura main.py v ukázce neudržuje žádný vlastní stav konverzace:

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)

Protože platforma provádí autorizaci context.get_history() pro každý požadavek, uživatel v rámci sdílené relace nikdy neobdrží historii konverzací jiného uživatele.

Rozdělit data jednotlivých uživatelů, která váš kontejner ukládá

Platforma pro vás izoluje historii konverzací. Pokud kontejner ukládá také vlastní data – soubory, řádky databáze nebo mezipaměť – tato data se automaticky nerozdělují. Použijte jako klíč ID relace i ID uživatele, aby dva uživatelé ve stejné relaci neviděli navzájem svá data:

partition = (agent_session_id, user_id)

Varování

Když uživatelé sdílejí relaci, platforma sama nerozdělí data, která ukládá váš kontejner. Pokud váš kontejner používá jako klíč pro tato data pouze ID relace, každý uživatel ve skupině uvidí stejná data. Do klíče oddílu vždy zahrňte ID uživatele.

Přečtěte si ID uživatele z kontextu platformy pro jednotlivé požadavky:

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 také vkládá uživatele do hlavičky požadavku x-agent-user-id. Pokud modul runtime nepoužívá kontext sady SDK, přečtěte si tuto hlavičku přímo.

Platforma naplní get_request_context().user_id protokolem 2.0.0. Nikdy nepoužívejte samotné ID relace jako jediný identifikátor dat patřících uživateli, pokud do relace může vstoupit více než jeden uživatel.

Praktickou ukázku úložiště pro jednotlivé relace, na které můžete stavět, najdete v ukázce agenta pro pořizování poznámek. Ukládá jeden soubor pro každou relaci pod $HOME. U sdílené relace tento klíč rozšiřte o ID uživatele z kontextu požadavku, aby každý uživatel získal vlastní oddíl.

Ověření izolace

Potvrďte záruku pomocí testu A-A-B vzorku, invoke_previous_response_isolation.py. Spusťte jej proti nasazenému agentovi se dvěma různými uživateli (v ukázce jsou jako výchozí nastaveni Alice a Bob):

  1. Jako Alice vytvořte odpověď ve sdílené relaci a zachyťte její id.
  2. Jako Alice vytvořte druhou odpověď ve stejné relaci s previous_response_id nastaveným na id z první odpovědi a zachyťte její id.
  3. Jako Bob v téže relaci odešlete požadavek s hodnotou previous_response_id nastavenou na druhou odpověď od Alice. Volání se nezdaří – Bob nemůže pokračovat v Aličině řetězci.

Použijte dva různé ID objektů nebo uživatele Entra. Dva štítky, které odkazují na stejnou identitu, nejsou platným testem napříč uživateli.

Odeslání zastaralé hlavičky izolace na cestě protokolu 2.0.0 vrací chybu, protože tento model byl nahrazen kontextem uživatele platformy.