Format Error & Urutan Pembacaan
Semua respons gagal memiliki struktur yang sama:
{"error":{"code":"...","message":"..."}}
Urutan pembacaan tetap tiga langkah:
- Kode status HTTP menentukan kategori besar;
error.codemenentukan penyebab spesifik; gunakan ini untuk percabangan kode;error.messageuntuk manusia, catat di log, jangan gunakan untuk pencocokan string.
Pahami dulu batasnya: yang bisa diperbaiki dengan retry (429, 503, dan timeout jaringan) dan yang harus diubah (yang lain). Mengaburkan kedua kategori adalah akar penyebab insiden produksi, seperti melakukan retry terus-menerus pada 402 hingga menghasilkan puluhan permintaan tidak valid per detik.
Disarankan untuk mencatat empat bidang secara tetap di log: waktu, kode status, error.code, dan estimasi prompt_tokens permintaan ini. Saat terjadi masalah, Anda dapat langsung melihat apakah satu jenis error meningkat tiba-tiba atau kegagalan terjadi secara keseluruhan. Tambahkan aturan peringatan: beri notifikasi segera jika 402 muncul sekali karena berarti layanan tidak tersedia untuk pengguna; untuk 429, lihat proporsinya—kejadian sporadis tidak perlu ditangani, tetapi jika terus-menerus muncul berarti desain konkurensi bermasalah.
Tabel Cepat Kode Status
| Kode Status | error.code | Arti | Coba Ulang? |
|---|---|---|---|
| 400 | — | Permintaan tidak valid, mis. prompt + max_tokens melebihi 100k | Tidak |
| 401 | — | Kunci tidak valid atau hilang | Tidak |
| 402 | no_credit | Saldo habis atau masa berlaku uji coba berakhir | Tidak |
| 403 | content_blocked | Konten diblokir | Tidak |
| 404 | — | Endpoint tidak ditemukan | Tidak |
| 429 | — | Memicu batas laju | Ya, backoff |
| 503 | upstream_busy | Layanan sibuk sementara | Ya, tunggu beberapa detik |
Tanda “—” berarti tidak ada string code tetap yang perlu Anda tangani khusus; cukup gunakan percabangan berdasarkan kode status.
4xx: Perbaiki Permintaan, Jangan Coba Ulang
400 Permintaan tidak valid
- Penyebab 1: Kesalahan format JSON, sering terjadi saat menulis string manual tanpa escape tanda kutip.
- Penyebab 2: prompt + max_tokens melebihi 100,000. Percakapan panjang paling sering memicu ini.
- Penyebab 3: Body permintaan melebihi 8 MB.
- Perbaikan: serialisasi menggunakan pustaka JSON; estimasi token sebelum mengirim, potong riwayat atau kurangi max_tokens jika melebihi batas; lihatPanduan Praktis Konteks Panjang.
401 Masalah Kunci
- Header hilang atau tidak ada awalan
Bearer. - Kunci telah digenerate ulang; kunci lama langsung tidak valid, namun beberapa mesin masih menggunakan nilai lama.
- Variabel lingkungan tidak diteruskan ke container atau tugas terjadwal.
402 no_credit
Saldo habis atau masa uji coba 7 hari telah berakhir. Kunjingi halaman akun untuk mengisi saldo prabayar agar layanan dapat dilanjutkan. Disarankan agar Anda memantau saldo layanan Anda sendiri agar tidak menunggu hingga pengguna melaporkan kesalahan.
403 content_blocked
Konten diblokir. Konten dewasa yang sah, fiksi, dan topik kontroversial tidak akan ditolak, namun konten seksual yang melibatkan anak di bawah umur akan selalu diblokir, termasuk dalam novel dan permainan peran. Jika Anda menerima 403, periksa apakah input atau riwayat percakapan mengandung konten tersebut; jangan hanya mengubah kata-kata dan mencoba lagi.
404 endpoint tidak ditemukan
Hanya ada dua endpoint: POST /v1/chat/completions dan GET /v1/models. Jika path tidak menyertakan /v1, memiliki slash berlebih, salah eja, atau Anda meminta endpoint embeddings atau gambar yang tidak didukung, Anda akan mendapatkan 404.
Ada satu jenis kesalahan 400 yang sering disalahpahami: token untuk percakapan panjang bertambah secara linear seiring jumlah putaran. Pengujian di siang hari mungkin berjalan lancar, tetapi setelah puluhan putaran, error 400 tiba-tiba muncul. Ini bukan karena ketidakstabilan API, melainkan karena jendela konteks telah penuh. Solusinya adalah membatasi riwayat percakapan; jika melebihi ambang batas, hapus putaran tertua atau kompres konten lama menjadi ringkasan. Jangan menunggu error muncul, perkirakan kapasitas sebelum mengirim permintaan.
Tips untuk men-debug 401: cetak empat karakter pertama dan terakhir dari kunci API ke dalam log, jangan cetak seluruhnya, lalu bandingkan dengan yang ditampilkan di halaman akun. Cara ini memastikan bahwa nilai yang dibaca oleh proses Anda adalah kunci yang Anda maksud, terutama dalam lingkungan container atau tugas terjadwal di mana variabel lingkungan mungkin hilang atau bernilai lama.
429 dan 503: Dua jenis yang perlu dicoba lagi
429 batas laju
Setiap kunci API dibatasi hingga 300 permintaan per menit. Tugas batch, beberapa instance yang berbagi satu kunci API, atau badai retry dapat memicu batas ini. Solusinya memiliki dua lapisan: klien membatasi laju terlebih dahulu (lihat kode di bawah), lalu lakukan retry dengan backoff untuk respons 429.
503 upstream_busy
Layanan sedang sibuk sementara. Coba lagi setelah beberapa detik. Jangan mengirim permintaan berturut-turut atau melakukan sepuluh kali percobaan dalam satu detik karena hal itu akan memperburuk situasi.
Timeout jaringan
Penghasilkan teks panjang membutuhkan waktu lebih lama, jadi jangan atur timeout terlalu pendek; gunakan 120 detik sebagai contoh. Perhatikan bahwa jika timeout terjadi, permintaan sebelumnya mungkin sudah dieksekusi di sisi server, sehingga panggilan ulang dapat menguras kuota secara berlebihan. Untuk permintaan generasi, batasi jumlah retry dan sebaiknya gunakan streaming untuk mengurangi waktu tunggu per sesi.
import os
import random
import time
import requests
URL = "https://api.wushenchaapi.com/v1/chat/completions"
HEADERS = {
"Authorization": "Bearer " + os.environ["API_KEY"],
"Content-Type": "application/json",
}
RETRYABLE = {429, 503} # 只重试这两类
FATAL = {400, 401, 402, 403, 404}
class ApiError(Exception):
def __init__(self, status, code, message):
super().__init__(f"{status} {code}: {message}")
self.status, self.code = status, code
def call(payload, max_retries=5, base=1.0, cap=30.0):
for attempt in range(max_retries + 1):
try:
r = requests.post(URL, headers=HEADERS, json=payload, timeout=120)
except (requests.ConnectionError, requests.Timeout):
r = None # 网络层失败,按可重试处理
if r is not None and r.ok:
return r.json()
if r is not None:
try:
err = r.json().get("error", {})
except ValueError:
err = {}
if r.status_code in FATAL or r.status_code not in RETRYABLE:
raise ApiError(r.status_code, err.get("code"), err.get("message"))
if attempt == max_retries:
raise ApiError(r.status_code if r is not None else 0, "retry_exhausted", "重试次数用尽")
delay = min(cap, base * (2 ** attempt)) * random.uniform(0.5, 1.0)
time.sleep(delay)
if __name__ == "__main__":
out = call({"model": "uncensored", "max_tokens": 100,
"messages": [{"role": "user", "content": "回复一个字:好"}]})
print(out["choices"][0]["message"]["content"])Poin penting: rumus backoff adalah min(cap, base × 2^n) × faktor acak. Jitter acak mencegah banyak klien melakukan retry secara bersamaan; status kode dalam kumpulan FATAL tidak dicoba lagi sama sekali.
Batasan laju klien dan permintaan diagnostik
Daripada menunggu 429 lalu melakukan backoff, lebih baik batasi laju terlebih dahulu. Alat kecil di bawah ini menjamin jumlah permintaan per menit tetap di bawah nilai yang ditetapkan dan aman untuk multithread:
import threading
import time
class RateGate:
"""简单的客户端限速:保证一分钟内请求数不超过 limit。"""
def __init__(self, limit=240): # 留出余量,低于 300/分钟
self.limit, self.stamps, self.lock = limit, [], threading.Lock()
def wait(self):
while True:
with self.lock:
now = time.time()
self.stamps = [t for t in self.stamps if now - t < 60]
if len(self.stamps) < self.limit:
self.stamps.append(now)
return
sleep_for = 60 - (now - self.stamps[0])
time.sleep(max(sleep_for, 0.05))Saat men-debug, cara paling bersih adalah memisahkan kode bisnis dan mengirim permintaan minimal menggunakan curl. Flag -i memungkinkan Anda melihat baris status dan header respons secara bersamaan:
curl -i https://api.wushenchaapi.com/v1/chat/completions \
-H "Authorization: Bearer $API_KEY" \
-H "Content-Type: application/json" \
-d '{"model":"uncensored","max_tokens":20,"messages":[{"role":"user","content":"ping"}]}'Jika curl berhasil tetapi kode bisnis gagal, masalahnya ada pada kode atau lingkungan Anda (proxy, variabel lingkungan, pengkodean). Jika curl juga gagal, periksa kunci API, saldo, dan jaringan.
Mengapa nilai batas laju ditetapkan 240 bukan 300: saat penyebaran multi-instances, setiap instance memiliki batas laju sendiri-sendiri, sehingga totalnya masih bisa melebihi batas keseluruhan; ditambah lagi, retry akan menduduki kuota. Menyisakan margin 20% adalah langkah yang lebih aman. Jika Anda memiliki beberapa instance, bagikan kuota setiap instance secara merata berdasarkan total, atau gunakan penghitung bersama untuk penjadwalan terpusat.
Daftar periksa troubleshooting
- Baca
error.codedi dalam body, jangan hanya melihat kode status. - 401: Prefix kunci API, variabel lingkungan, apakah kunci telah dibuat ulang.
- 400: Apakah JSON valid, apakah jumlah token (prompt + max_tokens) melebihi 100,000, apakah body permintaan melebihi 8 MB.
- 402: Saldo dan masa berlaku uji coba.
- 403: Apakah input dan riwayat mengandung konten yang dilarang.
- 404: Apakah path adalah /v1/chat/completions atau /v1/models.
- 429: Apakah beberapa instance berbagi satu kunci API, apakah sudah diterapkan batasan laju di sisi klien.
- 503: Apakah sudah dilakukan retry dengan backoff, apakah intervalnya setidaknya beberapa detik.
- Timeout: Apakah timeout cukup lama, apakah Anda dapat beralih ke streaming.
- Jika semua di atas telah disingkirkan: gunakan curl untuk mereproduksi dengan permintaan minimal.
Jika baru saja melakukan integrasi, lihat terlebih dahuluTutorial Integrasi. Lebih banyak parameter terdapat didokumentasi.
Cara menggunakan daftar: saat terjadi masalah, singkirkan kemungkinan dari atas ke bawah secara berurutan, jangan melompat-lompat. Sebagian besar kesalahan "aneh" akhirnya bermuara pada empat poin pertama.
Tambahan pengalaman: tuliskan kembali kesimpulan troubleshooting ke dokumentasi tim. Pada kesalahan yang sama di masa depan, Anda dapat langsung mencocokkannya.