AR ▾
الحصول على مفتاح API

دليل أخطاء رموز API بدون رقابة والتشخيص

لا يخيفك الخطأ، بل عدم معرفة ما إذا كان يجب إعادة المحاولة أو تعديل الكود. يفصل هذا الدليل الأخطاء حسب رمز الخطأ: شروط التشغيل، معايير الحكم، وطرق الإصلاح. يكون جسم الخطأ دائمًا بصيغة JSON، مثل كائن الخطأ الذي يحتوي على 'code' و'message'، لذا فإن الخطوة الأولى هي قراءة الجسم، وليس الاقتصار على رمز الحالة. يتضمن الكود المرفق إعادة المحاولة بالتراجع الأسي، وتحديد المعدل من جانب العميل، وقائمة تحقق.

تم التحديث في

نقاط رئيسية

  • فقط 429 و503 يستحقان إعادة المحاولة التلقائية؛ إعادة محاولة رموز الحالة الأخرى ستهدر موارد الطلبات فقط.
  • رمز 402 no_credit يعني نفاد الرصيد أو انتهاء صلاحية التجربة، ورمز 403 content_blocked يعني حظر المحتوى؛ وكلاهما ليسا مشكلتي شبكة.
  • يجب أن تتضمن إعادة المحاولة تراجعاً أسيًا مع اهتزاز عشوائي، مع تحديد الحد الأقصى لعدد المحاولات.
  • 300 طلب في الدقيقة لكل مفتاح، يجب تحديد المعدل من جانب العميل للمهام الدفعية.

صيغة جسم الخطأ وترتيب القراءة

جميع استجابات الفشل لها نفس الهيكل:

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

ترتيب القراءة ثابت على ثلاث خطوات:

  1. رمز الحالة HTTP، يحدد الفئة الرئيسية؛
  2. error.code، يحدد السبب المحدد، ويستخدم في البرنامج للتفرع؛
  3. error.message، للقراءة البشرية، وتسجيله في السجلات، لا تستخدمه للمطابقة النصية.

حدد بوضوح الفاصل بين: ما يمكن حله بإعادة المحاولة (429، 503، وانقطاع الشبكة) وما يتطلب تعديلًا (الباقي). خلط الفئتين هو السبب الأكثر شيوعًا للأعطال في الإنتاج، مثل إعادة المحاولة بشكل متكرر لرمز 402 مما يؤدي إلى إرسال عشرات الطلبات غير الصالحة في الثانية.

نوصي بتسجيل أربعة حقول ثابتة في السجلات: الوقت، رمز الحالة، error.code، وتقدير prompt_tokens لهذا الطلب. عند حدوث مشكلة، يمكنك رؤية ما إذا كانت فئة معينة من الأخطاء قد زادت فجأة أم أن الفشل عام. أضف قاعدة تنبيه: إشعار فوري عند ظهور 402 مرة واحدة لأنه يعني أن الخدمة غير متاحة للمستخدم؛ أما 429 فينظر إليه كنسبة، والحوادث العرضية لا تحتاج معالجة، ولكن الاستمرار يعني مشكلة في تصميم الطلبات المتزامنة.

البحث السريع عن رموز الحالة

رمز الحالةerror.codeالمعنىإعادة المحاولة؟
400—الطلب غير صالح، مثل تجاوز prompt مع max_tokens لـ 100kلا
401—المفتاح غير صالح أو مفقودلا
402no_creditنفاد الرصيد أو انتهاء صلاحية التجربةلا
403content_blockedحظر المحتوىلا
404—نقطة النهاية غير موجودةلا
429—تجاوز حدّ المعدلنعم، تراجع
503upstream_busyالخدمة مشغولة مؤقتاًنعم، بعد بضع ثوانٍ

الرمز “—” في الجدول يعني عدم وجود رمز ثابت يتطلب معالجة خاصة، اعتمد على رمز الحالة للتفرع.

4xx: عدل الطلب، لا تعيد المحاولة

400 طلب غير صالح

  • السبب الأول: خطأ في صيغة JSON، شائع عند كتابة السلاسل النصية يدوياً دون هروب علامات الاقتباس.
  • السبب الثاني: تجاوز prompt مع max_tokens لـ 100,000. المحادثات الطويلة هي الأكثر عرضة لذلك.
  • السبب الثالث: تجاوز حجم جسم الطلب لـ 8 MB.
  • طريقة الإصلاح: استخدم مكتبة JSON للتسلسل؛ قدّر الرموز قبل الإرسال، وقص السجل أو قلل max_tokens إذا تجاوزت الحد؛ انظردليل السياق الطويل.

