TH ▾
รับคีย์ API

คู่มือแก้ไขข้อผิดพลาด API แบบไม่เซ็นเซอร์

การเกิดข้อผิดพลาดไม่ใช่เรื่องน่ากลัว แต่ที่น่ากลัวคือไม่รู้ว่าควรลองใหม่หรือแก้โค้ด คู่มือนี้แยกวิเคราะห์รหัสสถานะทีละตัว: เงื่อนไขการเกิด, เกณฑ์ตัดสิน, วิธีแก้ไข ตัวข้อผิดพลาดเป็น JSON รูปแบบ object error ที่มี code และ message ดังนั้นขั้นตอนแรกในการแก้คืออ่าน body ไม่ใช่ดูแค่อัตราการตอบกลับ พร้อมตัวอย่างโค้ดการลองใหม่แบบ exponential backoff, โค้ดจำกัดอัตราฝั่งไคลเอนต์ และรายการตรวจสอบแบบติ๊กถูกได้

อัปเดตเมื่อ

จุดสำคัญ

  • มีเพียง 429 และ 503 เท่านั้นที่ควรลองใหม่โดยอัตโนมัติ การลองใหม่กับสถานะโค้ดอื่นจะเสียคำขอเปล่าๆ
  • 402 no_credit คือเงินหมดหรือเครดิตทดลองใช้หมดอายุ 403 content_blocked คือเนื้อหาถูกบล็อก ทั้งสองอย่างไม่ใช่ปัญหาเครือข่าย
  • การลองใหม่ต้องรวม exponential backoff พร้อม random jitter และกำหนดจำนวนครั้งสูงสุด
  • key แต่ละอันจำกัด 300 คำขอต่อนาที งานแบบ batch ต้องจำกัดอัตราฝั่งไคลเอนต์ก่อน

รูปแบบ error body และลำดับการอ่าน

การตอบสนองที่ล้มเหลวทั้งหมดมีโครงสร้างเดียวกัน:

{"error":{"code":"...","message":"..."}}

ลำดับการอ่านมี 3 ขั้นตอนคงที่:

  1. HTTP status code กำหนดหมวดหมู่;
  2. error.code กำหนดสาเหตุเฉพาะ ใช้ทำ branching ในโปรแกรม;
  3. error.message สำหรับมนุษย์ บันทึกใน log อย่าใช้ match string

ต้องแยกแยะให้ชัดเจนระหว่างสิ่งที่แก้ด้วยการ重试ได้ (429, 503 และ network timeout) กับสิ่งที่ต้องแก้ไขคำขอจึงจะผ่าน (ที่เหลือ) การสับสนระหว่างสองประเภทนี้คือสาเหตุหลักของ incidents ใน production เช่น การ重试 402 ซ้ำๆ จนเกิดคำขอ无效นับสิบต่อวินาที

แนะนำให้บันทึก 4 field คงที่ใน log: timestamp, status code, error.code และประมาณการ prompt_tokens ของคำขอนั้น เมื่อเกิดปัญหาจะเห็นได้ทันทีว่า error ประเภทใดเพิ่มขึ้นหรือล้มเหลวทั้งหมด พร้อมตั้ง alert rule: 402 แค่เกิดครั้งเดียวให้แจ้งเตือนทันทีเพราะหมายถึงบริการใช้งานไม่ได้สำหรับผู้ใช้; 429 ดูที่อัตรา หากเกิดเป็นครั้งคราวไม่ต้องจัดการ หากเกิดต่อเนื่องแสดงว่าการออกแบบ concurrency มีปัญหา

ตารางรหัสสถานะแบบด่วน

รหัสสถานะerror.codeความหมายทำซ้ำ?
400—คำขอไม่ถูกต้อง เช่น prompt รวม max_tokens เกิน 100kไม่
401—key ไม่ถูกต้องหรือไม่มีไม่
402no_creditเงินหมดหรือเครดิตทดลองใช้หมดอายุไม่
403content_blockedเนื้อหาถูกบล็อกไม่
404—endpoint ไม่อยู่ไม่
429—trigger rate limitใช่, backoff
503upstream_busyบริการกำลังยุ่งชั่วคราวใช่, รอไม่กี่วินาที

