Skip to main content
تبدأ معظم تطبيقات الذكاء الاصطناعي باستدعاء واجهة برمجة تطبيقات نموذج مباشرةً. يعمل ذلك جيدًا للنماذج الأولية، ولكن بمجرد أن تحتاج تطبيقات أو خدمات أو عملاء متعددون إلى الوصول، تصبح استدعاءات المزود المباشرة أصعب في الإدارة. تحتاج كل خدمة إلى مفتاح مزود، ويحتاج كل عميل إلى تعلم السلوك الخاص بالمزود، وينتهي الأمر بكل فريق إلى حل المصادقة والحدود والقابلية للمراقبة بطرق مختلفة قليلاً. توفر بوابة LLM مكانًا واحدًا لمصادقة المستدعين، وفرض حدود المعدل، وإخفاء مفاتيح المزود العلوي، وتسجيل القياس عن بُعد، والحفاظ على واجهة برمجة تطبيقات مستقرة لتطبيقاتنا الخاصة. في هذا البرنامج التعليمي، سنبني واحدة بلغة Rust باستخدام Axum و Postgres و SQLx وواجهة برمجة تطبيقات Venice AI. في النهاية، سيكون لديك بوابة تعرض نقطة النهاية /v1/chat/completions المتوافقة مع OpenAI، وتقبل رموز حاملك الخاصة، وتعيد توجيه الطلبات إلى Venice، وتدعم استجابات البث، وتصدر امتدادات ومقاييس OpenTelemetry مفيدة. هل أنت مهتم بتنفيذ الكود الكامل؟ اطلع على مستودع GitHub.

المتطلبات المسبقة

  • Rust 1.92+
  • Docker و Docker Compose
  • مفتاح Venice API
  • curl
  • إلمام أساسي بخدمات ويب Rust
قبل أن نبدأ، قم بتصدير مفتاح Venice API الخاص بك:
لن نكشف هذا المفتاح أبدًا لتطبيقات العميل. ستحتفظ البوابة به من جانب الخادم وسيقوم العملاء بالمصادقة باستخدام مفاتيح API خاصة بالبوابة بدلاً من ذلك.

ما الذي نبنيه

التنفيذ المرجعي هو خدمة Rust صغيرة مع بعض الأجزاء الواضحة: مخطط معماري يوضح عميلًا يستدعي بوابة Rust و Postgres و Venice AI و OpenTelemetry يرسل العميل طلبًا متوافقًا مع OpenAI إلى البوابة. تقوم البوابة بمصادقة المستدعي، وتحقق من حدود المعدل، وتعيد توجيه الطلب إلى Venice، وتسجل القياس عن بُعد على طول الطريق. كجزء من البوابة، سنضمن أن تظل هذه الخدمة قابلة للتوسع أفقيًا مع تغطية أقل قدر من مساحة السطح فيما يتعلق بواجهة برمجة التطبيقات نفسها. هناك عدة أسباب لذلك - أحدها بشكل أساسي هو أنه إذا كان لديك قدر كبير جدًا من الإنتاجية على سبيل المثال، فمن المؤكد تقريبًا أنك سترغب في استخدام النسخ المتماثلة (أي تشغيل أكثر من نسخة واحدة من نفس الخدمة). هذا يعني أنه إذا لم تكن قد فعلت ذلك بالفعل، فمن الناحية المعمارية سترغب في وضع خدمتك الأصلية والنسخ المتماثلة خلف موازن أحمال بحيث إذا تعطلت حاوية أو خدمة واحدة، فلن تتعرض الخدمة بأكملها لانقطاع. بالإضافة إلى ذلك، سنفترض أيضًا أننا نمتلك إنشاء مفاتيح API بطريقة ما، على الرغم من أن خدمة البوابة لا ينبغي أن تصنعها بمعزل عن غيرها. سيتم تمثيل هذا كجدول Postgres نقوم بتعبئته عند استخدامه محليًا. في الإنتاج، سيتم عادةً التعامل مع هذا بواسطة خدمة المصادقة. على الرغم من أنه من الممكن التعامل مع إنشاء مفتاح API في المنبع لكل مستخدم يستخدم بوابة LLM الخاصة بك، إلا أنه من الناحية العملية لا يُنصح بذلك بشكل عام. من خلال تفريغ هذه المسؤولية إلى الخدمة العلوية، فإنك تفرغ أيضًا أي تحكم قد يكون لديك عادةً - مما يعني أنك لا تستطيع تطبيق أشياء مثل تحديد المعدل وحدود الإنفاق بالكامل. تبقى شجرة المصدر صغيرة عن قصد:
دون مزيد من التأخير، لنبدأ في البناء.

