Mengembangkan integrasi Inference hooks
Bangun server keamanan AI yang menerima permintaan Inference hooks yang ditandatangani, memverifikasinya, dan mengembalikan putusan allow atau deny.
Integrasi Inference hooks adalah sebuah "AI security server" (server keamanan AI): layanan HTTPS yang dipanggil oleh Anthropic. Untuk setiap permintaan yang diatur, server Anda menerima POST bertanda tangan yang membawa transkrip percakapan dan merespons dengan "verdict" (putusan) allow atau deny. Halaman ini mendokumentasikan protokol untuk membangun server tersebut: skema permintaan dan putusan, verifikasi tanda tangan, dan kontrak operasional.
Untuk mengaktifkan Inference hooks dan mengarahkannya ke endpoint Anda, lihat Mengonfigurasi Inference hooks. Untuk mempelajari apa itu Inference hooks dan kapan menggunakannya, lihat ikhtisar Inference hooks.
Mendapatkan round trip putusan pertama
Integrasi terkecil yang berfungsi adalah server yang membaca setiap permintaan dan mengizinkannya. Jalankan salah satu server berikut, ekspos di URL https:// publik (misalnya, di belakang reverse proxy yang menterminasi TLS pada host yang Anda kendalikan, bukan layanan reverse-tunnel; lihat Menerima permintaan), lalu minta administrator Anda menetapkannya sebagai endpoint dan menguji koneksinya: hasil Test connection melaporkan putusan allow yang dikembalikan server Anda.
# Jalankan dengan: 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):
# Kuras body; transkrip bisa berukuran megabyte.
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()Menerima permintaan
Anthropic mengirim POST HTTPS ke URL yang dikonfigurasi administrator Anda. Seluruh URL yang dikonfigurasi adalah endpoint-nya: tidak ada sufiks path tetap, jadi pilih path apa pun yang sesuai dengan server Anda.
Host server keamanan AI Anda di tempat yang dapat dijangkau Anthropic: URL https:// pada port 443, pada host yang dapat dirutekan secara publik (rentang privat, loopback, dan carrier-grade NAT ditolak saat koneksi), dengan sertifikat yang tervalidasi terhadap trust store CA publik, dan merespons tanpa redirect. URL yang dikonfigurasi harus menjadi tujuan akhir. Host reverse-tunnel (ngrok dan layanan tunnel serupa) tidak didukung: kebijakan jaringan Anthropic memblokirnya. Host server Anda pada domain yang Anda kendalikan. Mengonfigurasi Inference hooks membahas cara administrator Anda menetapkan dan menguji URL tersebut.
Setiap permintaan membawa header tetap berikut, bersama dengan header permintaan kustom apa pun yang dikonfigurasi administrator Anda dan, setelah organisasi Anda memiliki "signing secret" (rahasia penandatanganan), header tanda tangan webhook-* yang dijelaskan di Memverifikasi tanda tangan:
| Header | Nilai |
|---|---|
Content-Type | application/json |
User-Agent | anthropic-dlp/1 |
Accept-Encoding | identity |
Saat ini ada satu event hook: frame prompt, dikirim sekali per permintaan inferensi yang diatur, sebelum inferensi dimulai. Anthropic menahan permintaan hingga server keamanan AI Anda merespons atau batas waktu putusan habis.
Frame prompt
Body permintaan adalah objek JSON dengan field-field berikut:
| Field | Tipe | Deskripsi |
|---|---|---|
type | string | Event hook. Saat ini selalu "prompt"; tipe event lain akan diperkenalkan di masa mendatang, jadi tangani nilai yang tidak dikenali dengan baik (lihat Kompatibilitas ke depan). |
request_id | string | Pengidentifikasi opaque per panggilan inferensi untuk korelasi. Sama dengan header webhook-id. |
tenant_id | string atau null | Pengidentifikasi opaque untuk organisasi pemilik permintaan. |
actor | object | Principal yang menjadi atribusi permintaan, didiskriminasi berdasarkan type ("user" adalah satu-satunya nilai yang dikirim saat ini): id (pengidentifikasi bertag, stabil di seluruh permintaan untuk akun yang sama) dan email_address (jika tersedia). Baik id maupun email_address dapat bernilai null. |
source | object | Aplikasi asal: application (lihat Nilai source). |
messages | array | Transkrip percakapan hingga titik inferensi. Lihat Blok konten. |
session_id | string atau null | Pengidentifikasi percakapan opaque, jika ada. Jangan mem-parse-nya. Untuk Claude Code, ini adalah pengidentifikasi sesi best-effort yang dinyatakan oleh klien. |
model | string atau null | Pengidentifikasi model publik untuk permintaan ini, jika tersedia. |
metadata | object | Map ekstensi yang dicadangkan dari kunci string ke nilai string, saat ini dikirim kosong. Jangan mensyaratkan apa pun darinya, dan toleransi ketidakhadirannya, kehadirannya, serta kunci apa pun yang muncul. |
Contoh body permintaan:
{
"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": {}
}Blok konten
Setiap entri dalam messages memiliki role berupa user atau assistant (hasil alat muncul di bawah role user, sesuai dengan model konten Messages API publik) dan array content berisi blok-blok yang didiskriminasi berdasarkan type:
type blok | Field |
|---|---|
text | text: konten teks. |
tool_use | id: pengidentifikasi yang dirujuk oleh hasil alat yang cocok. tool_name: nama alat. input: argumen yang diteruskan model ke alat. |
tool_result | content: output alat sebagai teks, dengan bagian-bagian digabungkan oleh baris baru; bagian biner seperti gambar diganti dengan penanda placeholder, dan byte mentah tidak pernah dikirim. is_error: apakah panggilan alat gagal. tool_name: nama alat, sehingga kebijakan dapat mengondisikan pada identitas alat tanpa merujuk silang ke blok sebelumnya. tool_use_id: id dari blok tool_use yang cocok. |
attachment | file_name: nama atau path file asli. media_type: tipe media lampiran. size_bytes: ukuran file asli. text: konten teks lampiran jika tersedia, seperti teks dokumen yang diekstrak, transkrip audio, atau metadata tautan. Byte lampiran mentah tidak pernah dikirim. |
Blok yang type-nya tidak Anda kenali adalah tambahan yang kompatibel ke depan. Satu-satunya field yang dijaminnya adalah type; kebijakan Anda boleh memeriksa field lain apa pun yang ada, tetapi tidak boleh menolak permintaan karena tipe yang tidak dikenali.
Apa yang terkandung dalam transkrip
Transkrip adalah percakapan sebagaimana dilihat pengguna akhir, hingga titik inferensi: teks transkrip, panggilan alat dan hasilnya, teks lampiran yang diekstrak, dan giliran sebelumnya. Transkrip tidak pernah menyertakan prompt sistem, definisi alat, konteks internal Anthropic, penalaran tersembunyi Claude, atau byte file mentah.
Giliran yang setiap bloknya dikecualikan akan dihilangkan seluruhnya, jadi jangan berasumsi pergantian user dan assistant yang ketat.
Transkrip dikirim tanpa pemotongan, sehingga percakapan panjang dengan lampiran besar menghasilkan body permintaan yang besar, hingga batas atas 10 MB. Naikkan batas body server Anda untuk menerima batas tersebut. Beberapa default umum jauh lebih kecil, termasuk nginx client_max_body_size sebesar 1 MB dan Express express.json() sebesar 100 kB, dan body yang ditolak dihitung sebagai kegagalan webhook, sehingga di bawah penanganan kegagalan Allow the request, prompt yang terlalu besar akan mencapai model tanpa diperiksa.
Nilai source
source.application adalah string terbuka, bukan enum tertutup. Nilai yang dikenal adalah claude-ai dan claude-code; pengujian koneksi menggunakan config-test. Nilai baru dapat muncul, dan server Anda tidak boleh menolak permintaan karena nilai yang tidak dikenalinya.
Perlakukan source.application sebagai metadata perutean yang bersifat saran, bukan batas kepercayaan: jangan menyandarkan keputusan kebijakan yang kritis terhadap keamanan hanya padanya.
Mengembalikan putusan
Respons dengan HTTP 200 dan body putusan JSON untuk kedua hasil; field action yang membedakan. Untuk mengizinkan permintaan:
{
"action": "allow"
}Untuk menolaknya:
{
"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"
}| Field | Batasan | Semantik |
|---|---|---|
action | "allow" atau "deny"; wajib | allow membiarkan inferensi berlanjut; deny menolaknya. |
deny_reason | string atau null; maksimal 500 karakter, nilai yang lebih panjang dipotong | Ditampilkan kepada pengguna akhir ketika action adalah deny; diabaikan pada allow. |
reference_id | string atau null; maksimal 50 karakter dari [A-Za-z0-9._:/-] | Pengidentifikasi Anda sendiri untuk evaluasi ini. Nilai ini dicatat pada aktivitas kepatuhan inference_hooks_request_denied milik penolakan tersebut dan tidak pernah ditampilkan kepada pengguna akhir. Jaga agar tetap opaque: tanpa konten permintaan dan tanpa data pribadi. |
Deny tidak pernah dibuang karena masalah pemformatan: deny_reason yang terlalu besar dipotong, reference_id yang salah format dibuang secara diam-diam, dan action tetap dihormati.
Kebalikannya tidak berlaku. Apa pun selain HTTP 200 dengan putusan yang dapat di-parse adalah kegagalan webhook, dan penanganan kegagalan organisasi Anda berlaku sebagai pengganti putusan. Secara khusus:
- Jangan memberi sinyal deny dengan status error. Respons non-200 adalah kegagalan, bukan deny.
- Nilai
actionapa pun selainallowataudenydiperlakukan sebagai kegagalan webhook.
Anthropic membaca maksimal 64 KiB dari body respons, dan body tersebut harus tidak terkompresi. Redirect tidak diikuti, dan cookie diabaikan. Field yang tidak dikenal dalam body putusan diabaikan, sehingga Anda dapat mengembalikan objek yang lebih kaya di samping field yang didokumentasikan di sini.
Memverifikasi tanda tangan
Permintaan ditandatangani sesuai spesifikasi Standard Webhooks, menggunakan tiga header. Anthropic mengirim nama header dalam huruf kecil, dan proxy bebas mengubah kapitalisasinya, jadi cari header tersebut tanpa membedakan huruf besar-kecil.
| Header | Isi |
|---|---|
webhook-id | Pengidentifikasi unik untuk pengiriman ini. Sama dengan request_id pada body. Gunakan sebagai kunci idempotensi dan sebagai komponen pertama dari payload yang ditandatangani. |
webhook-timestamp | Waktu Unix dalam detik, sebagai string desimal, saat permintaan ditandatangani. Tolak timestamp yang berjarak lebih dari lima menit dari jam server Anda, ke arah mana pun. |
webhook-signature | Satu atau lebih nilai v1,<base64> yang dipisahkan spasi, masing-masing merupakan HMAC-SHA256 atas {webhook-id}.{webhook-timestamp}.{raw body bytes}. Terima permintaan jika ada nilai yang cocok dengan milik Anda, menggunakan perbandingan waktu-konstan. |
Dua detail menyebabkan sebagian besar bug verifikasi:
- Verifikasi byte mentah. Hitung HMAC atas body persis seperti yang diterima, sebelum parsing JSON atau pengodean ulang apa pun.
- Dekode secret dengan dekoder base64 standar. Signing secret adalah nilai setelah prefiks
whsec_, dikodekan dengan alfabet base64 standar (+dan/), begitu pula tanda tangan di header. Dekoder URL-safe menghasilkan byte kunci yang salah setiap kali secret mengandung+atau/, yang terjadi hampir selalu.
Setelah organisasi Anda memiliki signing secret, setiap permintaan yang dikirim Anthropic ditandatangani, dan mengaktifkan Inference hooks mensyaratkannya, jadi tolak permintaan apa pun yang tiba tanpa tanda tangan. Satu pengecualian: pengujian koneksi yang dikirim sebelum penyimpanan pertama organisasi Anda tiba tanpa tanda tangan, karena signing secret belum ada. Terima permintaan tanpa tanda tangan hingga administrator Anda mengonfirmasi bahwa secret sudah ada, lalu tolak.
Merotasi secret adalah peralihan seketika, tetapi permintaan yang ditandatangani dengan secret sebelumnya masih dapat tiba selama sekitar satu menit setelahnya, ditambah apa pun yang sudah dalam perjalanan. Buat server keamanan AI Anda menerima tanda tangan dari kedua secret selama peralihan agar permintaan yang tertinggal tersebut tidak ditolak.
Contoh-contoh berikut adalah implementasi server, sehingga tidak ada tab shell: server keamanan AI adalah layanan HTTPS yang berjalan lama, bukan permintaan sekali jalan. Setiap contoh hanya menggunakan pustaka standar bahasa tersebut; proyek Standard Webhooks juga menerbitkan pustaka verifikasi untuk sebagian besar bahasa.
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()
)
# Bandingkan byte: compare_digest pada str memunculkan error untuk input non-ASCII.
return any(
hmac.compare_digest(expected, candidate.encode())
for candidate in signatures.split()
)Semantik operasional
Batas waktu dan percobaan ulang
Administrator Anda menetapkan batas waktu putusan antara 1 dan 10.000 md (5.000 md secara default). Anggaran tersebut mencakup seluruh pertukaran: koneksi, handshake TLS, permintaan, dan respons.
Anthropic mencoba ulang tepat satu kali, setelah jeda 100 md, dan hanya ketika upaya koneksi gagal. Percobaan ulang berbagi anggaran batas waktu yang sama dan membawa webhook-id yang sama serta tanda tangan yang sama. Setelah server keamanan AI Anda merespons, pertukaran tidak pernah dicoba ulang.
Kegagalan webhook
Batas waktu habis, status non-200 (termasuk redirect), body respons yang tidak dapat di-parse atau terlalu besar, dan endpoint yang tidak dapat dijangkau semuanya adalah kegagalan webhook. Kegagalan webhook tidak pernah menjadi deny; sebaliknya, pengaturan penanganan kegagalan organisasi Anda menentukan apakah permintaan yang terdampak diblokir atau dilanjutkan tanpa pemeriksaan.
Circuit breaker
Kegagalan webhook berkelanjutan yang disebabkan oleh server keamanan AI Anda memicu "circuit breaker" (pemutus sirkuit) yang menghentikan penegakan: Anthropic berhenti menghubungi server Anda, dan penanganan kegagalan berlaku untuk setiap permintaan.
Mulai 10 menit setelah terpicu, Anthropic menguji apakah server Anda telah pulih: paling banyak sekitar sekali per menit, satu permintaan, yang dibawa oleh lalu lintas organisasi Anda sendiri, dikirimkan ke server Anda untuk diperiksa, ditandatangani dan berbentuk seperti permintaan lainnya. Respons secara normal. Putusan yang valid, allow atau deny, mereset breaker dan penegakan dilanjutkan. Kegagalan webhook membiarkan breaker tetap terpicu, dan pengujian berlanjut. Bagaimanapun, permintaan uji itu sendiri tetap berlanjut untuk penggunanya: putusannya tidak ditegakkan, dan pengujian yang gagal tidak memblokirnya, bahkan di bawah Block the request. Administrator juga dapat mereset breaker kapan saja, dan perubahan konfigurasi administrator menghentikan pengujian otomatis; lihat Circuit breaker.
Setiap pemicuan dicatat sebagai aktivitas inference_hooks_circuit_breaker_tripped di Activity Feed, satu aktivitas per pemicuan. Selama breaker terpicu, tidak ada aktivitas Inference hooks per permintaan yang dicatat, sehingga aktivitas pemicuan adalah satu-satunya catatan feed tentang jendela waktu terpicu tersebut.
Latensi
Penegakan menambahkan round trip server keamanan AI Anda ke "latency" (latensi) setiap permintaan yang diatur di organisasi Anda. Jaga agar putusan tetap cepat, dan lakukan uji beban pada server Anda sebelum meluncurkannya ke organisasi besar.
Alamat IP sumber
Permintaan ke server keamanan AI Anda berasal dari 160.79.106.0/24, bagian dari rentang IP keluar Anthropic yang dipublikasikan. Masukkan blok tersebut ke allowlist, bukan rentang masuk di halaman yang sama, yang tidak mencakupnya. Allowlist mempersempit eksposur server Anda, tetapi bukan pengganti verifikasi tanda tangan: blok tersebut membawa lalu lintas egress Anthropic di luar Inference hooks.
Kompatibilitas ke depan
Protokol ini berkembang tanpa merusak server yang ditulis dengan benar. Server Anda harus mengabaikan:
- Field tingkat atas yang tidak dikenal pada frame prompt.
- Kunci yang tidak dikenal dalam
metadata. - Nilai
source.applicationbaru. - Nilai
actor.typebaru.actoradalah union yang didiskriminasi berdasarkantype, dan"user"adalah satu-satunya jenis yang dikirim saat ini; jenis di masa mendatang hanya menjamin bahwatypeada. - Blok konten dengan
typeyang tidak dikenali.
Jangan pernah menolak permintaan karena tipe blok atau field yang tidak dikenali; baca field yang Anda ketahui dan lewati sisanya.
Tipe event hook lain akan diperkenalkan di masa mendatang. Tipe event baru adalah tambahan yang tidak dapat ditangani server Anda dengan melewati sebuah field: permintaan tersebut tetap memerlukan putusan. Ketika type tingkat atas adalah nilai yang tidak Anda kenali, kembalikan putusan allow alih-alih status error; respons error adalah kegagalan webhook, dan kegagalan berkelanjutan memicu circuit breaker.
Merancang integrasi Anda
Server keamanan AI produksi membuat beberapa pilihan desain di luar protokol wire.
Deduplikasi berdasarkan webhook-id. Header webhook-id unik per pengiriman dan sama dengan request_id pada body, dan percobaan ulang akibat kegagalan koneksi menggunakannya kembali, sehingga berfungsi sebagai kunci idempotensi. Jika Anda mencatat putusan, gunakan header ini sebagai kunci catatan.
Catat putusan dan gabungkan penolakan. Simpan setiap putusan yang Anda kembalikan bersama reference_id-nya. Setiap penolakan dicatat sebagai aktivitas kepatuhan inference_hooks_request_denied yang membawa reference_id yang dikembalikan server Anda, sehingga Anda dapat menggabungkan penolakan di Activity Feed dengan catatan yang cocok di sistem Anda sendiri.
Arsipkan dengan server yang selalu mengizinkan. Untuk menangkap transkrip secara real time tanpa mengawasinya, kembalikan {"action": "allow"} tanpa syarat dan simpan frame setelah merespons. Ini adalah alternatif berbasis push untuk polling Compliance API, dan menjawab sebelum Anda menyimpan menjaga round trip Anda di luar jalur kritis pengguna.
Tulis deny_reason untuk pengguna akhir. Teks yang Anda kembalikan adalah apa yang dilihat pengguna ketika permintaan mereka diblokir, dipotong pada 500 karakter. Beri tahu mereka apa yang harus diubah, seperti jenis konten apa yang harus dihapus, alih-alih mengeluarkan kode pemindai yang hanya dapat ditafsirkan oleh tim Anda.
Langkah selanjutnya
Aktifkan Inference hooks, hubungkan dan uji endpoint Anda, serta kendalikan penegakan, penanganan kegagalan, dan peluncuran.
Apa itu Inference hooks, cara kerja round trip putusan, dan kapan menggunakannya.
Was this page helpful?