Cette étude de cas intègre AgentChat à un SaaS de gestion de projets multi-tenant appelé ProjectDesk. Chaque client utilise les mêmes API de projets et de tâches, mais l’utilisatrice Alice ne peut agir que dans workspace_a et l’utilisateur Bob uniquement dans workspace_b. Nous réutiliserons un seul document d’API tout en créant deux sessions avec des en-têtes de requête différents, accessibles en écriture uniquement.
La limite de sécurité ne réside pas dans un prompt demandant au modèle de rester à l’intérieur d’un espace de travail. ProjectDesk authentifie et autorise chaque requête d’outil HTTP. AgentChat sélectionne les opérations et injecte les identifiants de session appropriés ; ProjectDesk décide si chaque opération est autorisée.
Étape 1 : rendre l’API métier consciente du tenant
GET /v1/projects?status=active
→ { "items": [{ "project_id": "proj_12", "name": "Website launch", "role": "editor" }] }
POST /v1/projects/proj_12/tasks
{ "title": "Review launch checklist", "due_at": "2026-08-28" }
→ { "task_id": "task_91", "project_id": "proj_12", "status": "open" } ProjectDesk vérifie le jeton bearer, en déduit l’identité de l’utilisateur, confirme son appartenance à l’espace de travail indiqué par X-Workspace-ID et ajoute cet espace de travail à chaque requête de base de données. Un identifiant de projet seul ne permet jamais de contourner le filtre de locataire. Les autorisations de lecture et d’écriture sont vérifiées séparément.
Étape 2 : enregistrer un document ProjectDesk réutilisable
POST $AGENTCHAT_SITE_URL/api/api-documents
Authorization: Bearer ac_live_AGENTCHAT_KEY
Content-Type: application/json
{
"title": "ProjectDesk Projects and Tasks",
"description": "List authorized projects and create tasks inside them.",
"content": "# ProjectDesk API\nBase URL: https://projects.example.com/v1\n\nGET /projects?status=active returns only projects visible to the authenticated workspace member. Response items contain project_id, name, and role.\n\nPOST /projects/{project_id}/tasks body: title required, due_at optional ISO date. Create only after resolving a project through GET /projects. Response contains task_id, project_id, and status.\n\n401 means the session credential is missing or expired. 403 means the current member lacks permission. On either response, do not retry and tell the user that the chat credential or role must be updated."
} Ce document décrit une capacité, et non une identité. ProjectDesk enregistre une seule fois l’UUID du document renvoyé et l’associe aux conversations de chaque locataire. Aucun identifiant de locataire, jeton utilisateur ou secret n’est intégré au document.
Étape 3 : créer la session AgentChat d’Alice
POST $AGENTCHAT_SITE_URL/api/agent/sessions
Authorization: Bearer ac_live_AGENTCHAT_KEY
Content-Type: application/json
{
"title": "ProjectDesk assistant — Alice",
"llm_config_id": "llm_config_uuid",
"api_document_ids": ["projectdesk_document_uuid"],
"system_prompt": "Help the current member manage projects. Never infer access from names; rely on API results.",
"max_turns": 10,
"host_headers": [
{ "host": "projects.example.com", "header_key": "Authorization", "header_value": "Bearer alice_short_lived_token" },
{ "host": "projects.example.com", "header_key": "X-Workspace-ID", "header_value": "workspace_a" }
]
} La réponse de la session contient la configuration habituelle, mais ne renvoie jamais header_value. AgentChat peut injecter le jeton d’Alice dans une requête HTTP correspondante, tandis que le modèle ne peut voir ni le jeton ni l’en-tête d’autorisation enregistré.
Étape 4 : créer la session de Bob à partir du même document
POST $AGENTCHAT_SITE_URL/api/agent/sessions
Authorization: Bearer ac_live_AGENTCHAT_KEY
Content-Type: application/json
{
"title": "ProjectDesk assistant — Bob",
"llm_config_id": "llm_config_uuid",
"api_document_ids": ["projectdesk_document_uuid"],
"system_prompt": "Help the current member manage projects. Never infer access from names; rely on API results.",
"max_turns": 10,
"host_headers": [
{ "host": "projects.example.com", "header_key": "Authorization", "header_value": "Bearer bob_short_lived_token" },
{ "host": "projects.example.com", "header_key": "X-Workspace-ID", "header_value": "workspace_b" }
]
} Le contrat de l’API et la configuration du modèle peuvent être partagés, tandis que les en-têtes de session définissent l’appelant pour chaque conversation. Cela évite de générer des documents en double pour chaque client et permet de maintenir la rotation des identifiants indépendante de la documentation des capacités.
Étape 5 : exécuter la même instruction dans les deux sessions
POST /api/agent/sessions/alice_session_uuid/chat
{ "message": "Create a task called Review launch checklist in the Website launch project, due August 28." }
POST /api/agent/sessions/bob_session_uuid/chat
{ "message": "Create a task called Review launch checklist in the Website launch project, due August 28." } Pour Alice, http_request envoie le jeton d’Alice et workspace_a uniquement à projects.example.com. ProjectDesk renvoie les projets visibles par Alice ; l’agent identifie le projet demandé à partir de ce résultat et publie la tâche. L’exécution de Bob suit le même plan, mais ProjectDesk filtre en fonction de workspace_b. Si Bob ne peut pas voir ce projet, l’agent ne reçoit aucun projet correspondant et ne doit ni inventer ni réutiliser l’identifiant du projet d’Alice.
Comment la correspondance des hôtes empêche les fuites d’identifiants
AgentChat injecte un en-tête de session uniquement lorsque la destination HTTP correspond au nom d’hôte configuré et au port facultatif. Les redirections sont de nouveau vérifiées à chaque saut. Un identifiant d’accès pour projects.example.com n’est donc pas transmis à files.example.net, à un hôte d’images ou à une destination de redirection inattendue.
À quoi ressemble un échec d’autorisation
HTTP/1.1 403 Forbidden
Content-Type: application/json
{ "error": "insufficient_scope", "required_scope": "tasks:write" } Les instructions d’AgentChat considèrent les erreurs 401 et 403 comme des problèmes de configuration des identifiants. L’agent doit arrêter l’opération bloquée et indiquer à l’utilisateur quelle autorisation doit être vérifiée. Il ne doit pas demander le secret dans le chat, révéler les valeurs d’en-tête stockées ni réessayer plusieurs fois après un refus.
ProjectDesk doit conserver des réponses de refus utiles, mais non sensibles. Il peut indiquer le périmètre requis sans confirmer l’existence éventuelle d’un enregistrement inter-locataires. Les journaux d’audit doivent consigner l’acteur vérifié et la ressource tentée, tout en masquant les jetons porteurs et les autres valeurs d’en-tête.
Étape 6 : consommer séparément l’état d’AgentChat et l’état de ProjectDesk
GET /api/agent/sessions/alice_session_uuid/messages?after_id=&after_revision=0
GET /api/agent/sessions/alice_session_uuid/state
GET https://projects.example.com/v1/projects/proj_12/tasks L’interface de chat de ProjectDesk interroge les messages d’AgentChat et fusionne les révisions de l’assistant pendant le traitement. Une fois l’exécution terminée, le tableau de projet recharge les tâches depuis l’API ou la base de données de ProjectDesk. La réponse de l’assistant explique l’action, mais l’enregistrement de tâche sauvegardé — et non le texte — constitue l’état produit faisant autorité.
Utiliser des sessions distinctes pour des niveaux de privilèges distincts
Un assistant en lecture seule peut joindre des documents de recherche et de reporting avec un jeton limité aux autorisations de lecture. Un éditeur de projet peut joindre des documents permettant de créer des tâches avec un jeton doté d’autorisations d’écriture. Les opérations à haut risque, telles que les modifications de facturation, la suppression de comptes ou la publication publique, doivent utiliser une session plus restreinte ou un endpoint côté produit pour les approbations en attente.
Dans les flux d’approbation, l’agent crée une action en attente et en renvoie l’ID. ProjectDesk affiche à l’utilisateur la modification exacte, enregistre son approbation explicite et effectue l’opération irréversible dans son propre backend. La décision d’autorisation finale ne dépend jamais de l’interprétation par le modèle d’une phrase de confirmation.
Tests d’isolation en production
- Utilisez la session d’Alice pour demander l’identifiant connu d’un projet de workspace_b et vérifiez que ProjectDesk ne renvoie aucune donnée inter-locataires.
- Exécutez des prompts identiques pour Alice et Bob et vérifiez que leurs appels HTTP reçoivent des résultats autorisés différents.
- Faites expirer, révoquez, omettez et corrompez chaque jeton de session, puis vérifiez dans chaque cas le comportement 401 attendu.
- Accordez à un membre une portée en lecture seule et vérifiez que toute écriture de tâche renvoie 403 sans modifier la base de données.
- Redirigez une requête vers un autre nom d’hôte et vérifiez qu’AgentChat ne transmet pas les identifiants configurés.
- Inspectez les réponses de l’API de session, les résultats des outils, les journaux de l’application et les messages d’erreur afin de détecter toute fuite de secrets.
- Arrêtez puis reprenez une exécution, et vérifiez ensuite que le travail repris utilise toujours uniquement les en-têtes et les associations de documents de cette session.
Le modèle réutilisable multi-tenant
Enregistrez la capacité métier une seule fois. Créez une session AgentChat par conversation utilisateur. Associez les UUID des documents pertinents, injectez des identifiants à durée de vie limitée pour l’hôte API exact et appliquez l’identité et les politiques au sein de vos points de terminaison SaaS. Laissez ensuite AgentChat gérer le raisonnement et l’état persistant de la conversation, tandis que votre propre base de données reste la source de vérité.