ในตาราง “—” หมายถึงไม่มี code string คงที่ที่ต้องจัดการเฉพาะ ให้ทำ branching ตาม status code ได้เลย

4xx: แก้คำขอ อย่าทำซ้ำ

400 คำขอไม่ถูกต้อง

  • สาเหตุที่ 1: รูปแบบ JSON ผิดพลาด มักเกิดจากการพิมพ์ string ด้วยมือโดยไม่ได้ escape quote
  • สาเหตุที่ 2: prompt รวม max_tokens เกิน 100,000 การสนทนาที่ยาวมัก trigger กรณีนี้
  • สาเหตุที่ 3: request body เกิน 8 MB
  • วิธีแก้ไข: ใช้ไลบรารี JSON เพื่อทำ serialization; ประมาณจำนวน token ก่อนส่ง หากเกินให้ตัดประวัติหรือลด max_tokens; ดูการใช้งานบริบทยาว

401 ปัญหา key

  • Header หาย หรือขาด prefix Bearer
  • สร้าง key ใหม่แล้ว key เก่าหมดอายุทันที แต่เครื่องหนึ่งยังใช้ค่าเก่าอยู่
  • environment variable ไม่ได้ถูก inject เข้า container หรือ cron job

402 no_credit

ยอดเงินหมด หรือทดลองใช้ฟรี 7 วันหมดอายุ ไปหน้าบัญชีเพื่อเติมเงินแบบเติมเงินล่วงหน้าจะกู้คืนได้ แนะนำให้ตรวจสอบยอดเงินด้วยบริการของคุณเอง อย่ารอให้ผู้ใช้เจอข้อผิดพลาดก่อนค่อยมาตรวจสอบ

403 content_blocked

เนื้อหาถูกบล็อก เนื้อหาผู้ใหญ่ที่ถูกต้องตามกฎหมาย เรื่องแต่ง และหัวข้อที่มีข้อโต้แย้งจะไม่ถูกปฏิเสธ แต่เนื้อหาทางเพศที่เกี่ยวข้องกับเด็กจะถูกบล็อกทั้งหมด รวมถึงนวนิยายและบทบาทสมมติ เมื่อเจอ 403 ให้ตรวจสอบอินพุตและประวัติว่ามีเนื้อหาดังกล่าวหรือไม่ แทนที่จะเปลี่ยนคำพูดแล้วลองใหม่

404 เอนด์พอยต์ไม่พบ

มีเพียงสองเอนด์พอยต์: POST /v1/chat/completions และ GET /v1/models หากเส้นทางขาด /v1 มี斜杠 เกินไป พิมพ์ผิด หรือเรียกใช้ embeddings รูปภาพ หรืออินเทอร์เฟซอื่นๆ ที่ไม่รองรับ จะเกิด 404

ใน 400 มีอีกประเภทหนึ่งที่มักเข้าใจผิด: โทเคนของบทสนทนาที่ยาวจะเพิ่มขึ้นแบบเส้นตรงตามจำนวนรอบ การทดสอบในตอนกลางวันอาจปกติดี แต่หลังจากผ่านไปหลายสิบรอบจะเกิด 400 ทันที这不是接口不稳定,而是上下文到顶了。解决办法是给历史设上限,超过阈值就丢最旧的轮次,或者把旧内容压缩成摘要。别等报错再处理,在发请求前就估算好。

เคล็ดลับในการตรวจสอบ 401: พิมพ์คีย์ API 4 หลักแรกและ 4 หลักสุดท้ายลงในบันทึก (log) ไม่ต้องพิมพ์เนื้อหาทั้งหมด แล้วเปรียบเทียบกับที่แสดงในหน้าบัญชี วิธีนี้จะยืนยันได้ว่าคีย์ที่อ่านได้จากกระบวนการทำงานคือคีย์ที่คุณคิดหรือไม่ โดยเฉพาะในสภาพแวดล้อมของคอนเทนเนอร์และงานกำหนดเวลา การขาดตัวแปรสภาพแวดล้อมหรือการอ่านค่าเก่าเป็นสาเหตุที่พบบ่อยที่สุด

