Inference hooks 整合是一個 AI 安全伺服器:一個由 Anthropic 呼叫的 HTTPS 服務。對於每個受管控的請求,您的伺服器會收到一個已簽署的 POST,其中包含對話記錄,並以允許或拒絕的裁決作為回應。本頁面記錄了建置該伺服器的協定:請求與裁決的結構描述、簽章驗證,以及操作契約。
如需啟用 Inference hooks 並將其指向您的端點,請參閱設定 Inference hooks。如需了解 Inference hooks 是什麼以及何時使用,請參閱 Inference hooks 概述。
最小可運作的整合是一個讀取每個請求並允許其通過的伺服器。執行以下其中一個伺服器,將其公開於公共的 https:// URL(例如,透過終止 TLS 的反向代理或通道),然後請您的管理員將其設定為端點並測試連線:測試連線的結果會回報您的伺服器所回傳的允許裁決。
# 執行方式: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):
# 排空請求主體;逐字稿可能達數 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、位於可公開路由的主機上(私有、回送和電信級 NAT 範圍會在連線時被拒絕)、具有可通過公共 CA 信任存放區驗證的憑證,且回應時不進行重新導向。設定的 URL 必須是最終目的地。設定 Inference hooks 說明了您的管理員如何設定和測試 URL。
每個請求都會攜帶以下固定標頭,以及您的管理員所設定的任何自訂請求標頭,並且一旦您的組織擁有簽署密鑰後,還會包含驗證簽章中所述的 webhook-* 簽章標頭:
| 標頭 | 值 |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
目前只有一個 hook 事件:「prompt frame」(提示框架),在每個受管控的推論請求開始推論之前發送一次。Anthropic 會保留該請求,直到您的 AI 安全伺服器回應或裁決逾時為止。
請求主體是一個包含以下欄位的 JSON 物件:
| 欄位 | 類型 | 說明 |
|---|---|---|
type | string | hook 事件。目前一律為 "prompt";未來將引入其他事件類型,因此請妥善處理無法識別的值(請參閱向前相容性)。 |
request_id | string | 不透明的每次推論呼叫識別碼,用於關聯。等同於 webhook-id 標頭。 |
tenant_id | string 或 null | 請求所屬組織的不透明識別碼。 |
actor | object | 請求所歸屬的主體,以 type 區分(目前僅發送 "user" 這個值):id(帶標籤的識別碼,對於同一帳戶在不同請求間保持穩定)和 email_address(若可用)。id 和 email_address 都可能為 null。 |
source | object | 發起請求的應用程式:application(請參閱來源值)。 |
messages | array | 截至推論時間點的對話記錄。請參閱內容區塊。 |
session_id | string 或 null | 不透明的對話識別碼(若存在)。請勿解析它。對於 Claude Code,這是盡力而為、由用戶端聲明的工作階段識別碼。 |
model | string 或 null | 此請求的公開模型識別碼(若可用)。 |
metadata | object | 保留的擴充對應表,由字串鍵對應到字串值,目前發送時為空。請勿依賴其中任何內容,並容許其不存在、存在,以及出現任何鍵。 |
請求主體範例:
{
"type": "prompt",
"request_id": "req_abc123",
"tenant_id": "11111111-1111-1111-1111-111111111111",
"actor": {
"type": "user",
"id": "user_01AbCdEfGhIjKlMnOpQrStUv",
"email_address": "[email protected]"
},
"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,其值為 user 或 assistant(工具結果會出現在 user 角色下,與公開的 Messages API 內容模型一致),以及一個以 type 區分的 content 區塊陣列:
區塊 type | 欄位 |
|---|---|
text | text:文字內容。 |
tool_use | id:對應工具結果所參照的識別碼。tool_name:工具的名稱。input:模型傳遞給工具的引數。 |
tool_result | content:工具的輸出文字,各部分以換行符號連接;影像等二進位部分會以預留位置標記取代,且絕不會發送原始位元組。is_error:工具呼叫是否失敗。tool_name:工具的名稱,讓政策可以根據工具身分進行判斷,而無需交叉參照先前的區塊。tool_use_id:對應 tool_use 區塊的 id。 |
attachment | file_name:原始檔案名稱或路徑。media_type:附件的媒體類型。size_bytes:原始檔案的大小。text:附件的文字內容(若可用),例如擷取的文件文字、音訊轉錄或連結中繼資料。絕不會發送原始附件位元組。 |
若區塊的 type 是您無法識別的值,則屬於向前相容的新增項目。它唯一保證的欄位是 type;您的政策可以檢查其他存在的欄位,但不得因為無法識別的類型而拒絕請求。
對話記錄是終端使用者所看到的對話內容,截至推論時間點:對話文字、工具呼叫及其結果、擷取的附件文字,以及先前的回合。它絕不包含系統提示、工具定義、Anthropic 內部上下文、Claude 的隱藏推理,或原始檔案位元組。
若某個回合的所有區塊都被排除,則該回合會被完全省略,因此請勿假設使用者和助理會嚴格交替出現。
對話記錄會以未截斷的方式發送,因此包含大型附件的長對話會產生大型請求主體,上限為 10 MB。請提高您伺服器的主體限制以接受該上限。許多常見的預設值都小得多,包括 nginx 的 client_max_body_size 為 1 MB,以及 Express 的 express.json() 為 100 kB;而被拒絕的主體會被視為 webhook 失敗,因此在允許請求的失敗處理模式下,過大的提示將在未經檢查的情況下到達模型。
source.application 是一個開放字串,而非封閉的列舉。已知的值為 claude-ai 和 claude-code;連線測試使用 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 | string 或 null;最多 500 個字元,超過的值會被截斷 | 當 action 為 deny 時顯示給終端使用者;在 allow 時會被忽略。 |
reference_id | string 或 null;最多 50 個字元,限用 [A-Za-z0-9._:/-] | 您自己對此次評估的識別碼。它會記錄在拒絕的 inference_hooks_request_denied 合規活動中,且絕不會顯示給終端使用者。請保持其不透明:不含請求內容,也不含個人資料。 |
拒絕裁決絕不會因格式問題而被捨棄:過大的 deny_reason 會被截斷,格式錯誤的 reference_id 會被靜默捨棄,而 action 仍會被採用。
反之則不然。除了 HTTP 200 搭配可解析的裁決之外,任何其他回應都是 webhook 失敗,此時會套用您組織的失敗處理設定,而非裁決。特別是:
allow 或 deny 之外的任何 action 值都會被視為 webhook 失敗。Anthropic 最多讀取回應主體的 64 KiB,且主體必須未經壓縮。不會跟隨重新導向,且會忽略 cookie。裁決主體中的未知欄位會被忽略,因此您可以在此處記錄的欄位之外回傳更豐富的物件。
請求依照 Standard Webhooks 規範進行簽署,使用三個標頭。Anthropic 以小寫發送標頭名稱,而代理伺服器可自由變更大小寫,因此請以不區分大小寫的方式查詢。
| 標頭 | 內容 |
|---|---|
webhook-id | 此次傳遞的唯一識別碼。等同於主體的 request_id。可用作冪等性金鑰,以及簽署酬載的第一個組成部分。 |
webhook-timestamp | 請求簽署時的 Unix 時間(以秒為單位),以十進位字串表示。若時間戳記與您伺服器的時鐘相差超過五分鐘(無論方向),請拒絕該請求。 |
webhook-signature | 一個或多個以空格分隔的 v1,<base64> 值,每個都是對 {webhook-id}.{webhook-timestamp}.{raw body bytes} 計算的 HMAC-SHA256。若任何值與您計算的值相符(使用常數時間比較),則接受該請求。 |
有兩個細節會導致大多數驗證錯誤:
whsec_ 前綴之後的值,使用標準 base64 字母表(+ 和 /)編碼,標頭中的簽章也是如此。當密鑰包含 + 或 / 時(大多數情況下都會),URL 安全的解碼器會推導出錯誤的金鑰位元組。一旦您的組織擁有簽署密鑰,Anthropic 發送的每個請求都會經過簽署,且啟用 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,000 毫秒之間的裁決逾時(預設為 5,000 毫秒)。此預算涵蓋整個交換過程:連線、TLS 交握、請求和回應。
Anthropic 僅在連線嘗試失敗時重試一次,延遲 100 毫秒後進行。重試共用相同的逾時預算,並攜帶相同的 webhook-id 和相同的簽章。一旦您的 AI 安全伺服器已回應,該交換就不會再重試。
逾時、非 200 狀態碼(包括重新導向)、無法解析或過大的回應主體,以及無法連線的端點,都屬於 webhook 失敗。Webhook 失敗絕不會變成拒絕;而是由您組織的失敗處理設定決定受影響的請求是被封鎖,還是在未經檢查的情況下繼續進行。
可歸因於您的 AI 安全伺服器的持續 webhook 失敗會觸發「circuit breaker」(斷路器),停止強制執行:Anthropic 會停止聯繫您的伺服器,且失敗處理會套用於每個請求。復原需在管理端進行:修復伺服器,然後請您的管理員重新開啟強制執行裁決。請參閱斷路器。
強制執行會將您的 AI 安全伺服器的往返時間加到您組織中每個受管控請求的「latency」(延遲)上。請保持裁決快速,並在向大型組織推出之前對您的伺服器進行負載測試。
發送到您的 AI 安全伺服器的請求來自 160.79.106.0/24,這是 Anthropic 已發布的對外 IP 範圍的一部分。請將該區塊加入允許清單,而非同一頁面上的對內範圍,因為後者不涵蓋此區塊。加入允許清單可縮小您伺服器的暴露範圍,但不能取代簽章驗證:該區塊承載的 Anthropic 對外流量不僅限於 Inference hooks。
協定會在不破壞正確編寫的伺服器的情況下擴展。您的伺服器必須忽略:
metadata 中的未知鍵。source.application 值。actor.type 值。actor 是以 type 區分的聯集,目前僅發送 "user" 這一種;未來的種類僅保證 type 存在。type 的內容區塊。絕不因為無法識別的區塊類型或欄位而拒絕請求;讀取您知道的欄位並跳過其餘部分。
未來將引入其他 hook 事件類型。新的事件類型是您的伺服器無法透過跳過欄位來處理的新增項目:該請求仍需要裁決。當頂層 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?