إنشاء خدمة Rust

ابدأ بمشروع Rust ثنائي جديد:
أضف الاعتماديات التي نحتاجها في Cargo.toml - تمت إضافة الشروحات في مقتطف الكود:

تحميل التكوين

تقرأ البوابة كل شيء من متغيرات البيئة. بالنسبة لكود البنية التحتية مثل هذا، تُعد متغيرات البيئة افتراضًا جيدًا لأنه يمكن تشغيل نفس الملف الثنائي محليًا، أو في Docker Compose، أو في بيئة مستضافة دون تنسيق ملف تكوين منفصل. بالإضافة إلى ذلك، سيسمح لك العديد من المزودين بتخزين متغيرات البيئة الخاصة بك كأسرار في وقت تشغيل الحاوية الخاص بهم. غالبًا ما يكون هذا أكثر أمانًا من محاولة استخدام شيء مثل dotenv (أو dotenvy في Rust، حيث أن صندوق dotenv الأصلي مهجور في الغالب). قم بإنشاء src/config.rs:
بينما هناك الكثير من القيم المحتملة التي يتم تحليلها هنا من متغيرات البيئة، بشكل عام تحتاج فقط إلى اثنين:
  • رابط قاعدة البيانات
  • مفتاح Venice API الخاص بك
يهم افتراضيان هنا. VENICE_BASE_URL يشير إلى https://api.venice.ai/api/v1، و CAPTURE_GENAI_CONTENT يكون افتراضيًا false بحيث لا يتم تسجيل محتوى المطالبة إلا إذا قمت بتمكينه عن قصد. هذا الافتراضي الثاني هو الأكثر أهمية. يمكن للبوابة أن ترى كل مطالبة واستجابة تتدفق من خلالها، لكن قابلية المراقبة يجب ألا تصبح تلقائيًا التقاطًا للمحتوى. في معظم أنظمة الإنتاج، تُعد أعداد الرموز، والزمن، وأسماء النماذج، ورموز الحالة، وبيانات الفوترة الوصفية كافية للعمليات. بشكل عام، قد لا يكون تسجيل المطالبات والمحادثات في الإنتاج مسؤولية خصوصية فحسب - بل يمكن أن يكون أيضًا مسؤولية تخزين. تضيف إضافتها إنشاء امتدادات وآثار بمستوى عالٍ للغاية من التعدد (أي تفرد البيانات داخل مجموعة البيانات). يمكن أن يجعل هذا البحث في بيانات المراقبة الخاصة بك مكلفًا للغاية، بالإضافة إلى احتمال إعاقة الأداء عند البحث في البيانات.

إنشاء مخطط قاعدة البيانات

بعد ذلك، قم بإنشاء migrations/0001_api_keys.sql. سنقوم بتخزين أول 12 حرفًا فقط من كل مفتاح API للبوابة كبادئة بحث، بالإضافة إلى تجزئة SHA-256 للمفتاح الكامل. يتيح ذلك للبوابة العثور على صف مرشح بسرعة دون تخزين بيانات الاعتماد الأولية. البادئة ليست سرية. وهي موجودة للفهرسة. التجزئة هي ما يثبت أن المستدعي قدم المفتاح الكامل. هذا هو نفس الشكل الأساسي الذي تستخدمه العديد من أنظمة مفاتيح API: عرض المفتاح الأولي مرة واحدة، وتخزين تمثيل غير قابل للعكس، والاحتفاظ ببادئة قصيرة للبحث وسير عمل الدعم.
أضف الآن جدولًا لتحديد المعدل بنافذة ثابتة:
هذا المخطط صغير، لكنه يمنحنا الثوابت المهمة:
  • لا يتم تخزين مفاتيح API أبدًا بنص عادي.
  • يجب أن تكون إعدادات حد المعدل موجبة.
  • لا يمكن أن تظل المفاتيح المُبطلة نشطة.
  • يتم تحديد نافذة حد المعدل بشكل فريد بواسطة المفتاح ووقت البدء وطول النافذة.
