Claude Platform Docs
管理推論掛鉤

開發 Inference hooks 整合

建置 AI 安全伺服器,用於接收已簽署的 Inference hooks 請求、驗證這些請求,並回傳允許或拒絕的判定。

Inference hooks 整合是一個「AI security server」(AI 安全伺服器),也就是由 Anthropic 呼叫的 HTTPS 服務。對於每個受管控的請求,您的伺服器會收到一個已簽署的 POST,其中包含「transcript」(對話記錄)。您的伺服器需以允許或拒絕的「verdict」(判定)回應。本頁說明建置該伺服器所需的協定,包括請求與判定的結構描述、簽章驗證,以及運作合約。

若要啟用 Inference hooks 並將其指向您的端點,請參閱設定 Inference hooks。若要了解 Inference hooks 是什麼以及何時使用,請參閱 Inference hooks 概覽。

完成第一次判定往返

最小的可運作整合,是一個讀取每個請求並一律允許的伺服器。請依下列步驟操作:

  1. 執行下列其中一個伺服器。
  2. 將伺服器公開於公開的 https:// URL。例如,您可以將其置於您所控制主機上的 TLS 終止反向代理之後,但不可使用反向通道服務;請參閱接收請求。
  3. 請您的管理員將其設定為端點並測試連線。Test connection 的結果會顯示您的伺服器所回傳的允許判定。
# 執行方式:python server.py
from http.server import BaseHTTPRequestHandler, ThreadingHTTPServer


class VerdictHandler(BaseHTTPRequestHandler):
    protocol_version = "HTTP/1.1"  # keep the connection open between verdicts

    def do_POST(self):
        # 讀完整個 body;逐字稿可能達數 MB。
        self.rfile.read(int(self.headers.get("Content-Length", 0)))
        verdict = b'{"action": "allow"}'
        self.send_response(200)
        self.send_header("Content-Type", "application/json")
        self.send_header("Content-Length", str(len(verdict)))
        self.end_headers()
        self.wfile.write(verdict)


ThreadingHTTPServer(("", 8000), VerdictHandler).serve_forever()

接收請求

Anthropic 會向您的管理員所設定的 URL 傳送 HTTPS POST。整個設定的 URL 就是端點,沒有固定的路徑後綴,因此您可以選擇任何適合您伺服器的路徑。

請將您的 AI 安全伺服器託管在 Anthropic 能夠連線的位置,並符合下列條件:

  • 使用連接埠 443 上的 https:// URL。
  • 位於可公開路由的主機上。私有、迴路(loopback)及電信級 NAT 範圍會在連線時遭到拒絕。
  • 憑證必須能通過公開 CA 信任存放區的驗證。
  • 回應時不得使用重新導向。設定的 URL 必須是最終目的地。

不支援反向通道主機(ngrok 及類似的通道服務),因為 Anthropic 的網路政策會封鎖這類主機。請將您的伺服器託管在您所控制的網域上。關於管理員如何設定及測試 URL,請參閱設定 Inference hooks。

每個請求都會攜帶下列固定標頭。此外,請求也會包含您的管理員所設定的任何自訂請求標頭。當您的組織擁有簽署密鑰後,請求還會包含驗證簽章中所述的 webhook-* 簽章標頭。

標頭值
Content-Typeapplication/json
User-Agentanthropic-dlp/1
Accept-Encodingidentity

目前只有一種掛鉤事件,也就是提示框架(prompt frame)。每個受管控的推論請求會在推論開始前傳送一次提示框架。Anthropic 會暫停該請求,直到您的 AI 安全伺服器回應或判定逾時為止。

提示框架

請求主體是一個包含下列欄位的 JSON 物件:

欄位類型說明
type字串掛鉤事件。目前一律為 "prompt"。未來將推出其他事件類型,因此請妥善處理無法辨識的值(請參閱向前相容性)。
request_id字串每次推論呼叫的不透明識別碼,用於關聯比對。其值等於 webhook-id 標頭。
tenant_id字串或 null請求所屬組織的不透明識別碼。
actor物件請求所歸屬的主體,以 type 區分(目前唯一傳送的值為 "user")。包含 id(帶標籤的識別碼,同一帳戶的各請求間保持穩定)和 email_address(若可取得)。id 和 email_address 都可能為 null。
source物件發起請求的應用程式,包含 application(請參閱來源值)。
messages陣列截至推論時間點的對話記錄。請參閱內容區塊。
session_id字串或 null不透明的對話識別碼(若存在)。請勿解析此值。對於 Claude Code,這是由用戶端宣告、盡力提供的工作階段識別碼。
model字串或 null此請求的公開模型識別碼(若可取得)。
metadata物件保留的擴充對應表,鍵與值皆為字串,目前傳送時為空。請勿依賴其中的任何內容,並容許此欄位不存在、存在,或出現任何鍵。

請求主體範例:

{
  "type": "prompt",
  "request_id": "req_abc123",
  "tenant_id": "11111111-1111-1111-1111-111111111111",
  "actor": {
    "type": "user",
    "id": "user_01AbCdEfGhIjKlMnOpQrStUv",
    "email_address": "alice@example.com"
  },
  "source": {
    "application": "claude-ai"
  },
  "session_id": "22222222-2222-2222-2222-222222222222",
  "model": "claude-sonnet-4-5",
  "messages": [
    {
      "role": "user",
      "content": [
        {
          "type": "text",
          "text": "Summarize the attached report."
        },
        {
          "type": "attachment",
          "file_name": "q2-report.pdf",
          "media_type": "application/pdf",
          "size_bytes": 48213,
          "text": "Q2 revenue grew 14% quarter over quarter..."
        }
      ]
    }
  ],
  "metadata": {}
}

內容區塊

messages 中的每個項目都有一個 role 和一個 content 陣列:

  • role 為 user 或 assistant。工具結果會出現在 user 角色下,與公開 Messages API 的內容模型一致。
  • content 陣列由以 type 區分的區塊組成:
區塊 type欄位
texttext:文字內容。
tool_useid:對應工具結果所參照的識別碼。tool_name:工具名稱。input:模型傳遞給工具的引數。
tool_resultcontent:工具的文字輸出,各部分以換行符號連接。圖片等二進位部分會以預留位置標記取代,且絕不會傳送原始位元組。is_error:工具呼叫是否失敗。tool_name:工具名稱,讓政策無須交叉參照先前的區塊,即可依工具身分設定條件。tool_use_id:對應 tool_use 區塊的 id。
attachmentfile_name:原始檔案名稱或路徑。media_type:附件的媒體類型。size_bytes:原始檔案的大小。text:附件的文字內容(若可取得),例如擷取的文件文字、音訊逐字稿或連結中繼資料。絕不會傳送原始附件位元組。

下列欄位一律有值:type、text 區塊的 text,以及 tool_result 區塊的 content 和 is_error。其他欄位在值未知時都可能為 null。例如,圖片會以 attachment 區塊的形式送達,其 file_name 和 text 皆為 null。

若區塊的 type 是您無法辨識的值,代表這是向前相容的新增項目。此類區塊唯一保證存在的欄位是 type。您的政策可以檢查其他存在的欄位,但不得因為無法辨識的類型而拒絕請求。

對話記錄包含的內容

對話記錄是終端使用者所看到的對話,截至推論時間點為止。其中包含:

  • 對話記錄文字
  • 工具呼叫及其結果
  • 擷取的附件文字
  • 先前的回合

對話記錄絕不包含系統提示、工具定義、Anthropic 內部上下文、Claude 的隱藏推理或原始檔案位元組。

若某個回合的所有區塊都被排除,該回合會被完全省略。因此,請勿假設使用者與助理的回合會嚴格交替出現。

