將 Model Armor 記錄與 Gemini Enterprise 記錄相互關聯

本文說明如何在 Cloud Logging 中,將 Model Armor 淨化記錄與 Gemini Enterprise 平台記錄和追蹤範圍相互關聯。本文說明用戶端到代理程式 (輸入) 和代理程式到任何位置 (輸出) 流量的關聯機制,列出產生追蹤記錄的必要條件,並提供逐步說明和程式碼範例,協助您在記錄處理管道中加入這些記錄項目。

調查 Model Armor 追蹤範圍或記錄項目時,您可能需要在 Cloud Logging 中找出對應的記錄,才能取得要求的完整脈絡。例如:

  • 如果從追蹤記錄時距開始,您可能需要判斷使用者身分或檢查詳細的清除發現項目。
  • 如果從 Model Armor 淨化記錄項目開始,您可能需要將其與使用者身分或追蹤資訊建立關聯。

記錄關聯性的運作方式

在 Gemini Enterprise 中,Model Armor 可在下列通訊點篩選提示詞和回覆:

  • 用戶端到 Agent (傳入) 流量:使用者向 Gemini Enterprise 助理提交提示時,Gemini Enterprise 會直接呼叫 Model Armor API。產生的 Model Armor 平台記錄 (SanitizeOperation) 不會直接包含 OpenTelemetry tracespanId 欄位。如要將這些記錄與使用者身分和追蹤範圍建立關聯,請在安全資訊與事件管理 (SIEM) 或記錄處理管道中,使用工作階段符記執行記錄聯結

  • 代理到任何位置 (輸出) 的流量:當代理呼叫外部工具、Model Context Protocol (MCP) 伺服器或外部大型語言模型 (LLM) 時,流量會透過 Agent Gateway 和 Secure Web Proxy 傳輸。如果是輸出呼叫,啟用 OpenTelemetry 檢測功能後,Model Armor SanitizeOperation 記錄會直接包含 tracespanId 欄位。您可以直接篩選記錄,並在 Cloud TraceAgent Registry 中查看追蹤記錄範圍。

關聯機制摘要

Flow 路徑和轉送 Model Armor 記錄中的追蹤記錄 關聯方法
用戶端至代理 (輸入) Gemini Enterprise 直接呼叫 Model Armor API 系統不會填入 tracespanId 使用 client_correlation_idassistToken 的工作階段符記記錄加入情形
代理至任意目的地 (輸出) 透過 Agent Gateway 和 Secure Web Proxy 轉送 系統會填入 tracespanId 直接比對 trace ID 和檢查追蹤記錄範圍

事前準備

開始將 Model Armor 記錄與 Gemini Enterprise 記錄相互關聯前,請先按照下列步驟操作:

  1. 在 Gemini Enterprise 中啟用 Model Armor
  2. 如要在 Gemini Enterprise 和 Model Armor 記錄中產生追蹤記錄內容並查看追蹤記錄詳細資料,請在可觀測性設定中開啟「啟用 OpenTelemetry 追蹤記錄和記錄的檢測功能」,並視需要開啟「啟用提示詞輸入內容和回覆輸出內容的記錄功能」。如需操作說明,請參閱「開啟觀測能力設定」。
  3. 如要篩選輸出流量,請在 Agent Gateway 上設定 Model Armor

必要的角色

如要取得查看及關聯記錄和追蹤範圍所需的權限,請要求管理員在專案中授予您下列 IAM 角色:

如要進一步瞭解如何授予角色,請參閱「管理專案、資料夾和組織的存取權」。

您或許也能透過自訂角色或其他預先定義的角色,取得必要權限。

如要瞭解您可能需要的其他角色,請參閱「Trace 存取控管」和「Cloud Logging 存取控管」。

關聯用戶端至代理 (輸入) 記錄

