SSD Nodes Learn Hosting plans →
الأدلة Matt Connorبقلم Matt Connor · آخر تحديث في 2026-10-30

كيف تستضيف LiteLLM كبوابة موحّدة للنماذج

شغّل LiteLLM على VPS كنقطة نهاية متوافقة مع OpenAI، وأنشئ مفاتيح افتراضية بميزانيات لكل مفتاح، مع بدائل تلقائية وصور Docker مثبتة الإصدار.

ما الذي يفعله بوّاب LLM مستضاف ذاتياً

LiteLLM هو بوّاب LLM مفتوح المصدر تستضيفه بنفسك: نقطة نهاية HTTP واحدة تستدعيها جميع تطبيقاتك، ثم يمرّر كل طلب إلى المزوّد المناسب لمعالجته. يشير LLM إلى نموذج لغوي كبير. يتحدث البوّاب باستخدام واجهة برمجة التطبيقات لإكمالات المحادثة في OpenAI، لذلك تعمل معه أي مكتبة عميلة تتصل بـOpenAI مسبقاً بعد تغييرين فقط: عنوان URL الأساسي والمفتاح.

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

بعد تشغيله، ستحصل على ما يلي:

  • نقطة نهاية واحدة. تستهدف التطبيقات https://gateway.example.com/v1 وتطلب اسم نموذج اخترعته، مثل bulk أو strong.
  • مفاتيح افتراضية. يحصل كل تطبيق على مفتاحه الخاص، وقائمة النماذج المسموح بها، وحد الإنفاق الخاص به. ويمكنك إبطال مفتاح واحد دون المساس بالمفاتيح الأخرى.
  • بدائل تلقائية. يُعاد إرسال الطلب الفاشل أو الطلب ذي المحث الكبير إلى نموذج مختلف تلقائياً.
  • سجل مسجّل. يكتب كل طلب سجلاً يتضمن تكلفته، لذلك يمكن معرفة إجابة سؤال «أي تطبيق أنفق ذلك؟».

لماذا تشغّل البوابة بنفسك

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

ما تحتاج إليه

  • خادم VPS ‏(خادم افتراضي خاص) يعمل بنظام Ubuntu 24.04، مع تثبيت Docker وإضافة Compose.
  • اسم نطاق يشير إلى الخادم، إذا كانت الأجهزة خارج الخادم ستصل إلى البوابة عبر TLS ‏(أمان طبقة النقل).
  • مفتاح API واحد على الأقل لأحد المزوّدين.

لا تجري البوابة أي استدلال. فهي تمرّر الطلبات وتعيد بث الإجابات، لذلك يرتبط حمل CPU بحجم الطلبات لا بحجم النموذج. يستطيع خادم مزوّد بـ1 vCPU تشغيل عدد محدود من التطبيقات الداخلية دون مشاكل. أما ما ينمو فهو قاعدة البيانات، لأن البوابة تكتب صفاً للمصروفات لكل طلب.

اكتب config.yaml أولاً

يحدد ملف الإعدادات النماذج التي يمكن للعميل طلبها. تهم أربعة أقسام عليا: model_list وlitellm_settings وrouter_settings وgeneral_settings.

model_list:
  - model_name: bulk
    litellm_params:
      model: anthropic/claude-haiku-4-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: anthropic/claude-sonnet-5
      api_key: os.environ/ANTHROPIC_API_KEY
  - model_name: strong
    litellm_params:
      model: openai/gpt-5.5
      api_key: os.environ/OPENAI_API_KEY

litellm_settings:
  num_retries: 2
  request_timeout: 120
  json_logs: true
  set_verbose: false

router_settings:
  allowed_fails: 3
  cooldown_time: 30
  fallbacks: [{"bulk": ["strong"]}]
  context_window_fallbacks: [{"bulk": ["strong"]}]

general_settings:
  background_health_checks: true
  health_check_interval: 300

model_name هو الاسم الذي يرسله العملاء. أما litellm_params.model فهو النموذج الفعلي، ويُكتب بصيغة provider/model. سمِّ نماذجك وفق المهمة بدلاً من تسميتها وفق المورّد. سيستمر التطبيق الذي يطلب bulk في العمل عندما تقرر في الشهر المقبل أن bulk يجب أن يكون نموذجاً مختلفاً.