對話記錄會以未截斷的形式傳送,因此包含大型附件的長對話會產生很大的請求主體。實務上,模型的「context window」(上下文視窗)會讓主體維持在約 10 MB 以下,但協定允許最大 64 MiB。

許多常見的預設值遠小於此上限,例如 nginx 的 client_max_body_size 為 1 MB,Express 的 express.json() 為 100 kB。被拒絕的主體會被視為 webhook 失敗。因此,若失敗處理設定為 Allow the request,過大的提示會在未經檢查的情況下送達模型。

來源值

source.application 是開放式字串,而非封閉的列舉。常見的值包括 claude-ai、claude-code 和 cowork。連線測試和自動斷路器復原檢查則使用 config-test。未來可能出現新的值,您的伺服器不得因為無法辨識的值而拒絕請求。

請將 source.application 視為參考性的路由中繼資料,而非信任邊界。請勿僅依據此值做出攸關安全的政策決策。

回傳判定

無論結果為何,都請以 HTTP 200 和 JSON 判定主體回應,並以 action 欄位區分結果。若要允許請求:

{
  "action": "allow"
}

若要拒絕請求:

{
  "action": "deny",
  "deny_reason": "This prompt appears to contain customer payment card data, which your organization's policy does not allow.",
  "reference_id": "scan_01HXPT4R9V"
}
欄位限制語意
action"allow" 或 "deny";必填allow 讓推論繼續進行;deny 則拒絕推論。
deny_reason字串或 null;最多 500 個字元,超出部分會被截斷當 action 為 deny 時顯示給終端使用者;在 allow 時會被忽略。
reference_id字串或 null;最多 50 個字元,字元須來自 [A-Za-z0-9._:/-]您為此次評估自訂的識別碼。此值會記錄在該次拒絕的 inference_hooks_request_denied 合規活動中,且絕不會顯示給終端使用者。請保持其不透明,不要包含請求內容或個人資料。

拒絕判定絕不會因格式問題而被捨棄。過長的 deny_reason 會被截斷,格式錯誤的 reference_id 會被靜默捨棄,而 action 仍會被遵循。

反之則不然。除了 HTTP 200 搭配可解析的判定之外,任何回應都屬於 webhook 失敗,此時會套用您組織的失敗處理設定,而非判定。特別注意:

  • 請勿以錯誤狀態碼表示拒絕。非 200 的回應屬於失敗,而非拒絕。
  • allow 或 deny 以外的任何 action 值都會被視為 webhook 失敗。

Anthropic 最多讀取 64 KiB 的回應主體,且主體必須未經壓縮。Anthropic 不會跟隨重新導向,也會忽略 cookie。判定主體中的未知欄位會被忽略,因此您可以在本文記錄的欄位之外,回傳內容更豐富的物件。

驗證簽章

請求依照 Standard Webhooks 規範進行簽署,並使用三個標頭。Anthropic 以小寫傳送標頭名稱,但代理伺服器可能會變更其大小寫,因此請以不區分大小寫的方式查找這些標頭。

標頭內容
webhook-id此次傳遞的唯一識別碼,其值等於主體的 request_id。請將其用作冪等性金鑰,以及簽署酬載的第一個組成部分。
webhook-timestamp請求簽署時的 Unix 時間(以秒為單位),格式為十進位字串。若時間戳記與您伺服器時鐘的差距超過五分鐘(無論提前或落後),請拒絕該請求。
webhook-signature一個或多個以空格分隔的 v1,<base64> 值,每個值都是對 {webhook-id}.{webhook-timestamp}.{raw body bytes} 計算的 HMAC-SHA256。只要任一值與您計算的結果相符,即可接受請求。比對時請使用常數時間比較。

大多數驗證錯誤源自下列兩個細節:

  • 驗證原始位元組。 請在進行任何 JSON 解析或重新編碼之前,對收到的原始主體計算 HMAC。
  • 使用標準 base64 解碼器解碼密鑰。 簽署密鑰是 whsec_ 前綴之後的值,以標準 base64 字母表(+ 和 /)編碼,標頭中的簽章也是如此。若密鑰包含 + 或 /(大多數情況下都會包含),URL 安全解碼器會推導出錯誤的金鑰位元組。