單一 StreamAssist 互動會在 Cloud Logging 中產生三種不同的記錄項目:

  • Model Armor 淨化記錄 (SanitizeOperation):
    • 受監控的資源: modelarmor.googleapis.com/SanitizeOperation
    • 屬性:包含詳細的清除判定和安全發現 (例如個人識別資訊的編輯、負責任的 AI 技術篩選器比對,或提示注入偵測),但不包含追蹤內容或使用者身分。
    • 關聯鍵: labels."modelarmor.googleapis.com/client_correlation_id"
  • Gemini Enterprise StreamAssist 記錄 (consumed_api):
    • 受監控的資源: consumed_api
    • 屬性:包含使用者身分 (userIamPrincipal)、追蹤詳細資料 (tracespanId),以及工作階段符記 (response.assistToken)。
    • 關聯鍵: jsonPayload.response.assistToken
  • Gemini Enterprise ModelArmorAudit Log (Agent):
    • 受控資源: discoveryengine.googleapis.com/Agent,其中 jsonPayload.logMetadata.methodNameModelArmorAudit
    • 屬性:反映高層級的清除結果,並包含追蹤內容 (tracespanId),但不包含詳細的調查結果或關聯 ID。
    • 關聯鍵: trace

關聯性彙整索引鍵

Model Armor 淨化記錄包含以管道分隔結構的 client_correlation_id 標籤。這個標籤的第三個區段是 base64url 編碼的工作階段符記,與 consumed_api 記錄中為 StreamAssist 記錄的 assistToken 欄位相符。

client_correlation_id 標籤的格式如下:

AS|ASSISTANT_RESOURCE|SESSION_TOKEN

關聯 ID 包含下列值:

  • ASSISTANT_RESOURCE:Gemini Enterprise Assistant 資源的完整資源名稱,格式如下:
    projects/PROJECT/locations/LOCATION/collections/COLLECTION/engines/ENGINE/assistants/ASSISTANT
  • SESSION_TOKEN:不重複的工作階段符記,與 consumed_api 記錄中的 assistToken 相符,且已將 base64url 填補內容正規化。

比對邏輯

如要將 Model Armor 清理記錄項目與 Gemini Enterprise StreamAssist 記錄建立關聯,請在記錄處理管道中實作下列比對邏輯:

  1. 從 Model Armor 項目中擷取工作階段符記

    1. 在 Model Armor 項目中找到 labels 物件。
    2. 擷取 modelarmor.googleapis.com/client_correlation_id 標籤的值。
    3. 使用直立線字元 (|) 分隔這個標籤的值。
    4. 擷取第三個區段,代表以 base64url 編碼的工作階段權杖。
  2. StreamAssist 項目中擷取 assistToken:針對每個候選 StreamAssist consumed_api 記錄項目,請按照下列步驟操作:

    1. 找出 jsonPayload 物件。
    2. response.assistToken 欄位擷取權杖值。
  3. 正規化及比較權杖:如要比較權杖,請正規化兩個權杖字串:

    1. 將所有連字號 (-) 替換成加號 (+)。
    2. 將所有底線 (_) 換成正斜線 (/)。
    3. 移除結尾的等號 (=)。
    4. 如果正規化權杖相符,請將記錄項目建立關聯。
  4. 擷取相關資料:如果找到相符項目,請從相符項目中擷取下列欄位:

    • 使用者 IAM 身分StreamAssist 項目中的 userIamPrincipal 欄位
    • 追蹤記錄 IDStreamAssist 項目中的 trace 欄位
    • 範圍 IDStreamAssist 項目中的 spanId 欄位
    • 清理結果:Model Armor 項目中 sanitizationVerdict 欄位下方的 jsonPayload.sanitizationResult

Python 關聯範例

下列 Python 指令碼示範如何查詢 Cloud Logging 中的 Model Armor 和 Gemini Enterprise 記錄,執行權杖正規化和比對,並輸出相關記錄:

#!/usr/bin/env python3
from datetime import datetime, timedelta, timezone
from google.cloud import logging

# Google Cloud project ID
PROJECT_ID = "YOUR_PROJECT_ID"


