خطأ

الوكيل يقول «اكتمل» والميزة لا تعمل: لماذا يحدث ذلك وكيف تتحقق من عمل وكيل الذكاء الاصطناعي

آخر تحديث: · Skillaria.Top

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

لماذا يحدث ذلك

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

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

قصة نموذجية لـ«اكتمل» كاذبة

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

لكن ما حدث فعلًا:

  • فشل الترحيل في منتصفه، فبقي المخطط غير متسق؛
  • قالب البريد موجود، لكن خدمة البريد (SMTP) غير مُهيّأة في البيئة، فلا يُرسل البريد؛
  • سيناريو «المستخدم ينقر الرابط ← يدخل كلمة مرور جديدة ← يسجل الدخول» لم يُشغَّل قط.

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

لماذا لا يثبت الاختبار الأخضر أن الميزة تعمل

اختبار الوحدة يفحص وحدة بمعزل عن غيرها: الاعتماديات مستبدلة بمحاكاة، والبيئة الخارجية مفصولة. وهذا يمنح السرعة والدقة، لكنه يخلق عمى منهجيًا. والعمى ليس خللًا في الاختبارات، بل هو من تصميمها. فالاختبارات الوحدوية الخضراء تثبت أن كل جزء يعمل وحده، لا أن الأجزاء تعمل معًا.

هناك أربعة أنواع من العيوب لا يمكن لاختبار الوحدة اكتشافها مطلقًا:

نوع العيبمثاللماذا لا يلتقطه اختبار الوحدة
عدم تطابق الواجهاتمكوّن يُرجع مسارًا نسبيًا، ومكوّن آخر يتوقع مسارًا مطلقًاكل مكوّن يُختبَر بمحاكاته الخاصة، والمحاكاة تقبل أي صيغة
انتشار الحالةتغيّر مخطط قاعدة البيانات، لكن ذاكرة التخزين المؤقت لـ ORM ما زالت قديمةلا توجد في اختبار الوحدة قاعدة بيانات حقيقية ولا ذاكرة تخزين مؤقت
دورة حياة المواردلا تُحرَّر مقابض الملفات والاتصالات، فتتسرب الذاكرة مع الملفات الكبيرةالاختبار يعمل على بيانات صغيرة وينتهي قبل أن تنفد الموارد
البيئة والإعداداتمتغير بيئة مفقود، أو SMTP غير مُهيّأ، أو ترحيل لم يُطبَّقالاختبار لا يشغّل التطبيق بالإعدادات الحقيقية

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

قياس المشكلة: verification gap

قبل الإصلاح، احسب. خذ آخر 10 مهام أغلقها الوكيل. وعن كل مهمة أجب عن سؤالين: هل قال الوكيل «اكتمل»؟ وهل أكّد ذلك تحقق مستقل (شغّلت السيناريو بنفسك، أو اختبار E2E أخضر)؟ نسبة التناقض هي فجوة التحقق (verification gap) لديك.

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

التحقق الحقيقي: ثلاث طبقات

تشبيه مفيد هو ورشة صيانة السيارات. تمر السيارة بثلاث محطات، ولا يجوز تخطي أي منها.

  1. التشخيص (ثوانٍ): البنية النحوية، والأنواع، والمدقق. يلتقط الأخطاء المطبعية والأخطاء الواضحة.
  2. الاختبار على الحامل (دقائق): اختبارات الوحدة والتكامل، وتشغيل التطبيق. المحرك يعمل، لكن السيارة ما زالت في مكانها.
  3. قيادة الاختبار: سيناريو من طرف إلى طرف كما يسلكه المستخدم فعلًا، عبر API أو المتصفح أو CLI، مع قاعدة بيانات وإعدادات حقيقية.

قاعدة الترتيب: لا تنطلق في قيادة الاختبار ما دامت أخطاء التشخيص قائمة، ولا تُخرج السيارة من الورشة دون اجتياز قيادة الاختبار. اجتياز اختبارات الوحدة الخضراء يعني أنك اجتزت المحطة الثانية، لا الثالثة.

يمكن تجميع كل ذلك في مدخل واحد:

# Makefile
check:
	ruff check .
	mypy app
	pytest tests/unit tests/integration

e2e:
	docker compose up -d db mailhog
	alembic upgrade head
	pytest tests/e2e

verify: check e2e

هنا يشغّل E2E قاعدة بيانات حقيقية وخادم بريد اختبار، ويطبّق الترحيلات، ويسير في السيناريو كاملًا. فيظهر فشل الترحيل أو عدم إعداد البريد فورًا، لا عند العميل.

تعريف الإنجاز في AGENTS.md

لا ينبغي أن يقرر الوكيل بنفسه معنى «الإنجاز». يُكتب المعيار في ملف التعليمات بصيغة قابلة للتحقق:

## Definition of Done (تعريف الإنجاز)
الميزة تعتبر مكتملة فقط إذا تحققت كل البنود التالية:
1. ينتهي make check برمز الخروج 0
2. ينتهي make e2e برمز الخروج 0، ويوجد سيناريو الميزة في tests/e2e
3. تُطبَّق الترحيلات على قاعدة بيانات نظيفة، ويمكن التراجع عنها
4. المتغيرات البيئية الجديدة موثّقة في .env.example
5. سُجّل في PROGRESS.md ما اكتمل وكيف تم التحقق منه

ولا تُعد الأمور التالية دليلًا على الإنجاز:
- «الكود مكتوب ويبدو صحيحًا»
- «اختبارات الوحدة تنجح»
- «يعمل عندي محليًا»

ذكر «ما لا يُعدّ إنجازًا» صراحةً يعطي نتائج أفضل مما تتوقع: لم يعد الوكيل يتوقف عند أول نتيجة خضراء.