401 مشكلة في المفتاح

  • مفقود في Header، أو افتقاد بادئة Bearer .
  • تم إعادة توليد المفتاح، مما يجعل المفتاح القديم غير صالح فوراً، بينما لا تزال بعض الآلات تستخدم القيمة القديمة.
  • متغير البيئة لم يتم تضمينه في الحاوية أو المهام الدورية.

402 no_credit

نفد الرصيد أو انتهت صلاحية تجربة الـ 7 أيام المجانية. انتقل إلى صفحة الحساب لشحن رصيد مسبق الدفع واستعادة الخدمة. يُنصح بمراقبة رصيدك الخاص لتجنب اكتشاف الأخطاء من قبل المستخدمين.

403 content_blocked

تم حظر المحتوى. لن يتم رفض المحتوى البالغ القانوني، والخيالي، والمواضيع المثيرة للجدل، لكن محتوى الجنس الذي يتعلق بالأطفال سيتم حظره دائمًا، بما في ذلك الروايات وأدوار التمثيل. عند مواجهة خطأ 403، افحص الإدخال وسجل المحادثات بحثًا عن هذا النوع من المحتوى، ولا تحاول إعادة المحاولة بتغيير الصياغة.

404 نقطة النهاية غير موجودة

هناك نقطتا نهاية فقط: POST /v1/chat/completions و GET /v1/models. يؤدي نسيان /v1 في المسار، أو إضافة شرطة مائلة زائدة، أو خطأ إملائي، أو طلب واجهات غير مدعومة مثل embeddings أو الصور، إلى خطأ 404.

400: هناك فئة شائعة من الأخطاء: تزداد رموز (tokens) المحادثات الطويلة خطياً مع عدد الحلقات. قد تعمل الاختبارات نهاراً بشكل طبيعي، ثم تبدأ أخطاء 400 فجأة بعد عشرات الحلقات. هذا لا يعني عدم استقرار الواجهة، بل يعني أن نافذة السياق قد امتلأت. الحل هو تحديد حد للسياق التاريخي: إذا تجاوزت العتبة، احذف أقدم الحلقات أو اضغط المحتوى القديم في ملخص. لا تنتظر ظهور الخطأ لمعالجته؛ قدّر السياق قبل إرسال الطلب.

نصيحة لتشخيص 401: اطبع أول أربعة أحرف وآخر أربعة أحرف من مفتاح API في السجلات، ولا تطبع المفتاح كاملاً، ثم قارنه بما يظهر في صفحة الحساب. هكذا تتأكد من أن العملية تقرأ المفتاح الصحيح، خاصة في بيئات الحاويات والمهام المجدولة حيث يكون فقدان متغيرات البيئة أو قراءة قيمة قديمة هو السبب الأكثر شيوعاً.

429 و 503: فئتان يجب إعادة المحاولة

429 حدّ المعدل

حدّ المعدل هو 300 طلب في الدقيقة لكل مفتاح API. ستؤدي المهام الدفعية، ومشاركة مفتاح واحد بين عدة مثيلات، وعاصفة إعادة المحاولة إلى تفعيله. تتطلب الإصلاحات طبقتين: تقييد المعدل من جانب العميل أولاً (انظر الكود أدناه)، ثم إعادة المحاولة مع تخفيف الحمل عند استلام 429.

503 upstream_busy

الخدمة مشغولة مؤقتًا، أعد المحاولة بعد بضع ثوانٍ. لا ترسل طلبات متتالية فورًا، ولا تعد المحاولة عشر مرات في ثانية واحدة، لأن ذلك سيزيد الوضع سوءًا.

مهلة الشبكة