def correlate_logs(ma_entry, de_consumed_entries):
  """Correlates a Model Armor log entry with StreamAssist logs."""
  # 1. Extract client_correlation_id from Model Armor log labels
  labels = ma_entry.get("labels", {})
  client_corr_id = labels.get(
      "modelarmor.googleapis.com/client_correlation_id", ""
  )
  if not client_corr_id:
    return None

  # 2. Extract session token (3rd pipe-delimited segment)
  parts = client_corr_id.split("|")
  if len(parts) < 3:
    return None
  ma_token = parts[2]

  # 3. Normalize base64url padding for comparison
  ma_token_normalized = ma_token.replace("-", "+").replace("_", "/").rstrip("=")

  # 4. Search for matching assistToken in StreamAssist logs
  for de in de_consumed_entries:
    payload = de.get("jsonPayload", {})
    de_token = payload.get("response", {}).get("assistToken", "")
    de_token_normalized = (
        de_token.replace("-", "+").replace("_", "/").rstrip("=")
    )

    if ma_token_normalized == de_token_normalized:
      return {
          "user": payload.get("userIamPrincipal"),
          "trace": de.get("trace"),
          "span_id": de.get("spanId"),
          "verdict": (
              ma_entry.get("jsonPayload", {})
              .get("sanitizationResult", {})
              .get("sanitizationVerdict")
          ),
      }
  return None


def main():
  # Initialize Google Cloud Logging Client
  print(f"Connecting to Google Cloud Logging (Project: {PROJECT_ID})...")
  client = logging.Client(project=PROJECT_ID)

  # Calculate ISO timestamp for 1 hour ago
  one_hour_ago = (
      datetime.now(timezone.utc) - timedelta(hours=1)
  ).strftime("%Y-%m-%dT%H:%M:%SZ")
  print(f"Filtering logs starting from: {one_hour_ago}")

  # Build log query filters
  ma_filter = f"""
    resource.type="modelarmor.googleapis.com/SanitizeOperation"
    AND timestamp >= "{one_hour_ago}"
    """

  de_filter = f"""
    resource.type="consumed_api"
    AND jsonPayload.response.assistToken:*
    AND timestamp >= "{one_hour_ago}"
    """

  # Fetch Model Armor log entries
  print("Fetching Model Armor log entries...")
  ma_entries = [
      entry.to_api_repr()
      for entry in client.list_entries(filter_=ma_filter, max_results=100)
  ]
  print(f"Found {len(ma_entries)} Model Armor entries.")

  # Fetch Gemini Enterprise log entries
  print("Fetching Gemini Enterprise StreamAssist log entries...")
  de_entries = [
      entry.to_api_repr()
      for entry in client.list_entries(filter_=de_filter, max_results=500)
  ]
  print(f"Found {len(de_entries)} Gemini Enterprise entries.")

  # Perform Correlation
  print("\n================ Correlating Logs ================")
  correlated_results = []
  for ma in ma_entries:
    match = correlate_logs(ma, de_entries)
    if match:
      correlated_results.append(match)
      print(f"  User IAM Principal  : {match['user']}")
      print(f"  Sanitization Verdict: {match['verdict']}")
      print(f"  Trace ID            : {match['trace']}")
      print(f"  Span ID             : {match['span_id']}")
      print("-" * 50)

  print(f"\nDone. Total Correlated Records: {len(correlated_results)}")


if __name__ == "__main__":
  main()

將代理至任意目的地 (輸出) 記錄與追蹤記錄範圍建立關聯

當代理執行受 Agent Gateway 和 Model Armor 保護的工具呼叫 (例如與 MCP 伺服器或外部 API 互動) 時,該要求會屬於「代理對任何位置」流量。

在應用程式上啟用 OpenTelemetry 檢測功能後,產生的 SanitizeOperation 記錄檔項目會自動包含 tracespanId 欄位。

在 Cloud Logging 中篩選輸出記錄

如要找出與 Cloud Logging 中特定追蹤記錄相關聯的所有 Model Armor 消毒記錄,請使用下列查詢篩選器:

resource.type="modelarmor.googleapis.com/SanitizeOperation"
trace="TRACE_ID"

TRACE_ID 替換為代理程式互動中的追蹤 ID。

詳情請參閱「查看及分析記錄項目」。

查看追蹤記錄時距

在「追蹤」或「Agent Registry」中,您可以查看代理程式互動的執行圖表和時間軸。Model Armor 會產生下列範圍:

  • 父項範圍apply_guardrail "Google Cloud Model Armor"
  • 子跨度Request PathResponse Path

每個時距都包含政策 ID、安全性決策和相符的篩選器違規事項等屬性。詳情請參閱「查看 Model Armor 追蹤跨度」。

後續步驟