يخبر api_key: os.environ/ANTHROPIC_API_KEY LiteLLM بقراءة ذلك المتغير في وقت التشغيل. لا يظهر المفتاح الحرفي في الملف، وهذا مهم لأن config.yaml هو الملف الذي تودعه في مستودعك.

يتشارك إدخالان الاسم strong، وهذا مقصود. عندما تحمل أكثر من عملية نشر القيمة نفسها model_name، يتعامل معها الموجّه كعمليات قابلة للتبادل ويوزّع الطلبات الخاصة بهذا الاسم بينها. بهذه الطريقة يصمد strong أمام تعطل أحد المورّدين خلال فترة قصيرة: بمجرد أن تدخل عملية نشر في حالة تبريد (كما هو موضح أدناه)، تُرسل الطلبات الجديدة الخاصة بـstrong إلى العملية الأخرى.

يعيد num_retries: 2 المحاولة عند حدوث خطأ قابل لإعادة المحاولة، بحد أقصى مرتين. ويمكن لهذه المحاولات اختيار عملية نشر أخرى متاحة ضمن مجموعة النموذج نفسها. توضح وثائق بنية LiteLLM، التي جرى التحقق منها في 2026-10-29، أن إعادة المحاولة داخل المجموعة والبدائل بين المجموعات خطوتان منفصلتان. لذلك قد تصل إعادة محاولة strong إلى المورّد الآخر من دون تفعيل التحويل عند الفشل الموزون. ولا يُفعَّل البديل إلا بعد استنفاد محاولات إعادة المحاولة.

allowed_fails وcooldown_time خياران للموجّه، لذلك يوضعان ضمن router_settings. يبني الوكيل الوسيط موجّهه من هذا القسم. في v1.95.0، لا يصل cooldown_time المكتوب ضمن litellm_settings إلى الموجّه، ولذلك تبقى فترات التبريد سراً عند القيمة الافتراضية البالغة 5 ثوانٍ. يحدد cooldown_time: 30 مدة بقاء عملية النشر خارج التناوب بعد دخولها حالة التبريد. يصف مرجع الإعداد allowed_fails بأنه عدد مرات الفشل التي يجب أن تتجاوزها عملية النشر خلال cooldown_time قبل إدخالها في حالة التبريد. في مصدر v1.95.0، تكون allowed_fails: 3 هي القيمة الافتراضية. وعند استخدام هذه القيمة الافتراضية، يلتزم الموجّه بالمحفزات المضمّنة؛ ولا يتولى عدد مرات الفشل تحديد السلوك إلا عند تعيين رقم مختلف. تشمل هذه المحفزات إدخال عملية النشر في التبريد فوراً عند استلام استجابة 429 لمجموعة تضم أكثر من عملية نشر، وتجاوز نسبة الفشل 50% خلال الدقيقة الحالية بعد 5 طلبات على الأقل لهذه المجموعة، ووقوع أخطاء لا يعيد الموجّه المحاولة عندها، مثل استجابة 401 بسبب مفتاح غير صحيح. ويوجد محفز منفصل يُدخل عملية نشر واحدة في التبريد عندما تفشل جميع الطلبات خلال الدقيقة الحالية ويبلغ عدد الطلبات SINGLE_DEPLOYMENT_TRAFFIC_FAILURE_THRESHOLD، وقيمته الافتراضية 1000 طلب. إذا أعاد أحد المورّدين استجابات 500 على دفعة واحدة، يتوقف الموجّه عن تجربته في كل طلب منفرد، بينما لا تؤدي استجابة 500 عابرة كل بضع دقائق إلى تفعيل التبريد.

لدى fallbacks وcontext_window_fallbacks محفزات مختلفة، والثاني هو المفيد الذي يتجاهله بعض المستخدمين.

  • يُفعّل fallbacks عند فشل الطلب الأساسي.
  • يُفعّل context_window_fallbacks عندما يرفض المورّد الطلب لأنه يتجاوز نافذة السياق الخاصة بذلك النموذج، ولذلك يُرسل الطلب الكبير إلى نموذج يتسع له بدلاً من إعادة خطأ إلى العميل.