الاحتفاظ بهذه الثوابت في Postgres مفيد لأن كل مستدعي يجب أن يمر عبر حالة قاعدة البيانات هذه. حتى لو أضفنا لاحقًا واجهة برمجة تطبيقات إدارية، أو مهمة تدوير مفاتيح في الخلفية، أو ترحيلًا يستورد المفاتيح من نظام آخر، فإن قاعدة البيانات لا تزال ترفض الحالات المستحيلة مثل مفتاح نشط بطابع زمني للإبطال.

بناء عميل Venice

بعد ذلك، سنقوم بإنشاء src/venice.rs. يحتاج العميل فقط إلى معرفة رابط إكمال الدردشة العلوي، ومفتاح Venice API، وعدد مرات إعادة محاولة الإخفاقات العابرة. الحفاظ على هذا الغلاف صغيرًا أمر مقصود - يجب ألا تعيد البوابة تنفيذ واجهة برمجة تطبيقات Venice بالكامل. على المستوى الأساسي، تتمثل مهمة البوابة في إرفاق بيانات الاعتماد من جانب الخادم، وتطبيق مهلة، وإعادة محاولة الطلبات الآمنة للإعادة، وإرجاع الاستجابة العلوية في شكل يمكن للموجه إعادة توجيهه.
بالنسبة للطلبات غير المتدفقة، يمكننا إعادة محاولة أخطاء الاتصال، والمهلات، ورموز حالة HTTP العابرة:
يتم تطبيق إعادة المحاولة فقط على المسار غير المتدفق. بمجرد أن تبدأ استجابة البث، يمكن أن تؤدي إعادة المحاولة داخل البوابة إلى تكرار المخرجات الجزئية أو إرباك العملاء الذين تلقوا بالفعل قطعًا. بالنسبة للبث، فإن الافتراضي الأفضل هو إظهار الخطأ والسماح للمستدعي بتحديد ما إذا كان سيعيد محاولة الطلب بأكمله. للبث، ننشئ EventSource من نفس الطلب:
نقطة نهاية إكمال الدردشة في Venice متوافقة مع OpenAI، لذلك يمكن للبوابة قبول جسم مألوف:
يمكنك استبدال النموذج بأي نموذج قادر على الدردشة متاح لحساب Venice الخاص بك. لاحظ أن جسم الطلب لا يزال serde_json::Value. هذا اختيار توافق متعمد. إذا قمنا بنمذجة كل حقل إكمال دردشة ممكن في Rust، فعلينا الاستمرار في تحديث البوابة في كل مرة تضيف فيها واجهة برمجة التطبيقات العلوية خيارًا مفيدًا. من خلال تحليل ما نحتاجه فقط في مكان آخر، نسمح لمعلمات Venice الأحدث بالمرور دون إصدار بوابة.

مشاركة حالة التطبيق

قم بإنشاء src/state.rs:
يقوم Axum باستنساخ الحالة في المعالجات، لذلك يجب أن تكون الحالة نفسها رخيصة الاستنساخ. PgPool هو بالفعل مقبض تجمع مشترك، و Arc<Config> يبقي التكوين رخيصًا أيضًا. يمنح هذا كل معالج الوصول إلى نفس الأشياء الثلاثة: التكوين غير القابل للتغيير، واتصالات قاعدة البيانات المجمعة، وعميل Venice. يسهل الاحتفاظ بها في AppState واحد الاختبار لاحقًا لأن المعالجات تتلقى تبعياتها من خلال حالة Axum بدلاً من قراءة المتغيرات العامة.

