لماذا نقوم بنشر أنظمة الذكاء الاصطناعي على Modal بدلاً من AWS Lambda
غيّرت تقنية Serverless GPU ما هو ممكن اقتصادياً لأنظمة الذكاء الاصطناعي في بيئة الإنتاج. وقت تشغيل بارد (cold-start) أقل من ثانية واحدة، ودفع بالملي ثانية لوقت الـ GPU، والتقليص إلى الصفر (scale to zero) — تجعل Modal البنية التحتية للاستنتاج (inference) أمراً يسيراً للشركات المتوسطة.
المشكلة ليست في بناء نموذج ذكاء اصطناعي؛ بل في نقله إلى بيئة الإنتاج (production) دون الغرق في تكاليف البنية التحتية أو التعقيد التشغيلي. لقد رأينا في Verel Systems العديد من المشاريع التجريبية الواعدة للذكاء الاصطناعي تموت ببطء، ليس لأن النماذج كانت سيئة، بل لأن استراتيجية النشر (deployment) كانت فوضوية. تتراكم على الشركات "ديون الذكاء الاصطناعي" (AI debt) — مثل سلاسل موجّهات (prompt chains) متشابكة، ووكلاء (agents) غير مراقبين، وأنظمة RAG بجودة تجريبية فقط — والمساهم الأكبر في ذلك هو عدم التوافق بين الحوسبة السحابية التقليدية وطبيعة أعباء عمل الذكاء الاصطناعي المتغيرة والمتعطشة للـ GPU في العالم الحقيقي.
منصة AWS Lambda، رغم كل ميزاتها كخدمة serverless، لا توفر وحدات GPU. هذا يستبعدها فوراً من الغالبية العظمى من أعمال الذكاء الاصطناعي الجادة. عندما تتعامل مع توليد التضمينات (embeddings)، أو معالجة الصور، أو الاستنتاج على الدفعات (batch inference)، فإن وظائف الـ serverless التي تعتمد على الـ CPU فقط تصبح خياراً غير قابل للنقاش. البديل بالنسبة للكثيرين هو خيار مؤلم بين الهندسة المفرطة (overengineering) أو الإنفاق الزائد.
معضلة نشر الذكاء الاصطناعي: تعقيد مفرط أم تكلفة باهظة
فكر في عبء عمل الذكاء الاصطناعي النموذجي: غالباً ما يكون متقطعاً ومكثفاً (bursty). قد تحتاج إلى معالجة 100,000 وثيقة لخط معالجة (pipeline) RAG، أو توليد بضع مئات من الصور، أو تشغيل مهمة استنتاج دفعية (batch inference) كل ساعة. تتطلب هذه المهام قوة GPU كبيرة، ولكن لفترات قصيرة وغير متوقعة فقط.
هنا تظهر عيوب العروض السحابية القياسية:
- ▸AWS Lambda: كما ذكرنا، لا توجد وحدات GPU. نهاية النقاش بالنسبة للذكاء الاصطناعي.
- ▸ECS/EKS (إدارة الحاويات): هذه أدوات قوية بلا شك. ولكن بالنسبة لأعباء عمل الذكاء الاصطناعي المتغيرة، فإنها غالباً ما تمثل هندسة مفرطة (overengineered). إن إعداد وصيانة Kubernetes أو حتى مجموعات ECS لمهام GPU المتقطعة يتطلب جهداً تشغيلياً هائلاً. لقد رأينا شركات متوسطة تقضي أشهراً في محاولة تشغيل بيئة EKS مستقرة تدعم الـ GPU، لينتهي بها المطاف بتوقف مشروع الذكاء الاصطناعي في "برزخ المشاريع التجريبية" لأن البنية التحتية أصبحت وظيفة بدوام كامل. هذا مثال صارخ على تراكم ديون الذكاء الاصطناعي قبل أن يرى نموذج واحد النور في بيئة الإنتاج.
- ▸مثيلات الـ GPU المخصصة (EC2 G5, G4dn): هذا هو الخيار الأكثر وضوحاً، ولكنه أيضاً الأكثر هدراً للمهام المتقطعة. تكلف مثيلة A10G (مثل
g5.xlarge) حوالي 1.006 دولار في الساعة حسب الطلب (on-demand) في منطقةus-east-1. إذا كان خط معالجة التضمينات (embedding pipeline) لديك يعمل لمدة 3 ساعات يومياً، فأنت تدفع 3.018 دولار للحوسبة. ولكن إذا ظلت تلك المثيلة خاملة طوال الـ 21 ساعة المتبقية، فستستمر في الدفع مقابلها. على مدار شهر، يبلغ ذلك 724.32 دولار لمثيلة تعمل بشكل فعلي أقل من 15% من الوقت. بالنسبة للعديد من الشركات، خاصة تلك التي لا تملك فرق MLOps مخصصة، تكون هذه التكلفة باهظة وتجعل توسيع مشاريع الذكاء الاصطناعي غير مستدام مالياً. لا يمكنهم تحمل تكلفة خوادم GPU مخصصة لمهام استنتاج متقطعة، مما يؤدي إلى التخلي عن إثباتات المفاهيم (POCs) وهدر الميزانية.
هذا التعارض الجوهري هو السبب في أننا في Verel Systems بدأنا في البحث عن طريقة أفضل لتشغيل الذكاء الاصطناعي عملياً. كنا بحاجة إلى شيء يوفر حوسبة GPU، ويتوسع تلقائياً، ويحاسبنا فقط على ما نستخدمه فعلياً.
Modal: حوسبة GPU بدون خادم تعمل بكفاءة
ظهرت منصة Modal كحل واضح ومثالي. إنها منصة حوسبة GPU بدون خادم (serverless) مبنية للغة Python، ومصممة من الصفر للتعامل مع المشكلات المذكورة أعلاه. إنها تزيل عبء البنية التحتية، مما يسمح لمهندسينا بالتركيز على الذكاء الاصطناعي، وليس على ملفات YAML.
إليك ما تقدمه Modal:
- ▸بيئة GPU بدون خادم مدمجة مع Python: تكتب دوال Python القياسية، وتضيف إليها decorators، وتتولى Modal الباقي. توفر المنصة وصولاً إلى وحدات GPU عالية الأداء مثل NVIDIA A10G و H100، وهي ضرورية لأي مهمة تعلم عميق جادة.
- ▸وقت تشغيل بارد (Cold Starts) أقل من ثانية: هذا ميزة تنافسية رئيسية. بالنسبة للحاويات المُجهزة مسبقاً، يمكن لـ Modal تشغيل دالة الـ GPU في أقل من ثانية. هذه الاستجابة السريعة بالغة الأهمية للتطبيقات التي تواجه المستخدمين أو أعباء العمل التفاعلية، مما يسد الفجوة بين الـ serverless التقليدي والمثيلات المخصصة.
- ▸ديكوريتورز (Decorators) بسيطة: تحويل أي دالة Python إلى دالة قابلة للنشر على السحاب ومسرعة بواسطة الـ GPU هو أمر بسيط للغاية، يكفي إضافة
@stub.function(gpu="A10G"). هذا يغنيك تماماً عن ملفات Dockerfiles، وملفات تهيئة Kubernetes، والـ SDKs الخاصة بالسحابة، مما يقلل وقت التطوير والنشر بشكل كبير. - ▸بناء الحاويات تلقائياً: تقوم Modal ببناء صورة الحاوية (container image) بذكاء بناءً على ملف
requirements.txtأو أوامرpip_installالصريحة داخل تعريف الصورة. هذا يلغي الحاجة إلى صيانة Dockerfile يدوياً لمعظم حالات الاستخدام، مما يسرع دورات التطوير. - ▸إدارة الأسرار (Secrets): تتطلب أنظمة الإنتاج معالجة آمنة لمفاتيح واجهة برمجة التطبيقات (API keys) وبيانات الاعتماد. يتكامل كائن
modal.Secretفي Modal بسلاسة، مما يسمح لك بحقن الأسرار في دوالك دون كتابتها بشكل صريح في الكود أو الاعتماد على متغيرات البيئة التقليدية. - ▸طوابير المهام والتوسيع التلقائي: على الرغم من عدم تقديمها كخدمة منفصلة صريحة، فإن نموذج الـ serverless في Modal يوفر بطبيعته جدولة المهام والتوسيع التلقائي. ما عليك سوى استدعاء دالة عن بُعد (remote function)، وستقوم Modal بتوفير الحوسبة اللازمة، والتوسع للتعامل مع المهام المتزامنة، والتقليص إلى الصفر عند الخمول، مما يضمن لك الدفع مقابل وقت التشغيل الفعلي فقط.
- ▸الدفع بالملي ثانية: هذا هو التحول الاقتصادي الحقيقي. بدلاً من الدفع مقابل مثيلة خاملة، تتم محاسبتك فقط على الأجزاء من الثانية التي يعمل فيها الكود الخاص بك على الـ GPU المخصص.
أين نستخدم Modal في Verel Systems
في Verel Systems، قمنا بدمج Modal في العديد من الأجزاء الحيوية من خطوط معالجة الذكاء الاصطناعي في بيئة الإنتاج لدينا، مما يساعد عملائنا على تجنب ديون الذكاء الاصطناعي وتجاوز مرحلة المشاريع التجريبية المتعثرة.
- ▸خطوط معالجة التضمين (Embedding Pipelines): حالة الاستخدام الأكثر تكراراً لدينا. عندما نقوم باستيراد مجموعات بيانات ضخمة — على سبيل المثال، 100,000 وثيقة لنظام RAG — نحتاج إلى توليد التضمينات (embeddings). هذه مهمة قابلة للتوازي بشكل كبير وتتطلب استخداماً مكثفاً للـ GPU، لكنها ليست مستمرة. إن تشغيل مثيلة A10G لبضع ساعات ثم إيقافها هو بالضبط نوع العمليات اليدوية المعرضة للأخطاء التي تلغيها Modal. نقوم بتعريف دالة التضمين الخاصة بنا، وضبطها لتعمل على GPU من نوع A10G، ونترك لـ Modal مهمة التوسع والتنفيذ. كفاءة التكلفة هنا لا مثيل لها؛ نحن ندفع فقط مقابل وقت الحوسبة الفعلي، وليس مقابل ساعات خمول الـ GPU.
- ▸مهام توليد الصور: بالنسبة للعملاء الذين يطلبون توليد صور مخصصة (مثل الأصول التسويقية، أو نماذج المنتجات، أو الأدوات الداخلية)، فإن Modal هي الخيار المثالي. تستهلك نماذج مثل Stable Diffusion الكثير من موارد الـ GPU، لكن المهام غالباً ما تكون متقطعة. قد يقوم العميل بتوليد 500 صورة في ساعة واحدة، ثم لا شيء في الساعات الثلاث التالية. تتوسع Modal فوراً لتلبية الطلب وتتقلص إلى الصفر، مما يضمن فعالية التكلفة.
- ▸نقاط نهاية استنتاج النماذج للعملاء (Inference Endpoints): لا يستطيع العديد من عملائنا في الشركات المتوسطة تبرير نفقات خادم GPU مخصص للاستنتاج المتقطع. تتيح لنا Modal توفير نقاط نهاية استنتاج محددة للغاية للنماذج عبر الـ webhooks الخاصة بها، مما يوفر أداءً بمستوى الإنتاج دون حاجة العميل لإدارة أي بنية تحتية. هذا يمكنهم من تشغيل الذكاء الاصطناعي عملياً دون نفقات رأسمالية مقدمة أو أعباء تشغيلية مستمرة. هذه هي الطريقة التي ننقلهم بها من مجرد عرض تجريبي (demo) إلى خدمة فعلية ذات قيمة منشورة في بيئة الإنتاج.
- ▸مهام تعلم الآلة المجدولة (Scheduled ML Jobs): مهام مثل المراقبة اليومية لأداء النموذج، أو الكشف الأسبوعي عن انحراف البيانات (data drift)، أو فحوصات إعادة التدريب الدورية تناسب Modal تماماً. هذه مهام مجدولة، وغالباً ما تكون قصيرة المدة ومسرعة بواسطة الـ GPU، ولا تبرر تشغيل خادم بشكل مستمر. نقوم بتعريفها كدوال في Modal، ونقوم بجدولتها، وننسى أمر البنية التحتية تماماً.
مثال عملي: خط معالجة تضمين يتم تفعيله عبر S3
دعونا نلقي نظرة على مثال مبسط لكيفية إعداد خط معالجة تضمين (embedding pipeline) على Modal. تقوم هذه الدالة بمعالجة المستندات الجديدة المرفوعة إلى S3 bucket، وتوليد التضمينات باستخدام الـ GPU، وتخزينها في Qdrant.
import modal
import boto3
from qdrant_client import QdrantClient, models
from qdrant_client.http.models import Distance, VectorParams
from sentence_transformers import SentenceTransformer
import os
# Define a Modal Stub for our application
stub = modal.Stub(name="s3-embedding-pipeline")
# Define the image for our Modal functions
# We'll use a slim Debian base and install our Python dependencies.
# The `force_build` ensures a fresh build if requirements change.
embedding_image = (
modal.Image.debian_slim(python_version="3.10")
.pip_install(
"boto3",
"qdrant-client",
"sentence-transformers",
"torch", # Required for sentence-transformers with GPU
"transformers",
"accelerate", # For better GPU utilization
)
.apt_install("git") # Needed by some models
)
# Define a Modal Secret for Qdrant API key and S3 credentials
# These would be configured in Modal's UI or CLI
qdrant_secret = modal.Secret.from_name("qdrant-api-key")
aws_secret = modal.Secret.from_name("aws-credentials")
# The GPU-accelerated embedding function
@stub.function(
image=embedding_image,
gpu="A10G", # Allocate an A10G GPU
secrets=[qdrant_secret, aws_secret],
timeout=600, # 10 minutes timeout
memory=1024 # 1GB memory
)
def process_document_for_embedding(s3_uri: str):
print(f"Processing document from S3: {s3_uri}")
# Initialize S3 client
s3_client = boto3.client(
's3',
aws_access_key_id=os.environ["AWS_ACCESS_KEY_ID"],
aws_secret_access_key=os.environ["AWS_SECRET_ACCESS_KEY"]
)
bucket_name = s3_uri.split('/')[2]
object_key = '/'.join(s3_uri.split('/')[3:])
# Download document from S3
try:
response = s3_client.get_object(Bucket=bucket_name, Key=object_key)
document_content = response['Body'].read().decode('utf-8')
print(f"Downloaded {len(document_content)} bytes from {s3_uri}")
except Exception as e:
print(f"Error downloading {s3_uri}: {e}")
return
# Initialize embedding model (runs on GPU automatically if available)
# Using a common, efficient model for demonstration
model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2")
print("Embedding model loaded.")
# Generate embeddings
embeddings = model.encode([document_content], convert_to_tensor=True)
embeddings_list = embeddings.tolist()[0] # Convert tensor to list for Qdrant
# Initialize Qdrant client
qdrant_client = QdrantClient(
url=os.environ["QDRANT_HOST"],
api_key=os.environ["QDRANT_API_KEY"],
)
collection_name = "document_embeddings"
# Ensure collection exists
try:
qdrant_client.recreate_collection(
collection_name=collection_name,
vectors_config=VectorParams(size=len(embeddings_list), distance=Distance.COSINE),
)
print(f"Collection '{collection_name}' recreated (or created).")
except Exception as e:
# If collection already exists and recreate is not desired, handle accordingly
print(f"Could not recreate collection, might already exist: {e}")
# Store embeddings in Qdrant
try:
qdrant_client.upsert(
collection_name=collection_name,
points=[
models.PointStruct(
id=hash(s3_uri) % (2**63 - 1), # Simple ID generation
vector=embeddings_list,
payload={"s3_uri": s3_uri, "text_preview": document_content[:200]}
)
]
)
print(f"Embeddings for {s3_uri} stored in Qdrant.")
except Exception as e:
print(f"Error storing embeddings for {s3_uri} in Qdrant: {e}")
# This local entrypoint simulates triggering the function
@stub.local_entrypoint()
def main():
# Example S3 URIs to process
s3_documents = [
"s3://my-document-bucket/path/to/doc1.txt",
"s3://my-document-bucket/path/to/doc2.pdf", # Assume PDF parsing happens before this step
"s3://my-document-bucket/path/to/doc3.md"
]
# Trigger the remote function for each document
# For large batches, consider using .remote_batch()
for doc_uri in s3_documents:
process_document_for_embedding.remote(doc_uri)
print("All documents queued for processing.")
يوضح هذا الكود البرمجي العديد من المزايا الرئيسية لـ Modal:
- ▸تخصيص الـ GPU التعريفي: كل ما يتطلبه الأمر لتحديد العتاد هو كتابة
gpu="A10G". - ▸إدارة الاعتماديات: يتولى
embedding_image.pip_install(...)تثبيت جميع حزم Python المطلوبة. - ▸تكامل الأسرار (Secrets): يضمن
secrets=[qdrant_secret, aws_secret]التعامل مع البيانات الحساسة بأمان. - ▸قابلية التوسع: استدعاء
process_document_for_embedding.remote()يضع المهمة تلقائياً في طابور الانتظار ويقوم بتوسيع المثيلات حسب الحاجة. إذا كان لدينا 100,000 وثيقة، فستقوم Modal بتشغيل عدة مثيلات A10G بالتوازي لمعالجتها، ثم تتقلص إلى الصفر عند الانتهاء.
مقارنة Modal بالبدائل: تحليل التكاليف
دعونا نقارن التكاليف لمهمة تضمين نموذجية لـ 100,000 وثيقة. لنفترض أن هذه المهمة تتطلب ساعة واحدة من الحوسبة النشطة على GPU من نوع A10G.
- ▸Modal: تكلف مثيلة A10G على Modal حوالي 0.54 دولار في الساعة (0.00015 دولار في الثانية). بالنسبة لساعة واحدة من الحوسبة، تكون التكلفة 0.54 دولار. هذه تكلفة حوسبة فعلية فقط؛ دون أي وقت خمول.
- ▸AWS EC2 (g5.xlarge - المكافئ لـ A10G): عند الطلب (on-demand)، تكلف مثيلة
g5.xlargeحوالي 1.006 دولار في الساعة.- ▸إذا تمكنت من تشغيلها وإيقافها بدقة متناهية لمدة ساعة واحدة بالضبط: 1.006 دولار.
- ▸بشكل أكثر واقعية، إذا تركت المثيلة تعمل ليوم كامل (8 ساعات) لتكون جاهزة فقط: 1.006 * 8 = 8.048 دولار.
- ▸إذا تركتها تعمل على مدار الساعة طوال أيام الأسبوع (24/7) لمدة شهر لمهام متقطعة: 1.006 * 24 * 30 = 724.32 دولار. هذا هو دين الذكاء الاصطناعي الذي نتحدث عنه.
- ▸RunPod: تقدم مثيلات A10G عند الطلب مقابل حوالي 0.49 إلى 0.69 دولار في الساعة. على الرغم من أنها أرخص من EC2، إلا أنك لا تزال تدير دورة حياة المثيلة، والتشغيل البارد (cold boots)، وإعداد البرمجيات. لمدة ساعة واحدة: حوالي 0.60 دولار. هذا أفضل من EC2، لكنه ليس حلاً كاملاً بدون خادم (serverless).
- ▸Replicate: منصة GPU بدون خادم، وغالباً ما تكون ذات مستوى أعلى لنماذج محددة. بالنسبة لنماذج التضمين المخصصة، قد لا تكون مرنة بما يكفي أو قد تكون أكثر تكلفة لكل استدعاء. بافتراض مكافئ لـ A10G، قد تكلف مهمة مدتها ساعة واحدة من 1.00 إلى 2.00 دولار اعتماداً على نموذج التسعير الخاص بهم (والذي يتضمن غالباً رسوماً إضافية لتبسيط واجهة برمجة التطبيقات).
- ▸AWS SageMaker: منصة تعلم آلة مدارة بالكامل. على الرغم من قوتها، إلا أنها تأتي بتعقيدها الخاص وهيكل تكاليفها الخاص. بالنسبة لمهمة تضمين مخصصة، فمن المحتمل أن تستخدم نقطة نهاية SageMaker أو مهمة معالجة (processing job). قد تكلف مثيلة
ml.g5.xlargeلمهمة معالجة حوالي 1.40 دولار في الساعة. غالباً ما تجعل الأعباء الإدارية للمنصة المدارة أكثر تكلفة من EC2 الخام لنفس الحوسبة، كما أنها تربطك بالمنصة (vendor lock-in). لمدة ساعة واحدة: حوالي 1.40+ دولار.
الخلاصة: بالنسبة لأعباء عمل الذكاء الاصطناعي المتقطعة والمفاجئة، فإن Modal هي بلا شك الحل الأكثر فعالية من حيث التكلفة والأبسط تشغيلياً.