429 และ 503: สองประเภทที่ควรลองใหม่

429 ขีดจำกัดอัตรา

คีย์ละ 300 คำขอต่อนาที งานแบบแบตช์ การใช้คีย์เดียวกันโดยหลายอินสแตนซ์ หรือพายุการเรียกซ้ำ จะทำให้เกิดเหตุการณ์นี้ การแก้ไขมีสองชั้น: ไคลเอนต์ต้องจำกัดอัตราก่อน (ดูโค้ดด้านล่าง) แล้วจึงทำ retry แบบ backoff สำหรับ 429

503 upstream_busy

บริการกำลังยุ่งชั่วคราว ลองใหม่ในไม่กี่วินาทีก็ได้ อย่าส่งคำขอติดกันทันที หรือลองใหม่สิบครั้งภายในหนึ่งวินาที เพราะจะทำให้สถานการณ์แย่ลง

หมดเวลาเครือข่าย

การสร้างข้อความยาวใช้เวลานาน อย่าตั้งค่า timeout สั้นเกินไป ตัวอย่างใช้ 120 วินาที เมื่อ timeout แล้วลองใหม่ต้องระวัง: คำขอครั้งก่อนอาจทำงานที่เซิร์ฟเวอร์แล้ว การเรียกซ้ำอาจทำให้เสียเครดิตซ้ำ ดังนั้นสำหรับคำขอประเภทสร้างข้อความ ควรลดจำนวนครั้งในการ retry และควรใช้สตรีมมิงเพื่อลดเวลาในการรอต่อครั้ง

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"])

ข้อควรจำ: สูตรการถอยหลังคือ min(cap, base × 2^n) × สัมประสิทธิ์สุ่ม การสุ่มช่วยหลีกเลี่ยงการที่ไคลเอนต์หลายตัวลองใหม่พร้อมกันจนชนกัน; สถานะโค้ดในชุด FATAL จะไม่ลองใหม่เลย

การจำกัดอัตราฝั่งไคลเอนต์และคำขอวินิจฉัย

ดีกว่าที่จะจำกัดอัตราไว้ล่วงหน้า แทนที่จะรอจนได้ 429 แล้วค่อยถอยหลัง เครื่องมือเล็กๆ ด้านล่างนี้จะรับประกันว่าจำนวนคำขอภายในหนึ่งนาทีจะต่ำกว่าค่าที่ตั้งไว้ ปลอดภัยสำหรับมัลติเธรด:

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))

วิธีที่สะอาดที่สุดในการตรวจสอบคือแยกจากโค้ดธุรกิจ แล้วใช้ curl ส่งคำขอขั้นต่ำ -i จะเห็นทั้งสถานะและหัวตอบกลับพร้อมกัน:

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"}]}'

curl ทำงานได้แต่โค้ดธุรกิจทำงานไม่ได้ ปัญหาคือโค้ดหรือสภาพแวดล้อมของคุณ (proxy, ตัวแปรสภาพแวดล้อม, การเข้ารหัส); curl ก็ทำงานไม่ได้ ให้ดูคีย์ API ยอดเงิน และเครือข่าย

ทำไมตั้งค่าขีดจำกัดอัตราที่ 240 แทนที่จะเป็น 300: เมื่อติดตั้งหลายอินสแตนซ์ แต่ละอินสแตนซ์จะจำกัดอัตราของตัวเอง แต่รวมกันอาจเกินขีดจำกัดรวมได้ นอกจากนี้การ retry ยังใช้โควตาเพิ่ม การเผื่อพื้นที่ไว้ 20% เป็นวิธีที่ปลอดภัยกว่า หากคุณมีหลายอินสแตนซ์ ให้แบ่งโควตาของแต่ละอินสแตนซ์เท่าๆ กันจากจำนวนรวม หรือใช้ตัวนับแบบแชร์เพื่อจัดการรวม

