الدرس 8 من 20

الوكلاء طويلو المدى

الاسترداد والاستئناف

4 دقيقة للقراءة

الأشياء ستفشل. الشبكات تنقطع، واجهات API تنتهي مهلتها، الأخطاء تعطل وكيلك. السؤال ليس إذا ولكن كم بسلاسة تسترد.

أنماط الفشل

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

نمط إعادة التشغيل السلس

class ResilientAgent:
    def __init__(self, state_file: str, max_retries: int = 3):
        self.state_file = state_file
        self.max_retries = max_retries
        self.state = self.load_state()

    def run_with_recovery(self):
        """الحلقة الرئيسية مع استرداد تلقائي."""
        while not self.is_complete():
            task = self.get_current_task()

            for attempt in range(self.max_retries):
                try:
                    result = self.execute_task(task)
                    self.checkpoint(task, result)
                    break
                except RecoverableError as e:
                    self.log(f"المحاولة {attempt + 1} فشلت: {e}")
                    if attempt < self.max_retries - 1:
                        time.sleep(2 ** attempt)  # تراجع أسي
                    else:
                        self.mark_task_failed(task, str(e))
                        self.handle_failed_task(task)
                except FatalError as e:
                    self.log(f"خطأ قاتل: {e}")
                    self.save_state()  # حفظ التقدم
                    raise  # لا يمكن الاسترداد، الخروج

    def handle_failed_task(self, task: dict):
        """قرر ماذا تفعل مع المهام الفاشلة."""
        if task.get("critical"):
            # لا يمكن المتابعة بدون هذه المهمة
            raise FatalError(f"مهمة حرجة فشلت: {task['id']}")
        else:
            # تخطي والمتابعة
            self.log(f"تخطي مهمة غير حرجة: {task['id']}")
            self.advance_to_next_task()

إعداد البيئة عند الاستئناف

عند الاستئناف، البيئة قد تكون تغيرت:

class EnvironmentManager:
    def __init__(self, required_state: dict):
        self.required = required_state

    def verify_environment(self) -> list[str]:
        """تحقق إذا البيئة تطابق الحالة المتوقعة."""
        issues = []

        # تحقق من دليل العمل
        if not os.path.exists(self.required.get("work_dir", ".")):
            issues.append("دليل العمل مفقود")

        # تحقق من وجود الملفات المطلوبة
        for file in self.required.get("required_files", []):
            if not os.path.exists(file):
                issues.append(f"ملف مطلوب مفقود: {file}")

        # تحقق من حالة git
        if self.required.get("expected_branch"):
            current = self.get_current_branch()
            if current != self.required["expected_branch"]:
                issues.append(f"فرع خاطئ: {current} مقابل {self.required['expected_branch']}")

        return issues

    def restore_environment(self):
        """محاولة استعادة البيئة المتوقعة."""
        # الانتقال للفرع الصحيح
        if self.required.get("expected_branch"):
            subprocess.run(["git", "checkout", self.required["expected_branch"]])

        # استعادة من commit نقطة الحفظ إذا متوفر
        if self.required.get("checkpoint_commit"):
            subprocess.run(["git", "reset", "--hard", self.required["checkpoint_commit"]])

        # إعادة تثبيت التبعيات إذا لزم
        if os.path.exists("requirements.txt"):
            subprocess.run(["pip", "install", "-r", "requirements.txt"])

التحقق من نقطة الحفظ

لا تثق بنقاط الحفظ أعمى—تحقق منها:

def validate_checkpoint(state: dict) -> tuple[bool, list[str]]:
    """تحقق من سلامة نقطة الحفظ قبل الاستئناف."""
    errors = []

    # تحقق من الحقول المطلوبة
    required = ["task", "subtasks", "current_index", "created_at"]
    for field in required:
        if field not in state:
            errors.append(f"حقل مفقود: {field}")

    # تحقق من حدود الفهرس
    if state.get("current_index", 0) > len(state.get("subtasks", [])):
        errors.append("الفهرس الحالي خارج الحدود")

    # تحقق من صحة الطوابع الزمنية
    try:
        datetime.fromisoformat(state["created_at"])
    except (KeyError, ValueError):
        errors.append("تنسيق طابع زمني غير صالح")

    # تحقق من أن المهام المكتملة لها نتائج
    for completed in state.get("completed", []):
        if "result" not in completed:
            errors.append(f"مهمة مكتملة تفتقد النتيجة: {completed.get('task')}")

    return len(errors) == 0, errors

تدفق الاستئناف

def resume_agent(state_file: str):
    """تدفق استئناف كامل مع التحقق."""

    # 1. تحميل نقطة الحفظ
    if not os.path.exists(state_file):
        raise FileNotFoundError("لم يُعثر على نقطة حفظ")

    state = json.load(open(state_file))

    # 2. التحقق من نقطة الحفظ
    valid, errors = validate_checkpoint(state)
    if not valid:
        print(f"فشل التحقق من نقطة الحفظ: {errors}")
        if input("محاولة الإصلاح؟ (y/n): ") == "y":
            state = repair_checkpoint(state, errors)
        else:
            raise ValueError("نقطة حفظ غير صالحة")

    # 3. التحقق من البيئة
    env_manager = EnvironmentManager(state.get("environment", {}))
    issues = env_manager.verify_environment()
    if issues:
        print(f"مشاكل البيئة: {issues}")
        env_manager.restore_environment()

    # 4. إنشاء الوكيل والاستئناف
    agent = ResilientAgent(state_file)
    agent.run_with_recovery()

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

الوحدة التالية: بروتوكول سياق النموذج وبناء مهارات الوكيل. :::

اختبار

الوحدة 2: الوكلاء طويلو المدى

خذ الاختبار