أهلاً بك مجدداً في رحلتنا لبناء وكلاء ذكاء اصطناعي أذكياء وقادرين على التكيف.
في الدروس القليلة الماضية، قمنا ببناء درع الصلابة لوكيلنا. تعلمنا كيف نكتشف الأخطاء باستخدام try/except، وكيف نعيد المحاولة عند الفشل المؤقت، وأخيراً، كيف نتحقق من صحة مخرجات الأدوات لضمان جودتها. لقد أصبح وكيلنا الآن أكثر حذراً وقوة.
لكن، ماذا لو استمر الفشل؟ ماذا لو أعادت المحاولة مراراً وتكراراً دون جدوى؟ وماذا لو كانت الأداة التي نعتمد عليها معطلة بشكل دائم أو غير قادرة على إعطاء إجابة صحيحة؟ لا يمكن لوكيلنا أن يبقى عالقاً في حلقة مفرغة، فهذا ليس سلوكاً ذكياً.
هذا هو محور درسنا اليوم. هدفنا هو تصميم استراتيجية بديلة (Fallback Strategy) في حال فشل الأداة المتكرر. سنزود وكيلنا بـ "خطة بديلة" يلجأ إليها عندما تفشل الخطة الأساسية، مما يجعله قادراً على التعامل مع الفشل الحقيقي بمرونة وموثوقية.
1. ما هي الاستراتيجية البديلة (Fallback Strategy)؟
ببساطة، الاستراتيجية البديلة هي خطة الطوارئ أو "الخطة ب" التي ينفذها الوكيل عندما تفشل خطته الأساسية بشكل متكرر. إنها الإجابة على سؤال: "ماذا أفعل الآن بعد أن استنفدت كل محاولات الإصلاح؟".
فكر في الأمر بهذه الطريقة: عندما تستخدم تطبيق الخرائط لتحديد مسار (الأداة الأساسية)، وفجأة يفقد التطبيق الاتصال بالإنترنت ويتوقف عن العمل (فشل الأداة). قد تحاول إعادة تشغيل التطبيق (إعادة المحاولة). إذا لم ينجح ذلك، قد تلجأ إلى "خطط بديلة":
- استبدال الأداة: تفتح تطبيق خرائط آخر قمت بتنزيله مسبقاً.
- تبسيط المهمة: تتجاهل المسار الدقيق وتتبع اللافتات العامة باتجاه المدينة التي تقصدها.
- الفشل اللطيف: توقف السيارة وتسأل شخصاً عن الاتجاهات.
لاحظ أن الهدف من الخطة البديلة ليس دائماً تحقيق المهمة الأصلية بنفس الدقة، بل هو تجنب الفشل الكامل، أو إيجاد حل بديل، أو على الأقل التعامل مع الموقف بشكل لائق.
2. متى يتم تفعيل الخطة البديلة؟
لا يمكن للوكيل أن يقرر الانتقال إلى الخطة البديلة بشكل عشوائي. يجب أن تكون هناك محفزات (Triggers) واضحة تخبره أن الوقت قد حان للتخلي عن الخطة الأساسية. من أهم هذه المحفزات:
- تجاوز حد إعادة المحاولة: عندما تفشل آلية إعادة المحاولة التي بنيناها في الدرس (10.4) بعد الوصول إلى العدد الأقصى للمحاولات. هذا هو المحفز الأكثر شيوعاً.
- فشل التحقق المتكرر: عندما تُرجع دالة التحقق من الصحة (من الدرس 10.5)
Falseباستمرار لنفس الأداة، حتى لو لم تحدث أخطاء برمجية. - أخطاء حرجة وغير قابلة للاسترداد: عندما تعيد الأداة خطأً محدداً نعلم أنه دائم، مثل "مفتاح API غير صالح" أو "الخدمة لم تعد متوفرة". في هذه الحالة، لا فائدة من إعادة المحاولة.
- المهلة الزمنية (Timeout): عندما تستغرق الأداة وقتاً طويلاً جداً للرد، مما يشير إلى وجود مشكلة عميقة.
المخطط التالي يوضح هذا التدفق بشكل ممتاز. يبدأ العمل باستعلام، وإذا كانت النتيجة غير موثوقة (فشل)، يحاول النظام مرة أخرى (Retry). إذا فشلت المحاولة الثانية أيضاً، فإنه يلجأ إلى "استجابة احتياطية" (Fallback Response).