รายการตรวจสอบการแก้ไขข้อผิดพลาด

  1. อ่าน error.code ใน body อย่าดูแค่อัตราการตอบกลับ
  2. 401: ตรวจสอบ prefix ของ key, ตัวแปรสภาพแวดล้อม หรือการสร้าง key ใหม่
  3. 400: JSON ถูกต้องหรือไม่, prompt + max_tokens เกิน 100,000 หรือไม่, body ของคำขอเกิน 8 MB หรือไม่
  4. 402: ยอดเงินและอายุการใช้งานทดลองใช้
  5. 403: อินพุตและประวัติมีเนื้อหาที่ห้ามหรือไม่
  6. 404: เส้นทางเป็น /v1/chat/completions หรือ /v1/models หรือไม่
  7. 429: หลายอินสแตนซ์ใช้คีย์ API เดียวกันหรือไม่, มีการจำกัดอัตราฝั่งไคลเอนต์หรือไม่
  8. 503: ตรวจสอบว่าได้ทำ exponential backoff retry แล้วหรือยัง และเว้นระยะอย่างน้อยไม่กี่วินาที
  9. หมดเวลา: timeout เพียงพอหรือไม่, สามารถเปลี่ยนเป็นสตรีมมิงได้หรือไม่
  10. ตัดตัวเลือกทั้งหมดข้างต้นออกแล้ว: ใช้ curl เพื่อทำซ้ำด้วยคำขอที่น้อยที่สุด

เพิ่งเชื่อมต่อ API: ดูคู่มือการเชื่อมต่อ APIก่อน พารามิเตอร์อื่นๆ อยู่ในเอกสารประกอบ

วิธีใช้รายการ: เมื่อเกิดปัญหาให้ไล่ตรวจสอบจากบนลงล่างทีละข้อ อย่าข้ามไปมา ความผิดปกติส่วนใหญ่ที่ดู "แปลกประหลาด" มักจะอยู่ที่ 4 ข้อแรก

คำแนะนำเพิ่มเติม: บันทึกสรุปผลการแก้ไขข้อผิดพลาดกลับไปยังเอกสารของทีม เพื่อที่ครั้งหน้าเมื่อเจอข้อผิดพลาดเดียวกัน จะสามารถระบุสาเหตุได้ทันที

คำถามที่พบบ่อย

ต้องรอนานแค่ไหนก่อนลองใหม่เมื่อได้รับ 503?

เพียงไม่กี่วินาที แนะนำให้รอ 1-2 วินาทีในครั้งแรก จากนั้นเพิ่มตามลำดับเลขชี้กำลัง พร้อมเพิ่มค่าสุ่ม (jitter) และกำหนดจำนวนครั้งสูงสุด

ทำไมคำขอของฉันถึงเป็น 402 ตลอด?

เครดิตหมด หรือทดลองใช้ฟรี 7 วันหมดอายุ สามารถใช้งานต่อได้ทันทีเมื่อเติมเงินแบบเติมเงินล่วงหน้า เครดิตไม่หมดอายุ

403 content_blocked สามารถหลีกเลี่ยงได้โดยการเปลี่ยนคำพูดหรือไม่?

อย่าลอง เนื้อหาทางเพศที่เกี่ยวข้องกับผู้เยาว์จะถูกบล็อกเสมอ รวมถึงในนิยายและบทบาทสมมติ เนื้อหาสำหรับผู้ใหญ่ทั่วไปจะไม่เกิดข้อผิดพลาดนี้

429 จำกัดตามบัญชีหรือตามคีย์ API?

การจำกัดคือคีย์ API ละ 300 คำขอต่อนาที แต่ละบัญชีมีคีย์ API เพียงตัวเดียว ดังนั้นเมื่อหลายบริการใช้ร่วมกัน ต้องจำกัดอัตราร่วมกัน

กรอกแบบฟอร์มเพื่อรับคีย์ API

สร้างบัญชี คัดลอกคีย์ API แก้ไข Base URL การตั้งค่าก็ง่ายแค่นี้