يوفر Volcano Ark واجهة Doubao API، وشكلها قريب من OpenAI، لكن دلالة الأخطاء لها خصائصها. الأكثر تميزًا هو 404: الخدمة ليست متوقفة، والمشكلة غالبًا في معرّف نقطة الوصول للاستدلال أو اسم النموذج أو المنطقة أو المسار. ويُساء أيضًا تفسير 429: قد يكون حد سرعة أو تزامنًا أو حصة أو تأخرًا في السداد أو نفاد حزمة موارد، والمعالجة مختلفة تمامًا.

يُستحسن تثبيت ترتيب التشخيص في خمس خطوات: اقرأ رمز HTTP، ثم code وmessage في الجسم، ثم معرّف الطلب، وأعد إنتاج المشكلة بطلب أدنى، وأخيرًا عدّل المعاملات أو غيّر النموذج. يتناول هذا المقال 401 و404 و400 و429 والمهلة و5xx، مع شيفرة معالجة الأخطاء وقائمة السجلات. النماذج والفوترة مرجعهما أسعار النماذج.

ملاحظة: Doubao وVolcano Ark وVolcengine علامات تجارية لشركة ByteDance، وهذا المقال تقني فقط، ولا توجد علاقة تبعية أو وكالة أو شراكة مع ByteDance. قد تتغير رموز الخطأ وقواعد الحدود وسلوك الواجهات مع الإصدارات، والمرجع النهائي الوثائق الرسمية والاستجابة الفعلية.

أولًا: ميّز طبقتين من الأخطاء

يوضح رمز حالة HTTP نتيجة طبقة البروتوكول، بينما توضح حقول code وtype وmessage في الجسم السبب المحدد. قد يكون 401 بسبب غياب المفتاح أو خطئه أو انتهائه أو عدم صلاحية المشروع؛ وقد يكون 429 بسبب سرعة الطلبات أو تزامن مرتفع أو نفاد الحصة أو التأخر في السداد. تسجيل رمز الحالة وحده يجعل التشخيص تخمينًا.

HTTP المعنى الشائع سياق Doubao إعادة المحاولة
401 فشل المصادقة المفتاح مفقود أو تنسيقه خاطئ أو منتهٍ أو لا يطابق المنطقة لا، أصلح البيانات أولًا
403 لا صلاحية المفتاح لا يملك صلاحية النموذج أو نقطة الوصول لا، افحص التفويض
404 المورد غير موجود معرّف نقطة الوصول أو اسم النموذج أو المسار أو المنطقة غير صحيح لا، تحقق أولًا
400 معامل خاطئ سياق طويل، max_tokens غير صالح، صيغة صورة غير مدعومة لا، عدّل الطلب
429 حد أو حصة تجاوز RPM أو TPM أو التزامن، تأخر سداد أو نفاد حزمة حسب السبب
500/502/503 خطأ خادم خلل في المنبع أو تذبذب البوابة أو تعذر مؤقت نعم، بتباعد
مهلة مهلة اتصال أو قراءة الشبكة أو الانتظار أو إدخال طويل أو إخراج طويل إعادة محدودة

أسماء الحقول تتبع الاستجابة الفعلية: بعضها في error.code وبعضها في المستوى الأعلى، فلا يفترض الكود بنية واحدة.

ثانيًا: 401: أربعة أسباب لفشل المصادقة

لا علاقة لـ 401 بقدرات النموذج أو الرصيد، فافحص سلسلة البيانات أولًا:

  • هل الترويسة Authorization: Bearer <key> مع مسافة واحدة بعد النقطتين.
  • هل يُحمَّل المفتاح من متغير البيئة بشكل صحيح، وهل حوله علامات اقتباس أو مسافات أو أسطر جديدة.
  • هل حُذف المفتاح أو دُوّر أو انتهت صلاحيته.
  • هل يتطابق المشروع والمنطقة وBase URL التي ينتمي إليها المفتاح.

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

ثالثًا: 404: معرّف نقطة الوصول واسم النموذج الأكثر التباسًا