تستغرق توليد النصوص الطويلة وقتًا أطول، لذا لا تضبط مهلة الـ timeout على قيمة قصيرة جدًا، استخدم 120 ثانية كمثال. عند إعادة المحاولة بعد انتهاء المهلة، انتبه إلى أن الطلب السابق قد يكون قد نُفذ بالفعل على الخادم، وقد يؤدي الاستدعاء المتكرر إلى استهلاك الرصيد مرتين. لذلك، قلل عدد مرات إعادة المحاولة لطلبات التوليد، ويفضل استخدام البث المتدفق لتقليل وقت الانتظار لكل طلب.

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 لكن كود العمل يفشل، فالمشكلة في الكود أو البيئة (الوكيل، متغيرات البيئة، الترميز). وإذا فشل curl أيضًا، فافحص مفتاح API، والرصيد، والشبكة.

لماذا تم ضبط حد المعدل على 240 بدلاً من 300: عند النشر متعدد النسخ، كل نسخة لها حد معدل خاص بها، وقد يتجاوز المجموع الإجمالي؛ بالإضافة إلى أن إعادة المحاولة تستهلك حصة إضافية. ترك هامش 20% إجراء آمن. إذا كان لديك عدة نسخ، وزّع الحصة الإجمالية بالتساوي على كل نسخة، أو استخدم عدادًا مشتركًا لإدارة التوزيع.

قائمة استكشاف الأخطاء

  1. اقرأ error.code من جسم الاستجابة، ولا تعتمد فقط على رمز الحالة.
  2. 401: بادئة المفتاح، متغيرات البيئة، ما إذا كان قد تم إعادة توليده.
  3. 400: شرعية JSON، ما إذا كان مجموع الموجّه و max_tokens يتجاوز 100,000، ما إذا كان جسم الطلب يتجاوز 8 MB.
  4. 402: الرصيد وصلاحية التجربة.
  5. 403: هل يحتوي الإدخال وسجل المحادثات على محتوى محظور؟
  6. 404: هل المسار هو /v1/chat/completions أو /v1/models؟
  7. 429: هل تشارك عدة مثيلات مفتاح API واحد؟ هل طبقت تقييد المعدل من جانب العميل؟
  8. 503: هل طبقت إعادة المحاولة مع التخفيف؟ هل كان الفاصل الزمني بضع ثوانٍ على الأقل؟
  9. مهلة الوقت: هل مهلة timeout كافية، هل يمكن التبديل إلى البث المتدفق.
  10. بعد استبعاد جميع الأسباب السابقة: استخدم curl لإرسال طلب بسيط لإعادة إنتاج المشكلة.

إذا كنت تتصل حديثًا، انظر أولاً إلى التعليمات. تتوفر المزيد من المعلمات في الوثائق.

كيفية استخدام القائمة: استبعد الأسباب من الأعلى إلى الأسفل عند حدوث مشكلة، ولا تقفز بين البنود. تقع معظم الأعطال "الغريبة" في النهاية ضمن البنود الأربعة الأولى.

نصيحة إضافية: اكتب استنتاجات استكشاف الأخطاء في وثائق الفريق، بحيث يمكنك مطابقة أخطاء مماثلة في المرة القادمة مباشرةً.

الأسئلة الشائعة

كم من الوقت يجب الانتظار قبل إعادة المحاولة عند استلام 503؟

بضع ثوانٍ فقط. يُنصح بالانتظار من ثانية إلى ثانيتين في المحاولة الأولى، ثم زيادة الوقت بشكل أسي مع إضافة اهتزاز عشوائي، وتحديد حد أقصى لعدد مرات إعادة المحاولة.

لماذا يفشل طلبي دائماً بـ 402؟

نفد الرصيد أو انتهت صلاحية تجربة الـ 7 أيام المجانية. سيتم استعادة الخدمة بعد شحن رصيد مسبق الدفع، ولا ينتهي صلاحية الرصيد.

هل يمكن تجاوز خطأ 403 content_blocked عن طريق إعادة الصياغة؟

لا تحاول ذلك. سيتم حظر محتوى الجنس الذي يتعلق بالأطفال في جميع الحالات، بما في ذلك الروايات وأدوار التمثيل. لن يؤدي المحتوى البالغ القانوني إلى حدوث هذا الخطأ.

429 是按账户还是按 key 限制?

الحد هو 300 طلب في الدقيقة لكل مفتاح. نظرًا لأن كل حساب يحتوي على مفتاح واحد فقط، فيجب تحديد المعدل عند مشاركة الخدمة بين عدة خدمات.

املأ النموذج للحصول على مفتاح API

أنشئ حسابًا، وانسخ مفتاح API، وعدّل Base URL. الإعداد بهذه البساطة.