يوفر Doubao API واجهة متوافقة مع OpenAI، لكن «التوافق» لا يعني «النسخ الحرفي». أكثر ثلاث نقاط تسبب التعطل هي: كتابة Base URL بصيغة /v1، ووضع الاسم المعروض في model بدل معرّف الطراز أو معرّف نقطة الوصول، وتفعيل التفكير العميق في أول استدعاء فيُقطع الإخراج. تشغّل هذه المقالة curl وPython بالترتيب، ثم تشرح البث واستدعاء الأدوات والأخطاء الشائعة وقائمة الترحيل.
ملاحظة: Doubao وVolcengine Ark وVolcengine وByteDance أسماء أو علامات تجارية أو منتجات لشركة ByteDance والشركات التابعة لها؛ وOpenAI علامة تجارية مملوكة لمالكها. هذه المقالة للشرح التقني فقط، ولا توجد علاقة تبعية أو وكالة أو تزكية مع الشركات المذكورة. قد تتغير الواجهات والطرازات والحصص، فيُعتمد على الوثائق الرسمية وأسعار الطرازات داخل الموقع.
أولًا: تأكيد ثلاثة أمور قبل الربط
| العنصر | الاتصال المباشر بـVolcengine Ark | البوابة الموحدة |
|---|---|---|
| Base URL | https://ark.cn-beijing.volces.com/api/v3 |
https://ai.zhongshangqihang.com/v1 |
| المصادقة | Authorization: Bearer <ARK_API_KEY> |
Authorization: Bearer <مفتاح البوابة> |
| model | معرّف الطراز أو معرّف نقطة وصول ep- |
حسب قائمة الطرازات في الموقع |
| مناسب لـ | من يستخدم Doubao وحده ويشحن ويفوتر بنفسه | عدة طرازات وفاتورة وحصة موحدة |
يجب استخدام المفتاح وBase URL كزوج متطابق. المفتاح الرسمي مع عنوان البوابة، أو مفتاح البوابة مع العنوان الرسمي، يعيد عادةً 401 مباشرة. عند تغيير المدخل، غيّر الاثنين معًا.
ثانيًا: Base URL والمسار: /api/v3 وليس /v1
عنوان مستوى البيانات الرسمي هو https://ark.cn-beijing.volces.com/api/v3، ومسار إكمال المحادثة /chat/completions، أي العنوان الكامل https://ark.cn-beijing.volces.com/api/v3/chat/completions. لا يحمل البادئة /v1 الشائعة في OpenAI، وإضافة /v1 تعيد 404.
المنطقة مكتوبة في النطاق (cn-beijing). إذا كانت الموارد في منطقة أخرى، فاتبع العنوان في لوحة التحكم والوثائق. تذكر الوثائق الرسمية أيضًا أن حالات مثل Coding Plan تستخدم Base URL مختلفًا، وقد يؤدي الخطأ فيه إلى رسوم إضافية، لذا راجع قبل تغيير الخطة.
ثالثًا: ماذا يُكتب في model: معرّف الطراز أم نقطة الوصول؟
تقدم الوثائق الرسمية طريقتين:
- الاستدلال الجاهز: اكتب معرّف الطراز مباشرة، مثل
doubao-seed-2-1-pro-260915، دون إنشاء نقطة وصول مسبقًا. - نقطة وصول استدلال مخصصة: اكتب معرّف نقطة الوصول، ويبدأ عادةً بـ
ep-، ويناسب النشر الثابت والإدارة الدقيقة أو طرازات LoRA المضبوطة.
معرّف الطراز يحمل تاريخ الإصدار، فلا تكتب اسمًا معروضًا مثل «Doubao 2.1 Pro»، ولا تضف لاحقة من الذاكرة. عند استخدام توقيع Access Key (AK/SK)، يجب أن يكون model معرّف نقطة الوصول؛ وعند استخدام API Key يمكن كتابة معرّف الطراز أو نقطة الوصول وفق الوثائق. إذا عاد InvalidEndpointOrModel.ModelIDAccessDisabled، فالحساب الحالي لا يدعم الاستدعاء المباشر بمعرّف الطراز ويحتاج إنشاء نقطة وصول.
رابعًا: أصغر طلب عبر curl
أوقف التفكير العميق أولًا لتقليل المتغيرات. مثال المدخل الرسمي:
export ARK_API_KEY="مفتاح Volcengine Ark"
export ARK_MODEL_ID="doubao-seed-2-1-pro-260915"
curl "https://ark.cn-beijing.volces.com/api/v3/chat/completions" \
-H "Authorization: Bearer $ARK_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "'"$ARK_MODEL_ID"'",
"messages": [{"role": "user", "content": "أجب بكلمة OK فقط"}],
"thinking": {"type": "disabled"},
"max_tokens": 32
}'
إذا عاد 200 وكان choices[0].message.content يحتوي نصًا، فالمفتاح والعنوان والطراز صحيحة. إذا عاد 404، فافحص أولًا هل model يحمل لاحقة الإصدار وهل يلزم استخدام نقطة ep-؛ وإذا عاد 401، فافحص المفتاح والترويسة ولا تشك في الرصيد أولًا.
خامسًا: الاستدعاء بـPython عبر OpenAI SDK
import os
from openai import OpenAI
client = OpenAI(
api_key=os.environ["ARK_API_KEY"],
base_url="https://ark.cn-beijing.volces.com/api/v3",
)
resp = client.chat.completions.create(
model=os.environ.get("ARK_MODEL_ID", "doubao-seed-2-1-pro-260915"),
messages=[
{"role": "system", "content": "أنت مساعد تقني، وأجب بإيجاز."},
{"role": "user", "content": "اشرح ما هي نقطة وصول الاستدلال في ثلاث جمل."},
],
max_tokens=512,
extra_body={"thinking": {"type": "disabled"}},
)
print(resp.choices[0].message.content)
print(resp.usage)
لا يعرف OpenAI SDK حقل thinking، لذا يُمرر عبر extra_body؛ وإذا لم تدعمه البوابة أو الطراز، فاحذفه. بعد النجاح، افحص رموز الإدخال والإخراج في usage، ثم حدد المهلة والتزامن وميزانية التكلفة.
سادسًا: الإخراج المتدفق: delta وreasoning_content و[DONE]
بعد ضبط stream=True، يعيد الخادم عبر SSE كتلًا متتابعة، ويكون كائن الاستجابة chat.completion.chunk، والنص في choices[0].delta.content، ومحتوى التفكير في delta.reasoning_content، وتظهر finish_reason في الكتلة الأخيرة فقط، ثم يُرسل data: [DONE] عند الانتهاء.
stream = client.chat.completions.create(
model=os.environ["ARK_MODEL_ID"],
messages=[{"role": "user", "content": "اكتب مثال Python لقراءة ملف CSV"}],
stream=True,
stream_options={"include_usage": True},
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="")
قد تحمل كتلة التفكير content فارغًا، فلا تفترض أن كل كتلة تحتوي نصًا؛ وقد تقطع max_tokens الصغيرة الإخراج؛ وعند انقطاع الشبكة يبقى المحتوى المستلم، ويجب أن يستطيع العميل حفظ النتيجة الجزئية وتقرير إعادة المحاولة.
سابعًا: استدعاء الأدوات: tools وtool_calls وترتيب الإرجاع
صيغة tools قريبة من OpenAI: كل أداة تضم type: "function" وfunction، حيث name وdescription وparameters إلزامية، ويستخدم parameters مخطط JSON Schema. عندما يقرر الطراز الاستدعاء تكون finish_reason بقيمة tool_calls، ويكون message.tool_calls[i].function.arguments سلسلة JSON تحتاج تحليلًا.
ترتيب الإرجاع لا يجوز أن يخطئ: أضف أولًا رسالة assistant التي تحمل tool_calls إلى المحادثة، ثم أضف لكل استدعاء رسالة role: "tool" تحمل tool_call_id والنتيجة، ثم اطلب من الطراز مرة أخرى حتى تتوقف finish_reason عن كونها tool_calls. تكون parallel_tool_calls افتراضيًا true، وضبطها على false يقيّد الاستدعاء بأداة واحدة في المرة. في البث تحمل فروق استدعاء الأدوات index، ويجب تجميع arguments وفق index.
ثامنًا: الأخطاء الشائعة: كيف تحدد 401 و404 و429
| HTTP | رمز الخطأ النموذجي | المعنى | المعالجة |
|---|---|---|---|
| 401 | AuthenticationError |
مفتاح مفقود أو غير صالح أو ترويسة بلا Bearer |
أعد نسخ المفتاح وافحص ترويسة Authorization |
| 404 | InvalidEndpointOrModel.NotFound |
الطراز أو نقطة الوصول غير موجودة أو لا صلاحية للمساحة | انسخ المعرّف الكامل من لوحة التحكم وتحقق من المنطقة والمساحة |
| 404 | ModelNotOpen |
الطراز غير مفعّل في الحساب | فعّل الطراز في لوحة Ark |
| 404 | InvalidEndpointOrModel.ModelIDAccessDisabled |
الحساب لا يدعم الاستدعاء المباشر بمعرّف الطراز | أنشئ نقطة وصول مخصصة واستخدم معرّف ep- |
| 400 | InvalidParameter أو MissingParameter |
معامل مفقود أو نوع خاطئ أو الطراز لا يدعمه | راجع الوثائق وعدّل الحقول، وابدأ بأصغر طلب |
| 403 | AccessDenied أو AccountOverdueError |
لا صلاحية أو رصيد سالب | افحص المفتاح وصلاحيات المساحة وعالج التأخر |
| 429 | RateLimitExceeded.* |
تجاوز RPM/TPM أو ذروة حركة | خفّض التزامن، استخدم تراجعًا أسيًا، اطلب رفع الحصة عند الحاجة |
| 500/503 | InternalServiceError أو ServerOverloaded |
خطأ أو حمل زائد في المنبع | انتظر قليلًا ثم أعد المحاولة، وجهّز قناة بديلة |
تاسعًا: قائمة التحقق للترحيل
عند الانتقال من الاتصال الرسمي إلى البوابة، أو من بوابة إلى أخرى، افحص بالترتيب:
- ثبّت مجموعة مطالبات حقيقية، وسجّل الإخراج والزمن و
usageالحالية كخط أساس. - غيّر
base_urlوapi_keyمعًا، ولا تغيّر أحدهما وحده. - أعد تأكيد تحويل أسماء الطرازات وفق أسعار الطرازات، ولا تستخدم أسماء قديمة.
- تحقق من البث: زمن الرمز الأول و
delta.contentوfinish_reasonو[DONE]. - تحقق من استدعاء الأدوات:
toolsوtool_callsوإرجاعtool_call_idوترتيب الأدوار. - تحقق من دعم معاملات الامتداد مثل
thinkingوreasoning_effort، وهل تختلف قيمها الافتراضية. - اضبط مهلة الاتصال والقراءة، وأعد المحاولة فقط مع 429 و5xx والمهل، واضمن تكرارية عمليات الكتابة.
- حوّل 5%–10% من الحركة وراقب 3–7 أيام، مع إبقاء الاتصال الرسمي كقناة تراجع.
إذا كنت تحتاج مدخلًا موحدًا، فأنشئ مفتاح اختبار في صفحة التسجيل؛ وتغطية الطرازات ومعايير الفوترة في أسعار الطرازات. بوابة ZSQH AI خدمة مستقلة من طرف ثالث، ولا توجد بينها وبين ByteDance علاقة تبعية.
الأسئلة الشائعة
هل أضيف /v1 إلى Base URL؟
لا. مدخل Volcengine Ark المتوافق مع OpenAI هو https://ark.cn-beijing.volces.com/api/v3، ومسار المحادثة /chat/completions، وإضافة /v1 تعيد 404. وعنوان البوابة ينتهي عادةً بـ/v1، فلا تخلط بينهما.
هل أكتب معرّف الطراز أم معرّف نقطة الوصول الذي يبدأ بـep-؟
الاستدلال الجاهز يكتب معرّف الطراز؛ ونقطة الوصول المخصصة تكتب معرّف ep-؛ وعند توقيع Access Key يجب كتابة معرّف نقطة الوصول. إذا عاد ModelIDAccessDisabled، فالحل إنشاء نقطة وصول لا تغيير المفتاح.
هل أفعّل التفكير العميق عند أول استدعاء؟
يُستحسن إغلاقه أولًا، وتأكيد المسار بأقصر طلب. بعض طرازات Doubao تفعّله افتراضيًا، فيولّد التفكير محتوى قبل الإجابة، وقد تقطع max_tokens الصغيرة النص. فعّله بعد تأكيد المسار حسب المهمة.
لماذا لا يظهر محتوى في الإخراج المتدفق؟
تأكد من stream=True، ثم افحص هل تقرأ delta.content فقط وتتجاهل الكتل الفارغة؛ فمحتوى التفكير في delta.reasoning_content. افحص أيضًا max_tokens وتخزين الشبكة وهل يجمع الوكيل أحداث SSE في استجابة واحدة.
لماذا يجب إرجاع tool_calls من رسالة assistant؟
يتطلب البروتوكول أن تحمل رسالة assistant tool_calls، ثم كل رسالة role: "tool" تربط النتيجة بـtool_call_id. تجاهل هذه الخطوة قد يجعل الطراز يكرر الأداة أو يعيد 400. وفي البث يجب تجميع المعاملات وفق index.
قراءات ذات صلة:
