L'idée en 30 secondes
Multi-Tenant est une approche architecturale où un système d'agents sert de nombreux clients, mais chaque tenant (client individuel) est isolé.
L'isolation ne doit pas exister seulement dans les données. Elle est nécessaire sur toute la chaîne :
- contexte Runtime ;
- mémoire et cache ;
- accès aux outils ;
- limites de budget et rate limits ;
- audit et trace.
Quand c'est nécessaire : quand un service fonctionne pour de nombreux clients, équipes ou workspaces dans une infrastructure partagée.
Un LLM ne doit pas déterminer seul le contexte tenant. Le tenant doit être résolu via auth ou routing, et le système doit l'appliquer de façon obligatoire à chaque étape.
Problème
Sans isolation multi-tenant claire, le système fonctionne, mais les risques deviennent vite critiques.
Les agents LLM augmentent le risque de fuites cross-tenant, car une seule requête peut lire la mémoire, appeler des outils et écrire des données dans plusieurs systèmes.
Pannes typiques :
- le contexte d'un tenant apparaît dans la réponse d'un autre ;
- un tool call s'exécute avec les credentials d'un autre tenant ;
- mémoire ou cache sont mélangés entre clients ;
- un tenant consomme tout le budget partagé (noisy neighbor) ;
- l'audit ne permet pas de prouver qui a initié l'action.
En production, cela signifie fuites de données, incidents de sécurité et compliance complexe.
Solution
Ajouter Multi-Tenant comme frontière explicite d'isolation (tenant boundary) entre Agent Runtime et tous les états/actions du système.
Cette frontière définit :
- comment le tenant est identifié ;
- quelles ressources sont disponibles pour ce tenant ;
- quelles limites s'appliquent spécifiquement à ce tenant ;
- comment le contexte tenant est enregistré dans les logs et traces.
Analogie : comme des coffres-forts dans une banque.
Le bâtiment est unique, mais seul le propriétaire accède à son coffre.
Multi-Tenant permet de la même façon une plateforme partagée sans mélange des accès et des données.
Comment fonctionne Multi-Tenant
Multi-Tenant est une couche gouvernée entre la requête entrante et l'exécution des actions, qui isole de façon forcée chaque run par tenant_id.
Vue d'ensemble du flux complet : Identify → Isolate → Authorize → Execute → Audit
Identify
Le système résout le tenant via auth token, org mapping ou règles de routing.
Isolate
Runtime, mémoire, cache et contexte budget sont liés à un tenant_id spécifique.
Authorize
La couche policy vérifie role, tenant scopes, allowlist et limites par tenant.
Execute
Les appels d'outils s'exécutent uniquement avec tenant-scoped credentials et les ressources de ce tenant.
Audit
Chaque étape critique est loggée avec tenant_id, actor_id, reason_code, outcome.
Ce cycle permet de scaler un service pour de nombreux clients sans mélange cross-tenant.
Dans le code, cela ressemble à ceci
class MultiTenantArchitecture:
def __init__(self, auth, runtime, policy, tools, memory, budgets, audit):
self.auth = auth
self.runtime = runtime
self.policy = policy
self.tools = tools
self.memory = memory
self.budgets = budgets
self.audit = audit
def run(self, request, auth_token):
identity = self.auth.resolve(auth_token) or {}
tenant_id = identity.get("tenant_id")
actor_id = identity.get("actor_id")
if not tenant_id:
return {"ok": False, "reason_code": "tenant_missing"}
if not self.budgets.allowed(tenant_id=tenant_id):
return {"ok": False, "reason_code": "tenant_budget_exceeded"}
# Tout le contexte est strictement lié au tenant.
state = self.runtime.start(request=request, tenant_id=tenant_id)
memory_items = self.memory.retrieve(tenant_id=tenant_id, query=request["text"], top_k=4)
action = self.runtime.decide(state=state, memory_items=memory_items)
gate = self.policy.authorize(
tenant_id=tenant_id,
actor_id=actor_id,
action=action,
)
if not gate["ok"]:
self.audit.log(
tenant_id=tenant_id,
actor_id=actor_id,
action=action.get("name"),
outcome="denied",
reason_code=gate.get("reason_code", "policy_denied"),
)
return {"ok": False, "reason_code": gate.get("reason_code", "policy_denied")}
result = self.tools.execute(
action=action,
tenant_id=tenant_id,
scopes=gate.get("scopes", []),
)
self.audit.log(
tenant_id=tenant_id,
actor_id=actor_id,
action=action.get("name"),
outcome="executed",
reason_code=result.get("reason_code", "ok"),
)
return result
À quoi cela ressemble pendant l'exécution
Requête : "Mets à jour le statut de la commande #918 et envoie une confirmation au client"
Step 1
Auth + Routing: résout tenant_id = tenant_acme
Multi-Tenant Boundary: définit le contexte tenant et les limites par tenant
Step 2
Agent Runtime: forme action
Policy: vérifie role + tenant scopes + allowlist
Tool Execution: exécute l'action uniquement avec les credentials de tenant_acme
Step 3
Audit: enregistre tenant_id, actor_id, action, outcome, reason_code
Runtime: renvoie le résultat sans mélange avec les autres clients
Multi-Tenant ne change pas la logique de l'agent. Il la rend prévisible et sûre pour un environnement multi-client.
Quand c'est adapté et quand ça ne l'est pas
Multi-Tenant est nécessaire là où un système sert de nombreux clients ou équipes avec des accès différents.
Adapté
| Situation | Pourquoi Multi-Tenant est adapté | |
|---|---|---|
| ✅ | Un service d'agents sert de nombreux clients | La tenant boundary empêche les fuites cross-tenant de données et d'accès. |
| ✅ | Des budgets, quotas et policy rules différents sont nécessaires selon le tenant | Les limites par tenant protègent le système contre l'effet noisy-neighbor. |
| ✅ | Un audit est nécessaire pour la sécurité et la compliance | Les logs et trace enregistrent les actions avec un lien clair au tenant. |
Non adapté
| Situation | Pourquoi Multi-Tenant n'est pas adapté | |
|---|---|---|
| ❌ | Le système sert un seul client sans plan de scalabilité | Un habillage multi-tenant complet peut être une complexité inutile au départ. |
| ❌ | Les données et accès sont déjà isolés physiquement par installations séparées | Dans ce cas, une architecture single-tenant par installation est souvent suffisante. |
Dans un scénario simple single-tenant, un contexte de base suffit parfois :
result = runtime.run(request=request, tenant_id="default")
Problèmes et pannes typiques
| Problème | Ce qui se passe | Comment prévenir |
|---|---|---|
| Credential bleed | Un tool call utilise les clés d'un autre tenant | Tenant-scoped credentials + interdiction des clients globaux |
| Cache / memory bleed | Le cache ou la mémoire renvoie des données d'un autre tenant | Namespace key avec tenant_id, isolation du store et cas de test de fuite |
| Noisy neighbor | Un tenant consomme le budget partagé et dégrade le service pour les autres | Per-tenant budgets, rate limits, quotas et priorités |
| Usurpation du contexte tenant | Le système accepte tenant_id depuis le prompt ou payload sans validation auth | Le tenant est résolu uniquement depuis auth/routing, pas depuis le texte de requête du modèle |
| Audit incomplet | Impossible de prouver quel tenant a initié une action risquée | Champs d'audit obligatoires : tenant_id, actor_id, action, reason_code, outcome |
| Opérations d'écriture répétées | Un retry duplique une écriture ou un débit dans le tenant | Idempotency keys et déduplication pour les actions de mutation |
La plupart des incidents multi-tenant ne viennent pas du modèle, mais d'une frontière faible entre contexte et exécution.
Comment cela se combine avec d'autres patterns
Multi-Tenant est une couche architecturale transverse qui renforce la sécurité et la stabilité de tout le système.
- Agent Runtime — Runtime exécute les étapes, et Multi-Tenant fixe des frontières de contexte tenant à chaque étape.
- Tool Execution Layer — chaque
tool_calldoit s'exécuter avec des accès tenant-scoped. - Memory Layer — mémoire et cache doivent être isolés par
tenant_id. - Policy Boundaries — policy rules s'appliquent en tenant compte de tenant, role et scopes.
- Orchestration Topologies — dans les flows multi-agent, le contexte tenant doit passer dans toutes les branches.
- Hybrid Workflow Agent — les commits workflow doivent rester dans les ressources du tenant spécifique.
- Human-in-the-Loop Architecture — les étapes d'approval doivent aussi avoir audit et accès liés au tenant.
- Containerizing Agents — les conteneurs donnent un environnement stable, mais l'isolation tenant est assurée spécifiquement par la frontière Multi-Tenant.
Autrement dit :
- Multi-Tenant définit à qui appartient cette action et ce contexte
- Les autres couches d'architecture définissent comment cette action est exécutée
En bref
Multi-Tenant:
- isole les données, accès et state entre clients
- applique des limites de budget par tenant et des rate limits
- lie de façon obligatoire les tool calls à des tenant-scoped credentials
- rend l'audit transparent via tenant_id + reason_code
FAQ
Q: Est-ce suffisant d'ajouter simplement tenant_id à la requête ?
A: Non. tenant_id doit être imposé à travers Runtime, policy, tools, mémoire, cache et audit.
Q: Où arrivent le plus souvent les fuites cross-tenant ?
A: Le plus souvent dans les caches, la mémoire et les clients globaux pour APIs externes.
Q: Comment migrer en sécurité de single-tenant vers multi-tenant ?
A: Commencer avec tenant_id dans auth/routing, puis isoler mémoire/cache/tools, ajouter limites par tenant et audit, et seulement ensuite migrer les données par étapes.
Q: Qu'est-ce qui est prioritaire : budgets par tenant ou policy par tenant ?
A: Les deux sont importants. Policy protège les accès, les budgets protègent contre noisy-neighbor et l'explosion des coûts.
Et Ensuite
L architecture multi-tenant commence par l isolation, mais ne s arrete pas la. Ensuite, regardez comment garder la stabilite sous charge reelle:
- Memory Layer - comment construire une memoire tenant-scoped sans fuite entre clients.
- Containerizing Agents - comment garantir une execution reproductible pour chaque tenant.
- Policy Boundaries - comment separer permissions, roles et actions a risque.
- Production Stack - comment assembler tout cela en modele production maitrise.