3. أنواع الاستراتيجيات البديلة
عندما يقرر الوكيل أن الخطة (أ) قد فشلت، ما هي الخيارات المتاحة في الخطة (ب)؟ يمكن تصنيفها إلى عدة مستويات من التعقيد.

بناءً على هذا المخطط والأفكار التي ناقشناها، إليك بعض الاستراتيجيات الشائعة:
أ. استبدال الأداة (Tool Substitution)
هذه هي الاستراتيجية الأكثر مباشرة. إذا فشلت الأداة (س)، جرب الأداة (ص) التي تؤدي وظيفة مشابهة.
- مثال: لديك أداة أساسية دقيقة جداً لمعرفة الطقس تعتمد على خدمة مدفوعة (
get_hyperlocal_weather). إذا فشلت هذه الأداة، يمكن للوكيل أن يلجأ إلى أداة بديلة مجانية وأقل دقة (get_general_weather_forecast) لتقديم إجابة تقريبية بدلاً من لا شيء.
ب. تبسيط المهمة (Task Simplification / Workflow Adaptation)
أحياناً، لا يوجد بديل مباشر للأداة. في هذه الحالة، يمكن للوكيل محاولة تحقيق نسخة مبسطة من الهدف الأصلي.
- مثال: طلب المستخدم من الوكيل "احجز لي تذكرة طيران إلى دبي غداً صباحاً على الخطوط السعودية". فشلت أداة الحجز المباشر. بدلاً من الاستسلام، يمكن للوكيل أن يبسط المهمة ويلجأ إلى أداة بحث ويقول: "لم أتمكن من إتمام الحجز، ولكن هذه هي الرحلات المتاحة غداً إلى دبي. هل ترغب في اختيار إحداها لأحاول مرة أخرى؟".
ج. الفشل اللطيف وإبلاغ المستخدم (Graceful Failure & Informing the User)
هذه هي الخطة البديلة الأخيرة والملاذ الأخير. عندما تفشل كل المحاولات وكل الخطط البديلة، يجب على الوكيل أن ينسحب بأناقة.
- مثال: "أعتذر، لقد حاولت التواصل مع خدمة الطقس عدة مرات باستخدام طرق مختلفة ولكن دون جدوى. يبدو أن هناك مشكلة فنية خارجة عن إرادتي. لا يمكنني توفير معلومات الطقس في الوقت الحالي. هل يمكنني مساعدتك في شيء آخر؟".
هذه الاستجابة أفضل بكثير من أن يتوقف الوكيل عن العمل فجأة، أو أن يعطي معلومات خاطئة، أو أن يكرر رسالة الخطأ التقنية للمستخدم.
4. التنفيذ العملي في بايثون
لنقم بدمج هذه المفاهيم في الشيفرة البرمجية. سنبني سيناريو حيث يحاول الوكيل استخدام أداة أساسية، وعندما تفشل بشكل متكرر، يلجأ إلى أداة بديلة، وإذا فشلت هي الأخرى، يبلغ المستخدم بلطف.
سنقوم بمحاكاة أداتين للطقس: واحدة دقيقة ولكنها غير موثوقة، وأخرى عامة وموثوقة.
import random
# ----- الأدوات -----
def get_precise_weather(city: str) -> dict:
"""
الأداة الأساسية: دقيقة ولكنها تفشل أحياناً أو تعطي بيانات سيئة.
"""
print(f"محاولة الحصول على طقس دقيق لمدينة {city}...")
# محاكاة فشل عشوائي
roll = random.random() # يعيد رقم بين 0.0 و 1.0
if roll < 0.6: # 60% احتمال للفشل
raise ConnectionError("فشل الاتصال بخادم الطقس الدقيق.")
elif roll < 0.8: # 20% احتمال لبيانات غير صالحة
return {"city": city, "temperature": "غير معروف"}
else: # 20% احتمال للنجاح
return {"city": city, "temperature": 25, "humidity": 60}
def get_general_weather_forecast(city: str) -> str:
"""
الأداة البديلة (Fallback): عامة ولكنها موثوقة دائماً.
"""
print(f"فشلت الأداة الأساسية. اللجوء إلى توقعات الطقس العامة لمدينة {city}...")
forecasts = {
"الرياض": "مشمس وحار بشكل عام.",
"جدة": "رطب مع احتمال أمطار خفيفة.",
"دبي": "صحو ومشمس."
}
return forecasts.get(city, "لا تتوفر توقعات عامة لهذه المدينة.")
# ----- دالة التحقق (من الدرس السابق) -----
def validate_precise_weather_output(output: dict) -> bool:
"""تتحقق من أن درجة الحرارة هي رقم."""
if not isinstance(output.get("temperature"), (int, float)):
print("فشل التحقق: درجة الحرارة ليست رقماً.")
return False
return True
# ----- حلقة التنفيذ مع استراتيجية بديلة -----
def get_weather_for_user(city: str):
max_retries = 3
# الخطوة 1: محاولة استخدام الأداة الأساسية مع إعادة المحاولة
for attempt in range(max_retries):
try:
print(f"\n--- المحاولة رقم {attempt + 1} للأداة الأساسية ---")
precise_result = get_precise_weather(city)
# التحقق من صحة المخرجات
if validate_precise_weather_output(precise_result):
print("نجاح! تم الحصول على بيانات دقيقة.")
return f"الطقس الدقيق في {city}: درجة الحرارة {precise_result['temperature']} درجة، الرطوبة {precise_result['humidity']}%."
else:
# إذا فشل التحقق، اعتبرها محاولة فاشلة
continue # انتقل إلى المحاولة التالية في حلقة for
except Exception as e:
print(f"حدث خطأ: {e}")
# استمر في حلقة إعادة المحاولة
# الخطوة 2: إذا انتهت كل المحاولات، قم بتفعيل الخطة البديلة
print("\n--- فشلت الأداة الأساسية بعد كل المحاولات. تفعيل الخطة البديلة. ---")
try:
general_forecast = get_general_weather_forecast(city)
return f"لم أتمكن من الحصول على تفاصيل دقيقة. ولكن، التوقعات العامة لمدينة {city} هي: {general_forecast}"
except Exception as fallback_e:
# الخطوة 3: إذا فشلت الخطة البديلة أيضاً، قم بالفشل اللطيف
print(f"فشلت الخطة البديلة أيضاً: {fallback_e}")
return "أعتذر، أواجه صعوبة في الحصول على معلومات الطقس حالياً. يرجى المحاولة لاحقاً."
# --- لنجرب الآن ---
# قد تختلف النتيجة في كل مرة بسبب الطبيعة العشوائية للفشل
print(get_weather_for_user("الرياض"))
شرح الشيفرة:
- المحاولة الأساسية: ندخل في حلقة
forلمحاولة تشغيل الأداة الأساسيةget_precise_weatherثلاث مرات. - التحقق: بعد كل استدعاء ناجح (بدون استثناء)، نتحقق من صحة المخرجات باستخدام
validate_precise_weather_output. إذا كانت البيانات صالحة، نعيد النتيجة وننتهي. إذا لم تكن صالحة، ننتقل إلى المحاولة التالية. - الخطة البديلة: إذا انتهت حلقة
for(مما يعني أن كل المحاولات الثلاث قد فشلت إما بسبب استثناء أو فشل في التحقق)، يبدأ تنفيذ الكود الذي يلي الحلقة. هنا نستدعي أداتنا البديلةget_general_weather_forecast. - الفشل اللطيف: وضعنا استدعاء الأداة البديلة داخل كتلة
try/exceptأيضاً كإجراء احترازي. إذا فشلت هي الأخرى، نعيد رسالة اعتذار واضحة للمستخدم.
اختبر فهمك!
لنفترض أنك عدلت دالة get_weather_for_user لتستخدم أداة ترجمة. الأداة الأساسية هي translate_with_premium_api (دقيقة ومكلفة) والأداة البديلة هي translate_with_free_api (أقل دقة ومجانية).
إذا فشلت translate_with_premium_api ثلاث مرات، يجب على الكود أن يستدعي translate_with_free_api. إذا فشلت هذه الأخيرة أيضاً، يجب أن يعيد النص الأصلي مع رسالة توضيحية مثل: "فشلت الترجمة. النص الأصلي هو: [النص هنا]".
كيف ستعدل الجزء الأخير من الدالة (بعد حلقة for) لتحقيق هذا السلوك؟
Show answer
# هذا هو الجزء الذي يلي حلقة for
print("\n--- فشلت واجهة برمجة التطبيقات المميزة. تفعيل الواجهة المجانية. ---")
try:
# استدعاء الأداة البديلة
free_translation = translate_with_free_api(text_to_translate)
return f"الترجمة التقريبية: {free_translation}"
except Exception as fallback_e:
# الفشل اللطيف: إعادة النص الأصلي
print(f"فشلت الواجهة المجانية أيضاً: {fallback_e}")
return f"فشلت الترجمة. النص الأصلي هو: {text_to_translate}"
الفكرة الأساسية هي أن الملاذ الأخير ليس بالضرورة رسالة خطأ، بل يمكن أن يكون إعادة المدخلات الأصلية للمستخدم، وهو نوع من الفشل اللطيف العملي.
خلاصة الدرس
لقد قمنا اليوم بتزويد وكيلنا بواحدة من أهم مهارات الذكاء العملي: معرفة متى يجب التخلي عن خطة فاشلة والانتقال إلى خطة بديلة. هذا التحول من "المثابرة العمياء" إلى "التكيف الذكي" هو ما يميز الأنظمة القوية عن الهشة.
أهم النقاط التي تعلمناها:
- الاستراتيجية البديلة (Fallback) هي "الخطة ب" التي تمنع الوكيل من التجمد عند الفشل المتكرر.
- يتم تفعيلها بواسطة محفزات واضحة مثل تجاوز حد إعادة المحاولة أو فشل التحقق المستمر.
- تتنوع أنواع الاستراتيجيات البديلة من استبدال الأداة، إلى تبسيط المهمة، وصولاً إلى الانسحاب اللطيف وإبلاغ المستخدم.
- يمكن تنفيذ هذه المنطقية في بايثون عبر تنظيم الكود للتعامل مع فشل حلقة إعادة المحاولة كإشارة لتفعيل الخطة البديلة.
ماذا بعد؟
حتى الآن، قمنا ببرمجة منطق إعادة المحاولة والتحقق واللجوء للخطة البديلة بأنفسنا بشكل صريح. لقد بنينا "قواعد" يتبعها الوكيل. ولكن أليس من المفترض أن يكون "عقل" الوكيل (LLM) قادراً على المشاركة في اتخاذ هذه القرارات؟
في الدرس القادم، وهو الأخير في هذا الوحدة، سنتعلم كيف نربط كل هذه القطع معاً. سنتناول موضوع "تعديل حلقة المنطق لاتخاذ إجراء مختلف بناءً على نجاح أو فشل الأداة". سنرى كيف يمكننا جعل حلقة التفكير-التنفيذ (Think-Act) أكثر ديناميكية، بحيث يشارك نموذج اللغة في تقييم نتائج الأدوات ويساعد في تحديد الخطوة التالية، سواء كانت المتابعة، أو إعادة المحاولة، أو تفعيل خطة بديلة.