مصادقة مفاتيح API للبوابة

يرسل العميل مفتاح البوابة الخاص به على النحو التالي:
قم بإنشاء src/auth.rs وتنفيذ مستخرج Axum. يتيح المستخرج للمعالجات المحمية أن تصرح بأنها تتطلب مفتاحًا مصادقًا عليه:
تدفق المصادقة الفعلي هو:
  1. تحليل رمز الحامل.
  2. أخذ أول 12 بايت كبادئة مفتاح.
  3. تجزئة الرمز المرشح الكامل باستخدام SHA-256.
  4. تحميل صف المفتاح النشط بواسطة البادئة.
  5. مقارنة التجزئة المخزنة والتجزئة المرشحة في وقت ثابت.
يحافظ هذا على فصل بيانات اعتماد المنبع والبوابة. يمكن لتطبيقات الإنتاج الخاصة بك تدوير مفاتيح البوابة دون تغيير مفتاح Venice API، ولا يحتاج مفتاح Venice أبدًا إلى مغادرة الخادم. نمط المستخرج مفيد لأن المصادقة تصبح جزءًا من توقيع نوع المعالج. لا يمكن للمسار الذي يقبل AuthenticatedApiKey تخطي المصادقة عن طريق الخطأ داخل جسم الوظيفة؛ يجب على Axum بناء هذه القيمة قبل تشغيل المعالج. هذا يجعل المسار المحمي سهل التدقيق.

إضافة حدود معدل النافذة الثابتة

قم بإنشاء src/rate_limit.rs. يستخدم محدد المعدل عبارة SQL واحدة لإدراج نافذة جديدة أو زيادة النافذة الموجودة:
جملة WHERE api_key_rate_limit_windows.request_count < $3 هي الجزء المهم. عندما تكون النافذة ممتلئة بالفعل، لا يقوم Postgres بتحديث الصف ولا ينتج RETURNING أي صف. يمكن للمعالج تحويل ذلك إلى استجابة 429 Too Many Requests مع رأس Retry-After. النافذة الثابتة ليست أكثر محددات المعدل تطورًا، لكنها سهلة الشرح، وسهلة الفحص، وجيدة بما يكفي لبرنامج تعليمي عن البوابة. المفاضلة هي أن حركة المرور يمكن أن تتراكم حول حدود النافذة. إذا كنت بحاجة إلى سلوك أكثر سلاسة على نطاق واسع، فإن محدد سلة الرموز أو محدد النافذة المنزلقة المدعوم بـ Redis هو خطوة تالية طبيعية.

إرجاع أخطاء بأسلوب OpenAI

قم بإنشاء src/error.rs واجعل أخطاء التطبيق تنفذ IntoResponse:
بالنسبة للأخطاء التي يتم إنشاؤها بواسطة البوابة، قم بإرجاع جسم JSON بشكل يشبه أخطاء واجهة برمجة تطبيقات النموذج الشائعة:
بالنسبة لأخطاء Venice العلوية، احتفظ برمز الحالة والجسم العلوي. هذا يجعل تصحيح الأخطاء أسهل بكثير للعملاء لأن أخطاء التحقق من الصحة على مستوى المزود لا تزال تبدو مثل أخطاء التحقق من الصحة على مستوى المزود. يبقي هذا التقسيم البوابة صادقة بشأن مصدر الخطأ. إذا رفضت البوابة طلبًا لأن رمز الحامل مفقود أو أن المستدعي تجاوز الحد، فإنها ترجع خطأً بشكل البوابة. إذا رفض Venice طلب النموذج، فإننا نحتفظ بالجسم العلوي حتى يتمكن مطورو العميل من رؤية رسالة التحقق من صحة المزود بدلاً من فشل وكيل عام.

بناء الموجه