يوجد أيضاً content_policy_fallbacks، ويُستخدم عندما يرفض المورّد الطلب بسبب سياسة المحتوى. فعّله فقط إذا كان لديك وجهة مناسبة لإرسال هذه الطلبات إليها.

نشر LiteLLM على VPS باستخدام Docker Compose

أنشئ دليلاً يحتوي على ثلاثة ملفات: config.yaml وdocker-compose.yml و.env. تسحب خطوات البدء السريع من upstream الوسم latest. ثبّت وسم إصدار بدلاً منه، حتى يمنحك docker compose up -d في الشهر المقبل البوابة نفسها التي يمنحك إياها اليوم، ويصبح التراجع إلى إصدار سابق أمراً من سطر واحد.

services:
  litellm:
    image: ghcr.io/berriai/litellm:v1.95.0
    restart: unless-stopped
    command: ["--config", "/app/config.yaml", "--num_workers", "1"]
    ports:
      - "127.0.0.1:4000:4000"
    volumes:
      - ./config.yaml:/app/config.yaml:ro
    env_file: .env
    depends_on:
      db:
        condition: service_healthy

  db:
    image: postgres:16
    restart: unless-stopped
    environment:
      POSTGRES_USER: litellm
      POSTGRES_PASSWORD: ${POSTGRES_PASSWORD:?set POSTGRES_PASSWORD in .env}
      POSTGRES_DB: litellm
    healthcheck:
      test: ["CMD-SHELL", "pg_isready -U litellm"]
      interval: 5s
      timeout: 5s
      retries: 10
    volumes:
      - postgres_data:/var/lib/postgresql/data

volumes:
  postgres_data:

يقرأ Compose الملف .env مرتين هنا. يستخدمه أولاً لاستبدال ${POSTGRES_PASSWORD} داخل ملف Compose نفسه، ثم يستخدمه عبر env_file لتمرير كل متغير إلى الحاوية.

كان v1.95.0 هو الإصدار الحالي في August 2026. تحقّق من صفحة إصدارات المشروع وثبّت الإصدار الحالي عند النشر. ينشر كل إصدار توقيعاً، لذلك يمكنك التحقق من الصورة قبل الوثوق بها:

cosign verify --key https://raw.githubusercontent.com/BerriAI/litellm/v1.95.0/cosign.pub ghcr.io/berriai/litellm:v1.95.0

السطر الخاص بالمنفذ هو 127.0.0.1:4000:4000، وهذا ينشر المنفذ على واجهة loopback فقط. إذا كتبت 4000:4000 بدلاً منه، فستصبح بوابتك متاحة من الإنترنت بالكامل، لأن Docker يضيف قواعده الخاصة إلى سلسلة FORWARD في iptables، وتُقيَّم هذه القواعد قبل قواعد ufw، لذلك لا يمنعها ufw deny 4000. هذه هي الطريقة الأكثر شيوعاً لانكشاف بوابة مستضافة ذاتياً على الإنترنت: راجع كيفية نشر Docker لمنفذ حاوية مباشرةً متجاوزاً ufw. تصل حركة الشبكة من الخارج عبر reverse proxy بدلاً من ذلك.

أبقِ مفاتيح المزوّد خارج الصورة

يحتوي الملف .env على جميع الأسرار. يُمرَّر الملف كمتغيرات بيئية وقت التشغيل، لذلك لا تُضمَّن الأسرار في الصورة مطلقاً، ولا تُلتزم في المستودع.

LITELLM_MASTER_KEY=sk-REPLACE_ME
LITELLM_SALT_KEY=sk-REPLACE_ME_TOO
POSTGRES_PASSWORD=REPLACE_ME_AS_WELL
DATABASE_URL=postgresql://litellm:REPLACE_ME_AS_WELL@db:5432/litellm
STORE_MODEL_IN_DB=True
LITELLM_MODE=PRODUCTION
LITELLM_LOG=ERROR
ANTHROPIC_API_KEY=sk-ant-...
OPENAI_API_KEY=sk-proj-...

أنشئ مفتاحي LiteLLM باستخدام عشوائية حقيقية، ثم قيّد صلاحيات الملف:

printf 'sk-%s\n' "$(openssl rand -hex 32)"
chmod 600 .env

يمثل LITELLM_MASTER_KEY بيانات اعتماد المسؤول. يُستخدم لمصادقة Management API، وهو كلمة المرور الخاصة بواجهة Admin UI على /ui. يجب ألا تحتفظ به أيٌّ من التطبيقات مطلقاً.

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

يتيح لك STORE_MODEL_IN_DB=True إضافة النماذج وتعديلها من Admin UI دون تعديل config.yaml. هذا مريح، لكنه يقسم مصدر الحقيقة إلى جزأين. حدّد المصدر المعتمد، وسجّل هذا القرار بجانب ملف الإعداد.

المنطق الذي يبرر إبقاء المفاتيح خارج ملف الإعداد هو نفسه الذي يبرر إبقاءها خارج الأدوات التي تمنحها لوكيل. يشرح إبقاء أسرار المزوّد خارج وكلاء الذكاء الاصطناعي هذا النمط، بينما يشرح ملفات البيئة والأسرار في Docker Compose الجوانب العملية.

شغّل الخدمة وراقب الإقلاع الأول:

docker compose up -d
docker compose logs -f litellm

تحقّق من عمله فعلياً

هناك فحصان دون مصادقة وفحص واحد يتطلب المصادقة، وتفشل هذه الفحوص لأسباب مختلفة.

curl -s http://127.0.0.1:4000/health/liveliness
curl -s http://127.0.0.1:4000/health/readiness

لا يحتاج /health/liveliness إلى مصادقة، ويستجيب بـ"I'm alive!" ما دام process قيد التشغيل. ولا يحتاج /health/readiness إلى مصادقة أيضاً. ويعيد كائن JSON يحتوي على "status": "healthy" وحقل db، أو يعيد 503 عندما يتعذر الوصول إلى قاعدة البيانات. وجّه نظام المراقبة إلى readiness، لأن liveliness يبقى بحالة خضراء على gateway لا يستطيع البحث عن virtual key واحدة.

الفحص الذي يتحدث إلى providers هو الفحص الذي يتطلب المصادقة:

curl -s http://127.0.0.1:4000/health \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

يستجيب بمصفوفتَي healthy_endpoints وunhealthy_endpoints. إذا ظهر model في unhealthy_endpoints مع خطأ مصادقة، فهذا يعني أن provider key في .env غير صحيحة أو مفقودة. وهذا هو العطل الذي تريد اكتشافه الآن. وبما أن background_health_checks: true مضبوط، يشغّل proxy هذه الفحوص تلقائياً كل health_check_interval ثانية، ويعيد /health آخر نتيجة، لذلك لا يؤدي استقصاؤه إلى إرسال طلب اختبار إلى providers في كل مرة.

المفاتيح الافتراضية والميزانيات لكل مفتاح

يحصل كل تطبيق على مفتاح خاص به، ويُنشأ هذا المفتاح باستخدام المفتاح الرئيسي.

curl -s http://127.0.0.1:4000/key/generate \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{
    "key_alias": "nightly-summariser",
    "models": ["bulk"],
    "max_budget": 5,
    "budget_duration": "30d",
    "rpm_limit": 60,
    "tpm_limit": 200000
  }'

تتضمن الاستجابة حقلاً باسم key يبدأ بـ sk-. هذه السلسلة هي ما يحصل عليه التطبيق، وهي الشيء الوحيد الذي يحصل عليه التطبيق على الإطلاق.

  • models هي قائمة سماح تحدد ما يمكن لهذا المفتاح طلبه. يمكن للمفتاح أعلاه طلب bulk ولا شيء غيره.
  • تبلغ قيمة max_budget: 5 مع budget_duration: "30d" خمسة دولارات أمريكية لكل شهر تقويمي. يعيد LiteLLM ضبط ميزانية 30d في اليوم 1 من كل شهر عند منتصف الليل، وفق UTC ما لم تضبط timezone ضمن litellm_settings، وليس بعد مرور 30 يوماً متحركاً. لذلك، يتوقف المفتاح عن العمل بعد إنفاق خمسة دولارات حتى اليوم 1 التالي.
  • يحدّد rpm_limit وtpm_limit عدد الطلبات في الدقيقة وعدد الرموز في الدقيقة لهذا المفتاح وحده.
  • key_alias هو الاسم الذي ستتعرف عليه في سجل الإنفاق بعد ستة أسابيع. اضبطه دائماً.