當您的組織擁有簽署密鑰後,Anthropic 傳送的每個請求都會經過簽署,包括連線測試,因為設定流程會在第一次測試之前產生密鑰。由於啟用 Inference hooks 需要密鑰,請拒絕任何未簽署的請求。

唯一的例外是:若組織在密鑰成為必要條件之前就已啟用 Inference hooks,則在其管理員產生密鑰之前,會持續傳送未簽署的請求。請僅在管理員確認密鑰已存在之前接受未簽署的請求,確認之後即應拒絕。

輪替密鑰會立即切換,但在切換後約一分鐘內,仍可能收到以先前密鑰簽署的請求,此外還有已在傳輸中的請求。請讓您的 AI 安全伺服器在切換期間同時接受兩組密鑰的簽章,以免拒絕這些延遲抵達的請求。

下列範例是伺服器實作,因此沒有 shell 分頁,因為 AI 安全伺服器是長時間執行的 HTTPS 服務,而非一次性請求。每個範例都只使用該語言的標準函式庫。Standard Webhooks 專案也為大多數語言發布了驗證函式庫。

import base64
import hashlib
import hmac
import time

TOLERANCE_SECONDS = 300


def verify(secret: str, headers: dict[str, str], body: bytes) -> bool:
    """Return True if the body was signed by Anthropic for this organization.

    Anthropic sends header names in lowercase, but proxies are free to
    re-case them, so normalize the lookup to lowercase.
    """
    lowercased = {name.lower(): value for name, value in headers.items()}
    try:
        message_id = lowercased["webhook-id"]
        timestamp = lowercased["webhook-timestamp"]
        signatures = lowercased["webhook-signature"]
    except KeyError:
        return False  # unsigned request: not from Anthropic

    try:
        signed_at = int(timestamp)
    except ValueError:
        return False
    if abs(time.time() - signed_at) > TOLERANCE_SECONDS:
        return False  # replayed, or the clocks disagree

    try:
        key = base64.b64decode(secret.removeprefix("whsec_"), validate=True)
    except ValueError:
        return False  # misconfigured secret: reject rather than crash

    payload = f"{message_id}.{timestamp}.".encode() + body
    expected = b"v1," + base64.b64encode(
        hmac.new(key, payload, hashlib.sha256).digest()
    )

    # 比較位元組:對 str 使用 compare_digest 時,若輸入含非 ASCII 字元會引發例外。
    return any(
        hmac.compare_digest(expected, candidate.encode())
        for candidate in signatures.split()
    )

運作語意

逾時與重試

您的管理員可將判定逾時設定在 1 到 10,000ms 之間(預設為 5,000ms)。此時間預算涵蓋整個交換過程,包括連線、TLS 交握、請求和回應。

Anthropic 只會在連線嘗試失敗時重試,且僅重試一次,重試前會延遲 100ms。重試共用相同的逾時預算,並攜帶相同的 webhook-id 和相同的簽章。一旦您的 AI 安全伺服器已回應,該次交換就絕不會重試。

Webhook 失敗

下列情況都屬於 webhook 失敗:

  • 逾時
  • 非 200 狀態碼(包括重新導向)
  • 無法解析或過大的回應主體
  • 無法連線的端點

Webhook 失敗絕不會變成拒絕。受影響的請求會被封鎖,或在未經檢查的情況下繼續進行,由您組織的失敗處理設定決定。

斷路器

若持續發生可歸因於您 AI 安全伺服器的 webhook 失敗,就會觸發「circuit breaker」(斷路器)並停止強制執行。此時,Anthropic 會停止聯繫您的伺服器,且每個請求都會套用失敗處理設定。