الآن يمكننا توصيل مسارات HTTP في src/router.rs:
يبدأ معالج الدردشة بطلب AuthenticatedApiKey. إذا فشلت المصادقة، فلن يدخل Axum جسم المعالج أبدًا:
تتحقق البوابة فقط من الحقول التي تحتاجها لسلوك البوابة: model و messages و stream. يمر كل شيء آخر في جسم JSON إلى Venice. هذا يبقي البوابة متوافقة مع ميزات المزود التي قد ترغب في استخدامها لاحقًا. يجعل المعالج أيضًا وضعي الاستجابة صريحين. تنتظر الطلبات غير المتدفقة أن يقوم Venice بإرجاع استجابة JSON كاملة، ثم تسجل بيانات الاستجابة الوصفية قبل إرسال البايتات إلى المصدر. تعيد طلبات البث فورًا مع جسم text/event-stream مدعومًا بتدفق غير متزامن. يبقي هذا التقسيم المسار غير المتدفق بسيطًا مع إعطاء مسار البث تحكمًا كافيًا لمراقبة القطع أثناء مرورها.

دعم استجابات البث

تستخدم إكمالات دردشة البث الأحداث المرسلة من الخادم. يرسل Venice بيانات SSE، وتقوم البوابة بترحيل تلك البيانات إلى العميل. يجب على البوابة تجنب تخزين البث بالكامل مؤقتًا لأن ذلك سيهزم الغرض من البث. يهتم المستخدمون بالوقت اللازم للحصول على الرمز الأول، وليس فقط بالوقت اللازم للرمز النهائي. من خلال إعادة توجيه كل حدث علوي عند وصوله، يمكن للعملاء عرض المخرجات الجزئية بينما لا يزال النموذج يولد. قم بإنشاء src/sse.rs:
يتم ترميز كل رسالة مرة أخرى إلى تنسيق SSE:
هذا يحافظ على تجربة العميل التي تتوقعها SDKs المتوافقة مع OpenAI: تصل القطع كأحداث data: ...، وينتهي البث بـ data: [DONE]. مراقب البث هو أيضًا المكان الذي يمكننا فيه جمع البيانات الوصفية دون تغيير ما يراه العميل. يتم إعادة توجيه كل قطعة بتنسيق SSE، ولكن لا يزال بإمكان البوابة مراقبة معرفات الاستجابة، وأسباب الإنهاء، واستخدام الرموز، وحقول التكلفة، ومعلومات التوقيت أثناء مرور تلك القطع.

تسجيل قياسات GenAI عن بُعد

البوابات مفيدة لأن كل طلب يمر عبر مكان واحد. هذا يجعلها مكانًا رائعًا لتسجيل النموذج، والزمن، واستخدام الرموز، وأسباب الإنهاء، وتكلفة الفوترة، وتوقيتات البث. قم بإنشاء src/telemetry.rs وابدأ بتحليل الطلب:
ثم أنشئ امتدادًا باستخدام سمات GenAI الدلالية:
عندما تعود استجابة غير متدفقة، قم بإلغاء تسلسل حقول بيانات الاستجابة الوصفية المعروفة إلى بنى. لا تزال البوابة تعيد توجيه البايتات الأصلية إلى العميل، لكن القياس عن بُعد لا يحتاج إلى المرور عبر JSON عشوائي. يستخدم سجل الفوترة UUID لمفتاح البوابة بدلاً من رمز الحامل بنص عادي، ويأتي معرف الطلب من id استجابة Venice:
بالنسبة لاستجابات البث، سجل الوقت اللازم للقطعة الأولى والوقت بين قطع المخرجات أثناء ترحيل بث SSE. تكون هذه المقاييس مفيدة بشكل خاص عندما تهتم بالزمن المُدرك، وليس فقط بإجمالي وقت الطلب. القياس عن بُعد هو المكان الذي تصبح فيه البوابة أكثر من مجرد وكيل. بمجرد أن تتضمن الامتدادات النموذج المطلوب، والنموذج العلوي، وأعداد الرموز، وأسباب الإنهاء، والحالة، وسجلات الفوترة لكل مفتاح، يمكنك الإجابة على أسئلة تشغيلية عملية: أي العملاء ينفقون الأكثر، وأي النماذج أبطأ، وما إذا كان البث يحسن الزمن المُدرك، وما إذا كانت الأخطاء تأتي من المصادقة أو حدود المعدل أو النقل أو مزود النموذج.

