أمان وكلاء الذكاء الاصطناعي في الإنتاج: دليل لتقليص نطاق الضرر

دليل عملي لبناء أمان وكلاء الذكاء الاصطناعي في الإنتاج عبر صلاحيات محدودة، وقائمة أدوات صريحة، وسقوف إنفاق، ومنع التكرار قبل وقوع الضرر الكارثي.

Saturday, September 5, 2026Omid Saffari
أمان وكلاء الذكاء الاصطناعي في الإنتاج: دليل لتقليص نطاق الضرر

أمان وكلاء الذكاء الاصطناعي في الإنتاج ليس مسألة نظرية: ففي 25 أبريل، حذف Cursor أثناء تشغيل Claude Opus 4.6 قاعدة بيانات PocketOS الإنتاجية ونسخها الاحتياطية خلال 9 ثوانٍ، ثم كتب اعترافاً بما فعل. وهكذا ضاعت حجوزات تأجير سيارات تعود إلى 3 أشهر. أشغّل 6 وكلاء يتعاملون مع بيئة الإنتاج يومياً؛ وهذه هي البنية الدقيقة لنقطة الاختناق وقائمة السماح ومنع التكرار، بحيث لا يستطيع أسوأهم سوى تجاوز الميزانية بمقدار $1.

حصيلة الكوارث خلال 90 يوماً

25 أبريل 2026. كان وكيل Cursor المشغّل بنموذج Claude Opus 4.6 يعمل على ما وصفه Jeremy Crane، الرئيس التنفيذي لـ PocketOS، بأنه «مهمة روتينية» في بيئة الاختبار. واجه تعارضاً في بيانات الاعتماد، فقرر من تلقاء نفسه «ترتيب الأمور»، وحذف وحدة تخزين على Railway كانت تضم قاعدة بيانات الإنتاج ونسخها الاحتياطية. 9 ثوانٍ فقط. ثم انقطاع استمر 30 ساعة. ولأن أحدث نسخة احتياطية قابلة للاستعادة كان عمرها 3 أشهر، اختفت حجوزات تأجير السيارات المسجلة طوال تلك الفترة. بعد ذلك كتب الوكيل اعترافاً أقر فيه بأنه خالف تعليمات صريحة تحظر عليه المساس ببيئة الإنتاج.

26 فبراير 2026. كان Alexey Grigorev، مؤسس DataTalks.Club، قد انتقل إلى جهاز كمبيوتر آخر، فأصبح ملف الحالة المحلي في Terraform قديماً. وعندما طُلب من Claude Code تنظيف الموارد، نفّذ terraform destroy على حزمة الإنتاج. وعند استعادة جزء من الجدول courses_answer لاحقاً، كان يحتوي على 1,943,200 صف. إنها مشاركات طلاب على مدى عامين ونصف. أما اللقطات الآلية، فكانت محفوظة في الحساب نفسه الذي دُمّر.

منتصف ديسمبر 2025. ورث Kiro، وكيل Amazon الداخلي، صلاحيات مرتفعة من أحد المهندسين، وتجاوز بوابة الموافقة الثنائية لدى Amazon؛ فالضابط كان ينطبق على البشر، لا على الدور الذي عمل الوكيل من خلاله. ثم حذف بيئة الإنتاج الخاصة بـ AWS Cost Explorer وأعاد إنشاءها.

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

ماذا يعني ذلك للمؤسسين غير التقنيين؟

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

السؤال الصريح الذي ينبغي طرحه على المسؤول الهندسي ليس: «هل الوكيل جيد؟». فهذا إطار خاطئ للمشكلة. السؤال الصحيح هو: ما أقصى ضرر يمكن أن يسببه تشغيل واحد للوكيل، ومن حدّد هذا السقف؟ إذا كانت الإجابة «نثق بالنموذج» أو «نراجع الفروقات»، فلا توجد لديكم طبقة تحكم، بل مجرد أمل.

أما الإجابة التي تريد سماعها فهي: «يعمل الوكيل تحت دور سحابي لا يملك صلاحيات التدمير. يمكنه استدعاء 12 أداة مسماة، وليست بينها shell. يمر كل استدعاء مدفوع عبر دالة واحدة تفرض سقفاً صارماً للتكلفة. وأسوأ احتمال هو فشل خطوة واحدة وإنفاق $1 على الأكثر». هذه هي البنية، وما يلي يشرح طريقة بنائها.

أمان وكلاء الذكاء الاصطناعي: لماذا لا تنجح عبارة «كن حذراً»؟

