الدرس 01 · قاعدة

بدون حلقات، لا يوجد وكيل

يمكن كتابة سر كلود كود بالكامل في سطر واحد: while stop_reason == "tool_use"

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

ما الذي يفعله الوكيل بالضبط؟

عندما تقوم بتشغيل Claude Code في الوحدة الطرفية وتسمح له "بتنظيم جميع تعليقات المهام في قائمة"، فإن ما تراه هو: أنه يقرر من تلقاء نفسه أولاً grep، ثم cat بعض الملفات، ثم إخراج Markdown. لا ينفذ النموذج التعليمات البرمجية بنفسه - فهو يطلب فقط تنفيذه. ما يجعلها تبدو حقًا "باليدين والقدمين" هو 30 سطرًا أو نحو ذلك من كود الغراء المحيط بها.

هذا الدرس هو تفكيك دائرة الغراء والرؤية بوضوح. يطلق عليها حلقة الوكيل (حلقة الوكيل)، وبنيتها هي:

بينما Response.stop_reason == "tool_use":
    Response = LLM(messages, Tools) # 1. اسأل النموذج
    Execute_tools(response.tool_calls) # 2. قم بتشغيل ما طلبته
    messages.append(tool_results) # 3. تغذية النتائج مرة أخرى

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

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

انظر الرسائل[] تنمو خطوة بخطوة

مهمة محاكاة الحلقة التالية هي: "ما الملفات الموجودة في الدليل الحالي؟ ثم اقرأ package.json". انقر فوق الخطوة. في كل مرة تضغط عليها، فإنك تتقدم بإجراء واحد في الدورة. على اليسار توجد فقاعة الكلام التي يشاهدها الإنسان، وعلى اليمين توجد مصفوفة messages[] الفعلية المحشوة في النموذج - لاحظ كيف تنمو.

يجب إعادة إدخال نتائج الأداة في سجل الرسائل

أسهل مأزق بالنسبة للمبتدئين هو اعتبار "أداة التنفيذ" تأثيرًا جانبيًا - بمجرد تنفيذها، يتم تنفيذها. ومع ذلك، يمكن للنموذج رؤية الرسائل[] فقط في الجولة التالية من الاستدلال، ولا يعرف أن الأشياء غير الموجودة فيه قد حدثت. بدون خطوة الإلحاق، ستكون الحلقة بأكملها خاطئة.

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

  • انس أمر الإلحاق: لن يرى النموذج النتائج في المرة القادمة، وسوف يطلب نفس الأداة مرة أخرى - وستحصل على حلقة لا نهائية.
  • إلحاق ولكن فقدان tool_use_id: ستقوم Anthropic API بالإبلاغ مباشرة عن خطأ يجب أن تحتوي Tool_result على tool_use_id، وسوف تنهار الحلقة عند خطوة استدعاء API.

نفس المهمة: "احسب عدد ملفات بايثون الموجودة في المشروع". قم بتشغيل النسختين جنبًا إلى جنب لترى الفرق في النهايات.

فهم stop_reason

في كل مرة يعود فيها النموذج، فإنه سيجلب stop_reason. لتقرر ما إذا كنت تريد مواصلة الحلقة، ما عليك سوى إلقاء نظرة على هذا الحقل:

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

الكتابة بشكل غير صحيح كـ "استمر طالما لا يوجد tool_use"، أو تجاهل max_tokens والتعامل معها مباشرة كنهاية عادية، هي أخطاء شائعة.

اتخاذ الإجراءات اللازمة وتشغيلها

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

استنساخ git https://github.com/shareAI-lab/learn-claude-code
cd تعلم كلود كود
تثبيت النقطة -r متطلبات.txt
cp .env.example .env # املأ ANTHROPIC_API_KEY
وكلاء بايثون/s01_agent_loop.py

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

تفاعلي

Widget 1 · حلقة السائر · الرسائل[] النمو

افهم: يتم حشو نتائج الأداة مرة أخرى في الرسائل بتنسيق خاص (كتلة tool_result، مع tool_use_id)؛ هذه هي الطريقة الوحيدة ليعرف النموذج أنه "تم تنفيذ الأداة والنتيجة هي X" في الجولة التالية من الاستدلال.

对话视图未开始
点 Step 开始 →
messages[] JSONlength: 0
[]
stop_reason:
تفاعلي

Widget 2 · كسر الحلقة · التصحيح مقابل نسيت الإلحاق

افهم: طالما أن سطرًا واحدًا مفقودًا messages.append(tool_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)
    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 · حدد سبب التوقف · احكم على 4 إجابات صحيحة

كل جزء هو جزء من استجابة نموذجية حقيقية محتملة. انقر فوق "متابعة" أو "خروج" لمعرفة ما إذا كنت على صواب أم على خطأ.

答对 0 / 4