من يحق له وضع علامة «passing»

الخطوة التالية هي نزع حالة الإنجاز من يد الوكيل تمامًا. تُحفظ الميزات في قائمة (مثل feature_list.json) لكل ميزة فيها «السلوك» و«أمر التحقق» و«الحالة». ولا يمكن تحويل الميزة إلى passing إلا عبر سكربت: يشغّل السكربت make check المشترك، ثم أمر التحقق الخاص بالميزة، وعند النجاح فقط يسجّل الحالة مع هاش الالتزام (commit) كدليل. وإذا فشل، تبقى الميزة active، ولا توجد حالة «قريب من النجاح».

{
  "id": "F04",
  "behavior": "يتلقى المستخدم بريدًا فيه رابط ويغيّر كلمة المرور",
  "verification": "pytest tests/e2e/test_password_reset.py",
  "state": "active"
}

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

المُقيِّم المستقل

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

  • المخطِّط يحوّل المتطلب إلى ميزات مع معايير قبول، ولا يكتب شيفرة.
  • المولِّد ينفّذ ميزة واحدة ويشغّل الاختبارات، لكنه لا يقرر إن كانت مكتملة.
  • المُقيِّم يصدر الحكم وفق قائمة المعايير.

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

رسائل الخطأ التي يستطيع الوكيل إصلاحها بنفسه

لا تكون الفحوص مفيدة إلا إذا قادت مخرجاتها إلى الإصلاح. عبارة «Test failed» أو AssertionError مجردة لا تعني شيئًا للوكيل. الرسالة الجيدة تذكر ما الذي تعطّل، ولماذا توجد هذه القاعدة، وكيف يُصلَح:

ERROR arch-check: app/services/refund.py يستورد app/db/session.py
WHY: طبقة الخدمات لا تصل إلى قاعدة البيانات مباشرة، بل عبر المستودعات فقط
FIX: استخدم RefundRepository من app/repositories/refund.py

هذا الشكل يحوّل الفحص من منع إلى تلميح: يصلح الوكيل المخالفة دون تدخلك.

حيث ما زال التحقق اليدوي ضروريًا

الأتمتة لا تُلغي الإنسان، لكنها تقلّص دوره. التحقق اليدوي ضروري حيث يصعب تحويل المعيار إلى صيغة رسمية: المظهر البصري للواجهة، والنصوص، وسهولة السيناريو. ويكون وفق قائمة معايير القبول، لا بالنقر العشوائي. وإذا كتبت للوكيل التعليق نفسه للمرة الثانية، فهذه إشارة إلى أن عليك تحويله إلى فحص آلي: اختبار، أو قاعدة مدقق، أو حارس grep في make check.

كيف يغيّر E2E سلوك الوكيل قبل التشغيل

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

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

أخطاء شائعة

  • أن تطلب من الوكيل «تحقق بدقة أكبر».السياق لم يتغير، فسيبقى التقييم مرتفعًا.
  • اعتبار تغطية 100% ضمانًا.تغطية الوحدات لا تغطي الترابط بين الوحدات.
  • إعادة الهيكلة قبل تأكيد الميزة.الترتيب صارم: الصحة ← الأداء ← الأسلوب.
  • محاكاة أكثر الأجزاء هشاشة في E2E.يجب تشغيل قاعدة البيانات والترحيلات والإعدادات بشكل حقيقي في اختبار E2E؛ ويمكن استبدال واجهات برمجة التطبيقات الخارجية المدفوعة فقط بخادم اختبار.

الخلاصة

عندما يقول الوكيل «اكتمل» والميزة لا تعمل، فالمشكلة في الغالب في إجراءات التحقق لا في النموذج. احسب فجوة التحقق، واكتب تعريف الإنجاز في AGENTS.md، واجعل E2E إلزاميًا، وسلّم حكم الإنجاز للسكربت، وأضف مُقيِّمًا مستقلًا. وقد شرحنا هذا النهج عمليًا في دورة هندسة الهارنس، على مشروع حقيقي.

الأسئلة الشائعة

هل يمكن جعل الوكيل يشغّل اختبارات E2E بنفسه؟

نعم، إذا كتبت الأمر في تعريف الإنجاز في AGENTS.md كبند إلزامي، ومُنح الوكيل الأدوات اللازمة. لكن الأكثر موثوقية أن تُشغَّل عبر خطاف أو CI بالإضافة إلى ذلك، حتى لا تعتمد على ذاكرة الوكيل.

اختبارات E2E بطيئة، فهل يجب تشغيلها بعد كل تعديل؟

لا. المدقق وفحص الأنواع واختبارات الوحدة تُشغَّل دائمًا، أما E2E فعند التغييرات التي تمس أكثر من طبقة، وقبل الإعلان عن اكتمال الميزة.

كيف أتحقق من واجهة أمامية أنجزها الوكيل؟

باختبار E2E في المتصفح (مثل Playwright) ينقر فيه على الواجهة الحقيقية، أما المظهر فبمراجعة يدوية وفق قائمة، أو بلقطات شاشة يراجعها المُقيِّم.

هل تكفي مراجعة الكود البشرية بدل المُقيِّم؟

المراجعة البشرية مفيدة، لكنها مكلفة ولا تتسع لساعات من العمل المستقل. المُقيِّم المزوّد بمعايير يلتقط المشكلات الواضحة قبل الإنسان، ويترك القرارات المتنازع عليها للإنسان.

تعلّم هذا الموضوع بالفيديو

يُشرح هذا الموضوع بالتفصيل مع التطبيق العملي في دورة «Harness Engineering: инженерия надёжности AI-агентов».

الانتقال إلى الدورة