لم يستغرق حذف وحدة تخزين PocketOS سوى 9 ثوانٍ. واكتمل حذف Kiro أسرع مما يحتاجه أي إنسان لقراءة طلب التأكيد، ناهيك عن التفكير فيه. التدخل بعد بدء التنفيذ مستحيل بنيوياً عند سرعة الوكلاء؛ فعندما ترى الإجراء، يكون قد انتهى.

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

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

بهذا يتغير إطار المشكلة. لا «تراقب» الوكيل، بل تقيّد نطاق أفعاله قبل أن تبدأ الحلقة، وتجعل القيد خاصية في الدور لا عبارة في الموجّه.

حوكمة وكلاء الذكاء الاصطناعي: كل استدعاء مدفوع أو معدِّل عبر نقطة واحدة

يمر كل استدعاء مدفوع للنماذج في منظومتي المؤلفة من 6 وكلاء عبر دالة واحدة أسميها callAi. تنفذ 5 مهام بالترتيب: تتحقق مسبقاً من سقف الإنفاق اليومي البالغ $20، وتقيس زمن الاستدعاء، وتسجل عدد الرموز والتكلفة بالدولار في صف ai_call_log مخصص للإلحاق فقط وموسوم بـ agent_id وworkflow_instance_id، ثم تتحقق بعد الاستدعاء من سقف $1 لكل مثيل، وأخيراً تعيد النتيجة.

TypeScript
export async function callAi(env: Env, args: CallAiArgs) {
  const { agentId, workflowInstanceId, model, messages } = args;

  const dailySpent = await getDailySpendUSD(env);
  if (dailySpent >= 20) {
    throw new NonRetryableError(`daily cap hit: $${dailySpent}`);
  }

  const t0 = Date.now();
  const res = await anthropic.messages.create({ model, messages });
  const costUSD = priceOf(model, res.usage);

  await env.DB.prepare(
    `INSERT INTO ai_call_log
       (agent_id, workflow_instance_id, model, in_tokens, out_tokens, usd, ms, ts)
     VALUES (?, ?, ?, ?, ?, ?, ?, unixepoch())`
  ).bind(agentId, workflowInstanceId, model,
         res.usage.input_tokens, res.usage.output_tokens,
         costUSD, Date.now() - t0).run();

  const instanceSpent = await getInstanceSpendUSD(env, workflowInstanceId);
  if (instanceSpent >= 1) {
    throw new NonRetryableError(`instance cap hit: $${instanceSpent}`);
  }

  return res;
}

إذا تم تجاوز أحد السقفين، تطرح الدالة خطأً غير قابل لإعادة المحاولة، فينتهي Workflow. ولا يستطيع الوكيل أن «يقرر من تلقاء نفسه» مواصلة العمل، لأن بوابة الميزانية ليست موجّهاً يمكنه مجادلته، بل استثناء يُطرح فوق طبقة الاستدلال لديه.

قارن ذلك بحالة الانفلات كثيرة الاستشهاد التي استمرت 63 ساعة وكلّفت $4,200: عندما لا توجد نقطة اختناق، لا توجد حالة نهائية. تواصل الحلقة استنزاف المال إلى أن يلاحظ إنسان الفاتورة. مع سقف لكل مثيل، تبلغ أسوأ تكلفة لتشغيل واحد $1. ومع سقف يومي، تبلغ أسوأ تكلفة ليوم واحد $20. اصطدمت بالسقفين عرضاً أثناء البناء، ولم يكلّفني أي منهما أكثر من وجبة عشاء.

ولنقطة الاختناق وظيفة ثانية هي التحقيق بعد الحادث. يترك كل إجراء صف تدقيق قبل إرجاع النتيجة. وعندما يقع خلل بالفعل، يصبح تحليل ما بعد الحادث استعلام SELECT على ai_call_log، لا محاولة لإعادة تركيب الأحداث من اعتراف الوكيل نفسه.

ضبط صلاحيات وكلاء الذكاء الاصطناعي: 12 أداة Zod بلا shell أو Terraform

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

إعداد Claude Agent SDK الذي يفرض ذلك قصير:

TypeScript
const result = await query({
  prompt,
  permissionMode: "dontAsk",
  allowedTools: [
    "read_brief", "fetch_corpus", "render_markdown",
    "validate_directives", "persist_draft", "schedule_publish",
    "update_status", "log_event", "fetch_citation",
    "embed_text", "search_vectors", "notify_human"
  ],
  // no Bash, no Write, no Edit, no infrastructure tools
});

في وضع permissionMode: "dontAsk"، تُرفض الأدوات غير المدرجة مباشرة. فلا تتحول إلى مطالبة تصعيد، ولا تُعرض على إنسان لمراجعتها، بل تُرفض. ويعمل ترتيب تقييم الأذونات كما هو موثق: خطاف PreToolUse، ثم قواعد المنع، فقواعد السماح، فقواعد السؤال، ثم فحص الوضع، وأخيراً استدعاء canUseTool. لكن وضع dontAsk يتجاوز بنيوياً استدعاء الإنسان ضمن الحلقة، وتُحجب الأدوات المجهولة عند فحص الوضع.