عند نفاد الميزانية، يفشل الاستدعاء بخطأ من النوع budget_exceeded وبـرسالة بهذا الشكل:

Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget>

يعتمد رمز الحالة على الإصدار. في الإصدار المثبت v1.95.0، يكون الرمز HTTP 429. ابتداءً من الإصدار v1.104.0، يكون الرمز الافتراضي HTTP 422، ويؤدي ضبط budget_exceeded_status_code: 429 ضمن litellm_settings إلى استعادة الرمز السابق. أما حالات تجاوز rpm_limit وtpm_limit الفعلية، فتعيد 429 في كلا الإصدارين. تحقق من الرمز الذي تحصل عليه بعد كل ترقية.

رمز الحالة هو سبب هذا الالتباس، لأن أياً من الرمزين لا يوضح أن السبب هو الميزانية. تعرض مكتبة العميل الرمز 429 على أنه حد لمعدل الطلبات، وقد تعيد المحاولة مع التأخير التدريجي مقابل ميزانية لن تعود حتى موعد إعادة الضبط. كما تعرض الرمز 422 على أنه طلب تعذّر معالجته. سجّل نص الاستجابة بجانب رمز الحالة، وإلا فسيبدو نفاد الميزانية كأنه سبب آخر في كل مرة.

افحص المفاتيح واضبطها عبر واجهة الإدارة البرمجية نفسها:

curl -s "http://127.0.0.1:4000/key/info?key=sk-..." \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY"

curl -s -X POST http://127.0.0.1:4000/key/update \
  -H "Authorization: Bearer $LITELLM_MASTER_KEY" \
  -H 'Content-Type: application/json' \
  -d '{"key": "sk-...", "max_budget": 25}'

تظل الميزانية المفروضة عند البوابة سارية حتى عندما يكون سبب المشكلة هو الوكيل نفسه، ولذلك تشكل أساس التحكم في تكاليف وكلاء الذكاء الاصطناعي على VPS.

إرسال الأعمال المجمّعة إلى نموذج منخفض التكلفة

وجّه العميل إلى البوابة. عنوان Base URL والمفتاح واسم النموذج:

curl -s http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{
    "model": "bulk",
    "messages": [{"role": "user", "content": "Say hello in five words."}]
  }'

تعمل أي مكتبة عميل لـOpenAI بالطريقة نفسها: اضبط base_url على https://gateway.example.com/v1، واضبط api_key على المفتاح الافتراضي.

تُطبَّق الآن سياسة التوجيه من config.yaml دون أن يعرف المستدعي ذلك. يذهب الطلب إلى bulk إلى النموذج منخفض التكلفة. إذا فشل هذا الاستدعاء بعد إعادة المحاولة المحددة له، يُعاد إرسال الطلب إلى strong. وإذا كان الـprompt طويلاً جداً بالنسبة إلى bulk، يرسله context_window_fallbacks إلى strong بدلاً من إرجاع خطأ. تُنفَّذ الأعمال المجمّعة، مثل تمريرة تصنيف أو تراكم ملخصات، باستخدام النموذج منخفض التكلفة افتراضياً، ولا تتكلف الطلبات الصعبة مبلغاً أكبر إلا عند الحاجة.

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

كيف تعرف أن التحويل الاحتياطي حدث؟

هذه هي حالة الفشل التي تكلّف المال، لأن كل شيء يبدو سليماً. يعيد التحويل الاحتياطي الناجح استجابة HTTP 200 مع نص استجابة عادي. قد يتوقف النموذج منخفض التكلفة ليوم كامل، وتُخدَم كل الطلبات بهدوء من النموذج الأعلى تكلفة، ولا يظهر الدليل الأول إلا في الفاتورة.

الدليل موجود فعلاً في رؤوس الاستجابة. اطلبها:

curl -s -D - -o /dev/null http://127.0.0.1:4000/v1/chat/completions \
  -H "Authorization: Bearer sk-<the virtual key>" \
  -H 'Content-Type: application/json' \
  -d '{"model":"bulk","messages":[{"role":"user","content":"ping"}]}' \
  | grep -i '^x-litellm'
  • x-litellm-model-group هو اسم النموذج الذي وُجّه الطلب إليه، مثل bulk. أما x-litellm-model-id فهو معرّف النشر الذي أجاب، ويمكنك البحث عنه باستخدام /v1/model/info?litellm_model_id=.... عندما لا يكون هذا المعرّف واحداً من عمليات النشر bulk لديك، فهذا يعني أن تحويلاً احتياطياً حدث.
  • x-litellm-attempted-fallbacks وx-litellm-attempted-retries يعرضان عدد هذه الحالات. في الطلب السليم، لا يعرض أي منهما عدداً أكبر من 0.
  • x-litellm-response-cost هي تكلفة هذا الطلب الواحد بالدولار الأمريكي.
  • x-litellm-call-id هو المعرّف الذي تستخدمه للعثور على الطلب نفسه في سجلاتك.

سجّل x-litellm-attempted-fallbacks مع كل طلب، وأطلق تنبيهاً متى تجاوزت قيمته 0. هذا الرقم الواحد هو الفارق بين سياسة توجيه تعمل فعلاً وسياسة توجيه تحولت بصمت إلى «استخدم النموذج الأعلى تكلفة دائماً».

النسخة الكاملة من ذلك هي التتبّع، وهي تستحق إعداداً مستقلاً: Langfuse مستضاف ذاتياً لتتبّع طلبات الوكلاء. يوفّر LiteLLM ردّ الاتصال، لذا يتطلب ربطه سطرين بالإضافة إلى بيانات الاعتماد.

litellm_settings:
  success_callback: ["langfuse"]
  failure_callback: ["langfuse"]
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_HOST=https://langfuse.example.com

بالنسبة إلى Langfuse v3 والإصدارات الأحدث، توصي صفحة Langfuse في LiteLLM الآن بالإعداد المسبق langfuse_otel؛ وما زال ردّ الاتصال langfuse أعلاه موثّقاً. اضبط failure_callback وsuccess_callback أيضاً. إذا تخطيته، فلن تحتفظ إلا بالتتبعات التي لم يحدث فيها أي خطأ. وبصرف النظر عن ذلك، يكتب LiteLLM صفاً لتكلفة كل طلب في Postgres، وتقرأ واجهة الإدارة في /ui هذا الجدول. يزداد حجم الجدول مع حركة الشبكة، لذا راقبه إذا كانت مساحة القرص محدودة.

ضع البوابة خلف Reverse Proxy

يجب ألا يصل أي شيء خارج الخادم إلى المنفذ 4000. أنهِ TLS في nginx أو Caddy، ثم مرّر الطلبات إلى عنوان loopback.

location / {
    proxy_pass http://127.0.0.1:4000;
    proxy_http_version 1.1;
    proxy_set_header Host $host;
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
    proxy_set_header X-Forwarded-Proto $scheme;
    proxy_buffering off;
    proxy_read_timeout 600s;
}

هناك سطران من هذه الإعدادات يغفل عنهما كثيرون. يهم proxy_buffering off لأن الاستجابة المتدفقة تتكون من سلسلة من server-sent events. وعند تفعيل التخزين المؤقت، يجمع nginx المقاطع في مخازن مؤقتة قبل تمريرها، ما يؤخر وصولها إلى العميل. ويهم proxy_read_timeout 600s لأن القيمة الافتراضية البالغة 60 ثانية في nginx هي أطول مدة ينتظرها بين قراءتين من البوابة، وليست حداً زمنياً للاستجابة بأكملها. ترسل الاستجابة غير المتدفقة أي بيانات فقط بعد اكتمال التوليد. لذلك قد تتجاوز الاستجابة الطويلة 60 ثانية من دون وصول بايت واحد، ويحصل العميل على الخطأ 504، بينما يسجل سجل الأخطاء upstream timed out (110: Connection timed out) while reading response header from upstream. أما الاستجابة المتدفقة التي تواصل إرسال المقاطع، فيمكن أن تستمر مدة أطول بكثير من 60 ثانية؛ ولا ينقضي المهلة إلا إذا توقف النموذج عن الإرسال طوال هذه المدة.

