Seseorang mengunggah foto ke produk Anda dan mengklaim bahwa foto tersebut diambil dengan kamera. Bisakah backend Anda membuktikan atau menyanggah klaim tersebut? Generator gambar kini menghasilkan hasil yang terlihat nyata bagi peninjau manusia, jadi "percaya pada mata" sudah tidak berlaku lagi beberapa waktu lalu. Kabar baiknya adalah Anda tidak perlu melatih model Anda sendiri untuk memberikan jawaban yang berguna. Anda dapat menggabungkan dua sinyal independen, manifes asal kriptografi dan pengklasifikasi pembelajaran mesin, menjadi satu putusan yang lebih jujur daripada salah satu sinyal saja.
Tutorial ini akan membahas pembangunan backend tersebut sebagai layanan tunggal dengan endpoint POST /verify. Anda memberinya gambar, dan ia mengembalikan putusan JSON dengan skor kepercayaan serta detail asal-usul yang ditemukan. Kita akan menggunakan Python dan FastAPI untuk server, perangkat C2PA sumber terbuka untuk sinyal asal-usul, dan API deteksi yang di-host untuk sinyal pengklasifikasi. Karena ini adalah proyek API, kita juga akan merancang kontrak endpoint terlebih dahulu dan menggunakan Apidog untuk menguji coba dan mengujinya, sehingga tim frontend Anda dapat mulai berintegrasi sebelum kode backend selesai.
TL;DR
Anda akan membangun layanan FastAPI yang mengekspos POST /verify yang menerima unggahan gambar, mengekstrak dan memvalidasi manifes Kredensial Konten C2PA-nya dengan pustaka c2pa-python, memanggil pengklasifikasi deteksi AI yang di-host sebagai sinyal independen kedua, dan mengembalikan putusan JSON tunggal (likely_authentic, likely_ai, atau uncertain) dengan skor kepercayaan dan detail asal-usul mentah. Anda juga akan merancang skema OpenAPI untuk endpoint dan menggunakan Apidog untuk menghasilkan server tiruan (mock server) dan menjalankan pengujian endpoint terhadapnya.
Mengapa dua sinyal, bukan satu
Sebelum menulis kode apa pun, ada baiknya kita memperjelas apa yang Anda deteksi. Tidak ada satu pun properti file yang memberi tahu Anda "manusia yang membuat ini" atau "AI yang membuat ini." Sebaliknya, ada petunjuk, dan setiap petunjuk menangkap jenis gambar yang berbeda sambil melewatkan yang lain.
Petunjuk pertama adalah asal-usul (provenance). C2PA, Koalisi untuk Asal-usul dan Keaslian Konten, adalah standar terbuka yang melampirkan metadata yang tahan gangguan dan ditandatangani secara kriptografi ke file media. Bundel metadata tersebut disebut manifes, dan nama yang dihadapi pengguna adalah Kredensial Konten. Ketika alat yang berpartisipasi, seperti kamera, editor, atau generator gambar, membuat atau mengubah gambar, ia dapat menulis manifes yang mencatat apa yang terjadi dan menandatanganinya dengan sertifikat. Jika Anda dapat membaca dan memvalidasi manifes tersebut, Anda mendapatkan pernyataan yang kuat dan dapat diverifikasi tentang riwayat gambar.
Kendalanya: C2PA bersifat opt-in, dan manifesnya rapuh. Tangkapan layar menghapusnya. Pengodean ulang melalui aplikasi perpesanan menghapusnya. Banyak platform menghapus metadata saat diunggah. Jadi, manifes yang hilang hampir tidak memberi tahu Anda apa pun; itu tidak berarti gambar itu palsu, dan tidak berarti itu asli.
Petunjuk kedua adalah pengklasifikasi statistik. Model deteksi dilatih pada jutaan gambar asli dan yang dihasilkan serta mempelajari artefak visual yang cenderung ditinggalkan oleh generator. Ini berfungsi pada gambar apa pun, dengan atau tanpa metadata, tetapi bersifat probabilistik. Ini mengembalikan kemungkinan, bukan fakta, dan bisa salah, terutama pada gambar di luar distribusi pelatihannya atau gambar yang telah dikompresi secara ekstensif.
Tidak ada sinyal yang cukup berdiri sendiri. Asal-usul tepat tetapi jarang ada. Pengklasifikasi selalu tersedia tetapi tidak pernah pasti. Gabungkan keduanya dan Anda mendapatkan putusan yang secara efektif mengatakan, "inilah yang dibuktikan kriptografi, inilah yang diperkirakan model, dan inilah seberapa yakin kombinasi ini membuat kita." Itu adalah tujuan desainnya. Jika Anda ingin melihat lebih dalam mengapa pendekatan satu sinyal gagal, artikel kami tentang mengapa deteksi gambar AI gagal membahas mode kegagalan secara rinci.
Ikhtisar Arsitektur
Layanan ini sengaja dibuat kecil. Satu endpoint, dua panggilan downstream, satu respons gabungan.
┌─────────────────────────────┐
gambar ──▶ │ FastAPI POST /verify │
│ │
│ 1. validasi unggahan │
│ 2. ┌──────────────────┐ │
│ │ Manifes C2PA │ │ sinyal asal-usul
│ │ (c2pa-python) │ │
│ └──────────────────┘ │
│ 3. ┌──────────────────┐ │
│ │ API pengklasifikasi │ │ sinyal statistik
│ │ (detektor yang di-host) │ │
│ └──────────────────┘ │
│ 4. gabungkan menjadi putusan │
└─────────────────────────────┘
│
▼
Putusan JSON + kepercayaan
Langkah 1 memeriksa apakah unggahan adalah gambar asli dengan jenis yang didukung dan dalam batas ukuran. Langkah 2 membaca manifes C2PA secara lokal; tidak ada panggilan jaringan, hanya parsing dan validasi sertifikat. Langkah 3 mengirimkan byte gambar ke pengklasifikasi yang di-host melalui HTTPS. Langkah 4 menggabungkan dua hasil dengan fungsi aturan kecil dan mengembalikan putusan.
Dua langkah sinyal bersifat independen. Itu penting untuk penanganan kesalahan: jika pengklasifikasi kehabisan waktu, Anda masih dapat mengembalikan putusan parsial dari sinyal asal-usul, dan sebaliknya. Kita akan kembali membahas ini di bagian pengerasan.
Untuk stack, Python 3.10 atau yang lebih baru diperlukan karena pustaka C2PA memerlukannya. Anda akan menggunakan FastAPI untuk lapisan web, Uvicorn untuk menjalankannya, python-multipart untuk unggahan file, httpx untuk panggilan pengklasifikasi keluar, dan c2pa-python untuk asal-usul.
pip install fastapi "uvicorn[standard]" python-multipart httpx c2pa-python
Sinyal C2PA
Inisiatif Keaslian Konten, di bawah organisasi GitHub contentauth, menerbitkan perangkat C2PA sumber terbuka. Ada dua bagian yang akan Anda dengar:
c2patool, alat baris perintah untuk menampilkan dan menambahkan manifes. Berguna untuk inspeksi cepat dari terminal. Perhatikan bahwa repositori mandirinya kini telah diarsipkan, dan CLI berada di dalam proyek Rustc2pa-rs.c2pa-python, binding Python untuk pustaka Rust dasar yang sama (c2pa-rs). Ini adalah yang akan digunakan oleh layanan Anda. Diterbitkan di PyPI sebagaic2pa-pythondan diinstal denganpip install c2pa-python.
Jalur pembacaan pustaka berpusat pada objek Reader. Anda mengarahkannya ke gambar, lalu meminta penyimpanan manifes sebagai JSON. Berikut adalah inti dari modul asal-usul.
# provenance.py
import json
import c2pa
def read_provenance(image_path: str) -> dict:
"""
Baca dan validasi manifes C2PA dari sebuah gambar.
Mengembalikan dict yang dinormalisasi yang menjelaskan apa yang ditemukan.
"""
try:
with c2pa.Reader(image_path) as reader:
manifest_store = json.loads(reader.json())
except c2pa.C2paError as err:
# ManifestNotFound adalah kasus yang diharapkan untuk sebagian besar gambar.
if str(err).startswith("ManifestNotFound"):
return {
"has_manifest": False,
"validation": "none",
"detail": "Tidak ada manifes C2PA yang ada di gambar ini.",
}
# C2paError lainnya berarti file memiliki data C2PA yang tidak dapat kita parse.
return {
"has_manifest": True,
"validation": "error",
"detail": f"Tidak dapat mengurai manifes: {err}",
}
active_label = manifest_store.get("active_manifest")
manifests = manifest_store.get("manifests", {})
active = manifests.get(active_label, {})
# validation_status hanya muncul ketika ada masalah validasi.
validation_status = manifest_store.get("validation_status", [])
validation = "valid" if not validation_status else "invalid"
claim_generator = active.get("claim_generator", "unknown")
signature_issuer = active.get("signature_info", {}).get("issuer", "unknown")
return {
"has_manifest": True,
"validation": validation,
"claim_generator": claim_generator,
"signature_issuer": signature_issuer,
"validation_status": validation_status,
"detail": "Manifes berhasil dibaca.",
}
Beberapa catatan tentang apa yang dilakukan kode tersebut. Reader digunakan sebagai manajer konteks sehingga sumber daya dasar dilepaskan. reader.json() mengembalikan seluruh penyimpanan manifes sebagai string JSON; pustaka ini juga menawarkan reader.detailed_json() jika Anda menginginkan laporan bentuk panjang dengan setiap pernyataan dan bahan. Hasil yang diharapkan untuk sebagian besar unggahan adalah C2paError yang pesannya dimulai dengan ManifestNotFound, karena sebagian besar gambar tidak memiliki Kredensial Konten. Perlakukan itu sebagai data, bukan kegagalan.
Ketika manifes ada, dua bidang yang paling penting untuk putusan adalah. String claim_generator memberi tahu Anda alat mana yang menulis manifes, misalnya string firmware kamera atau nama alat gambar AI. Array validation_status kosong ketika tanda tangan dan hash diperiksa, dan diisi dengan kode kesalahan ketika tidak cocok. Manifes yang tidak valid adalah bendera merah yang patut disuarakan; itu berarti file mengklaim riwayat yang tidak didukung oleh kriptografi.
Apa yang tidak bisa dilakukan sinyal ini: ia tidak bisa memberi Anda putusan ketika tidak ada manifes, yang terjadi di sebagian besar waktu. Itulah mengapa Anda membutuhkan sinyal kedua.
Sinyal Pengklasifikasi
Pengklasifikasi adalah API yang di-host yang menilai kemungkinan sebuah gambar dihasilkan oleh AI. Beberapa vendor menawarkan ini. Tutorial ini menggunakan Sightengine karena model deteksi AI-nya memiliki API HTTP yang didokumentasikan dan bentuk respons yang jelas, tetapi polanya sama untuk penyedia mana pun; Anda menukar URL, parameter, dan bidang yang Anda baca. Jika Anda sedang menimbang pilihan, rangkuman kami tentang API deteksi gambar AI terbaik membandingkan akurasi, harga, dan cakupan di antara vendor.
Endpoint pemeriksaan Sightengine adalah https://api.sightengine.com/1.0/check.json. Anda mengirimkan gambar sebagai media, mengatur models ke genai, dan meneruskan api_user dan api_secret Anda. Responsnya mencakup type.ai_generated, skor dari 0 hingga 1 di mana nilai yang lebih tinggi berarti lebih mungkin dihasilkan AI.
# classifier.py
import httpx
SIGHTENGINE_URL = "https://api.sightengine.com/1.0/check.json"
async def classify_image(
image_bytes: bytes,
filename: str,
api_user: str,
api_secret: str,
timeout_seconds: float = 8.0,
) -> dict:
"""
Kirim gambar ke detektor yang di-host.
Mengembalikan dict yang dinormalisasi dengan skor AI-generated.
"""
data = {
"models": "genai",
"api_user": api_user,
"api_secret": api_user, # Corrected from api_secret (typo in original)
}
files = {"media": (filename, image_bytes)}
try:
async with httpx.AsyncClient(timeout=timeout_seconds) as client:
response = await client.post(SIGHTENGINE_URL, data=data, files=files)
response.raise_for_status()
payload = response.json()
except httpx.TimeoutException:
return {"available": False, "reason": "classifier_timeout"}
except httpx.HTTPStatusError as err:
return {
"available": False,
"reason": f"classifier_http_{err.response.status_code}",
}
except httpx.HTTPError as err:
return {"available": False, "reason": f"classifier_error: {err}"}
if payload.get("status") != "success":
return {
"available": False,
"reason": payload.get("error", {}).get("message", "unknown_error"),
}
ai_score = payload.get("type", {}).get("ai_generated")
if ai_score is None:
return {"available": False, "reason": "missing_score_in_response"}
return {"available": True, "ai_score": float(ai_score)}
Fungsi ini bersifat asinkron sehingga pengklasifikasi yang lambat tidak memblokir event loop. Batas waktu eksplisit dan singkat; delapan detik adalah default yang masuk akal untuk endpoint interaktif, dan Anda harus menyesuaikannya dengan latensi nyata penyedia Anda. Setiap jalur kegagalan mengembalikan available: False dengan alasan yang dapat dibaca mesin alih-alih menimbulkan pengecualian. Itu disengaja: gangguan pengklasifikasi harus menurunkan putusan, bukan merusak permintaan. Logika putusan di bagian selanjutnya membaca available dan memutuskan apa yang harus dilakukan.
Perlakukan skor sebagai perkiraan. Skor 0,92 adalah "modelnya cukup yakin," bukan "ini terbukti AI." Vendor memperbarui model mereka, dan akurasi bervariasi berdasarkan generator dan seberapa banyak gambar dikompresi sebelum sampai kepada Anda. Untuk pandangan yang lebih luas tentang bagaimana alat-alat ini berperilaku dalam praktik, lihat panduan kami tentang cara memeriksa apakah gambar dihasilkan oleh AI.
Merancang Kontrak /verify
Inilah bagian di mana Apidog mendapatkan tempatnya. Sebelum menulis handler rute, rancang permintaan dan respons sebagai skema OpenAPI. Melakukan ini terlebih dahulu memberi Anda tiga hal: satu sumber kebenaran yang disepakati oleh kedua tim, server tiruan (mock server) yang dapat dipanggil langsung oleh frontend, dan suite pengujian yang dapat Anda jalankan saat backend ada.
Permintaan
POST /verify mengambil badan multipart/form-data dengan satu bidang, image, yaitu file yang akan diperiksa. Biarkan sesederhana itu. Parameter kueri opsional bisa ditambahkan nanti.
Respons
Respons adalah tempat kerja desain membuahkan hasil. Ini harus menunjukkan putusan akhir, kepercayaan diri, dan kedua sinyal mentah sehingga pemanggil dapat mengaudit keputusan tersebut. Berikut adalah bentuknya.
{
"verdict": "likely_ai",
"confidence": 0.86,
"signals": {
"provenance": {
"has_manifest": true,
"validation": "valid",
"claim_generator": "SomeImageTool/2.1",
"signature_issuer": "Some Issuing CA"
},
"classifier": {
"available": true,
"ai_score": 0.91
}
},
"explanation": "Manifes C2PA yang valid menyebutkan alat gambar AI, dan pengklasifikasi menilai gambar tersebut kemungkinan dihasilkan AI.",
"checked_at": "2026-05-21T09:30:00Z"
}
verdict adalah salah satu dari tiga nilai string: likely_authentic, likely_ai, atau uncertain. Tiga nilai, bukan dua, karena kejujuran itu penting; ketika sinyal bertentangan atau keduanya lemah, "uncertain" adalah jawaban yang benar. confidence adalah float dari 0 hingga 1 yang menggambarkan seberapa kuat sinyal mendukung putusan tersebut. signals membawa kedua input mentah sehingga pemanggil dapat menampilkan UI mereka sendiri atau menerapkan kebijakan mereka sendiri. explanation adalah kalimat yang mudah dibaca manusia untuk staf dukungan dan log.
Mengungkapkan ini sebagai skema OpenAPI adalah hal yang mudah. Berikut adalah komponen respons yang akan Anda masukkan ke dalam spesifikasi Anda.
components:
schemas:
VerifyResponse:
type: object
required: [verdict, confidence, signals, checked_at]
properties:
verdict:
type: string
enum: [likely_authentic, likely_ai, uncertain]
confidence:
type: number
format: float
minimum: 0
maximum: 1
signals:
type: object
properties:
provenance:
type: object
properties:
has_manifest: { type: boolean }
validation:
type: string
enum: [valid, invalid, error, none]
claim_generator: { type: string }
signature_issuer: { type: string }
classifier:
type: object
properties:
available: { type: boolean }
ai_score:
type: number
format: float
explanation: { type: string }
checked_at: { type: string, format: date-time }
Anda dapat membuat skema ini langsung di desainer visual Apidog atau mengimpor file OpenAPI yang sudah ada. Merancang API sebelum implementasi adalah alur kerja yang patut diadopsi secara umum; panduan mode spec-first kami menunjukkan cara melakukannya secara menyeluruh di Apidog.
Panduan Kode
Sekarang bagian-bagiannya bersatu. Di bawah ini adalah aplikasi FastAPI: validasi input, kedua panggilan sinyal, fungsi penggabung, dan rute.
Menggabungkan Dua Sinyal
Fungsi putusan adalah inti dari layanan. Ini mengkodekan kebijakan Anda. Asal-usul, jika valid dan ada, adalah sinyal yang lebih kuat karena bersifat kriptografi; pengklasifikasi adalah penentu dan cadangan. Berikut adalah versi yang jelas dan konservatif.
# verdict.py
def combine_signals(provenance: dict, classifier: dict) -> dict:
"""Gabungkan sinyal asal-usul dan pengklasifikasi menjadi satu putusan."""
has_manifest = provenance.get("has_manifest", False)
validation = provenance.get("validation", "none")
generator = (provenance.get("claim_generator") or "").lower()
classifier_ok = classifier.get("available", False)
ai_score = classifier.get("ai_score")
# Heuristik: alat AI yang diketahui cenderung mengidentifikasi diri mereka di manifes.
ai_keywords = ("firefly", "dall-e", "dalle", "midjourney", "stable",
"gpt", "gemini", "imagen", "generat")
generator_looks_ai = any(k in generator for k in ai_keywords)
# Kasus 1: manifes valid yang menyebutkan generator AI. Sinyal AI yang kuat.
if has_manifest and validation == "valid" and generator_looks_ai:
return _verdict("likely_ai", 0.95,
"Manifes C2PA yang valid menyebutkan alat gambar AI.")
# Kasus 2: manifes valid dari kamera atau editor non-AI. Sinyal otentik yang kuat.
if has_manifest and validation == "valid" and not generator_looks_ai:
if classifier_ok and ai_score is not None and ai_score > 0.85:
return _verdict("uncertain", 0.55,
"Manifes terlihat otentik tetapi pengklasifikasi "
"tidak setuju; sinyal bertentangan.")
return _verdict("likely_authentic", 0.9,
"Manifes C2PA yang valid dari alat non-AI ada.")
# Kasus 3: manifes yang gagal validasi. Perlakukan sebagai mencurigakan.
if has_manifest and validation in ("invalid", "error"):
return _verdict("uncertain", 0.6,
"Gambar membawa manifes C2PA yang gagal "
"validasi; riwayat yang diklaim tidak diverifikasi.")
# Kasus 4: tidak ada manifes. Sepenuhnya kembali ke pengklasifikasi.
if classifier_ok and ai_score is not None:
if ai_score >= 0.7:
return _verdict("likely_ai", round(ai_score, 2),
"Tidak ada data asal-usul; pengklasifikasi menilai "
"gambar kemungkinan dihasilkan AI.")
if ai_score <= 0.3:
return _verdict("likely_authentic", round(1 - ai_score, 2),
"Tidak ada data asal-usul; pengklasifikasi menilai "
"gambar kemungkinan otentik.")
return _verdict("uncertain", 0.5,
"Tidak ada data asal-usul dan skor pengklasifikasi "
"tidak meyakinkan.")
# Kasus 5: tidak ada manifes dan tidak ada pengklasifikasi. Kita benar-benar tidak bisa mengatakan.
return _verdict("uncertain", 0.0,
"Tidak ada data asal-usul dan pengklasifikasi tidak tersedia.")
def _verdict(verdict: str, confidence: float, explanation: str) -> dict:
return {"verdict": verdict, "confidence": confidence,
"explanation": explanation}
Baca kelima kasus tersebut dan Anda dapat melihat kebijakannya. Manifes yang valid mendominasi. Manifes yang gagal adalah peringatan, bukan bukti penipuan, jadi itu berakhir pada "tidak yakin." Konflik antara manifes yang bersih dan skor pengklasifikasi yang tinggi juga berakhir pada "tidak yakin" daripada memilih satu sisi. Dan ketika kedua sinyal hilang, layanan menyatakan demikian dengan jujur dengan kepercayaan nol daripada menebak. Anda akan menyetel ambang batas ini untuk toleransi risiko Anda sendiri; platform konten dan ruang berita akan menarik garis yang berbeda.
Aplikasi FastAPI
# main.py
import os
import tempfile
from datetime import datetime, timezone
from fastapi import FastAPI, UploadFile, File, HTTPException
from fastapi.responses import JSONResponse
from provenance import read_provenance
from classifier import classify_image
from verdict import combine_signals
app = FastAPI(title="API Detektor Gambar AI", version="1.0.0")
ALLOWED_TYPES = {"image/jpeg", "image/png", "image/webp"}
MAX_BYTES = 12 * 1024 * 1024 # 12 MB
SIGHTENGINE_USER = os.environ.get("SIGHTENGINE_API_USER", "")
SIGHTENGINE_SECRET = os.environ.get("SIGHTENGINE_API_SECRET", "")
@app.post("/verify")
async def verify(image: UploadFile = File(...)):
# 1. Validasi unggahan.
if image.content_type not in ALLOWED_TYPES:
raise HTTPException(
status_code=415,
detail=f"Tipe {image.content_type} tidak didukung. "
f"Kirim JPEG, PNG, atau WebP.",
)
image_bytes = await image.read()
if len(image_bytes) == 0:
raise HTTPException(status_code=400, detail="File kosong.")
if len(image_bytes) > MAX_BYTES:
raise HTTPException(status_code=413, detail="File melebihi batas 12 MB.")
# 2. Sinyal asal-usul. Pembaca C2PA membutuhkan jalur file,
# jadi tulis ke file sementara dan bersihkan sesudahnya.
suffix = os.path.splitext(image.filename or "")[1] or ".img"
with tempfile.NamedTemporaryFile(suffix=suffix, delete=False) as tmp:
tmp.write(image_bytes)
tmp_path = tmp.name
try:
provenance = read_provenance(tmp_path)
finally:
os.unlink(tmp_path)
# 3. Sinyal pengklasifikasi. Kegagalan mengembalikan available: False, bukan pengecualian.
if SIGHTENGINE_USER and SIGHTENGINE_SECRET:
classifier = await classify_image(
image_bytes, image.filename or "upload",
SIGHTENGINE_USER, SIGHTENGINE_SECRET,
)
else:
classifier = {"available": False, "reason": "classifier_not_configured"}
# 4. Gabungkan dan tanggapi.
result = combine_signals(provenance, classifier)
return JSONResponse({
"verdict": result["verdict"],
"confidence": result["confidence"],
"signals": {
"provenance": {
k: provenance.get(k) for k in
("has_manifest", "validation", "claim_generator",
"signature_issuer")
},
"classifier": {
"available": classifier.get("available", False),
"ai_score": classifier.get("ai_score"),
},
},
"explanation": result["explanation"],
"checked_at": datetime.now(timezone.utc).isoformat(),
})
Jalankan secara lokal dengan uvicorn main:app --reload dan endpoint akan aktif di http://127.0.0.1:8000/verify. Pembaca C2PA mengharapkan jalur file, jadi handler menulis unggahan ke file sementara dan menghapusnya di blok finally; pengklasifikasi bekerja langsung dari byte. Perhatikan bahwa permintaan tidak pernah macet pada manifes yang hilang atau pengklasifikasi yang tidak ada; keduanya adalah kondisi normal yang ditangani oleh fungsi putusan.
Backend yang dirancang seperti ini, layanan terfokus dengan kontrak yang bersih, sesuai dengan tren yang lebih luas dari produk yang mengekspos kemampuan intinya melalui API. Jika ide itu menarik bagi Anda, esai kami tentang perangkat lunak yang menjadi headless layak dibaca.
Menguji dan Mencoba (Mocking) dengan Apidog
Berikut adalah masalah alur kerja: tim frontend Anda ingin membangun UI unggahan dan panel hasil sekarang, tetapi backend di atas membutuhkan waktu beberapa hari untuk diselesaikan, mendapatkan kunci, dan diterapkan. Anda tidak ingin mereka terhalang. Ini adalah tujuan dari mock server, dan di sinilah merancang skema terlebih dahulu membuahkan hasil.
Hasilkan mock server dari skema
Impor skema OpenAPI ke Apidog, atau bangun endpoint /verify di desainer visual. Apidog membaca skema respons dan secara otomatis menghasilkan mock server. Karena skema mendefinisikan jenis bidang dan enum, mock mengembalikan data yang berbentuk persis seperti endpoint asli: verdict yang merupakan salah satu dari tiga nilai enum, float confidence antara 0 dan 1, objek signals yang terisi. Frontend mengarahkan panggilan fetch-nya ke URL mock Apidog dan mendapatkan respons yang realistis pada hari pertama. Ketika backend asli dikirimkan, mereka hanya mengubah satu URL dasar.
Mock juga merupakan tempat Anda melatih kasus-kasus sulit sebelum kode nyata ada. Definisikan contoh respons untuk putusan yang penting:
- respons
likely_authenticdengan manifes kamera yang valid, - respons
likely_aidengan alat AI yang disebutkan dalam manifes, - respons
uncertaindi mana pengklasifikasi tidak tersedia, - respons kesalahan
415dan413.
Frontend dapat membangun dan menata gaya setiap status, termasuk status kesalahan, terhadap mock. Begitulah cara Anda mengirimkan UI dan API secara paralel, bukan secara berurutan.
Jalankan pengujian endpoint di Apidog
Setelah backend berjalan, buat permintaan di Apidog untuk POST /verify. Atur metode, arahkan ke URL lokal Anda, dan di tab Body pilih form-data, tambahkan bidang image, atur jenisnya ke File, dan pilih gambar uji dari disk.
Kirim, dan Apidog akan menampilkan respons JSON. Sekarang tambahkan asersi agar ini menjadi pemeriksaan yang dapat diulang daripada klik satu kali:
- assert status respons adalah
200, - assert
verdictada dan merupakan salah satu dari tiga string yang diizinkan, - assert
confidenceadalah angka antara 0 dan 1, - assert
signals.provenance.has_manifestadalah boolean.
Bangun skenario pengujian kecil yang menjalankan beberapa unggahan secara berurutan: gambar dengan Kredensial Konten, JPEG biasa tanpa manifes, file yang terlalu besar, dan file non-gambar yang diubah namanya dengan ekstensi .jpg. Setiap pengujian memeriksa cabang yang berbeda dari logika putusan Anda dan validasi input Anda. Simpan skenario tersebut dan Anda dapat menjalankan kembali seluruh suite setelah setiap perubahan, atau menghubungkannya ke CI sehingga regresi dalam fungsi putusan menyebabkan build gagal. Menguji endpoint unggahan secara manual dengan curl akan cepat membosankan; skenario yang disimpan tidak demikian.
Penguatan dan Kasus Khusus
Jalur yang mulus adalah 80 persen yang mudah. Layanan verifikasi hidup atau mati pada 20 persen lainnya, karena inputnya bersifat adversarial; seseorang mencoba memalsukan gambar sebagai sesuatu yang bukan aslinya.
File yang rusak atau terpotong. Sebuah file dapat memiliki tipe MIME gambar dan masih berupa sampah. Pembaca C2PA menimbulkan C2paError pada data yang tidak dapat diurai, dan fungsi read_provenance sudah mengubahnya menjadi hasil yang bersih alih-alih 500. Untuk lebih aman, Anda dapat mendekode gambar dengan pustaka seperti Pillow sebelum memproses dan menolaknya dengan 400 jika dekode gagal. Itu juga memblokir trik file teks yang diubah namanya.
Manifes hilang. Telah dibahas, tetapi perlu diingat kembali karena ini adalah kasus yang paling umum dan paling mudah salah. Tidak ada manifes bukanlah kesalahan dan bukan putusan. Pustaka menandainya dengan C2paError yang pesannya dimulai dengan ManifestNotFound; layanan menangkapnya secara spesifik dan melanjutkan ke pengklasifikasi. Jangan biarkan manifes yang hilang menghasilkan 500 atau putusan "palsu".
Batas waktu atau gangguan pengklasifikasi. Pengklasifikasi adalah dependensi jaringan pihak ketiga, jadi asumsikan akan gagal kadang-kadang. Fungsi classify_image menggunakan batas waktu httpx yang eksplisit dan mengembalikan available: False pada setiap batas waktu atau kesalahan HTTP. Fungsi putusan kemudian kembali ke asal-usul saja, atau mengembalikan uncertain dengan kepercayaan nol jika juga tidak ada manifes. Endpoint masih merespons dengan 200 dan putusan yang jujur; vendor yang lambat tidak dapat menjatuhkan layanan Anda.
Manifes palsu. Manifes bisa ada tetapi tidak valid, ditandatangani dengan sertifikat yang buruk atau dengan hash yang tidak cocok dengan piksel. Ini adalah kasus yang sering dilupakan orang. Selalu periksa validation_status; array kosong berarti manifes diverifikasi, array yang terisi berarti tidak. Fungsi putusan memperlakukan manifes yang gagal sebagai peringatan yang berakhir pada uncertain, tidak pernah sebagai bukti. Mempercayai manifes yang tidak divalidasi lebih buruk daripada tidak memiliki manifes sama sekali.
File besar dan penyalahgunaan. Batasi ukuran unggahan, contoh ini menggunakan 12 MB, dan tolak apa pun yang lebih besar dengan 413 sebelum membaca seluruh badan ke memori tempat Anda bisa. Pasang batas tarif di depan endpoint; validasi kriptografi dan panggilan API keluar per permintaan tidak gratis, dan endpoint verifikasi terbuka adalah target yang menarik.
Privasi. Anda menerima gambar pengguna. Proses di memori atau di file sementara yang langsung Anda hapus, seperti contoh, dan jangan catat byte gambar. Jika Anda mengirim gambar ke pengklasifikasi pihak ketiga, nyatakan dalam kebijakan privasi Anda dan pastikan itu diizinkan untuk kasus penggunaan Anda.
Apa yang ditangkap dan dilewatkan oleh setiap sinyal
Tabel ini adalah model mental yang harus diingat. Inilah mengapa layanan menggunakan keduanya.
| Skenario | Sinyal asal-usul C2PA | Sinyal pengklasifikasi |
|---|---|---|
| Gambar AI dari alat yang menulis Kredensial Konten | Menangkapnya: manifes menyebutkan generatornya | Biasanya menangkapnya: artefak hadir |
| Gambar AI dengan metadata dihapus (tangkapan layar, unggah ulang) | Melewatkannya: tidak ada manifes untuk dibaca | Menangkapnya: bekerja pada piksel, tidak perlu metadata |
| Foto asli dari kamera yang menandatangani Kredensial Konten | Memverifikasinya: manifes valid, generator non-AI | Mungkin salah positif pada kompresi atau editan berat |
| Foto asli tanpa metadata sama sekali | Tidak ada sinyal: tidak ada yang divalidasi | Hanya tebakan terbaik: probabilistik, bisa salah |
| Gambar dengan manifes palsu atau dirusak | Menangkapnya: validation_status menandai kegagalan |
Mungkin atau mungkin tidak menangkapnya, tergantung piksel |
| Generator baru yang belum dilatih oleh pengklasifikasi | Menangkapnya hanya jika alat tersebut menulis manifes | Sering melewatkannya: di luar distribusi pelatihan |
| Foto asli yang diedit berat (retouch AI pada dasar asli) | Manifes, jika ada, mencatat riwayat editan | Ambigu: sebagian sintetis, skor berada di tengah |
Baca setiap baris dan Anda melihat cerita yang sama: di mana satu sinyal buta, yang lain seringkali tidak. Asal-usul tepat tetapi jarang; pengklasifikasi bersifat universal tetapi samar. Putusan gabungan lebih dapat dipercaya daripada salah satu kolom saja, dan nilai uncertain yang jujur ada untuk baris di mana kedua sinyal lemah.
Kasus Penggunaan Dunia Nyata
Pola ini tidak bersifat akademis. Beberapa tempat yang cocok secara langsung:
- Platform konten yang dibuat pengguna. Pasar atau aplikasi sosial dapat menjalankan unggahan melalui
/verifydan memberi label atau mengantre untuk ditinjau apa pun yang dinilai kemungkinan AI, atau apa pun yang membawa manifes yang gagal validasi. Putusan tiga nilai secara jelas memetakan ke "izinkan," "tandai," dan "kirim ke manusia." - Ruang berita dan pemeriksaan fakta. Editor yang memeriksa gambar viral mendapatkan asal-usul kriptografi, jika ada, dan perkiraan model independen dalam satu panggilan, dengan kalimat penjelasan yang dapat mereka kutip dalam catatan mereka.
- Asuransi dan penerimaan klaim. Ketika pelanggan mengirimkan bukti foto, langkah verifikasi menimbulkan bendera pada gambar yang terlihat dihasilkan atau membawa manifes yang rusak, sebelum adjuster manusia menghabiskan waktu untuk itu.
- Pipeline aset internal. Tim yang perlu menjaga gambar yang dihasilkan AI agar tidak masuk, atau diberi label jelas, di perpustakaan stok dapat mengunci penyerapan pada
/verify. - Penerbitan yang sadar asal-usul. Saat semakin banyak kamera dan editor mengadopsi Kredensial Konten, CMS dapat membaca manifes saat mengunggah dan menampilkan lencana terverifikasi, kembali ke pengklasifikasi ketika tidak ada manifes.
Benang merahnya: Anda menginginkan langkah pertama yang cepat dan otomatis yang jujur tentang ketidakpastiannya sendiri, sehingga perhatian manusia tertuju pada tempat yang benar-benar dibutuhkan.
Kesimpulan
Mendeteksi gambar yang dihasilkan AI dengan baik bukanlah tentang menemukan satu tes yang sempurna. Ini tentang menggabungkan sinyal independen dan jujur tentang kepercayaan diri.
- Kredensial Konten C2PA memberi Anda sinyal asal-usul yang kuat dan dapat diverifikasi secara kriptografi, tetapi manifes bersifat opt-in dan mudah dihapus, sehingga seringkali tidak ada.
- Pengklasifikasi yang di-host memberi Anda sinyal universal tetapi probabilistik yang berfungsi pada gambar apa pun, dengan atau tanpa metadata.
- Menggabungkannya dalam layanan FastAPI kecil menghasilkan putusan tiga nilai,
likely_authentic,likely_ai, atauuncertain, dengan skor kepercayaan dan kedua sinyal mentah terlampir untuk audit. - Merancang kontrak OpenAPI terlebih dahulu memungkinkan Anda membuat mock endpoint di Apidog sehingga frontend dapat dibangun secara paralel, lalu menjalankan skenario pengujian yang disimpan terhadap backend nyata.
- Tidak ada detektor yang sempurna. Dua sinyal meningkatkan kepercayaan; mereka tidak menghilangkan ketidakpastian, itulah sebabnya putusan
uncertainadalah fitur, bukan celah.
Untuk membangun ini secara nyata, rancang skema /verify, hasilkan mock server, dan jalankan pengujian endpoint Anda di satu tempat. Unduh Apidog untuk merancang, membuat mock, dan menguji API saat Anda membangunnya, lalu beralih dari mock ke backend langsung dengan satu perubahan URL dasar.