بدء تشغيل الخادم

الآن قم بتوصيل كل شيء معًا في src/main.rs:
عند بدء التشغيل، تقوم البوابة بما يلي:
  1. قراءة التكوين.
  2. تهيئة القياس عن بُعد.
  3. الاتصال بـ Postgres.
  4. تشغيل ترحيلات SQLx.
  5. بناء حالة التطبيق المشتركة.
  6. بدء تشغيل خادم Axum.
يعد تشغيل الترحيلات عند بدء التشغيل مناسبًا لهذا البرنامج التعليمي لأن docker compose up يمكنه إحضار المكدس بأكمله إلى حالة عمل. في نشر إنتاج أكبر، قد تفضل تشغيل الترحيلات كخطوة إصدار منفصلة بحيث تتم مراجعة تغييرات المخطط وتطبيقها قبل بدء تشغيل مثيلات بوابة جديدة.

زرع مفتاح بوابة محلي

للتطوير المحلي، قم بإنشاء scripts/seed_api_key.sh. يقوم البرنامج النصي بإدراج مفتاح API للبوابة في Postgres عن طريق تخزين بادئته وتجزئة SHA-256 الخاصة به:
المفتاح المحلي الافتراضي هو:
للنشر الحقيقي، قم بإنشاء مفاتيح عشوائية أطول، وأظهرها مرة واحدة للمستدعي، وقم بتخزين التجزئة فقط. البرنامج النصي للزرع ممل عمدًا لأن بيانات الاعتماد المحلية يجب أن تكون سهلة إعادة إنشائها. الإصدار الإنتاجي هو المكان الذي ستضيف فيه إنشاء مفاتيح أقوى، وتسجيل تدقيق، وانتهاء صلاحية، وتدفق عرض لمرة واحدة.

التشغيل محليًا

للتشغيل محليًا، سنستخدم Docker Compose لبدء كل من البوابة و Postgres. يبقي هذا البرنامج التعليمي قابلاً للتكرار: لا يحتاج القراء إلى قاعدة بيانات مُهيأة يدويًا، ويمكن للبوابة استخدام نفس شكل DATABASE_URL الذي ستستخدمه في نشر حاوي.
سنحتاج أيضًا إلى Dockerfile صغير يقوم ببناء ملف Rust الثنائي ونسخه إلى صورة وقت تشغيل أصغر:
لتشغيل المكدس، استخدم الأمر التالي:
لا تنسَ أنه يمكنك أيضًا تشغيل هذا منفصلاً بعلامة -d إذا كنت تريد استخدام الطرفية الخاصة بك لأشياء أخرى بعد ذلك (ثم استخدم docker compose down لإزالته). في طرفية أخرى، قم بزرع مفتاح بوابة التطوير:
إذا كانت آلتك تشغل بالفعل Postgres على المنفذ 5432، فقم بإزالة تعيين منفذ المضيف لخدمة Postgres في Compose. تحتاج البوابة فقط إلى الوصول إلى Postgres على شبكة Docker الداخلية. الشيء المهم الذي يجب مراعاته هو أن مفتاح Venice API ينتمي فقط إلى بيئة البوابة. يجب أن تستخدم طلبات العميل مفتاح البوابة المزروع. هذا الفصل هو الهدف الكامل من وضع بوابة أمام مزود النموذج.

اختبار البوابة

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

توسيع هذه البوابة

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

الانتهاء

شكرًا للقراءة! نأمل أن يكون هذا قد ساعدك في رؤية كيفية بناء بوابة LLM عملية بلغة Rust دون تحويلها إلى مشروع منصة ضخم. من خلال الجمع بين Axum و Postgres و SQLx و OpenTelemetry وواجهة برمجة تطبيقات إكمالات الدردشة المتوافقة مع OpenAI من Venice، يمكننا بناء بوابة صغيرة بما يكفي لفهمها ومفيدة بما يكفي للجلوس أمام تطبيقات حقيقية.