في Volcano Ark طريقتان شائعتان لتحديد النموذج: نقطة وصول للاستدلال تُنشأ في لوحة التحكم وتبدأ عادة بـ ep-، ومعرّف نموذج مباشر تدعمه بعض النماذج. يُكتب الاثنان في الحقل model، لكن الصلاحيات والحدود ونطاق الاستخدام قد تختلف. خلط معرّف نقطة الوصول باسم النموذج أو نسخ نقطة وصول من مشروع آخر يعيد 404 أو ما يشبه «المورد غير موجود».

ترتيب التشخيص:

  1. تأكد أن Base URL هو عنوان منطقة مثل https://ark.cn-beijing.volces.com/api/v3، دون زيادة أو نقصان /api/v3.
  2. تحقق في لوحة التحكم من وجود نقطة الوصول وأنها تابعة للمشروع الحالي وغير موقوفة.
  3. تأكد هل طريقة الاستدعاء الحالية تتطلب معرّف نقطة وصول أم معرّف نموذج، ولا تخلط بينهما من الذاكرة.
  4. أرسل طلب curl أدنى لاستبعاد تأثير الدمج التلقائي للمسار في SDK وإعادة كتابة الوسيط.
  5. افحص نوع محتوى الاستجابة: إذا عادت صفحة HTML للخطأ فقد لا يكون الطلب وصل إلى Ark، بل أوقفته بوابة أو وسيط عكسي.

عادةً يشير 404 مع HTML إلى مشكلة مسار أو وسيط؛ أما 404 مع جسم JSON فيشبه أكثر مشكلة نموذج أو نقطة وصول.

رابعًا: 400: تجاوز السياق وأخطاء المعاملات

نطاق 400 واسع، وأشهر أسبابه تجاوز السياق. تُحسب نافذة السياق عادة من رموز الإدخال زائد رموز الإخراج، وقد يجعل max_tokens الكبير طلبًا كان يمر سابقًا يتجاوز الحد. الصور وتعريفات الأدوات ورسائل التاريخ تستهلك رموزًا أيضًا.

المعالجة ليست تكبير الحد، بل ترك هامش للإدخال:

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

عدّ الرموز تقديري، وعند الاقتراب من الحد يكون ترك هامش 10% إلى 20% أكثر أمانًا.

خامسًا: 429: الحد والتزامن والحصة تُعالج منفصلة

لـ 429 أربعة أسباب:

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

عند وجود Retry-After انتظر المدة المحددة، وإلا فاستخدم تباعدًا أُسّيًا مع تذبذب عشوائي. لا تعد المحاولة في 429 الناتج عن الحصة أو التأخر في السداد، لأنه يطيل زمن العطل فقط.

قد يحدث 429 المتدفق عند إنشاء الاتصال أو كحدث خطأ داخل التدفق. يجب أن يعالج العميل رمز الحالة وأخطاء التدفق معًا، لا أن ينظر إلى أول مقطع فقط.

سادسًا: المهلة وانقطاع التدفق: حدد الموضع على ثلاث مراحل

تُقسم المهلة إلى ثلاث مراحل:

  • مهلة الاتصال: DNS أو TLS أو وسيط الخروج أو تعذر الشبكة، وتفشل عادة خلال ثوانٍ.
  • مهلة أول رمز: وصل الطلب إلى المنبع لكنه في الصف أو يعالج إدخالًا طويلًا، ويظهر كغياب المقطع الأول مدة طويلة.
  • مهلة القراءة: الفاصل بين مقطعين طويل جدًا، وقد يعود إلى إخراج طويل أو استدلال مستغرق أو قطع الاتصال بجهاز وسيط.

تحتاج المراحل الثلاث إعدادات مهلة مختلفة. المهلة الكلية الكبيرة جدًا تُبقي الطلبات المعطلة محتلة لمجمع الاتصالات؛ والصغيرة جدًا تقتل إخراجًا طويلًا سليمًا. يُقترح اتصال بنحو 5 ثوانٍ، ومهلة قراءة حسب P95 للأعمال، وميزانية كلية في طبقة التطبيق.