從觸發後 10 分鐘開始,Anthropic 會檢查您的伺服器是否已復原。Anthropic 最多約每分鐘一次,向您的伺服器傳送與 Test connection 相同的合成測試請求(source.application 為 config-test)。此請求與其他請求一樣經過簽署,且不含任何使用者內容。請正常回應此請求:

  • 有效的判定(允許或拒絕皆可)會重設斷路器,並恢復強制執行。
  • Webhook 失敗會讓斷路器維持觸發狀態,檢查也會持續進行。

管理員也可以隨時重設斷路器,而管理員變更設定會停止自動檢查。請參閱斷路器。

每次觸發都會在活動摘要中記錄為一筆 inference_hooks_circuit_breaker_tripped 活動,每次觸發對應一筆活動。斷路器處於觸發狀態期間,不會記錄任何個別請求的 Inference hooks 活動,因此觸發活動是摘要中關於該觸發期間的唯一記錄。

延遲

強制執行會讓您組織中每個受管控請求的「latency」(延遲)增加您 AI 安全伺服器的往返時間。請讓判定保持快速,並在推廣至大型組織之前,先對您的伺服器進行負載測試。

來源 IP 位址

傳送至您 AI 安全伺服器的請求來自 160.79.106.0/24,此範圍屬於 Anthropic 公布的輸出 IP 範圍。請將此區塊加入允許清單,而非同一頁面上的輸入範圍,因為輸入範圍並未涵蓋此區塊。

加入允許清單可以縮小您伺服器的暴露面,但無法取代簽章驗證,因為此區塊承載的 Anthropic 輸出流量不僅限於 Inference hooks。

向前相容性

協定會持續擴充,但不會破壞正確撰寫的伺服器。您的伺服器必須忽略下列項目:

  • 提示框架上的未知頂層欄位。
  • metadata 中的未知鍵。
  • 新的 source.application 值。
  • 新的 actor.type 值。actor 是以 type 區分的聯集,目前唯一傳送的種類是 "user"。未來的新種類僅保證 type 存在。
  • type 無法辨識的內容區塊。

絕不要因為無法辨識的區塊類型或欄位而拒絕請求。請讀取您認得的欄位,並略過其餘欄位。

未來將推出其他掛鉤事件類型。新的事件類型無法靠略過欄位來處理,因為請求仍然需要判定。當頂層 type 是您無法辨識的值時,請回傳允許判定,而非錯誤狀態碼。錯誤回應屬於 webhook 失敗,而持續的失敗會觸發斷路器。

設計您的整合

除了傳輸協定之外,正式環境的 AI 安全伺服器還需要做出幾項設計選擇。

依據 webhook-id 去除重複。 webhook-id 標頭在每次傳遞中都是唯一的,其值等於主體的 request_id,且連線失敗時的重試會沿用此值,因此可作為冪等性金鑰使用。若您會記錄判定,請以此值作為記錄的鍵。

記錄判定並關聯拒絕事件。 請儲存您回傳的每個判定及其 reference_id。每次拒絕都會記錄為一筆 inference_hooks_request_denied 合規活動,其中包含您伺服器所回傳的 reference_id。如此一來,您就能將活動摘要中的拒絕事件,與您自有系統中的對應記錄關聯起來。

使用一律允許的伺服器進行封存。 若要即時擷取對話記錄而不加以管控,請無條件回傳 {"action": "allow"},並在回應之後保存框架。這是輪詢 Compliance API 之外的推送式替代方案。先回應再保存,可讓您的往返時間不會落在使用者的關鍵路徑上。

為終端使用者撰寫 deny_reason。 當使用者的請求遭到封鎖時,他們看到的就是您回傳的文字,且超過 500 個字元的部分會被截斷。請告訴使用者需要修改什麼,例如應移除哪一類內容,而不是輸出只有您的團隊才能解讀的掃描器代碼。

後續步驟

啟用 Inference hooks、連接並測試您的端點,以及控制強制執行、失敗處理和推出方式。

了解 Inference hooks 是什麼、判定往返如何運作,以及何時使用。

Was this page helpful?