SiCore TokenWorks
LLM APIAPI Gateway

كيفية التبديل بين مزوّدي نماذج اللغة الكبيرة دون تغيير الكود الخاص بك

SiCore TokenWorks Team·2026-09-03

معظم الناس لا يبدأون مشروعًا وهم يفكرون في قابلية نقل المزوّد. يأخذون مفتاحًا من أي بائع يسهل التسجيل لديه، ويكتبون التكامل، ويُطلقون المنتج. بعد بضعة أشهر يقرؤون تقييمًا مرجعيًا، أو يفتحون فاتورة، أو يصطدمون بحد معدل الطلبات، فيرغبون في تجربة شيء آخر. عندها يظهر الألم، لأنهم رسّخوا افتراضات حول المزوّد الأول في طبقة HTTP.

الحل ليس بعض مكتبات التجريد. إنه صيغة نقل البيانات. أصبحت واجهة إكمال المحادثة من OpenAI بهدوء البروتوكول الافتراضي للتحدث مع نماذج اللغة الكبيرة، وبمجرد أن تكتب وفقًا لها، يصبح التبديل بين المزوّدين في الغالب مسألة تغيير نصين.

الواجهة التي قلّدها الجميع

عرّفت OpenAI عقدًا بسيطًا: تُرسل JSON عبر POST إلى /v1/chat/completions، وتحصل على سلسلة choices[0].message.content في المقابل. يبدو الطلب هكذا:

curl https://api.openai.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{
    "model": "gpt-4o",
    "messages": [{"role": "user", "content": "Explain DNS in one paragraph"}]
  }'

كل نموذج رئيسي تقريبًا يتحدث هذه اللهجة الآن. DeepSeek وQwen وERNIE وDoubao وغيرها جميعًا تكشف عن نقطة نهاية تقبل نفس النص وتعيد نفس الشكل. هذا يعني أن كود العميل لديك لا يهمه من يوجد على الطرف الآخر. يمكنك تبديل سلسلة model ونقطة النهاية ولا شيء آخر يتغير.

هنا نفس الاستدعاء موجّه إلى مُجمِّع يحمل عدة من هذه النماذج تحت عنوان أساسي واحد:

curl https://api.token8341.com/v1/chat/completions \
  -H "Content-Type: application/json" \
  -H "Authorization: Bearer $TOKENWORKS_API_KEY" \
  -d '{
    "model": "deepseek-chat",
    "messages": [{"role": "user", "content": "Explain DNS in one paragraph"}]
  }'

عميل واحد، نماذج كثيرة

في Python عادةً تُنشئ نسخة من OpenAI SDK مرة واحدة وتمرر اسم النموذج مع كل طلب. إذا كان مزوّدك يدعم الواجهة المتوافقة، فتضبط base_url مرة واحدة وتبقي كل شيء آخر كما هو:

from openai import OpenAI

client = OpenAI(
    base_url="https://api.token8341.com/v1",
    api_key="sk-your-tokenworks-key",
)

def ask(model: str, prompt: str) -> str:
    resp = client.chat.completions.create(
        model=model,
        messages=[{"role": "user", "content": prompt}],
    )
    return resp.choices[0].message.content

# Same function, three different model families.
print(ask("gpt-4o", "Summarize this log for me."))
print(ask("deepseek-chat", "Summarize this log for me."))
print(ask("qwen-max", "Summarize this log for me."))

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

stream = client.chat.completions.create(
    model="deepseek-chat",
    messages=[{"role": "user", "content": "Write a haiku about databases"}],
    stream=True,
)
for chunk in stream:
    delta = chunk.choices[0].delta.content or ""
    print(delta, end="")

استدعاء الدوال، ووضع JSON، والتضمينات كلها تسير على نفس السطح المتوافق، لذا لن تقتصر على المحادثة العادية عند التبديل.

ما لا يمنحك إياه التبديل

أريد أن أكون واضحًا بشأن الجزء الذي لا يتغير مجانًا. النقل قابل للنقل؛ أما النماذج فليست كذلك.

النماذج المختلفة تستجيب للمطالبات بشكل مختلف. قد يكون موجّه النظام المضبوط لـ GPT-4o أقل أداءً على نموذج استدلالي يريد بنية مختلفة. تختلف نوافذ السياق، وأحيانًا كثيرًا: قد يستوعب نموذج ما 128k رمزًا بينما يستوعب آخر 32k. تختلف المُرمِّزات أيضًا، لذا يكلف المستند نفسه عددًا مختلفًا من الرموز على كل نموذج. الحد الأقصى لطول الإخراج هو مِقبض آخر خاص بكل نموذج، وليس بكل بروتوكول.

لذا فإن "التبديل دون تغيير الكود" هو في الحقيقة "التبديل دون إعادة كتابة عميل HTTP الخاص بك". لا يزال يتعين عليك إجراء تقييم قبل توجيه حركة الإنتاج إلى نموذج جديد. الخبر السار أن الواجهة المتوافقة تجعل هذا التقييم رخيصًا في التنفيذ، لأن أداة الاختبار مجرد حلقة على أسماء النماذج.

نمط عملي

ما أفعله فعليًا في المشاريع الجانبية هو الاحتفاظ بجدول إعدادات صغير، لا بتغييرات في الكود:

MODELS = {
    "fast": "deepseek-chat",
    "smart": "qwen-max",
    "frontier": "gpt-4o",
}

def run(task, prompt):
    return ask(MODELS[task], prompt)

هل تريد تجربة نموذج أرخص لمسار fast؟ غيّر سطرًا واحدًا. هل تريد الرجوع إلى مزوّد ثانٍ عندما يصل الأول إلى حد معدل الطلبات؟ التقط الاستثناء وأعد المحاولة بسلسلة النموذج التالية. لا شيء من هذا يمس بناء الطلب.

نمط الرجوع الاحتياطي هذا هو حيث تستحق نقطة نهاية متعددة النماذج قيمتها. مع عنوان أساسي واحد ومفتاح واحد يمكنك التوجيه وإعادة المحاولة والاختبار A/B عبر المزوّدين، كل ذلك عبر سطري إعداد العميل نفسهما اللذين كتبتهما في اليوم الأول.

غرائب الاستجابات التي يجب الانتباه إليها

النقل متوافق، لكن بعض الاختلافات على مستوى الاستجابة لا تزال تتسرب. نماذج الاستدلال تُعيد أحيانًا حقلًا إضافيًا مثل reasoning_content إلى جانب الرسالة العادية. الكود الذي يقرأ choices[0].message.content يستمر في العمل بغض النظر، لكنك قد ترغب في إظهار نص الاستدلال في عرض تصحيح الأخطاء. رسائل الخطأ تختلف أيضًا: مزوّد يُعيد سلسلة insufficient_quota المفيدة، وآخر يُعيد 429 مجردًا بدون نص. إذا كنت تبني عمليات إعادة المحاولة، فتعامل مع أي 429 أو 5xx على أنه "تراجع وأعد المحاولة" بدلًا من تحليل نص خاص بالبائع.

لا شيء من هذا سبب للبقاء مقيدًا بمزوّد واحد. إنه سبب لإبقاء معالجة الأخطاء عامة واختيار النموذج متغيرًا.

هذا هو الموقف الذي تتخذه SiCore TokenWorks: نقطة نهاية متوافقة مع OpenAI على https://api.token8341.com/v1 حيث تكون سلسلة النموذج هي الشيء الوحيد الذي يتغير عند التنقل بين GPT-4o وClaude وGemini وDeepSeek وQwen وERNIE وDoubao وSpark وPangu.