في التدفق افحص أيضًا تخزين الوسيط العكسي. التخزين يؤخر إنزال المقاطع فيبدو الطلب «معلقًا». وبعد انقطاع التدفق لا تُدخل المحتوى الناقص في قاعدة البيانات كأنه نتيجة كاملة.

سابعًا: معرّف الطلب والسجلات: دليل التشخيص

تحمل استجابة Volcano Ark عادة معرّف طلب، ومن الحقول الشائعة X-Request-Id، وقد تعيد بعض الواجهات معرّف سجل مثل X-Tt-Logid. الاسم يتبع الاستجابة الفعلية، ويُحفظ رأس الاستجابة كاملًا لا حقل واحد فقط.

يجب أن يولّد العميل أيضًا trace_id خاصًا به يبقى ثابتًا عبر كل محاولات الطلب التجاري نفسه. تسجل السجلات على الأقل الوقت وtrace_id وBase URL والنموذج أو نقطة الوصول ورمز الحالة وcode وmessage والمدة وعدد المحاولات وهل الطلب متدفق واستهلاك الرموز.

يُسجَّل من المفتاح أول أحرف فقط أو بصمة تجزئة، ولا تُخزَّن نصوص المستخدم والاستجابات الكاملة ككتل. يكفي حفظ أول 500 حرف من جسم الخطأ.

ثامنًا: إعادة المحاولة والتباعد: شيفرة قابلة للاستخدام

تُغلق الشيفرة التالية إعادة المحاولة التلقائية في SDK، وتقرر إعادة المحاولة حسب رمز الحالة ومحتوى الخطأ، وتحترم Retry-After، وتولّد trace_id لكل طلب منطقي:

import os
import random
import time
import uuid

import httpx
from openai import OpenAI, APIConnectionError, APIStatusError, APITimeoutError

client = OpenAI(
    api_key=os.environ["ARK_API_KEY"],
    base_url=os.environ.get(
        "ARK_BASE_URL", "https://ark.cn-beijing.volces.com/api/v3"
    ),
    timeout=httpx.Timeout(connect=5.0, read=180.0, write=30.0, pool=10.0),
    max_retries=0,  # إيقاف إعادة المحاولة التلقائية في SDK؛ تتولاها الشيفرة أدناه
)

def parse_error(exc: APIStatusError) -> tuple[str, str]:
    try:
        body = exc.response.json()
    except Exception:
        return "", exc.response.text[:200]
    err = body.get("error", body)
    return str(err.get("code") or err.get("type") or ""), str(err.get("message") or "")

def is_retryable(exc: Exception) -> bool:
    if isinstance(exc, (APITimeoutError, APIConnectionError)):
        return True
    if not isinstance(exc, APIStatusError):
        return False
    if exc.status_code >= 500:
        return True
    if exc.status_code != 429:
        return False
    code, message = parse_error(exc)
    text = f"{code} {message}".lower()
    # إعادة المحاولة في 429 الناتج عن الحصة أو التأخر أو الصلاحية بلا جدوى؛ أضف الكلمات حسب النص الفعلي
    return not any(k in text for k in ("quota", "arrears", "balance", "permission"))

def ask(prompt: str, max_attempts: int = 4) -> str:
    trace_id = uuid.uuid4().hex[:12]
    for attempt in range(1, max_attempts + 1):
        try:
            resp = client.chat.completions.create(
                model=os.environ["ARK_MODEL"],  # معرّف نقطة الوصول أو معرّف النموذج
                messages=[{"role": "user", "content": prompt}],
                max_tokens=1024,
            )
            return resp.choices[0].message.content
        except Exception as exc:
            status = getattr(exc, "status_code", "")
            code, message = parse_error(exc) if isinstance(exc, APIStatusError) else ("", "")
            print(
                f"trace={trace_id} attempt={attempt} status={status} "
                f"code={code} message={message[:200]}"
            )
            if attempt == max_attempts or not is_retryable(exc):
                raise
            retry_after = getattr(
                getattr(exc, "response", None), "headers", {}
            ).get("retry-after")
            try:
                wait = float(retry_after) if retry_after else min(2 ** (attempt - 1), 20)
            except ValueError:
                wait = min(2 ** (attempt - 1), 20)
            time.sleep(wait + random.uniform(0, 1))
    raise RuntimeError("نفدت محاولات إعادة المحاولة")

ثلاث قواعد إضافية: تأكد من حالة الطلبات ذات الأثر الجانبي بمفتاح عدم تكرار قبل إعادة الإرسال؛ اضبط ميزانية إعادة المحاولة على مستوى النظام؛ وراقب نسبة 429 وكمون P95 واخفض التزامن أولًا عند الارتفاع.

تاسعًا: قائمة التحقق قبل مخاطبة الدعم

  1. وقت العطل والمنطقة الزمنية، وهل هو مستمر أم متقطع.
  2. Base URL والمسار الكامل واسم النموذج أو معرّف نقطة الوصول.
  3. رمز HTTP وcode وmessage ومعرّف الطلب.
  4. طلب curl أدنى قابل لإعادة الإنتاج مع إخفاء المفتاح.
  5. هل الطلب متدفق، وهل السياق طويل، وهل يحمل صورًا أو استدعاء أدوات.
  6. هل عُدّل المفتاح أو نقطة الوصول أو الحصة أو الوسيط حديثًا.
  7. نطاق التأثير: مفتاح واحد أو مشروع واحد أو منطقة واحدة أم كل الخدمات.

بعد تجهيز هذه المعلومات يستطيع فريق الدعم مطابقتها مباشرة مع سجلات المنبع. وبعد كل عطل أضف سجل مراقبة أو تنبيهًا واحدًا.

عاشرًا: الأسئلة الشائعة

1. هل 404 يعني دائمًا إيقاف النموذج؟

لا. الأكثر شيوعًا أن معرّف نقطة الوصول أو اسم النموذج خاطئ، أو أن نقطة الوصول حُذفت، أو أن المنطقة لا تطابق، أو أن المسار ينقصه /api/v3. انظر أولًا هل الاستجابة JSON أم صفحة HTML، ثم تحقق بندًا بندًا.

2. كم مرة نعيد المحاولة عند 429؟

لحد السرعة والازدحام المؤقت أعد المحاولة مرتين إلى ثلاث مع تباعد أُسّي. ولا تعد المحاولة في 429 الناتج عن نفاد الحصة أو التأخر في السداد أو نقص الصلاحية؛ عالج الحساب أو الباقة أولًا. واحترم Retry-After.

3. كيف يُحسب طول السياق فعلًا؟

يُحسب عادة من رموز الإدخال زائد رموز الإخراج، ويستهلك max_tokens من الحصة الكلية. الصور وتعريفات الأدوات ورسائل التاريخ تدخل في الإدخال، فاترك هامشًا عند الاقتراب من الحد.

4. كيف تُضبط مهلة الطلب المتدفق؟

مهلة القراءة تقيد عادة الفاصل بين مقطعين لا المدة الكلية. اضبط مهلة الاتصال منفصلة، ثم ميزانية كلية ومهلة خمول في طبقة التطبيق. تخزين الوسيط يؤخر إنزال المقاطع.

5. ماذا نفعل إذا زادت الأخطاء بعد إدخال بوابة؟

تحقق أولًا هل الخطأ من البوابة أم من المنبع: افحص شكل جسم الاستجابة ورمز الحالة ومعرّف الطلب. احتفظ بـ trace ID وأرسل طلبًا أدنى عبر الاتصال المباشر وعبر البوابة للمقارنة. معظم المشاكل في دمج المسار أو تمرير ترويسة المصادقة أو المهلة.

الخطوة التالية

أنشئ مفتاح اختبار في ZSQH AI Gateway، ونفّذ تمرينًا على 401 و404 و429 والمهلة، ثم اربط مراقبة الإنتاج. النماذج المتاحة وطريقة الفوترة في أسعار النماذج.

قراءات ذات صلة