للحصول على الشهادة، يُعد Certbot مع Let's Encrypt على nginx المسار الأقصر. وإذا كان الخادم يقدّم عدة حاويات بالفعل، فإن Traefik أمام عدة تطبيقات Compose يتولى التوجيه والشهادات في مكان واحد.

أصبح الـgateway الآن نقطة فشل واحدة

كن صريحاً بشأن ما أنشأته. يعتمد كل تطبيق تملكه الآن على حاوية واحدة في VPS واحد. أثناء توقفها، لا يمكن لأي مكوّن استدعاء أي نموذج، بما في ذلك المزوّدون الذين يعملون بشكل سليم. وينتج عن ذلك أربعة أمور.

  • يؤدي إعداد سيئ إلى إيقاف كل شيء دفعة واحدة. يعيد restart: unless-stopped تشغيل العملية بعد تعطلها، ويعيد تشغيل حاوية لا تستطيع تحليل config.yaml مراراً. اقرأ docker compose logs litellm بعد كل تغيير في الإعدادات، وأجرِ تغييرات الإعدادات عندما يتوفر لديك وقت لمراقبتها.
  • يكون Postgres ضمن مسار الطلب عند فقدان القيمة من الذاكرة المؤقتة. يستخدمه البحث عن المفتاح الافتراضي عندما لا يكون المفتاح مخزناً مؤقتاً؛ ويجري تسجيل الإنفاق بشكل غير متزامن بعد إرسال الاستجابة. يشير إرجاع /health/readiness إلى 503 إلى أن الـgateway يعمل، لكنه لا يستطيع تنفيذ أيٍّ من العمليتين.
  • استخدم الأنوية أولاً، ثم أضف مثيلات. إرشاد المشروع للإنتاج، الذي يوصي بعامل واحد لكل مثيل (--num_workers 1)، مخصص لـKubernetes أو لمُوسِّع تلقائي آخر للحاويات يضيف المثيلات نيابةً عنك. أما في VM واحدة أو حاوية مستقلة مثل هذه، فيعني العكس: اضبط عدد العمال على عدد vCPU في الجهاز. في الجهاز ذي 1 vCPU أعلاه، يظل العدد --num_workers 1؛ ارفعه في جهاز أكبر، وتذكّر أن كل عامل يستهلك ذاكرته الخاصة. تؤدي إضافة gateway ثانية خلف موزّع أحمال إلى إزالة الحاوية الوحيدة من نقطة الفشل. لكنها لا تزيل قاعدة البيانات.
  • أنشئ نسخاً احتياطية لما لا يمكن إعادة إنشائه. يشمل ذلك config.yaml و.env، إلى جانب pg_dump لقاعدة البيانات. يؤدي فقدان LITELLM_SALT_KEY إلى جعل بيانات اعتماد المزوّد المشفّرة داخل ذلك التفريغ عديمة الفائدة، لذلك يجب أن ينتمي ملف البيئة والتفريغ إلى مهمة النسخ الاحتياطي نفسها: نسخ restic الاحتياطية إلى تخزين خارج الخادم.

تتم الترقية بتعديل وسم الصورة وتشغيل docker compose up -d. يشغّل LiteLLM prisma migrate deploy عند بدء التشغيل افتراضياً، لذلك تُجري الحاوية الجديدة ترحيل مخطط قاعدة البيانات عند إقلاعها الأول. أنشئ التفريغ قبل تغيير الوسم، لأن إعادة الصورة القديمة لا تلغي عملية ترحيل سبق تنفيذها.