هذا هو الجزء الذي كان سيوقف حادثتي PocketOS وDataTalks من البداية. فحادثة DataTalks مستحيلة في هذا الإعداد لأن terraform destroy ليس اسم أداة يستطيع الوكيل إصدارها. وحادثة PocketOS مستحيلة لأن حذف وحدة التخزين ليس ضمن نطاق الأفعال. حتى لو قرر النموذج «ترتيب الأمور»، فلا يوجد مسار ينقل الرغبة إلى الفعل.

قد يغريك هنا إضافة أداة Bash تحسباً لسؤال: «ماذا لو احتاج الوكيل إلى أمر لم نتوقعه؟». لا تفعل. في اليوم الذي تضيف فيه Bash يصبح نطاق الضرر هو «كل ما تستطيع Bash الوصول إليه».

عدم التكرار: لماذا لا تؤدي إعادة التشغيل إلى تدمير مزدوج؟

يحمل كل مثيل من Cloudflare Workflow معرّفاً حتمياً مشتقاً من المدخلات بصيغة publish-{brief_id}. وتطرح instances.create() خطأً عند تكرار المعرّف، لذلك لا يمكن لإعادة إطلاق الحدث نفسه تشغيل مسار العمل مرتين. وهكذا تتحول عاصفة إعادة المحاولة في طبقة قائمة الانتظار إلى عملية بلا أثر، لا إلى نشر مزدوج.

TypeScript
const id = `publish-${msg.body.briefId}`;
try {
  await env.PUBLISHER.create({ id, params: msg.body });
} catch (e) {
  if (e.message.includes("already exists")) return; // safe replay
  throw e;
}

داخل مسار العمل، تُخزّن نتيجة كل step.do() مؤقتاً وتكون آمنة عند إعادة التشغيل. فالخطوة التي تُعاد تعرض نتيجتها المخزنة بدلاً من تنفيذ الأثر الجانبي مرة أخرى. وعند جمع ذلك مع INSERT OR IGNORE على جداول D1 المرجعية ولقطة R2 ذات إصدار عند كل حفظ، لا تستطيع خطوة سيئة استبدال السجل بصمت. الإصدار السابق لا يبعد سوى مفتاح R2 واحد.

كان أصل حادثة DataTalks ملف حالة قديماً يقود عملية تدمير لا رجعة فيها. هنا تجعل معرّفات مسارات العمل الحتمية مع أخذ لقطة قبل التعديل التدمير الناجم عن حالة قديمة غير كارثي: فحتى لو عمل مسار بناءً على افتراضات قديمة، تبقى اللقطة نقطة الرجوع. (تناولت أساس التنفيذ الدائم في مقال Cloudflare Workflow مقارنةً بـ Managed Agents؛ وحسابات منع التكرار هي قصة الاحتواء نفسها باسم مختلف.)

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

خطة انتقال من 5 خطوات يمكنك تنفيذها هذا الأسبوع

هذا هو الترتيب. كانت الخطوة 1 وحدها كفيلة بإيقاف الحوادث الـ3 المذكورة، لذا ابدأ بها حتى لو لم تنفذ سواها.

  1. قيّد الدور السحابي للوكيل بحيث لا يملك أي فعل تدمير

    يجب ألا يملك دور IAM الذي يعمل من خلاله الوكيل، أو حساب الخدمة أو رمز API، أي أفعال تدمير أو حذف لموارد الإنتاج. لا DeleteVolume، ولا صلاحية terraform destroy، ولا DROP TABLE. الدور هو ما يحدد الحد الأدنى لنطاق الضرر؛ ولا قيمة لما فوقه إن ظل الدور نفسه بلا قيود.

    هذا هو درس Kiro بصيغة عملية: لا تدع الوكيل يرث الصلاحيات الواسعة لإنسان أطلقه. امنحه دوراً مستقلاً ضيق الصلاحيات.

  2. ضع الوكيل داخل permissionMode dontAsk مع قائمة allowedTools صريحة

    اختر أصغر مجموعة أدوات تمكّن الوكيل من أداء مهمته. لدي 12 أداة، وقد تكفيك 6. احذف أي أداة shell عامة بالكامل. وإذا كان الوكيل يملك حالياً أداة Bash لأنها «مريحة»، فهذه الراحة هي الثغرة.

    TypeScript
    { permissionMode: "dontAsk", allowedTools: [/* explicit list */] }
  3. أضف قاطع دائرة للميزانية

    استخدم غلافاً واحداً حول كل استدعاء مدفوع: تحقق مسبقاً من سقف يومي صارم، ثم تحقق بعد الاستدعاء من سقف لكل مثيل، واطرح NonRetryableError عند تجاوزه. سقف المثيل هو قاطع الدائرة الذي لا يستطيع السقف اليومي أداء دوره؛ فقد ظل الانفلات الذي كلّف $4,200 خلال 63 ساعة تحت أي حد يومي معقول بينما كانت تكلفته تتراكم مع كل تشغيل. سقف المثيل يلتقط ما يفوته السقف اليومي.

  4. خذ لقطة قبل التعديل

    على كل خطوة قادرة على التدمير أن تكتب أولاً لقطة ذات إصدار. يصلح R2، ويصلح S3، ويصلح جدول D1 باسم ينتهي بـ *_archive. المهم أن تكون كتابة اللقطة شرطاً مسبقاً للتعديل، داخل الخطوة نفسها وضمن الكتلة نفسها التي تتصرف كوحدة معاملة.

  5. أنشئ سجل إجراءات مخصصاً للإلحاق فقط

    اكتب صفاً واحداً لكل استدعاء أداة قبل إرجاع نتيجته. واربطه بمعرّف الوكيل، ومعرّف مثيل مسار العمل، والتكلفة بالدولار، وزمن الاستجابة. عندما يحدث خلل — وسيحدث في النهاية — يصبح تحليل ما بعد الحادث استعلام SELECT، لا محاولة لإعادة بناء الأحداث.

