AgentChat
← 所有文章
安全·10 分钟阅读

如何为每位 SaaS 用户提供安全且具备租户感知能力的 AI 代理

使用每次聊天的请求头和现有的授权层,按用户、工作区或租户隔离代理操作。

本案例研究将 AgentChat 集成到一个名为 ProjectDesk 的多租户项目 SaaS 中。每位客户使用同一套项目和任务 API,但用户 Alice 只能在 workspace_a 中操作,用户 Bob 只能在 workspace_b 中操作。我们将复用一份 API 文档,同时创建两个使用不同只写请求标头的会话。

安全边界不是一条要求模型停留在某个工作区内的提示词。ProjectDesk 会对每个 HTTP 工具请求进行身份验证和授权。AgentChat 选择操作并注入正确的会话凭证;ProjectDesk 决定每项操作是否获准执行。

第 1 步:让业务 API 感知租户

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 会验证承载令牌,从中推导用户身份,确认其属于 X-Workspace-ID 工作区,并将该工作区添加到每个数据库查询中。仅凭项目 ID 绝不能绕过租户过滤器。读取作用域和写入作用域会分别进行检查。

第 2 步:注册一个可复用的 ProjectDesk 文档

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."
}

本文档描述的是能力,而不是身份。ProjectDesk 只保存一次返回的文档 UUID,并将其附加到每个租户的聊天中。文档中不会嵌入租户 ID、用户令牌或密钥。

第 3 步:创建 Alice 的 AgentChat 会话

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" }
  ]
}

会话响应包含常规配置,但绝不会返回 header_value。AgentChat 可以将 Alice 的令牌注入匹配的 HTTP 请求中,而模型既看不到该令牌,也看不到存储的授权标头。

第 4 步:使用同一文档创建 Bob 的会话

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" }
  ]
}

API 契约和模型配置可以共享,而会话标头则定义每次对话的调用方。这样可以避免为每位客户生成重复文档,并使凭据轮换独立于功能文档。

步骤 5:在两个会话中运行相同的指令

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." }

对于 Alice,http_request 只会将 Alice 的令牌和 workspace_a 发送到 projects.example.com。ProjectDesk 返回 Alice 可见的项目;代理根据该结果确定所请求的项目并发布任务。Bob 的运行遵循相同的计划,但 ProjectDesk 会根据 workspace_b 进行筛选。如果 Bob 看不到该项目,代理就不会收到匹配的项目,因此不得捏造或重复使用 Alice 的项目 ID。

主机匹配如何防止凭据泄露

只有当 HTTP 目标与配置的主机名和可选端口匹配时,AgentChat 才会注入会话标头。每次重定向跳转时,系统都会重新进行匹配。因此,projects.example.com 的凭据不会被发送到 files.example.net、图片主机或意外的重定向目标。

重要边界: 主机匹配限制了机密信息的发送位置。接收请求的 ProjectDesk API 仍必须验证令牌、工作区成员资格、资源所有权和操作范围。

权限失败的表现

HTTP/1.1 403 Forbidden
Content-Type: application/json

{ "error": "insufficient_scope", "required_scope": "tasks:write" }

AgentChat 的指令将 401 和 403 视为凭据配置问题。代理应停止被阻止的操作,并告知用户需要检查哪项权限。不应在聊天中请求机密信息、泄露已存储的标头值,也不应针对被拒绝的请求反复重试。

ProjectDesk 应确保拒绝响应有用但不包含敏感信息。它可以说明所需的权限范围,而无需确认是否存在跨租户记录。审计日志应记录已验证的操作者和尝试访问的资源,同时对持有者令牌及其他标头值进行脱敏。

第 6 步:分别使用 AgentChat 状态和 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

ProjectDesk 聊天界面会轮询 AgentChat 消息,并在处理过程中合并助手的修订内容。运行完成后,项目看板会从 ProjectDesk API 或数据库重新加载任务。助手响应会解释所执行的操作,但权威的产品状态是保存的任务记录,而不是文字描述。

针对不同的权限级别使用不同的会话

只读助手可以使用限定为只读权限范围的令牌附加搜索和报告文档。项目编辑者可以使用具有写入权限范围的令牌附加任务编写文档。对于更改账单、删除账户或公开发布等高风险操作,应使用范围更窄的会话,或使用产品侧的待批准端点。

在审批流程中,代理会创建一个待处理操作并返回其 ID。ProjectDesk 向用户显示确切的变更内容,记录明确的批准,然后在自己的后端执行不可逆操作。最终的授权决定绝不会依赖模型对确认短语的解释。

生产环境隔离测试

  1. 使用 Alice 的会话请求一个已知的 workspace_b 项目 ID,并验证 ProjectDesk 不会返回跨租户数据。
  2. 对 Alice 和 Bob 运行完全相同的提示,并验证他们的 HTTP 调用收到不同的授权结果。
  3. 分别使每个会话令牌过期、撤销、缺失或损坏,并验证是否产生正确的 401 行为。
  4. 为成员授予只读作用域,并验证所有任务写入请求均返回403,同时不会更改数据库。
  5. 将请求重定向到另一个主机名,并验证AgentChat不会转发已配置的凭据。
  6. 检查会话API响应、工具结果、应用程序日志和错误消息,确认其中没有泄露机密信息。
  7. 停止并继续运行,然后验证恢复的工作仍然只使用该会话的请求头和文档绑定。

可复用的多租户模式

只注册一次业务能力。为每个用户对话创建一个AgentChat会话。附加相关文档UUID,为确切的API主机注入短期凭据,并在SaaS端点内部强制执行身份和策略。然后让AgentChat管理推理和持久化的对话状态,同时由你自己的数据库继续作为事实来源。