الدرس 08 · تزامن

دع العمل يجري وحده، والوكيل لا يتوقف

«أطلق وانسَ — الوكيل لا ينتظر أثناء تشغيل الأمر.»

⏱ ~10 د · 📝 3 مكونات تفاعلية · 🧑‍💻 مبني على shareAI-lab · s08_background_tasks.py

ألم الاستدعاء المتزامن المُعيق

أداة bash في S02 متزامنة: subprocess.run(..., timeout=120). تشغيل أمر كـ npm install الذي يستغرق 90 ثانية يُجمّد حلقة الوكيل بأكملها 90 ثانية. المستخدم يحدق في الطرفية لا يعلم هل تعطّل البرنامج أم يعمل.

حل s08: منح الوكيل أداة background_run. تعود فوراً بـ task_id، والأمر يعمل في thread منفصل. الوكيل يواصل الحلقة ويعمل في أشياء أخرى؛ حين تنتهي المهمة الخلفية تُضيف النتيجة إلى قائمة الإشعارات.

def run(self, command: str) -> str:
    task_id = str(uuid.uuid4())[:8]
    self.tasks[task_id] = {"status":"running", ...}
    thread = threading.Thread(target=self._execute, args=(task_id, command), daemon=True)
    thread.start()
    return f"Background task {task_id} started"   # 立即返回

كيف تعود النتائج إلى الوكيل؟

المفتاح queue آمنة للخيوط: thread الخلفية يُضيف إليها عند الانتهاء؛ الخيط الرئيسي يُفرّغها قبل كل استدعاء LLM، ويُضيف إشعارات الاكتمال إلى messages كرسائل بدور user.

def agent_loop(messages):
    while True:
        # Drain bg notifications before each LLM call
        notifs = BG.drain_notifications()
        if notifs:
            messages.append({
                "role": "user",
                "content": f"<background-results>{notif_text}</background-results>",
            })
        response = client.messages.create(...)
        ...

بهذا إن أطلق الوكيل في الجولة N مهمة خلفية، وانتهت تلك المهمة في الجولة N+3، فإن استدعاء LLM التالي يحمل النتيجة تلقائياً — يرى النموذج كتلة <background-results> ويعلم: «انتهت تلك المهمة، أُكمل».

عرض الجدول الزمني

يتيح المكوّن التالي المحاكاة: الخيط الرئيسي يُحقق كل ثانية (يحاكي إيقاع حلقة الوكيل)؛ يمكنك إطلاق مهام خلفية في أي وقت. شاهد كيف يلتقي المسارين عند «نقطة التفريغ».

أي الأوامر يجب إرسالها للخلفية؟

ليس كل أمر يستحق الخلفية. معياران للحكم:

  1. المدة: ما دون ثوانٍ قليلة، التشغيل المتزامن أبسط ولا حاجة لصيانة queue.
  2. أهمية الانتظار: إن كانت الخطوة التالية تعتمد مباشرة على النتيجة (مثل cat file.txt ثم grep مباشرة)، فلا معنى للخلفية — ستنتظر على أي حال.
تفاعلي

Widget 1 · Timeline · الخيط الرئيسي + خيطان خلفيان

اضغط Spawn لإسناد عمل للخيط الخلفي. الخيط الرئيسي يفحص notification queue في كل tick. شاهد كيف تتشابك المسارات الثلاثة.

🧠 الخيط الرئيسي (حلقة الوكيل)
⚙ الخيط الخلفي A
(خامل)
⚙ الخيط الخلفي B
(خامل)
queue: []
تفاعلي

Widget 2 · Fire & Forget · 8 أوامر، أيها يستحق الخلفية؟

لكل أمر اختر foreground (انتظر متزامناً) أو background (أطلق غير متزامن). فكّر في بُعدين: «هل أحتاج النتيجة فوراً؟» + «كم تستغرق؟»

أصبت في 0 / 8
تفاعلي

Widget 3 · Drain Timing · في أي جولة يرى الوكيل الإشعار

القاعدة الجوهرية: الخيط الرئيسي يُفرّغ Queue قبل كل استدعاء LLM. 5 سيناريوهات: في أي جولة تدخل نتيجة المهمة الخلفية ضمن ما يراه الوكيل؟

أصبت في 0 / 5