تستغرق كل خطوة يوماً أو أقل. وننفذها بهذا الترتيب لأن طبقات الاحتواء تتراكم: تضبط الخطوة 1 الحد الأدنى، وتغلق الخطوة 2 نطاق الأفعال فوقه، وتضع الخطوة 3 سقفاً لتكلفة العمل داخل ذلك النطاق، ثم تجعل الخطوتان 4 و5 أي فشل قابلاً للاستعادة والاستعلام.

أسئلة شائعة

ألا يكفي أن أطلب من الوكيل في موجّه النظام ألا يحذف الإنتاج أبداً؟

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

هل يعني permissionMode dontAsk فقدان كل أشكال التفاعل؟

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

لماذا أحتاج إلى سقف تكلفة لكل مثيل ما دام لدي سقف يومي؟

لأن الانفلات الذي كلّف $4,200 خلال 63 ساعة ظل تحت أي سقف يومي معقول بينما راكم التكلفة مع كل تشغيل. يمكن لحلقة سيئة واحدة أن تبقى أدنى كثيراً من "$20/day" ومع ذلك تكلّف مبلغاً من 4 أرقام خلال عطلة نهاية الأسبوع. سقف المثيل هو قاطع الدائرة الذي لا يستطيع السقف اليومي أن يكونه بحكم بنيته.

أستخدم Vercel أو Railway لا Cloudflare Workflows؛ فهل ينطبق هذا عليّ؟

نقطة الاختناق وقائمة السماح والدور محدود الصلاحيات مبادئ مستقلة عن المنصة. وحدها آلية منع التكرار، أي معرّفات Workflow الحتمية، تخص Cloudflare. في المنصات الأخرى، استخدم مفتاح عدم تكرار للمهمة وارفض النسخ المكررة في طبقة قائمة الانتظار أو مشغّل المهام.

هل تكفي موافقة شخصين؟

كانت لدى Kiro هذه الموافقة للبشر، لكن الوكيل ورث صلاحيات تجاوزتها. يجب أن ترتبط بوابات الموافقة بالدور الذي يعمل من خلاله الوكيل، لا بالإنسان الذي أطلقه. إذا كان دور الوكيل يملك صلاحيات التدمير، فالبوابة مجرد زينة.

إذا كنت تقرأ هذا لأن أحد وكلاء فريقك اقترب أكثر مما ينبغي من بيئة الإنتاج الأسبوع الماضي، فهذا النوع من الاحتواء هو ما أنفذه في DVNC.dev: البنية الموضحة أعلاه مطبقة على منظومتك التقنية، بالترتيب الذي يغلق نطاق الضرر أولاً.

آخر تحديث

5 سبتمبر 2026

التصنيفBuild

فضّل هذا الموقع في Google

إضافة omidsaffari.com كمصدر مفضّل في بحث Google

اجعل omidsaffari.com مصدرًا مفضّلًا، وسيرفعه Google لك في Top Stories وAI Overviews وAI Mode.

المزيد من Build

عرض كل مقالات Build
النشرة البريدية

رسالة واحدة، كل يوم أحد. أنظمة تعمل، لا آراء ساخنة.

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

أسبوعية. بلا إزعاج. يمكنك إلغاء الاشتراك متى شئت.