الدرس 01 · أساسيات

بلا حلقة، لا وكيل

يمكن تلخيص سر Claude Code في سطر واحد: while stop_reason == "tool_use"

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

ما الذي يفعله الـ agent فعلياً؟

حين تشغّل Claude Code في الطرفية وتطلب منه «جمع كل تعليقات TODO في قائمة»، تراه يقرر بنفسه: يبدأ بـ grep، ثم يقرأ عدة ملفات بـ cat، ثم يُخرج Markdown. النموذج لا ينفذ الكود بنفسه — بل يطلب تنفيذه فحسب. ما يجعله يبدو «لديه يدان وقدمان» هو ذلك الكود الرابط المؤلف من نحو 30 سطراً يحيط به.

هذا الدرس يُفكك ذلك الكود الرابط ويُوضحه. يُسمى agent loop (حلقة الوكيل)، وبنيته هي:

while response.stop_reason == "tool_use":
    response = LLM(messages, tools)      # 1. ask the model
    execute_tools(response.tool_calls)   # 2. run what it asked for
    messages.append(tool_results)        # 3. feed results back

هذا كل شيء. Claude Code في بيئة الإنتاج يُضيف فوق هذا صلاحيات وhooks وsub-agents وعزل worktree وضغط الذاكرة — لكن النواة لا تزال هذه الأسطر الأربعة.

الحدس الأساسي: كل استجابة من النموذج إما «أريد استدعاء أداة» أو «انتهيت». طالما في الحالة الأولى تستمر الحلقة؛ حين تدخل الحالة الثانية تخرج الحلقة. المعيار هو حقل stop_reason في الاستجابة.

شاهد messages[] تنمو خطوة بخطوة

الحلقة التالية تحاكي مهمة: «ما الملفات في الدليل الحالي؟ ثم اقرأ package.json». اضغط Step، كل ضغطة تقدّمك خطوة واحدة في الحلقة. يسار: فقاعة المحادثة التي يراها الإنسان، يمين: مصفوفة messages[] الفعلية المُمررة للنموذج — راقب كيف تنمو.

يجب إعادة نتائج الأدوات إلى سجل الرسائل

أكثر خطأ يرتكبه المبتدئون هو اعتبار «تنفيذ الأداة» مجرد تأثير جانبي — نُنفذ وننتهي. لكن النموذج في الجولة التالية لا يرى إلا messages[]، وكل ما ليس فيها لا يعلم أنه حدث. بدون خطوة append، تنهار الحلقة بأكملها.

الأخطاء ليست كلها متشابهة. نوعان شائعان:

  • نسيان append: النموذج لن يرى النتيجة في الجولة التالية، فسيُكرر طلب الأداة نفسها — وينتج عن ذلك حلقة لا نهائية.
  • append مع فقدان tool_use_id: Anthropic API ستُعيد خطأ مباشراً tool_result must have tool_use_id، وستنهار الحلقة عند خطوة استدعاء API.

المهمة ذاتها: «عدّ ملفات Python في المشروع». شغّل النسختين جنباً إلى جنب وشاهد الفارق في النتيجة.

فهم stop_reason

كل استجابة من النموذج تحمل stop_reason. قرار الاستمرار أو الخروج يعتمد على هذا الحقل:

  • tool_use — النموذج يريد استدعاء أداة، استمر في الحلقة.
  • end_turn — النموذج أنهى ما يريد قوله، اخرج من الحلقة.
  • max_tokens — الإنتاج بلغ حد الـ token فاقتُطع، اخرج من الحلقة (عالجه عادةً كاستثناء ونبّه المستخدم بعدم اكتمال الإخراج).
  • stop_sequence — صادف تسلسل إيقاف مخصصاً، اخرج من الحلقة.

الخطأ الشائع هو الكتابة كـ «استمر طالما لم يكن tool_use»، أو تجاهل max_tokens ومعاملتها كـ end_turn عادية.

جرّبه محلياً

استنسخ shareAI-lab/learn-claude-code محلياً:

git clone https://github.com/shareAI-lab/learn-claude-code
cd learn-claude-code
pip install -r requirements.txt
cp .env.example .env  # fill in ANTHROPIC_API_KEY
python agents/s01_agent_loop.py

ثم اطلب منه شيئاً حقيقياً: ساعدني في عدّ ملفات .py في هذا المستودع. ستراه يُرسل ls -R، يرى الإخراج، يُرسل find . -name "*.py" | wc -l، ثم يُعطيك الإجابة. العملية برمتها هي هذه الحلقة تدور.

تفاعلي

Widget 1 · Loop Stepper · نمو messages[]

افهم: نتائج الأدوات تُضاف إلى messages بصيغة خاصة (كتلة tool_result مع tool_use_id)؛ هذه هي الطريقة الوحيدة التي يعلم بها النموذج في الجولة التالية أن «الأداة نُفذت والنتيجة هي X».

عرض المحادثةلم يبدأ
اضغط Step للبدء →
messages[] JSONlength: 0
[]
stop_reason:
تفاعلي

Widget 2 · Break the Loop · التطبيق الصحيح مقابل نسيان append

افهم: بمجرد إغفال سطر واحد messages.append(tool_results)، يتوقف الـ agent عن كونه agent — يتحول إلى آلة تنسى نتائجها في كل جولة.

while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    msgs.append({"role":"user",
                 "content": results})
محاكاة التشغيل
while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    # BUG: forgot to append results
    # msgs.append(...)
محاكاة التشغيل
تفاعلي

Widget 3 · Spot the stop_reason · حدّد النمط الصحيح في 4 استجابات

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

أصبت في 0 / 4