تشغيل الكود المُولَّد بالذكاء الاصطناعي بأمان: ما تعلمناه من Vercel Sandbox
جدول المحتويات
عن الكاتب

Ahmed Mahmoud
كاتب ومطوّر
مهندس برمجيات شغوف بتطوير الويب وتصميم تجربة المستخدم.
مؤسّس ديفيا · eng-ahmed.com ↗أهم النقاط
يشغّل Vercel Sandbox (حزمة `@vercel/sandbox`) الكود داخل جهاز افتراضي دقيق معزول (Firecracker microVM)، وليس داخل حاوية ضمن عملية تطبيقنا نفسها — فإذا تم اختراق الـ sandbox، لا يمكنه قراءة ذاكرة Vercel Function أو متغيرات البيئة الخاصة بنا.
الـ sandbox عابر بطبيعته: ننشئه، نشغّل الأوامر، نقرأ المخرجات، ثم نوقفه. لا توجد حالة دائمة بين عمليات التشغيل ما لم نحفظها بأنفسنا صراحةً.
تنفّذ الدالة `sandbox.runCommand()` عملية داخل الـ sandbox وتُعيد المخرجات القياسية (stdout) والأخطاء (stderr) ورمز الخروج؛ بينما تكشف `sandbox.domain(port)` خادمًا قيد التشغيل عبر رابط عام لمعاينة حية.
نلجأ إلى Sandbox في حالتين: عندما يكتب النموذج نصًا برمجيًا يحتاج إلى التشغيل وإعادة نتيجة، وفي ميزة موجهة للمستخدم مثل "شغّل هذا الكود" كمعاينة لمكوّن تم توليده بالذكاء الاصطناعي.
لا يُعد Sandbox الأداة المناسبة لمنطق البناء أو التكامل المستمر (CI) الموثوق الذي كتبناه نحن — فهذا مكانه خط أنابيب النشر نفسه. Sandbox مخصص للكود الذي لم نكتبه ولا نثق به.
لماذا لا يمكننا تشغيل الكود المُولَّد بالذكاء الاصطناعي داخل Vercel Function الخاصة بنا مباشرة؟
تشترك Vercel Function في العملية ونظام الملفات وبيئة التشغيل مع بقية تطبيقنا. تشغيل نص غير موثوق كأنه كود داخل نفس العملية — عبر `child_process.exec` أو، الأسوأ، عبر `eval` — يضع كل سر يمكن للدالة رؤيته، بما في ذلك مفاتيح الـ API وروابط قواعد البيانات، ضمن نطاق الخطر لأي شيء كتبه النموذج.
يمكن لمقتطف كود مُولَّد أن يقرأ متغيرات البيئة، أو يفتح اتصالاً صادرًا لتسريبها، أو ببساطة يستهلك المعالج بالكامل ويحرم كل طلب آخر تخدمه الدالة في نفس الوقت.
في Devya نتعامل مع أي كود لم نكتبه بأنفسنا باعتباره غير موثوق افتراضيًا، وهذا يشمل الكود الذي يولّده نموذج بناءً على طلب. الكود غير الموثوق يحتاج إلى حدود حوسبة خاصة به: نظام ملفات خاص، وسياق شبكة خاص، وقيود موارد يمكننا فرضها ثم التخلص منها.
ما الذي يعمل فعليًا خلف الكواليس في Vercel Sandbox؟
يوفّر Vercel Sandbox جهازًا افتراضيًا دقيقًا من نوع Firecracker لكل sandbox — وهي نفس تقنية الافتراضية التي تستخدمها AWS Lambda لعزل المستأجرين عن بعضهم البعض، وليست مجرد حاوية بمساحة أسماء (namespace) أو cgroup.
الفرق العملي يكمن في مسار الهروب المحتمل: الهروب من حاوية يعني تجاوز حدود مساحة أسماء داخل نواة (kernel) يشاركها الحمل مع جيرانه، بينما الهروب من جهاز افتراضي دقيق يعني إيجاد ثغرة على مستوى الـ hypervisor ضد نواة لا يستخدمها أي شيء آخر.
إنشاء واحد يتم بنداء واحد:
```ts import { Sandbox } from '@vercel/sandbox'; const sandbox = await Sandbox.create({ runtime: 'node22', timeout: 60_000, // بالمللي ثانية — الحد الأقصى قبل أن يوقفه Vercel قسرًا resources: { vcpus: 2 }, }); ```
يحدد `runtime` صورة النظام الأساسية، و`timeout` هو سقف صارم نضبطه حسب حالة الاستخدام، و`resources.vcpus` يتحكم في كمية المعالج المتاحة للجهاز الافتراضي. نضبط أقصر مهلة يمكن أن تتحملها الميزة بدلاً من إعادة استخدام قيمة افتراضية واحدة في كل مكان — بيئة تجربة كود مباشرة تحصل على ثوانٍ، ومهمة دفعية تحصل على دقائق.
كيف ننفّذ فعليًا مقتطف كود ولّده نموذج داخل sandbox؟
نكتب الكود المُولَّد إلى ملف داخل الـ sandbox، ثم نشغّله كعملية فرعية — مخرجات النموذج لا تمر أبدًا عبر `eval` أو دالة `Function` داخل دالتنا الخاصة، معزولة كانت أم لا.
```ts await sandbox.writeFiles([ { path: 'snippet.js', content: Buffer.from(generatedCode) }, ]); const result = await sandbox.runCommand({ cmd: 'node', args: ['snippet.js'], }); const stdout = await result.stdout(); const exitCode = result.exitCode; ```
تُعيد لنا `runCommand()` بالضبط ما كانت ستعيده عملية فرعية عادية: stdout وstderr ورمز الخروج. الفرق هو أين تم تشغيل تلك العملية فعليًا — داخل جهاز افتراضي قابل للتخلص منه بدلاً من بجانب أسرار تطبيقنا الحية.
هل يحتفظ Vercel Sandbox بالحالة بين عمليات التشغيل؟
لا. الـ sandboxes عابرة بالتصميم — كل استدعاء لـ `Sandbox.create()` ينشئ جهازًا افتراضيًا جديدًا بنظام ملفات نظيف، واستدعاء `sandbox.stop()`، أو بلوغ المهلة الزمنية، يزيله بالكامل، بما في ذلك أي شيء كُتب على القرص.
إذا احتاجت ميزة إلى تذكّر شيء بين عمليات التشغيل — محادثة مفسّر كود متعددة الأدوار مثلاً — يجب أن تعيش تلك الحالة خارج الـ sandbox: كتابة النتائج إلى قاعدة بيانات أو تخزين كائنات من داخل العملية المعزولة، أو حفظ ملخص صغير يعيد الطالب بناءه في sandbox جديد لاحقًا. نتعامل مع كل sandbox كحوسبة قابلة للتخلص منها، وليس كمكان لتخزين أي شيء.
هل يمكننا بث مخرجات sandbox إلى المتصفح أثناء تشغيله؟
نعم. تقبل `runCommand()` خيار `detached`، الذي يعيد مقبضًا (handle) يمكننا القراءة منه أثناء إنتاج المخرجات بدلاً من انتظار انتهاء الأمر بالكامل — وهو نفس النمط الذي نستخدمه لبث مخرجات النموذج رمزًا برمزًا، لكن هنا نبث stdout الخاص بالـ sandbox.
```ts const result = await sandbox.runCommand({ cmd: 'node', args: ['agent.js'], detached: true, }); for await (const chunk of result.stdout) { controller.enqueue(chunk); // إعادة توجيه إلى استجابة ReadableStream } ```
لا شيء من هذا يحتاج إلى بيئة edge — بث مخرجات sandbox عبر Vercel Function يعمل على بيئة Node.js الافتراضية دون أي إعداد إضافي، تمامًا مثل بث استجابة نموذج ذكاء اصطناعي.
ما الفرق الفعلي بين Sandbox و child_process داخل Function؟
child_process داخل Function
حدود العزل: نفس العملية والحاوية الخاصة بالتطبيق. نظام الملفات: يشارك نظام ملفات الدالة. حركة الشبكة الصادرة: تشارك سياق شبكة الدالة. نطاق الضرر عند التعطل أو التعليق: قد يُسقط الدالة بأكملها. مناسب لـ: استدعاءات عمليات فرعية موثوقة كتبناها نحن أنفسنا.
Vercel Sandbox
حدود العزل: جهاز افتراضي دقيق (Firecracker) منفصل. نظام الملفات: معزول، يُمسح عند الإيقاف. حركة الشبكة الصادرة: مساحة شبكة خاصة به. نطاق الضرر عند التعطل أو التعليق: محصور داخل الـ sandbox. مناسب لـ: كود غير موثوق، أو مولَّد بواسطة نموذج، أو مُقدَّم من مستخدم.
متى يجب ألا نلجأ إلى Vercel Sandbox؟
ليس للكود الذي نثق به وكتبناه بأنفسنا — إنشاء جهاز افتراضي دقيق يضيف زمن انتقال حقيقيًا مقارنة بعملية فرعية داخل دالة جاهزة بالفعل، ولا توجد فائدة عزل تستحق دفع هذه التكلفة.
وليس لخط أنابيب تكامل مستمر أو بناء كامل أيضًا؛ فذلك مكانه خطوة البناء الخاصة بالمنصة، لا sandbox وقت التشغيل. نلجأ إلى Sandbox تحديدًا عندما يكون الكود قيد التنفيذ إما مولَّدًا بواسطة نموذج أو مقدَّمًا من مستخدم لا نثق به، وعندما تحتاج الميزة فعليًا إلى تشغيل شيء ما — عملية فرعية، نظام ملفات، لغة برمجة عشوائية — بدلاً من مجرد استدعاء API لنموذج ذكاء اصطناعي وإعادة نص.
الأسئلة الشائعة
س: هل Vercel Sandbox هو نفسه Vercel Function؟
ج: لا. تشغّل Vercel Function الكود المنشور الخاص بك ضمن بيئة تطبيقك وسياق شبكته. أما Sandbox فهو جهاز افتراضي دقيق منفصل وعابر يتم توفيره وقت التشغيل خصيصًا لتنفيذ كود لا تثق به، بنظام ملفات خاص به ودون وصول إلى أسرار دالتك.
س: ما تقنية العزل التي يستخدمها Vercel Sandbox؟
ج: أجهزة Firecracker الافتراضية الدقيقة — يحصل كل sandbox على نواة خاصة به، وليس مجرد مساحة أسماء لحاوية، وهي نفس فئة العزل التي تستخدمها AWS Lambda بين المستأجرين.
س: هل يمكن لـ sandbox الوصول إلى متغيرات البيئة أو قاعدة البيانات لدينا؟
ج: لا، إلا إذا مررناها صراحةً. يبدأ الـ sandbox ببيئة نظيفة، ونحن لا نُدخل إلا بيانات اعتماد محدودة النطاق وقصيرة الأجل إلى sandbox على وشك تشغيل كود غير موثوق.
س: إلى متى يمكن أن يعمل sandbox؟
ج: تضبط مهلة زمنية عند إنشائه، ويتم إيقافه قسرًا بمجرد بلوغ تلك المهلة. نضبط أقصر مهلة يمكن أن تتحملها الميزة — ثوانٍ لبيئة "شغّل هذا الكود" التفاعلية، ومدة أطول للمهام الدفعية.
س: هل يجب أن نستخدم Sandbox لتشغيل سكربتات البناء الخاصة بنا؟
ج: لا — فهذا دور خط أنابيب النشر. يستحق Sandbox تكلفته، من زمن توفير الجهاز الافتراضي وعدم وجود حالة دائمة، تحديدًا مع الكود الذي لم نكتبه: مخرجات نموذج، مقتطفات مقدَّمة من مستخدمين، أي حالة يكون فيها حد العزل هو صلب الأمر.
قراءات إضافية
هندسة الذكاء الاصطناعي
بناء خادم MCP بعيد في Next.js باستخدام mcp-handler 2: الأدوات وOAuth ومواصفة 2026-07-28 عديمة الحالة
أصبحنا نبني خوادم MCP البعيدة كـ Route Handler واحد في Next.js باستخدام mcp-handler 2 ومواصفة MCP عديمة الحالة بإصدار 2026-07-28. نشرح هنا كيف نركّبها، وكيف نصمّم أدوات يستدعيها النموذج بشكل صحيح، وكيف نؤمّنها بـ OAuth وبيانات RFC 9728، وكيف نختبرها قبل اتصال أي عميل حقيقي.
هندسة الذكاء الاصطناعي
لماذا لا يوقف موجّه نظام أفضل حقنَ الأوامر: ملاحظاتنا الميدانية حول الثلاثية القاتلة والأدوات محددة الصلاحيات والمخرجات غير الموثوقة
أثناء بناء مساعد لصندوق الدعم يملك صلاحية استدعاء الأدوات، أدخل فريقنا تذكرة اختبار تحتوي على تعليمات مخفية، فنفّذ النموذج بعضها. هذه ملاحظاتنا الميدانية حول سبب كون حقن الأوامر مشكلة معمارية لا مشكلة صياغة، وكيف تكشف الثلاثية القاتلة ميزات الذكاء الاصطناعي الخطرة، وكيف نقيّد الأدوات بصلاحيات المستخدم المسجّل، ولماذا تحتاج الآثار الجانبية إلى تأكيد بشري، وكيف يتحول عرض Markdown إلى قناة لتسريب البيانات.