اقرأ ملاحظات الإصدار لكل نسخة تتجاوزها، وليس فقط النسخة التي ستستقر عليها. يؤدي الانتقال من v1.95.0 إلى v1.104.0 أو أحدث إلى تجاوز عدة تغييرات كاسرة للتوافق: رمز حالة الميزانية المذكور أعلاه، وproxy يرفض البدء عند عدم ضبط المفتاح الرئيسي أو كونه فارغاً أو معروفاً للعامة، وproxy يخرج عند فشل إعداد قاعدة البيانات أثناء بدء التشغيل، وهو ما يظهر تحت restart: unless-stopped على شكل حلقة إعادة تشغيل. إذا كنت قادماً من v1.102.x أو أقدم، تطلب منك ملاحظات v1.104.0 أيضاً إنشاء فهرسي LiteLLM_SpendLogs بنفسك.

FAQ

هل يضيف LiteLLM زمن تأخير ملحوظاً إلى كل استدعاء؟

ينشر المشروع رقماً قدره 8 ms عند المئين 95 مع 1000 طلب في الثانية، كما ورد في ملف README الخاص به في أكتوبر 2026. تعامل مع ذلك باعتباره رقماً صادراً عن المورّد. العامل الذي يؤثر فعلياً في زمن التأخير لديك هو المسافة الشبكية بين تطبيقاتك والبوابة، لأنك أضفت دورة ذهاب وإياب واحدة إلى كل استدعاء. شغّل البوابة في المنطقة نفسها التي توجد فيها التطبيقات التي تستدعيها، ثم قِس زمن التأخير الإضافي الفعلي لديك باستخدام الترويسة x-litellm-overhead-duration-ms في استجابة حقيقية.

لماذا توقف البث بعد أن وضعت nginx أمام البوابة؟

لأن nginx يخزّن استجابات المنبع مؤقتاً افتراضياً، ولأن إكمال الاستجابة المتدفقة يتكون من سلسلة من الأحداث المرسلة من الخادم. عند تفعيل proxy_buffering، يجمع nginx الأجزاء في المخازن المؤقتة قبل إعادة توجيهها، ما يؤخر تسليمها إلى العميل. اضبط proxy_buffering off; في كتلة location. ارفع قيمة proxy_read_timeout في الكتلة نفسها أيضاً. افتراضياً، يتوقف nginx عن الانتظار بعد 60 ثانية من دون بيانات من البوابة. ويمكن أن تصل عملية توليد طويلة وغير متدفقة إلى هذه المهلة بسهولة، فيتلقى العميل خطأ 504.

ماذا يحدث عندما تنفد ميزانية المفتاح الافتراضي؟

يفشل الاستدعاء بخطأ من النوع budget_exceeded، وبصيغة رسالة مثل Budget has been exceeded! Key=<key> Current cost: <spend>, Max budget: <max_budget>. في الإصدار المثبت v1.95.0، تكون الحالة HTTP 429؛ أما ابتداءً من v1.104.0، فالقيمة الافتراضية هي HTTP 422. لا يوضح أي من رمزي الحالة أن السبب هو الميزانية. وهذه هي المشكلة: إذ تبلغ مكتبة العميل عن 429 باعتباره تجاوزاً لمعدل الطلبات، وقد تعيد المحاولة، فيبدأ المستخدمون بالبحث عن مشكلة خنق بدلاً من قراءة الرسالة. سجّل نص الاستجابة إلى جانب رمز الحالة. تحقّق من الموضع الفعلي للمفتاح باستخدام /key/info?key=sk-... مقابل المفتاح الرئيسي، وارفع الحد باستخدام /key/update إذا كانت الميزانية مضبوطة على قيمة منخفضة جداً.

هل تستطيع البوابة التوجيه إلى نموذج محلي وإلى نماذج مستضافة؟

نعم، ويكون ذلك إدخالاً إضافياً في model_list. استخدم البادئة ollama_chat/ مع api_base، مثلاً model: ollama_chat/llama3.1 إلى جانب api_base: http://ollama:11434. في شبكة Compose الافتراضية، يشير localhost داخل الحاوية إلى تلك الحاوية نفسها. لذلك استخدم اسم خدمة Compose أو عنوان المضيف على شبكة Docker، ولا تستخدم 127.0.0.1 مطلقاً. والاستثناء هو استخدام شبكة المضيف، حيث تشارك الحاوية مكدس شبكة المضيف. تشغيل النموذج المحلي مهمة منفصلة: راجع استضافة LLM ذاتياً باستخدام Ollama على VPS.

هل كان هذا المقال مفيدًا؟