AgentChat
← すべての記事
セキュリティ·10分で読めます

すべてのSaaSユーザーに安全でテナントを認識するAIエージェントを提供する方法

チャットごとのヘッダーと既存の認可レイヤーを使い、ユーザー、ワークスペース、テナントごとにエージェントのアクションを分離して維持する。

このケーススタディでは、AgentChatをProjectDeskというマルチテナント型のプロジェクト管理SaaSに統合します。すべての顧客が同じプロジェクトAPIとタスクAPIを使用しますが、ユーザーAliceはworkspace_aでのみ操作でき、ユーザーBobはworkspace_bでのみ操作できます。1つのAPIドキュメントを再利用しながら、書き込み専用のリクエストヘッダーが異なる2つのセッションを作成します。

セキュリティ境界は、モデルにワークスペース内にとどまるよう求めるプロンプトではありません。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 ドキュメントを 1 つ登録する

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 は一致する HTTP リクエストに Alice のトークンを注入できますが、モデルはトークンも保存された認証ヘッダーも見ることができません。

ステップ 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を捏造したり再利用したりしてはいけません。

ホストのマッチングによって認証情報の漏洩を防ぐ仕組み

AgentChat は、HTTP の宛先が設定されたホスト名およびオプションのポートと一致する場合にのみ、セッションヘッダーを挿入します。リダイレクトについては、各ホップで再度照合されます。そのため、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 のチャット UI は 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セッションを1つ作成します。関連するドキュメントUUIDを紐付け、対象のAPIホスト専用の短期間有効な認証情報を注入し、SaaSのエンドポイント内部で認証とポリシーを適用します。そのうえで、推論と永続的な会話状態の管理はAgentChatに任せ、独自のデータベースを信頼できる唯一の情報源として維持します。