بايثون في عصر الذكاء الاصطناعي: دورة مكثفة
17 مفهومًا · اقرأ قبل أن تكتب
لقد تعلمت بالفعل قيادة وكيل ترميز الذكاء الاصطناعي في Agentic Coding Crash Course. يمكنك فتح جلسة، وإعطاء تعليمات ل Claude Code أو OpenCode، ومشاهدته وهو يقوم بتحرير الملفات وتشغيل الأوامر. إذن هذا هو السؤال الصادق الذي توجد هذه الدورة للإجابة عليه:
إذا كان الوكيل يكتب لغة بايثون، فلماذا تحتاج إلى تعلم أي لغة بايثون على الإطلاق؟
لأنه يجب على شخص ما أن يقرأها. يُنتج الوكيل مئات الأسطر من التعليمات البرمجية ذات المظهر العملي في ثوانٍ. و"المظهر العملي" هو الفخ. يعمل الذكاء الاصطناعي على تحسين النتائج المقبولة، وليس الصحيحة. يبدو الرمز الذي يُرجع الرقم الخطأ بهدوء، أو يسقط سجلاً، أو يسرب مفتاح واجهة برمجة التطبيقات (API) الخاص بك، تمامًا مثل الرمز الذي يعمل. الشخص الوحيد الذي يمكنه معرفة الفرق هو الشخص الذي يستطيع قراءة ما كتبه الوكيل وإثبات أنه يفعل ما يقصده.
هذه هي المهمة كلها الآن. عدم كتابة الحلقات من صفحة فارغة. الوكيل يفعل ذلك. وظيفتك هي الجزء الذي لا يزال يتطلب وجود إنسان: تحديد معنى صحيح، والتحقق من أن الكود الذي تم إنشاؤه يفي به.
هذه هي البوابة إلى البرمجة في عصر الذكاء الاصطناعي. هذا الجزء من الكتاب عبارة عن دورة تدريبية عميقة مدتها من 3 إلى 5 أشهر تتمحور حول مشروع واحد (SmartNotes). هذه الدورة التدريبية المكثفة هي نسخة قابلة للقراءة بنسبة 80%: ما يكفي من معرفة القراءة والكتابة بلغة بايثون لبدء قراءة كود الوكيل اليوم. يأتي ** بعد** Agent Coding وقبل Build AI Agents، وPostgres for AI، وBuilding a Digital FTE، والتي تفترض جميعها أنه يمكنك قراءة لغة Python التي ينشئها الوكيل. للحصول على المعالجة الكاملة لأي موضوع هنا، اتبع الروابط الموجودة في تلك الدورة.
فكرة واحدة تنظم كل شيء أدناه: في العالم القديم، كنت تتعلم لغة بايثون من خلال كتابتها. هنا تتعلمها من خلال قراءتها: التنبؤ بما تفعله التعليمات البرمجية، وتشغيلها، والتحقق من السبب، ثم تغييرها، وأخيرًا إنشاء كود خاص بك. لدينا اسم لتلك الحلقة، وهي العمود الفقري للدورة بأكملها.
كل شيء في هذه الدورة هو شيء تفعله داخل جلسة Claude Code، وليس مجرد القراءة عنه. أبقِ الوحدة الطرفية مفتوحة مع تشغيل claude وجرب كل فكرة عند وصولك إليها.
كل شيء في هذه الدورة هو شيء تفعله داخل جلسة OpenCode، وليس مجرد القراءة عنه. أبقِ الوحدة الطرفية مفتوحة مع تشغيل opencode وجرب كل فكرة عند وصولك إليها. إذا كنت تراقب التكلفة، فوجهها إلى نموذج رخيص مثل deepseek-v4-flash لعمليات التشغيل الروتينية (راجع دورة Agentic Coding).
قم بإعداد بيئتك (مرة واحدة)
تحتوي هذه الدورة التدريبية على مرافق صغير يمكنك تنزيله مرة واحدة: قاعدة الدورة التدريبية. لا تحتاج إلى القراءة بشكل صارم، ولكنها تجعل كل شيء يتصرف بالطريقة التي يفترضها المقرر الدراسي. فهو يشحن ملف قواعد قصير، AGENTS.md، يحول وكيلك إلى مدرس منضبط تعتمد عليه هذه الدورة: فهو يتنبأ قبل أن يكشف، ويعطي تلميحات بدلاً من الإجابات، ولا يضعف الاختبار أبدًا بهدوء لمجرد اجتيازه. يقرأ وكيلك هذا الملف تلقائيًا في كل مرة يبدأ فيها، ولهذا السبب يمكن أن تظل مطالباتك هنا قصيرة. تحتوي القاعدة أيضًا على بداية التدريب على المشروع ومفتاح إجابة يمكنك التحقق من اختباراتك الخاصة به.
تنزيل python-crash-course-base.zip
قم بفك ضغطه، ثم افتح الوكيل الخاص بك داخل المجلد:
cd python-crash-course
claude
cd python-crash-course
opencode
لاحظ السفن الأساسية لا يوجد مشروع بايثون بعد، عن قصد. يعد إنشاء طاولة العمل الخاصة بك من لا شيء أول مهارة حقيقية، وهو الشيء التالي الذي ستفعله في المفهوم 3. في الوقت الحالي، ما عليك سوى فتح وكيلك في المجلد والتأكد من تحميل القواعد:
ما هي القواعد التي تتبعها لهذه الجلسة؟
يجب أن يصف طريقة القراءة قبل الكتابة وعادة إجراء الاختبارات للتحقق. يتم ذلك عندما يجيب وكيلك بهذه القواعد. (هل تفضل البدء من مجلد فارغ بدلاً من ذلك؟ وهذا يعمل أيضًا؛ حيث ستقوم فقط بتعيين الانضباط بنفسك أثناء تقدمك.)
الجزء الأول: العقلية الجديدة
1. تقرأ الكود؛ الوكيل يكتبه
في دورات بايثون التقليدية، المهارة التي يتم التدريب عليها هي الإنتاج: اكتب الوظيفة، اكتب الحلقة، اكتب الفصل. لقد أزال الذكاء الاصطناعي عنق الزجاجة هذا. المهارة المهمة الآن هي التوجيه والتحقق.
| الوظيفة القديمة (معظمها آلية الآن) | الوظيفة الجديدة (لا تزال ملكك) |
|---|---|
| اكتب التنفيذ من صفحة فارغة | قم بوصف ما تريده بدقة، بكلمات يمكن للوكيل التصرف بناءً عليها |
| حفظ بناء الجملة | *التعرف على بناء الجملة بشكل جيد بما يكفي لقراءتها بشكل نقدي |
| احصل على الكود للتشغيل | أثبت أن الكود صحيح، وليس قابلاً للتشغيل فقط |
هذه هي قاعدة 10-80-10 المطبقة على البرمجة. أنت تمتلك أول 10% (تقرر ما تريد بناءه وما تعنيه كلمة "صحيح") وآخر 10% (تتحقق منه). يمتلك الوكيل 80% في المنتصف (إنشاء الكود). القراءة بطلاقة هي ما يتيح لك الحفاظ على كلا الطرفين.

القراءة أسهل بكثير من الكتابة. لا تحتاج إلى أن تتذكر، من لا شيء، أن القائمة تستخدم الأقواس المربعة. ما عليك سوى التعرف على ["a", "b"] كقائمة عندما تراها. هذا هو عائق هذه الدورة بأكملها: الاعتراف، وليس التذكر.
2. طريقة PRIMM-AI+
PRIMM-AI+ هي الطريقة التي تتعلم بها قراءة التعليمات البرمجية دون التحديق في صفحة فارغة. تتحرك كل قطعة جديدة من لغة بايثون عبر خمس خطوات، والوكيل هو شريكك في كل خطوة:
| خطوة | خطاب | ماذا تفعل | دور الوكيل |
|---|---|---|---|
| يتنبأ | ص | قبل تشغيل أي شيء، خمن ما سيفعله الكود | يبقى هادئًا – هذا هو تخمينك |
| يجري | ر | قم بتشغيله وشاهد الناتج الحقيقي | ينفذ الكود لك |
| يفتش | أنا | قارن تخمينك بالواقع؛ اسأل لماذا | يشرح سطراً سطراً، حسب الطلب |
| يُعدِّل | م | قم بتغيير شيء واحد، توقع مرة أخرى، قم بالتشغيل مرة أخرى | إجراء التعديل الذي تصفه |
| يصنع | م | تحديد الإصدار الخاص بك من البداية؛ دع الوكيل يبنيه؛ يؤكد | يولد ضد المواصفات الخاصة بك |
AI+ هو الجزء الذي يجعل هذا الإصدار عصر الذكاء الاصطناعي لطريقة تدريس قديمة. يوجد شيئين فوق PRIMM الكلاسيكي: الوكيل متاح دائمًا كشريك في القراءة ("اشرح السطر 3،" "ما الذي يمكن أن يكسر هذا؟")، ولا شيء مقبول على أساس الإيمان. يبني التنبؤ والتشغيل والتحقيق طلاقة القراءة لديك؛ تعديل-الصنع هو المكان الذي تبدأ فيه توجيه الوكيل. في النهاية، يبدو الأمر Make طبيعيًا كما هو الحال الآن مع Predict.
حيث يكون الاختبار مناسبًا: في كل مكان. المحرك الذي يشغل التحقيق، والتعديل، والإنشاء هو الاختبار: فحص قصير يحدد ما تعنيه كلمة "صحيح"، حتى يتمكن الكمبيوتر من التحقق من عمل الوكيل بدلاً من مراقبته. هذه ليست خطوة تقوم بها مرة واحدة في النهاية. بدءًا من الوظيفة التالية التي تقرأها، يمر اختبار صغير: أولاً كسطر assert واحد (المفهوم 5)، ثم على الكائنات (المفهوم 8) وقواعد التحقق من الصحة (المفهوم 11)، وأخيرًا كحلقة كاملة الجيل القائم على الاختبار حيث تكتب الاختبار أولاً ويكتب الوكيل التعليمات البرمجية لاجتيازه (المفهوم 16). تعامل مع كل جزء من التعليمات البرمجية التي يقدمها لك الوكيل على أنها غير موثوق بها حتى ينص الاختبار الذي كتبته على خلاف ذلك.
الغريزة هي الانتقال مباشرة إلى "التشغيل": فقط انظر إلى الإجابة. لا. الفجوة بين توقعك والنتيجة الحقيقية هي المكان الذي يحدث فيه التعلم. يخبرك التنبؤ الخاطئ بالضبط بالجزء الذي تم كسره من نموذجك العقلي، وهو أفضل بكثير من أي توقع صحيح. التنبؤ بصوت عالٍ أو كتابيًا أو للوكيل. ثم اركض.
3. طاولة العمل الخاصة بك في خمس دقائق
تفترض هذه الدورة التدريبية أنك قد أكملت دورة تدريبية مكثفة حول Agentic Coding، حيث تقوم بتثبيت الوكيل وتعلم كيفية تشغيله. إذا تخطيت هنا مباشرة ولم تقم بتثبيت أي شيء* حتى الآن، فقم بإعداد وكيل ترميز واحد قبل المضي قدمًا. ستقوم هذه الأداة الفردية بتثبيت كل شيء آخر لك:
- Claude Code: اتبع الدليل الرسمي على docs.claude.com/en/docs/claude-code (يعمل على أنظمة التشغيل macOS وLinux وWindows عبر WSL).
- OpenCode (بديل مفتوح المصدر): opencode.ai.
- uv (بايثون نفسها + الحزم): docs.astral.sh/uv; أو اسمح للوكيل بتثبيته لك في الخطوة التالية.
لا تحتاج إلى فهم أي من هذه الأدوات بعمق. قم بتثبيت وكيل واحد، وافتحه في مجلد، ثم تابع. بقية هذا القسم يقوم الوكيل بإجراء الإعداد.
أنت بحاجة إلى ثلاثة أشياء: طريقة لتشغيل بايثون، وبعض أدوات الجودة، ووكيلك. يغطي أحد أدوات التثبيت المثبتين الأولين: uv، وهو مدير حزم سريع يقوم بتثبيت Python نفسه وإدارة مشروعك.
لا تقم بتثبيتها يدويًا من الذاكرة، فهذا هو بالضبط نوع الإعداد غير المتقن الذي يجيده الوكيل. في المجلد الذي فتحت فيه وكيلك، أعطه التعليمات التالية:
قم بإعداد مشروع Python جديد هنا باستخدام
uv. أضفpytestللاختبارات، وpyrightللتحقق من النوع، وruffللتنسيق. قم بإنشاءhello.pyالذي يطبع "جاهز". ثم قم بتشغيله وأظهر لي الإخراج.
سيقوم الوكيل بتشغيل أوامر uv وإنشاء الملفات وتشغيل البرنامج النصي. تشاهد وتوافق على كل خطوة. هذا * هو * طاولة العمل الخاصة بك. أنت لم تحفظ أمراً واحداً
لا تقم بتثبيتها يدويًا من الذاكرة، فهذا هو بالضبط نوع الإعداد غير المتقن الذي يجيده الوكيل. في المجلد الذي فتحت فيه وكيلك، أعطه التعليمات التالية:
قم بإعداد مشروع Python جديد هنا باستخدام
uv. أضفpytestللاختبارات، وpyrightللتحقق من النوع، وruffللتنسيق. قم بإنشاءhello.pyالذي يطبع "جاهز". ثم قم بتشغيله وأظهر لي الإخراج.
سيقوم OpenCode بتشغيل أوامر uv وإنشاء الملفات وتشغيل البرنامج النصي. تشاهد وتوافق على كل خطوة. هذا * هو * طاولة العمل الخاصة بك. أنت لم تحفظ أمراً واحداً
كيف يبدو ذلك في الواقع
لا تشاهد في صمت. يروي الوكيل كل خطوة ويسأل قبل تشغيل أي شيء. تبدو الجلسة الحقيقية تقريبًا مثل هذا (يظهر Claude Code، وOpenCode متطابق تقريبًا):
> Set up a new Python project here using uv. Add pytest, pyright, ruff.
Create a hello.py that prints "ready", run it, and show the output.
● I'll initialize the project with uv.
$ uv init . ← the agent proposes a command
Allow? (y/n) y ← you approve
Initialized project
● $ uv add --dev pytest pyright ruff
Allow? (y/n) y
+ pytest + pyright + ruff
● Created hello.py:
print("ready")
● $ uv run hello.py ← this is how a Python file gets run
ready ← the output prints right here
Done. Your workbench is ready.
ثلاثة أشياء يجب ملاحظتها، لأنها هي الحلقة المادية التي ستكررها طوال الدورة:
- الكود موجود في ملف (
hello.py)، وليس في نافذة الدردشة. - تقوم بتشغيل ملف باستخدام
uv run hello.py: يقوم هذا الأمر الواحد بتنفيذ كل برنامج نصي ومثال في هذه الدورة التدريبية. - يطلب الوكيل الإذن قبل كل أمر، ثم يطبع الناتج تحته. أنت توافق، تقرأ النتيجة.
لذا، عندما يقول المثال أدناه "تشغيله"، لديك خياران سهلان: الصق الكود في ملف وأخبر الوكيل "قم بتشغيل هذا الملف وأرني المخرجات"، أو الصق المقتطف ببساطة في الدردشة وقل "قم بتشغيل هذا وأريني ما يطبعه." وفي كلتا الحالتين، لن تكتب Python يدويًا أبدًا.
إذا أدت أي خطوة إلى ظهور خطأ باللون الأحمر، فأنت لست بحاجة إلى فهمه. انسخ النص الأحمر والصقه مرة أخرى وقل: "فشل هذا. اقرأ الخطأ وقم بإصلاحه." إذا أبلغ الوكيل أن uv لم يتم تثبيته، فاطلب منه تثبيت uv أولاً. التحرر من التعثر هو عمل الوكيل؛ ملاحظة أنك عالق هي ملكك.
وإليك ما تفعله كل أداة، في سطر واحد. سترى الأربعة مذكورة باستمرار من هنا فصاعدًا:
| أداة | ماذا يفعل لك |
|---|---|
| الأشعة فوق البنفسجية | يقوم بتثبيت Python وحزم مشروعك بسرعة |
| بيتيست | يقوم بتشغيل الاختبارات والتقارير التي نجحت والتي فشلت |
| ** بيرايت ** | يقرأ ملصقات النوع في الكود ويلاحظ عدم تطابق العلامات قبل تشغيله |
| ** راف ** | التحقق من النمط والتنسيق - مدقق إملائي للتعليمات البرمجية |
بيرايت وراف هما آلتا قراءة. عندما يقوم الوكيل بإنشاء تعليمات برمجية، تقرأها هذه الأدوات نيابةً عنك وتلتقط فئة كاملة من الأخطاء على الفور: نوع خاطئ، أو متغير غير مستخدم، أو اسم به خطأ مطبعي. إنها السطر الأول للتحقق، حتى قبل أن تقرأ سطرًا بنفسك. (إرشادات الإعداد الكاملة: البرمجة في عصر الذكاء الاصطناعي، المرحلة 1: طاولة العمل.)
شاهد آلات القراءة الخاصة بك تلتقط شيئًا ما
هذه الأدوات ليست مجردة. اطلب من الوكيل كتابة دالة صغيرة ثم استدعائها بنوع خاطئ من القيمة:
def total_with_tax(price: float, tax_rate: float) -> float:
return price + (price * tax_rate)
total_with_tax("100", 0.15) # oops — "100" is text, not a number
قم بتشغيل pyright عليه وستحصل على هذا قبل تنفيذ الكود:
error: Argument of type "Literal['100']" cannot be assigned to
parameter "price" of type "float" (reportArgumentType)
قرأ بيرايت تلميحات الكتابة، ورأى أنه تم تمرير النص حيث تم الوعد ب float، واكتشف عدم التطابق على الفور. يلتقط راف فئة مختلفة من الأخطاء: بقايا الفوضى التي يتركها العميل وراءه أحيانًا:
F401 `os` imported but unused
أنت لا تحفظ هذه الرسائل. تقرأها بالطريقة التي تقرأ بها تسطير المدقق الإملائي: هناك شيء غير مناسب هنا، ويشير إلى السطر المحدد. عندما يقوم الوكيل بإنشاء مائة سطر، تقرأ هاتان الأداتان كل ذلك في أقل من ثانية، وهذا هو بالضبط سبب تشغيلهما قبل أن تبدأ في القراءة بنفسك.
الجزء الثاني: الأشكال التي ستراها في كل مكان
هذه هي الأشكال الأساسية التي ربما تشكل 70% من لغة بايثون التي ستقرأها على الإطلاق. انتقل عبر كل واحدة باستخدام توقع → تشغيل → تحقيق: اقرأها، وخمن النتيجة، ثم قم بتشغيلها في جلستك للتحقق منها.
4. القيم والمتغيرات والأنواع الأربعة
المتغير هو مربع مسمى يحتوي على قيمة. علامة = تعني "ضع القيمة الموجودة على اليمين في المربع الموجود على اليسار": فهي ليست "تساوي" بالمعنى الرياضي. كل قيمة لها نوع، وفي الوقت الحالي لا يوجد سوى أربعة أنواع تحتاج إلى التعرف عليها. نكتب النوع مباشرة بعد الاسم، ك name: type، بحيث يمكنك أنت والوكيل رؤية ما يجب أن يحمله كل صندوق في لمحة سريعة:
name: str = "Ayesha" # str — text, always in quotes
age: int = 30 # int — a whole number
price: float = 19.99 # float — a number with a decimal point
is_active: bool = True # bool — only ever True or False
الأجزاء : str، : int، : float، : bool هي تلميحات الكتابة. إنها اختيارية في Python، ولكننا ندرجها دائمًا طوال هذه الدورة: جزئيًا لأنها تجعل التعليمات البرمجية أسهل في القراءة، وجزئيًا لأنها، كما سترون في المفهوم 10، هي الطريقة التي تخبر بها الوكيل ما يجب بناءه بالضبط. في الوقت الحالي، اقرأ name: str باللغة الإنجليزية البسيطة: "يحتوي المربع name على نص."
توقع: ما رأيك في هذا المطبوع؟
age: int = 30
age = age + 1
print(age)
قم بتشغيله. الإخراج هو 31. تحقق: يبدو السطر age = age + 1 مستحيلًا إذا قرأت = ك "يساوي": 30 لا يمكن أن يساوي 31. لكن = يعني تعيين: خذ القيمة الحالية ل age (30)، وأضف 1، ثم ضع النتيجة (31) مرة أخرى في المربع. يتم تشغيل الجانب الأيمن أولاً، ثم يتم تحديث الصندوق. (لاحظ أننا كتبنا فقط تلميح النوع : int في المرة الأولى التي ظهر فيها age؛ وبمجرد أن يحتوي الصندوق على نوع، لا تكرره في كل عملية إعادة تعيين.) هذه الفكرة تزعج الجميع تقريبًا في البداية، ولهذا السبب توقعتها أولاً.
عندما ترى x = something، اقرأها من اليمين إلى اليسار: "احسب something، ثم قم بتخزينها في x." الاسم الموجود على اليسار هو مجرد تسمية.
5. الوظائف وتواقيعها
الوظيفة عبارة عن كتلة من التعليمات البرمجية قابلة لإعادة الاستخدام تحمل اسمًا. أنت تعطيه مدخلات (تسمى الوسائط)، وتعطيك مخرجات. السطر الأول، التوقيع، هو أهم سطر ستتعلم قراءته، لأنه عقد:
def total_with_tax(price: float, tax_rate: float) -> float:
return price + (price * tax_rate)
اقرأ التوقيع قطعة قطعة:
def: "أقوم بتعريف دالة."total_with_tax: اسمه.(price: float, tax_rate: float): يتطلب إدخالين، كلا الرقمين العشريين. الجزء: floatعبارة عن تلميح للكتابة (المزيد حول ذلك في المفهوم 10).-> float: يعطي رقمًا عشريًا.
بنفس واحد: "أعطني السعر ومعدل الضريبة، كلا الرقمين العشريين، وسأعيد لك رقمًا عشريًا." يمكنك فهم ما تعد به الدالة دون قراءة سطر واحد من نصها. ولهذا السبب فإن التوقيعات مهمة جدًا عند التحقق من كود الذكاء الاصطناعي. يمكن أن يكون الجسم مخطئًا؛ يخبرك التوقيع بما كان من المفترض أن تفعله.
توقع ثم نفذ:
def total_with_tax(price: float, tax_rate: float) -> float:
return price + (price * tax_rate)
print(total_with_tax(100.0, 0.15))
الإخراج: 115.0. إذا توقعت 115.0، فيمكنك قراءة دالة بالفعل.
ثبته باختبار
الدالة هي أول شيء يمكنك إثبات صحته، لأنه يحتوي على عقد واضح: بالنظر إلى هذه المدخلات، يجب أن تعيد ذلك المخرج. يمكنك تأمين ذلك باستخدام assert: ادعاء من سطر واحد لا يفعل شيئًا عندما يكون صحيحًا ويتعطل بصوت عالٍ في اللحظة التي يكون فيها كاذبًا:
assert total_with_tax(100.0, 0.15) == 115.0 # "I claim this must equal 115.0"
assert total_with_tax(0.0, 0.15) == 0.0 # a free item costs nothing
قم بتشغيل هذين الخطين وسترى لا شيء. الصمت يعني كل مطالبة. قم بتغيير 115.0 إلى 999.0 وسيتعطل على الفور. هذه هي الفكرة الكاملة وراء الاختبار: تكتب المعنى الصحيح مرة واحدة، وسيقوم الكمبيوتر بإعادة فحصه لك في كل مرة. وهذان السطران يمثلان بالفعل مواصفات صغيرة. التمسك بهم. في المفهوم 16، ستقوم بتسليم هذه التأكيدات الدقيقة إلى الوكيل وتطلب منه كتابة الوظيفة لإرضائها. (وهذا ليس من قبيل الصدفة: فنفس الوظيفة الضريبية تظهر مرة أخرى هناك باعتبارها دورة TDG الكاملة).
6. المجموعات: تجميع البيانات
القيم الفردية نادرة في الكود الحقيقي. تأتي معظم البيانات في مجموعات، وهناك أربع حاويات ستشاهدها مرارًا وتكرارًا:
notes: list[str] = ["buy milk", "call Sara", "finish report"] # list — ordered, can change
note: dict[str, str | bool] = {"title": "Meeting", "done": False} # dict — labeled pairs (key: value)
tags: set[str] = {"work", "urgent"} # set — unique items, no duplicates
point: tuple[int, int] = (3, 5) # tuple — ordered, cannot change
تصف التلميحات ما هو داخل الحاوية: list[str] هي "قائمة نصية"، وdict[str, str | bool] هي "إملاء مفاتيحه عبارة عن نص وقيمه إما نصية أو صحيحة/خطأ" (| تعني "أو")، وset[str] هي "مجموعة من النصوص"، وtuple[int, int] هي "زوج من أعداد كاملة." لا تحتاج إلى كتابة هذه من الذاكرة. تحتاج إلى قراءتها.
أكثر اثنتين ستقابلهما هما list (تسلسل يمكنك إضافته وإعادة ترتيبه) وdict (مجموعة من القيم المصنفة: العمود الفقري لكل جزء من البيانات التي يمررها وكيل الذكاء الاصطناعي تقريبًا، لأنه يرتبط مباشرة ب JSON).
توقع ثم نفذ:
note: dict[str, str | bool] = {"title": "Meeting", "done": False}
print(note["title"])
note["done"] = True
print(note)
الإخراج:
Meeting
{'title': 'Meeting', 'done': True}
تحقيق: note["title"] يصل إلى الإملاء ويسحب القيمة المسماة "title". السطر الثاني يغير القيمة المسماة "done". الإملاء هو الطريقة التي سيسلمك بها الوكيل ملاحظة أو مستخدمًا أو استجابة واجهة برمجة التطبيقات: أي شيء يحتوي على حقول مسماة.
7. التحكم في التدفق والفهم
تتخذ التعليمات البرمجية القرارات باستخدام if، وتتكرر مع for. المسافة البادئة (المسافات في بداية السطر) هي الطريقة التي تعرف بها Python ما ينتمي داخل if أو for. إنها ليست زخرفة، بل هي هيكل.
prices: list[int] = [10, 250, 50, 400]
for price in prices: # do this once for each price in the list
if price > 100: # decision
print(f"{price} is expensive")
else:
print(f"{price} is fine")
توقع الخطوط التي تطبع "باهظة الثمن" قبل تشغيلها. (الجواب: 250 و 400.)
يستخدم اثنان من هذه الأسطر f-string: نص مع f في المقدمة، حيث يتم استبدال أي شيء داخل {...} بقيمته. لذلك f"{price} is expensive" يسقط price الحالي في الجملة. سوف ترى سلاسل f باستمرار؛ فقط عليك أن تدرك أن {name} تعني "ضع نص هذه القيمة هنا."
عندما يكون للقرار أكثر من نتيجتين، فإن elif (اختصار ل "else if") يربطهما. تتحقق بايثون من كل فرع من الأعلى إلى الأسفل وتقوم بتشغيل الأول الصحيح:
def grade_for(score: int) -> str:
if score >= 90:
return "A"
elif score >= 70:
return "B"
elif score >= 50:
return "C"
else:
return "F"
توقع grade_for(72)، ثم قم بالتشغيل. إنه "B": 72 >= 90 خطأ، لذلك ينتقل إلى الفرع التالي، 72 >= 70 صحيح، ويتوقف عند هذا الحد. اقرأ سلسلة if/elif/else ك "تحقق من هذه بالترتيب؛ خذ المطابقة الأولى." سترى أيضًا الشروط مدمجة مع and وor — if score >= 70 and attended: يعني أن كلاهما يجب أن يكون صحيحًا — وهو كل ما تحتاجه لقراءة قرارات "المعايير المتعددة".
بمجرد أن تتمكن من قراءة حلقة for، يمكنك قراءة السطر الوحيد الأكثر شيوعًا في بايثون: الفهم. إنها مجرد حلقة for مطوية على سطر واحد لإنشاء قائمة جديدة:
prices: list[int] = [10, 250, 50, 400]
# The long way:
expensive: list[int] = []
for price in prices:
if price > 100:
expensive.append(price)
# The comprehension — exactly the same result:
expensive = [price for price in prices if price > 100]
يضيف .append(price) في الإصدار الطويل price إلى نهاية القائمة؛ هذا الشكل thing.action() هو استدعاء الأسلوب الذي ستقابله في المفهوم 8. اقرأ الفهم من اليسار إلى اليمين: "أعطني price، لكل price في prices، إذا كان price > 100." كلاهما ينتج [250, 400]. سترى الفهم في كل مكان في كود الذكاء الاصطناعي لأن تنظيف البيانات وإعادة تشكيلها هو معظم العمل. احصل على الراحة عند التعرف على هذا الشكل ويمكنك قراءة كمية كبيرة من لغة بايثون الحقيقية.
الصق أي سطر لا تفهمه في جلستك: "اشرح هذا السطر كما لو أنني لم أقم بتشفيره مطلقًا: [n['text'] for n in notes if n['done']]." هذه هي خطوة التحقيق الخاصة ب PRIMM-AI+، عند الطلب. استخدمه باستمرار. إنه أسرع من أي مرجع.
8. الفئات والأشياء – قراءة المخطط
حتى الآن كانت بياناتك فضفاضة: note كان عبارة عن إملاء، وprice كان عائمًا. تتيح لك الفئة تجميع البيانات ذات الصلة و الإجراءات التي تنتمي إليها في شكل واحد مسمى. فكر في الفصل باعتباره مخططًا وكائنًا (يُسمى أيضًا مثيلًا) كشيء تم إنشاؤه من هذا المخطط: مخطط Note واحد، والعديد من الملاحظات الفعلية المبنية منه.
فيما يلي فصل يصمم ملاحظة واحدة:
class Note:
def __init__(self, title: str, body: str) -> None:
self.title = title # an attribute — data this note carries
self.body = body
self.done = False
def mark_done(self) -> None: # a method — an action this note can perform
self.done = True
اقرأها في أجزاء:
class Note:، يحدد المخطط المسمىNote.__init__: طريقة الإعداد. يتم تشغيله مرة واحدة تلقائيًا، عندما تقوم بإنشاء ملاحظة جديدة، ويقوم بتخزين بيانات البداية. (-> Noneيعني أنه لا يعيد أي شيء، بل يقوم فقط بتكوين الكائن.)self: هذا الكائن بالتحديد. داخل المخطط،self.titleتعني "*عنوان هذه الملاحظة". تأخذ كل طريقةselfأولاً حتى تعرف الكائن الذي تعمل عليه.self.title،self.body،self.done— السمات: البيانات التي تحملها كل ملاحظة.mark_done، طريقة: دالة تعيش داخل الفئة وتعمل على الكائن.
الآن قم ببناء واحدة واستخدمها. توقع المخرجات قبل تشغيلها:
n: Note = Note(title="Groceries", body="milk, eggs")
print(n.done)
n.mark_done()
print(n.done)
الإخراج:
False
True
تحقيق: Note(title="Groceries", body="milk, eggs") يقوم بتشغيل __init__ ويعيد كائنًا نهائيًا، مخزنًا في n. n.done يقرأ إحدى السمات؛ n.mark_done() يستدعي الطريقة التي تغيره. النقطة (.) تعني دائمًا نفس الشيء: "الوصول إلى هذا الكائن للحصول على الشيء المسمى باسمه."
هذا النمط الوحيد - object.attribute لقراءة البيانات وobject.method() لمطالبة الكائن بفعل شيء ما - يفتح معظم لغة Python الحقيقية. كل ما ستقرأه تقريبًا في كود الوكيل هو كائنات: نموذج Pydantic هو فئة، والوكيل هو كائن، واتصال قاعدة البيانات هو كائن. عندما ترى agent.run(task) أو db.save(note)، فأنت تقرأ "اطلب من هذا الكائن تشغيل إحدى طرقه." (نموذج الكائن الكامل هو البرمجة في عصر الذكاء الاصطناعي، المرحلة 5.)
ثبته باختبار
يمكنك التحقق من كائن بالطريقة التي قمت بها بالتحقق من الوظيفة في المفهوم 5: تأكيد الحالة التي يجب أن تكون عليها قبل وبعد تشغيل الطريقة:
n: Note = Note(title="Groceries", body="milk, eggs")
assert n.done is False # a fresh note starts unfinished
n.mark_done()
assert n.done is True # ...and is finished after mark_done()
نفس الحركة، هدف جديد: الطريقة هي مجرد وظيفة مرتبطة بكائن، لذا فإن نفس السطر الواحد assert يثبت أنه يتصرف. عندما يكتب الوكيل فصلًا دراسيًا لك، فهذه هي الطريقة التي تتحقق بها من أنه يفعل ما قصدته.
كما هو الحال مع كل شيء في هذه الدورة، الهدف هنا هو الاعتراف. عندما يقوم الوكيل بإنشاء فئة، فأنت تريد أن تكون قادرًا على الإشارة إليها والقول "هذه هي طريقة الإعداد، وهذه هي سماتها، وهذه طريقة تغير حالتها." وهذا يكفي للتحقق من أنه يفعل ما طلبته.
9. المزيد من الأشكال التي ستقابلها في التعليمات البرمجية التي تم إنشاؤها: while، والتقطيع، وtry/except، وinput()
نادرًا ما تكتب هذه العناصر الأربعة، لكن الوكيل يستخدمها باستمرار، لذلك تحتاج إلى التعرف عليها عندما تقرأ مخرجاتها وتتحقق منها. توقع سريع → تشغيل → التحقيق في كل منها يكفي.
while: كرر ذلك حتى يتغير الشرط. عندما يتم تشغيل حلقة for مرة واحدة لكل عنصر في مجموعة، فإن حلقة while تستمر طالما ظلت حالتها صحيحة:
count: int = 3
while count > 0: # keep looping while this is true
print(count)
count = count - 1
print("done")
** توقع، ثم قم بالتشغيل. ** الإخراج: 3، 2، 1، done. اقرأها ك "استمر في القيام بذلك أثناء count > 0." أحد المخاطر التي تستحق التعرف عليها في التعليمات البرمجية التي تم إنشاؤها: إذا أصبحت الحالة أبدًا خاطئة، فإن الحلقة تعمل إلى الأبد، لذلك عندما ترى while، قم بإلقاء نظرة سريعة على ما يجعلها تتوقف في النهاية.
التقطيع: احصل على جزء من قائمة أو سلسلة. النقطتان الموجودتان داخل الأقواس المربعة تعني "نطاق من المواضع":
letters: list[str] = ["a", "b", "c", "d", "e"]
print(letters[1:3]) # from position 1 up to (not including) 3
print(letters[:2]) # the first two
print(letters[-1]) # the last one
** توقع، ثم قم بالتشغيل. ** الإخراج: ['b', 'c']، ['a', 'b']، e. يبدأ العد في 0؛ [1:3] هو "من 1 إلى 3 ولكن لا يشمل ذلك"؛ -1 يعني "الأخير". تعمل نفس الصيغة على النص: "hello"[:3] هو "hel".
**try / except: حاول القيام بشيء قد يفشل. ** قد تفشل بعض العمليات: القسمة على صفر، وقراءة ملف مفقود، وانتهاء مهلة مكالمة الشبكة. try/except يقوم بتشغيل التعليمات البرمجية الخطرة ويكتشف الخطأ بدلاً من التعطل:
def safe_divide(a: float, b: float) -> float:
try:
return a / b
except ZeroDivisionError: # if dividing by zero is attempted...
return 0.0 # ...do this instead of crashing
توقع safe_divide(10.0, 2.0) وsafe_divide(10.0, 0.0)، ثم قم بالتشغيل. المخرجات: 5.0 و0.0. اقرأها ك "جرب هذا؛ إذا فشلت بهذه الطريقة المحددة، افعل ذلك بدلاً من ذلك." سترى try/except ملفوفًا حول أي شيء يمكن أن يفشل في كود الوكيل.
**input(): توقف مؤقتًا واقرأ ما يكتبه المستخدم. ** تستخدم البرامج النصية التفاعلية وأدوات سطر الأوامر input() لطرح سؤال على الشخص وانتظار الإجابة:
name: str = input("What's your name? ") # waits here until the user types and presses Enter
print(f"Hello, {name}!")
الشيء الوحيد الذي يجب التعرف عليه، والذي غالبًا ما ينزلق الكود الذي تم إنشاؤه في مكان واحد، هو أن **input() يُرجع النص دائمًا **، حتى عندما يكتب المستخدم رقمًا. ولإجراء العمليات الحسابية باستخدامه، يجب أن يقوم الكود بتحويله أولاً:
age_text: str = input("Your age? ") # "30" comes back as text, not a number
age: int = int(age_text) # int(...) converts text to a whole number
print(f"Next year you'll be {age + 1}")
إذا رأيت input() يتم إدخاله مباشرة في العمليات الحسابية دون وجود int(...) أو float(...) حوله، فهذا خطأ يجب الإبلاغ عنه: إما أن البرنامج سيتعطل أو يلصق النص معًا بدلاً من إضافته.
ثبته باختبار
حالة الخطأ هي بالضبط الشيء الذي يستحق التثبيت: التأكيد على أن المدخلات الخطيرة تمت معالجتها، ولم يتم تعطلها:
def test_safe_divide() -> None:
assert safe_divide(10.0, 2.0) == 5.0
assert safe_divide(10.0, 0.0) == 0.0 # handled, not a crash
يعد كل مكان شائعًا لحدوث خطأ ما في التعليمات البرمجية التي تم إنشاؤها: while التي لا تنتهي أبدًا، أو الشريحة التي يتم قطعها بمقدار واحد ([1:3] تمنحك عنصرين، وليس ثلاثة)، أو except الذي يبتلع الخطأ الذي تحتاج إلى معرفته بصمت، أو input() الذي لا يتم تحويل نصه أبدًا إلى رقم قبل الرياضيات. إن التعرف على الشكل هو ما يتيح لك التوقف والنظر عن كثب إلى تلك البقع بالضبط.
الجزء الثالث: مفاهيم القوة في عصر الذكاء الاصطناعي
هذه المفاهيم هي التي تفصل بايثون "hello World" عن بايثون التي ستراها فعليًا في كود الوكيل وتعلم الآلة. لن تكتب معظمها من الصفر بعد؛ يقوم الوكيل بذلك، وهدفك هو التعرف المحض: عندما يظهر أحد الأشخاص، فأنت تعرف الغرض منه وما يفعله تقريبًا. الاستثناء الوحيد هو الأول، تلميحات الكتابة (المفهوم 10): تلك التي تتعلمها كيف تكتب، لأنها تمثل الطريقة التي تخبر بها الوكيل ما يجب أن يبنيه بالضبط. (يتم استخلاص العديد منها مباشرة مما يستخدمه مهندسو الذكاء الاصطناعي للإنتاج يوميًا.)
تقفز الوتيرة هنا عن قصد. قبل صفحتين كنت تقرأ age = age + 1؛ سترى الآن المولدات وأجهزة التحقق من الصحة وasync. هذا جيّد. ليس من المتوقع منك أن تكتب أيًا من هذا. وظيفتك الوحيدة للمفاهيم 11-15 هي التعرف على كل شكل والقول باللغة الإنجليزية البسيطة الغرض منه. إذا كانت الكتلة تبدو كثيفة، فاقرأ ملخص "اقرأها ك..." المكون من سطر واحد ثم تابع. لا أحد يحفظ هذه؛ تتعلم كيفية اكتشافهم.
10. اكتب التلميحات — التسميات التي توجه الوكيل
لقد قابلت واحدًا بالفعل في المفهوم 5: price: float. تلميح النوع هو تصنيف يوضح نوع البيانات التي يتوقعها المتغير أو الوظيفة. لا تتطلب لغة Python هذه الأدوات، ولكنها في عصر الذكاء الاصطناعي هي أقوى أدواتك، وذلك لسبب واحد:
تلميحات الكتابة هي تعليمات للوكيل. عندما تكتب توقيعًا كهذا قبل أن تطلب من الوكيل ملئه —
def summarize_notes(notes: list[dict], max_words: int) -> str:
... # the agent writes this part
لقد أخبرت الوكيل، بدقة ودون لبس، أن الإدخال عبارة عن قائمة إملاءات، وأن هناك حدًا لعدد الكلمات وهو رقم صحيح، والإخراج عبارة عن سلسلة واحدة. يقوم الوكيل الآن بإنشاء الشيء الصحيح في كثير من الأحيان، لأنك أزلت التخمين. ويقرأ بيرايت هذه التسميات ويضع علامة على أي عدم تطابق قبل تشغيل الكود.
وفقًا لمصطلحات TDG (التي تمت تغطيتها بالكامل في البرمجة في عصر الذكاء الاصطناعي)، **تعد كتابة الأنواع جزءًا من كتابة المواصفات. ** فهي أول 10% ملك لك.
نظرًا لأن الأنواع عبارة عن تعليمات، فإن أقوى طلبات التعليمات البرمجية تمنح الوكيل ثلاثة أشياء وتطلب رابعًا:
- التوقيع بالأنواع:
summarize_notes(notes: list[dict], max_words: int) -> str. يؤدي هذا إلى تثبيت المدخلات والمخرجات بحيث لا يكون هناك أي تخمين. - واحد أو مثالين للإدخال → الإخراج. (هذه مضاعفة كاختباراتك.)
- أي قيود: "يجب التعامل مع قائمة فارغة"، "عدم الاتصال بالشبكة"، "إبقائها أقل من 20 سطرًا."
- اطلب منه التحقق: "ثم قم بتشغيل
pytestوpyrightوأريني النتيجة."
طلب غامض ("اكتب شيئًا لتلخيص الملاحظات") يحصل على رمز غامض. يحصل التوقيع المكتوب بالإضافة إلى المثال على رمز يمكنك التحقق منه بالفعل. وعندما تكون النتيجة خاطئة، لا تضغط فقط على إعادة المحاولة: الصق الإخراج الفاشل وقل ما هو الخطأ: "إنه يتعطل في قائمة فارغة. تعامل مع ذلك." تصحيح واحد مستنير يتفوق على عشر عمليات إعادة رمي عمياء.
11. Dataclasses وPydantic – بيانات منظمة ومتحقق من صحتها
يعتبر الإملاء الأولي ({"learning_rate": 0.001}) مرنًا ولكنه خطير: مفتاح مطبعي أو نوع خاطئ يمر بصمت ويفسد كل شيء في اتجاه مجرى النهر. أداتان لإصلاح هذا.
Pydantic ليس مدمجًا في Python، لذا فإن الأمثلة أدناه تحتاج إلى إضافتها إلى مشروعك مرة واحدة: أخبر وكيلك "أضف pydantic إلى هذا المشروع" (يتم تشغيله uv add pydantic). dataclass مدمج ولا يحتاج إلى أي شيء.
فئة البيانات تعطي بياناتك شكلاً محددًا. هذا هو نفس Note من المفهوم 8. لكن لاحظ أنه لا يوجد __init__: السطر @dataclass يكتب طريقة الإعداد من أجلك، لذا عليك فقط إدراج السمات وأنواعها.
from dataclasses import dataclass
@dataclass
class Note:
title: str
body: str
done: bool = False # default value
يذهب Pydantic إلى أبعد من ذلك: فهو يتحقق من صحة البيانات في وقت التشغيل ويرفض أي شيء غير مناسب:
from pydantic import BaseModel, Field
class ModelConfig(BaseModel):
learning_rate: float = Field(gt=0.0, lt=1.0) # must be between 0 and 1
batch_size: int = Field(gt=0) # must be positive
إذا حاولت إنشاء ModelConfig باستخدام learning_rate=-0.05، فسيقوم Pydantic بإصدار خطأ واضح على الفور بدلاً من ترك قيمة مكسورة تسمم التدريب بعد ساعات. هذا هو السبب وراء وجود Pydantic في كل مكان في كود الوكيل: فهو يقوم أيضًا بإنشاء مخططات JSON التي يستخدمها LLM لاستدعاء الأدوات تلقائيًا. ستقابله مرة أخرى في اللحظة التي تبدأ فيها بناء الوكلاء.
ثبته باختبار
نموذج Pydantic هو في الأساس اختبار يتم تشغيله في كل مرة يقوم فيها برنامجك بذلك. ولكن لا يزال يتعين عليك تثبيت السلوك الذي يهمك، بما في ذلك حالات الخطأ. لقد أكدت حتى الآن أن شيئًا ما صحيح؛ هنا تؤكد أنه يجب رفض الإدخال السيئ، باستخدام pytest.raises:
import pytest
from pydantic import ValidationError
def test_rejects_negative_learning_rate() -> None:
with pytest.raises(ValidationError): # the block below MUST raise this error
ModelConfig(learning_rate=-0.05, batch_size=32)
ينجح هذا الاختبار فقط في حالة رفض التكوين السيئ. إن اختبار حالات الفشل مهم بقدر أهمية اختبار النجاحات: سيسعد الوكيل بإنشاء التحقق الذي يبدو صارمًا ولكنه يسمح بمرور البيانات السيئة بهدوء، واختبار "يجب رفعه" هو كيفية اكتشاف ذلك.
@dataclass عبارة عن مصمم ديكورهذا الرمز @ الموجود على السطر أعلى الوظيفة أو الفئة هو مصمم: تسمية تضيف سلوكًا إلى ما يليها، دون أن تكتب هذا السلوك بنفسك. @dataclass يمنح الفصل تلقائيًا مُنشئًا ونسخة مطبوعة قابلة للقراءة. لا تحتاج إلى كتابة الديكور بعد؛ فقط عليك أن تدرك أن @something تعني "إكمال الشيء التالي بقوى إضافية."
12. المولدات وyield — البث دون نفاد الذاكرة
عندما تحتاج التعليمات البرمجية إلى معالجة مليون سجل، فإن تحميلها جميعًا في قائمة مرة واحدة يمكن أن يؤدي إلى استنفاد ذاكرة جهاز الكمبيوتر الخاص بك. يحل المولد هذه المشكلة عن طريق إعادة العناصر واحدة تلو الأخرى، عند الطلب. الإشارة التي يجب البحث عنها هي الكلمة الرئيسية yield بدلاً من return:
from collections.abc import Iterator
def stream_notes(lines: list[str]) -> Iterator[str]:
for line in lines:
yield line.strip().lower() # hand back one item, then pause
نوع الإرجاع Iterator[str] هو إشارة واضحة للمولد: فهو لا يعد ب list[str] (كومة جاهزة). يعد ب مكرر، وهو دفق تسحبه من عنصر واحد في كل مرة.
تعمل الوظيفة العادية مع return على إنشاء النتيجة بأكملها وتسليمها دفعة واحدة. يتوقف المولد الذي يحمل yield مؤقتًا بعد كل عنصر ويقوم فقط بالجزء التالي من العمل عندما يُطلب منه ذلك. والنتيجة حقيقية: فتدفق مجموعة كبيرة من البيانات من خلال المولد بدلاً من تحميلها في قائمة يمكن أن يؤدي إلى خفض الذاكرة القصوى بشكل كبير، لأنك لن تحتفظ بالأمر برمته مرة واحدة. عندما ترى yield، اقرأها على النحو التالي: "هذا ينتج دفقًا، وليس كومة."
13. with — افتح وأغلق الأشياء بأمان
الملفات، واتصالات قاعدة البيانات، وجلسات الشبكة - أي شيء يجب فتحه ثم إغلاقه بشكل موثوق، حتى لو تعطل شيء ما بينهما - يستخدم عبارة with:
with open("notes.txt") as file:
contents: str = file.read()
# the file is automatically closed here, even if an error happened above
يتم ضمان تنظيف الشيء الذي تتم إدارته (هنا، الملف) عند انتهاء الكتلة ذات المسافة البادئة. في كود الذكاء الاصطناعي، سترى with ملتفًا حول حالة وحدة معالجة الرسومات، ووضع تقييم النموذج، والمؤقتات. عندما ترى with، اقرأها على النحو التالي: "قم بإعداد شيء ما، واستخدمه داخل هذه الكتلة، ثم قم بهدمه بأمان مهما كان الأمر."
14. async / await - أشياء كثيرة في وقت واحد
عندما يقوم الوكيل بإجراء 20 استدعاء لواجهة برمجة التطبيقات، فإن القيام بها واحدًا تلو الآخر يعني الانتظار 20 مرة. يتيح الكود غير المتزامن للبرنامج إطلاق جميع الطلبات والتعامل مع الاستجابات فور وصولها: وهو الفرق بين الوقوف في 20 قائمة انتظار واحدة تلو الأخرى والانضمام إلى كل 20 قائمة انتظار مرة واحدة. الإشارات هي الكلمات الرئيسية async و await:
async def query_llm(prompt: str) -> str:
response = await call_the_api(prompt) # pause here, let other work proceed
return response
كمثال توضيحي: إذا استغرق كل 20 استدعاء لواجهة برمجة التطبيقات حوالي 0.1 ثانية، فإن تشغيلها واحدة تلو الأخرى يستغرق ثانيتين تقريبًا، في حين أن تشغيلها بشكل متزامن مع async يمكن أن ينتهي في وقت أبطأ مكالمة فردية تقريبًا، لأن الإجمالي يتم تحديده بواسطة أبطأ مكالمة، وليس المجموع. لا تحتاج إلى كتابة رمز غير متزامن حتى الآن. ما عليك سوى التعرف على: async/await يعني "تم تصميم هذا الرمز للقيام بالعديد من الأشياء البطيئة في نفس الوقت." سوف تعتمد عليه بشدة عند إنشاء وكلاء.
15. طرق Dunder - لماذا يعمل model(x)
أغرب شيء ستقرأه هو طريقة تحتوي على شرطات سفلية مزدوجة على كل جانب، مثل __init__ أو __call__. تُلقب هذه ب "dunders" (شرطة سفلية مزدوجة)، وقد قابلت واحدًا بالفعل: طريقة الإعداد __init__ من المفهوم 8 هي dunder. إنها طرق خاصة تسمح للكائنات الخاصة بك بالتصرف مثل الكائنات المضمنة.
class Pipeline:
def __init__(self, factor: float) -> None: # runs when you create the object
self.factor = factor
def __call__(self, x: float) -> float: # runs when you "call" the object like a function
return x * self.factor
pipeline = Pipeline(2.5)
print(pipeline(10.0)) # prints 25.0 — the object is called like a function
__init__ يقوم بإعداد كائن جديد؛ __call__ هو ما يجعل pipeline(10.0) يعمل كما لو كان الكائن دالة. هذا هو بالضبط السبب وراء كتابة model(x) في PyTorch والمكتبات المشابهة وليس model.forward(x): يحدد إطار العمل __call__ للقيام بالإعداد المهم قبل التشغيل. عندما ترى الdunders، اقرأها على النحو التالي: "يتم تعليم هذا الكائن ليتصرف مثل كائن Python الأصلي."
توقف ولاحظ ما حدث. لا يمكنك حتى الآن كتابة منشئ أو وظيفة غير متزامنة من صفحة فارغة. ولا تحتاج إلى ذلك. ولكن يمكنك الآن فتح ملف أنشأه الوكيل والتعرف عليه: "هذه فئة بخصائصها وأساليبها، وهذا تلميح للنوع، وهذا نموذج Pydantic يحدد مخطط أداة، وهذا مولد لتدفق البيانات، وهذه الكتلة with تدير موردًا، وهذه العناصر الصغيرة تنشئ مجموعة بيانات مخصصة." وهذا الاعتراف هو مهارة التحقق التي يعتمد عليها الكتاب بأكمله.
الجزء 4: حلقة TDG، من النهاية إلى النهاية
لقد انتهيت من التنبؤ والتشغيل والتحقيق خلال الجزأين 2 و3. والآن الحرفان الأخيران من PRIMM-AI+، تعديل و Make، وهما معًا دورة الجيل القائم على الاختبار (TDG): الطريقة التي تحدد البرمجة في عصر الذكاء الاصطناعي.
16. دورة كاملة في أداتك
TDG يقلب النظام القديم. بدلاً من كتابة الكود → ربما اختباره، يمكنك اتباع ما يلي: اكتب اختبارًا فاشلًا يحدد "الصحيح" → اسمح للوكيل بإنشاء الكود → قم بتشغيل الاختبار للتحقق. الاختبار ليس فكرة لاحقة؛ إنها *المواصفات. ويجب أن يأتي منك، لأنك إذا سمحت للوكيل بكتابة الكود والاختبار، فإنك تتحقق من عمل الذكاء الاصطناعي مقابل توقعات الذكاء الاصطناعي نفسه: لا توجد إشارة مستقلة على الإطلاق.
إليك دورة وظيفة الضريبة من المفهوم 5. تابع ذلك خلال جلستك.

الخطوة 1: تكتب المتطلبات كاختبارات فاشلة. أنشئ test_tax.py:
from tax import total_with_tax
def test_basic() -> None:
assert total_with_tax(100.0, 0.15) == 115.0
def test_zero_price() -> None:
assert total_with_tax(0.0, 0.15) == 0.0
لقد رأيت هذه التأكيدات الدقيقة من قبل. إنهما السطران اللذان كتبتهما في المفهوم 5، وقد تم نقلهما الآن إلى ملف test_*.py حتى يتمكن pytest من تشغيلهما تلقائيًا. assert X == Y يعني "أنا أعلن أن X يجب أن يساوي Y؛ وافشل بصوت عالٍ إذا لم يحدث ذلك." هذين السطرين يحددان المعنى الصحيح قبل وجود أي كود. قم بتشغيل pytest الآن وسيفشل: لا يوجد tax.py حتى الآن. هذا الفشل هو الهدف. لديك تعريف دقيق وقابل للتنفيذ لكلمة "تم".
الشيء الوحيد الجديد هنا هو الترتيب: في المفاهيم 5 و8 و11 قمت بكتابة اختبار الكود الموجود بالفعل. في TDG الحقيقي، تكتب الاختبار الفاشل أولاً، ثم تطلب من الوكيل إنشاء رمز لاجتيازه. يتوقف الاختبار عن كونه فحصًا تجريه بعد ذلك ويصبح المواصفات التي يبني عليها الوكيل.
الخطوة 2: يقوم الوكيل بإنشاء التنفيذ. الآن قم بإعطاء الأداة الخاصة بك التعليمات:
اقرأ
test_tax.py. قم بإنشاءtax.pyباستخدام وظيفةtotal_with_tax(price: float, tax_rate: float) -> floatالتي تجعل كلا الاختبارين ناجحين. ثم قم بتشغيلpytestوأريني النتيجة.
يقرأ كلود كود اختباراتك، ويكتب tax.py، ويقوم بتشغيل pytest. نظرًا لأنك كتبت الاختبارات، فلديك فحص مستقل لما تم إنتاجه.
اقرأ
test_tax.py. قم بإنشاءtax.pyباستخدام وظيفةtotal_with_tax(price: float, tax_rate: float) -> floatالتي تجعل كلا الاختبارين ناجحين. ثم قم بتشغيلpytestوأريني النتيجة.
يقرأ OpenCode اختباراتك، ويكتب tax.py، ويقوم بتشغيل pytest. نظرًا لأنك كتبت الاختبارات، فلديك فحص مستقل لما تم إنتاجه.
الخطوة 3: قم بالتحقق والتكرار. اقرأ الناتج pytest. علامتان باللون الأخضر (2 passed) تعني أن الكود يلبي المواصفات التي حددتها. إذا كان باللون الأحمر، فإنك تقرأ الفشل (المفهوم التالي)، ثم قم بالتحسين. أنت لا تقوم فقط بإعادة المطالبة بشكل أعمى وتأمل. وراقب شيئًا واحدًا عن كثب: إذا نجح الوكيل في اجتياز اختبار فاشل عن طريق تحرير الاختبار بدلاً من الكود، فأوقفه. الاختبار هو المواصفات الخاصة بك. تحويله إلى اللون الأخضر عن طريق إضعافه يخفي الخلل فقط.
هذه هي الحلقة بأكملها، ويتم تعيينها بشكل واضح على PRIMM-AI+ وقاعدة 10-80-10:
| خطوة TDG | من يقود | بريم-AI+ |
|---|---|---|
| كتابة المتطلبات + الاختبارات الفاشلة | أنت (أول 10%) | يصنع |
| توليد التنفيذ | الوكيل (80%) | — |
| قم بإجراء الاختبارات، والتحقق، والتكرار | أنت (آخر 10%) | تحقيق / تعديل |
الوكيل جيد في اقتراح الاختبارات التي فاتتك، اسأله، "ما هي حالات الحافة التي يجب أن أختبرها لحساب الضريبة؟". لكن القرار بشأن تحديد الحالات "الصحيحة" يظل قرارك. أسعار سلبية؟ صفر ضريبة؟ أعداد كبيرة جداً؟ كل واحدة تضيفها هي مواصفات أكثر وضوحًا.
TDG ليس حفلًا مخصصًا للميزات الكبيرة. قم بتشغيله على كل وحدة ينتجها الوكيل ولها عقد قابل للتحقق: كل وظيفة، كل طريقة، كل قاعدة تحقق. كان السطر الواحد asserts من المفاهيم 5 و8 و11 هو البذرة؛ إن وجود مجلد tests/ المتزايد والمليء بها هو ما يتيح لك الاستمرار في الثقة في المشروع عندما يقوم الوكيل بتغييره. قاعدة عملية: **إذا لم تتمكن من كتابة اختبار لشيء ما، فأنت لم تفهم بعد معنى كلمة "صحيح" لذلك. ولا الوكيل أيضًا. ** كتابة الاختبار هي الطريقة التي تكتشف بها ذلك. مع نمو مشاريعك، هذا أيضًا ما يحميك من التراجعات: يقوم الوكيل بإصلاح شيء ما وكسر شيء آخر بصمت. مجموعة الاختبار الخاصة بك هي المنبه الذي يلتقطها.
قراءة التتبع (عندما ينكسر)
عندما تفشل بايثون، فإنها تطبع تتبع: جدار من النص الأحمر يبدو مخيفًا وهو في الواقع هدية. اقرأها من الأسفل إلى الأعلى: السطر الأخير يسمي الخطأ، والأسطر الموجودة فوقه تتتبع مكان حدوثه.
Traceback (most recent call last):
File "tax.py", line 2, in total_with_tax
return price + (price * tax_rate)
TypeError: can't multiply sequence by non-int of type 'float'
خلاصة القول هي القصة بأكملها: شيء ما كان عبارة عن سلسلة (str) بينما كان ينبغي أن يكون رقمًا: price جاء ك "100" (نص) بدلاً من 100.0. لا تحتاج إلى إصلاح هذا بنفسك؛ تحتاج إلى قراءتها جيدًا بما يكفي لإخبار الوكيل بالمشكلة: "يصل السعر كنص، وليس كرقم. تعامل مع ذلك." الوصف الدقيق يحصل على حل دقيق. (لتصحيح الأخطاء مرحلته الخاصة: البرمجة في عصر الذكاء الاصطناعي، المرحلة 4.)
إن أغلى عادة في برمجة الذكاء الاصطناعي هي الضغط على "حاول مرة أخرى" عند فشل لم تقرأه. سيقوم الوكيل بكل سرور بإنشاء إجابة خاطئة * مختلفة *. اقرأ التتبع، وافهم المشكلة الفعلية، ثم قم بوصفها. موجه واحد مستنير يتفوق على عشرة عميان.
حفنة من الأخطاء التي ستقابلها بالفعل
أنت لا تحفظ الإصلاحات. يمكنك التعرف على الفئة من السطر الأخير ووصفها للوكيل. تغطي هذه الستة تقريبًا كل ما يراه المبتدئ عند تشغيل التعليمات البرمجية التي تم إنشاؤها:
| السطر الأخير يقول... | ما يعنيه عادة | ماذا أقول للوكيل |
|---|---|---|
ModuleNotFoundError / ImportError | لم يتم تثبيت الحزمة، أو هناك خطأ إملائي في الاسم | "X غير مثبت - أضفه بالأشعة فوق البنفسجية" |
NameError | يتم استخدام اسم لم يتم تعريفه مطلقًا (غالبًا ما يكون خطأ مطبعي) | "y تم استخدامه ولكن لم يتم تعريفه مطلقًا - خطأ مطبعي؟" |
TypeError | نوع القيمة الخاطئ — النص الذي كان متوقعًا فيه رقم، وما إلى ذلك. | "يصل رقم كنص - قم بتحويله" |
AttributeError | سؤال كائن عن شيء لا يحتوي عليه (note.titel) | "هذا الكائن ليس له سمة titel - من المحتمل أن يكون خطأ مطبعي" |
KeyError | طلب الإملاء لمفتاح غير موجود فيه | "المفتاح 'x' ليس موجودًا في الإملاء - تعامل مع الحالة المفقودة" |
IndexError | طرح قائمة لمنصب بعد نهايته | "القائمة أقصر مما يفترضه الكود" |
إن المهارة الوصفية هي نفس الإعداد (المفهوم 3): أنت لست من يصلح الخطأ، أنت من يقرأه ويسميه بدقة. الاسم الدقيق يحصل على إصلاح دقيق؛ "لقد كسر" يحصل على تخمين آخر.
17. من هدف غامض إلى قطع محددة
أظهر المفهوم 16 كيفية تحديد وحدة واحدة والتحقق منها. لكن مشاريعك الخاصة لا تأتي كوظيفة واحدة مرتبة. تصل هذه الكلمات كجملة غامضة: "اصنع لي أداة تلخص ملاحظاتي". لا يمكنك كتابة اختبار واحد لذلك. المهارة التي تحول الجملة إلى كود تم إنشاؤه هي التحليل: تقسيم الهدف إلى أجزاء صغيرة، كل منها صغير بما يكفي لإعطاء توقيع مكتوب واختبار. هذا هو الجزء الذي لا يستطيع الوكيل أن يفعله لك، لأنه هو المكان الذي تقرر فيه ماهية الشيء في الواقع.
ها هي الحركة، وهي نفسها في كل مرة.
1. قل الهدف في جملة واحدة. "اقرأ ملاحظاتي واطبع عدد الملاحظات التي تم إنجازها، والتي لم يتم تنفيذها."
**2. اذكر الخطوات التي ستنفذها يدويًا. ** اكتب الأفعال: تحميل الملاحظات ← عد الكلمات المنجزة ← العثور على العناوين المعلقة ← تلخيصها في سطر ← اطبعها. كل فعل هو وحدة مرشحة.
**3. قم بإعطاء كل وحدة توقيعًا مكتوبًا: الإدخال إلى الإخراج. ** هذه * هي * المواصفات. لا جثث بعد؛ الوكيل يكتب تلك.
def load_notes(path: str) -> list[Note]: ... # text file -> list of notes
def count_done(notes: list[Note]) -> int: ... # notes -> how many are done
def pending_titles(notes: list[Note]) -> list[str]: ... # notes -> titles not done
def summarize(notes: list[Note]) -> str: ... # notes -> one summary line
**4. TDG كل وحدة، بالترتيب. ** كل سطر أعلاه هو الآن بالضبط دورة المفهوم 16: اكتب اختبار الفشل، واطلب من الوكيل إنشاء الجسم، والتحقق منه. على سبيل المثال، summarize صغير وقابل للاختبار:
def test_summarize() -> None:
notes = [Note(title="groceries", done=True), Note(title="call sara", done=False)]
assert summarize(notes) == "1 of 2 done; pending: call sara"
**5. قم بتوصيلهم معًا واختبر التدفق بالكامل. ** اطلب من الوكيل كتابة main() الذي يستدعي القطع بالترتيب، ثم اكتب اختبارًا واحدًا للنتيجة الشاملة. منتهي.
لاحظ ما حدث: أصبحت الجملة الغامضة أربعة عقود صغيرة يمكن التحقق منها بشكل مستقل. كتب الوكيل كل سطر من التعليمات البرمجية الفعلية. ولكنك وجدت القطع وحددت ما تعد به كل قطعة. إن تحليل الواجهة الأمامية هو 10% بشرية مما يجعل 80% من الوكيل جديرًا بالثقة.
يمكنك أن تطلب من الوكيل مساعدتك في العثور على الوحدات: "أريد إنشاء أداة تلخص ملاحظاتي. ما هي الوظائف الثلاث إلى الخمس التي سأحتاجها، مع مدخلاتها ومخرجاتها؟" هذه قائمة بداية رائعة. لكن أنت تقرر التوقيعات النهائية وأنت تكتب الاختبارات. لا تسمح أبدًا للوكيل بتحليل المشكلة والتحقق من إجابته، وإلا فقد فقدت فحصك المستقل.
إرشادي موثوق: إذا كان من الصعب كتابة اختبار لوحدة ما، فهي تفعل الكثير. قم بتقسيمها. الوظيفة التي لا يمكنك اختبارها هي وظيفة لا يمكنك التحقق منها، مما يعني أنه لا يمكنك أيضًا الوثوق بما أنشأه الوكيل لها. قابلية الاختبار والتحلل الجيد هما نفس المهارة.
هذا هو الجسر من "يمكنني التحقق من وظيفة واحدة" إلى "يمكنني بناء شيء كامل باستخدام الذكاء الاصطناعي". هذه هي بالضبط الطريقة التي ينمو بها مشروع الكتاب SmartNotes: قطعة واحدة متحللة ومختبرة في كل مرة. ستتدرب على ذلك في المشروع 5 المصغر، ثم من الصفر بدون سقالات في المشروع 6.
بمجرد قيامك بتحليل الهدف، يصبح هذا قالبًا يمكنك لصقه في Claude Code أو OpenCode في كل مرة. إنه يشمل النظام بأكمله لهذه الدورة: الاختبارات أولاً، وموافقتك قبل الكود، والتحقق في النهاية:
I want to build: [your one-sentence goal]
First, break it into 3–5 small functions or classes.
For each unit, propose ONLY:
1. a typed signature (inputs and output)
2. expected behavior in one line
3. edge cases to consider
4. pytest tests
Do NOT write any implementation yet. Wait for me to approve the tests.
After I approve, generate the implementation to pass those tests.
Then run pytest, pyright, and ruff, and show me only the
changed files and the test results.
الكلمتان الحاملتان هما فقط وليس: إنهما تمنعان الوكيل من المضي قدمًا وكتابة التعليمات البرمجية (والاختبارات الخاصة به) قبل أن تقرر معنى كلمة "صحيح". يمكنك البقاء متحكمًا في المواصفات؛ الوكيل يقوم بالكتابة.
الجزء الخامس: الحكم
متى لا تعتمد على الذكاء الاصطناعي - وكيفية التحقق مما لا يمكنك قراءته بالكامل
إن طلاقة القراءة لها سقف، والصدق فيها هو في حد ذاته مهارة.
- الرموز الحساسة للأمان غير قابلة للتفاوض. تتم قراءة أي شيء يتعلق بكلمات المرور، أو مفاتيح واجهة برمجة التطبيقات، أو المدفوعات، أو بيانات المستخدم بعناية، ويفضل أن تتم مراجعته بواسطة شخص يعرف المجال. الذكاء الاصطناعي يكتب بثقة تعليمات برمجية غير آمنة. إذا لم تتمكن من التحقق منه، فلا تقم بشحنه. (راجع البرمجة في عصر الذكاء الاصطناعي، المرحلة 8 لمراجعة الأمان.)
- عندما لا تستطيع قراءتها حقًا، اعتمد أكثر على الاختبارات، وليس على الإيمان. إذا كانت الوظيفة التي تم إنشاؤها تتجاوز مستواك الحالي، فإن فحوصاتك
pytestتصبح أكثر أهمية، وليس أقل: إنها الجزء الذي يمكنك التحقق منه. إن الاختبارات الخضراء على المدخلات التي اخترتها هي دليل حقيقي؛ "يبدو صحيحا" ليس كذلك. - التغييرات الصغيرة والواضحة تكون أحيانًا أسرع يدويًا. إن إعادة تسمية متغير واحد أو إصلاح رقم واحد يمكن أن يكون إجراؤها بنفسك أسرع من وصفها. التعرف على هذه اللحظات يأتي مع الممارسة.
الفريق الأحمر الاختبارات الخاصة بك
اختباراتك هي وسيلة التحقق الوحيدة التي تتحكم فيها بشكل كامل، لذا فإن الاختبار الضعيف أسوأ من عدم وجود اختبار، لأنه يعطي ثقة زائفة. عندما يقوم الوكيل بإنشاء رمز لا يمكنك قراءته بالكامل (مدقق Pydantic المخادع، كتلة async)، فإن اختباراتك تحمل العبء بالكامل. جعلهم متعارضين:
- لا تدع الوكيل أبدًا يكتب الاختبارات التي يجب أن يجتازها الكود الخاص به. هذا هو النمط المضاد الوحيد الذي يهزم بهدوء كل شيء آخر في هذه الدورة: إذا كتب الذكاء الاصطناعي كلاً من الكود والشيك، فلن يكون لديك إشارة مستقلة: فقط الوكيل يتفق مع نفسه. أنت تكتب الاختبارات، أو على الأقل تقرأ وتمتلك كل تأكيد. بكل الوسائل اسأل "ما هي حالات الحافة التي أفتقدها؟". ثم قرر أي منها يهم نفسك.
- لا تدع الوكيل يُضعف الاختبار أبدًا لنجاحه. عندما يفشل الاختبار، يقوم الوكيل أحيانًا "بإصلاحه" عن طريق تغيير الاختبار بدلاً من الكود: تخفيف التأكيد، وحذف حالة الحافة، وخفض القيمة المتوقعة. يتحول ذلك من اللون الأحمر إلى الأخضر مع ترك الحشرة في مكانها. انتبه لذلك ولا تسمح به إلا إذا قررت أن الاختبار نفسه كان خاطئًا حقًا. الاختبار هو المواصفات. لا يحق للوكيل إعادة كتابة المواصفات لتناسب الكود الخاص به.
- اختبر حالات الفشل، وليس فقط المسار السعيد. اسأل كل وحدة: *ما المدخلات التي قد تؤدي إلى كسر هذا؟ * قائمة فارغة، صفر، رقم سالب، مفتاح مفقود، نص حيث كان الرقم متوقعًا. اختبأ الخطأ في المشروع 4 في الحالة التي لم يختبرها أحد.
- إذا لم تتمكن من التفكير في اختبار واحد لها، فأنت لم تفهمه بعد. وبالتأكيد لا يمكنك التحقق من إصدار الوكيل. هذه هي الإشارة الخاصة بك لمزيد من التحلل (المفهوم 17) أو أن تطلب من الوكيل شرح الكود سطرًا تلو الآخر قبل قبوله.
إرشادي غامض يجب الاحتفاظ به: تثبت الاختبارات الخضراء أن الكود الخاص بك يفعل ما تقوله اختباراتك. لا أكثر. جودة التحقق الخاص بك هي بالضبط جودة اختباراتك. (يتم ترك الملفات، والمكتبة القياسية، والنظام البيئي للحزمة، والتصحيح الأعمق، والمراجعة الأمنية الحقيقية عمدًا ل البرمجة في عصر الذكاء الاصطناعي. تمنحك هذه الدورة التدريبية المكثفة الحكم؛ ويمنحك هذا الجزء العمق.)
راجع الفرق قبل قبوله
عندما تعمل في Claude Code أو OpenCode، فإن الوكيل لا يجيب فقط. * يغير الملفات * . قراءة الكود هي نصف المهمة؛ النصف الآخر يقرأ ما تغير. قبل قبول أي مجموعة من التغييرات التي تم إنشاؤها، قم بتشغيل هذه القائمة:
- ما هي الملفات التي تم تغييرها؟ لا ينبغي لطلب إصلاح وظيفة واحدة أن يمس خمسة ملفات بهدوء.
- هل غيّر الاختبارات؟ إذا تم تعديل اختباراتك، فاكتشف السبب قبل أي شيء آخر (انظر النمط المضاد أعلاه).
- هل أضافت تبعيات؟ الحزم الجديدة تعني كودًا جديدًا لم تكتبه قيد التشغيل على جهازك. تأكد من أن كل واحدة منها حقيقية وضرورية.
- هل لمست أي شيء حساس؟ الأسرار، والمفاتيح، والمصادقة، والمدفوعات، وبيانات المستخدم: تتم قراءتها بعناية أو مراجعتها من قبل شخص يعرف المجال.
- **هل مر
pytestوPyright وRuff جميعًا؟ ** اللون الأخضر في جميع أنحاء الثلاثة هو الأرضية، وليس السقف. - **هل يمكنك شرح الوظيفة أو الفصل الرئيسي باللغة الإنجليزية البسيطة؟ ** إذا لم تتمكن من ذلك، فلن تتمكن من التحقق منها: اطلب من الوكيل أن يرشدك سطرًا تلو الآخر قبل الموافقة.
هذه عادة سريعة وليست احتفالية. لكن "قبول الكل" دون قراءة الفرق هو كيف ينزلق تغيير خاطئ صغير إلى مشروع عمل دون أن يلاحظه أحد.
الخطوط العريضة لهذه الدورة التدريبية بأكملها: ** يقوم الوكيل بإحضار الكود؛ أنت تصدر الحكم حول ما إذا كان صحيحًا أم لا. ** يعتمد هذا الحكم على القراءة بطلاقة وعلى الاختبارات التي كتبتها بنفسك. لا يعتبر أي منهما اختياريًا، ولا يعد أيًا منهما صعبًا بمجرد التدرب على الحلقة.
الجزء السادس: مشاريع الممارسة
القراءة عن الحلقة ليست مثل تشغيلها. تنقلك هذه المشاريع الستة عبر PRIMM-AI+ بالترتيب: من قراءة التعليمات البرمجية، إلى اختبارها، إلى إنشاء شيفرة خاصة بك، إلى التحقق من التعليمات البرمجية الحقيقية على نمط الوكيل مع وجود خطأ مخفي فيها، إلى إنشاء أداة صغيرة شاملة تستخدم كل مفهوم في وقت واحد، وأخيرًا إلى بناء واحد من هدف من جملة واحدة دون أي سقالات على الإطلاق. افعلها في جلسة Claude Code أو OpenCode بالترتيب؛ كل واحد يفترض المهارات من الذي سبقه.
يخبرك كل مشروع بالهدف والمهارات التي يمارسها، ثم يمنحك بداية. جرب ذلك بنفسك أولاً: اكتب تنبؤاتك، ثم اكتب اختباراتك، ثم أخبر الوكيل. وعندها فقط افتح الحل. الحلول ليست موجودة لنسخها: يمكن للوكيل إنشاء الكود لك على أي حال. إنها موجودة حتى تتمكن من التحقق من صحة مواصفاتك واختباراتك. هذه هي المهارة التي يتم تصنيفها.
الرمز المرجعي هو الجزء الأقل أهمية في كل حل. ما يهم هو: هل حددت اختباراتك معنى كلمة "صحيح"؟ هل توقعت النتيجة بشكل صحيح؟ هل يمكنك قراءة النتيجة والقول أنها كانت صحيحة؟ إذا مرت اختباراتك بالرمز المرجعي، فقد حددت المشكلة جيدًا. هذا هو الفوز.
المشروع 1 - القراءة والتنبؤ (توقع · تشغيل · استقصاء)
المهارات: وظائف القراءة والفهم وlen؛ التنبؤ بالإخراج قبل التشغيل
بداية. لا تقم بتشغيل هذا بعد. اقرأها واكتب ما سينتجه كل سطر من السطور الثلاثة print:
def shout(text: str) -> str:
return text.upper() + "!"
names: list[str] = ["ada", "alan", "grace"]
greetings: list[str] = [shout(n) for n in names if len(n) > 3]
print(len(names))
print(greetings)
print(shout("hi"))
الآن قم بتشغيله في جلستك وقارنه. عندما كان توقعك خاطئًا، اسأل الوكيل "لماذا أنتج السطر X ذلك؟". هذه الفجوة هي الدرس.
الحل
3
['ALAN!', 'GRACE!']
HI!
len(names)→3: ثلاثة عناصر في القائمة.greetings→ فقط الأسماء التي يزيد طولها عن 3 أحرف هي التي تمر بمرشحif len(n) > 3."ada"هو بالضبط 3 (فشل)،"alan"هو 4 و"grace"هو 5 (نجاح). يتم الصراخ على كل منهم، معطيًا['ALAN!', 'GRACE!'].shout("hi")→"HI!": بأحرف كبيرة، مع إضافة!.
إذا قرأت الفهم ك "أصرخ بكل اسم، لكل اسم في الأسماء، إذا كان أطول من 3 أحرف،" فقد قرأته بشكل صحيح.
المشروع 2 — أول دورة TDG لك على وظيفة (إنشاء)
المهارات: كتابة الاختبارات assert كمواصفات، ومطالبة الوكيل، والتحقق باستخدام pytest.
الهدف. أنشئ دالة initials(full_name: str) -> str تقوم بإرجاع الأحرف الأولى من الاسم: "ada lovelace" → "AL".
البادئ. اكتب الاختبارات أولاً، في test_initials.py، قبل وجود أي تطبيق. قم بتغطية الحالة العادية وحالتين على الأقل من الحالات (اسم واحد؛ مسافات إضافية). ثم اطلب من وكيلك:
اقرأ
test_initials.py. قم بإنشاءinitials.pyباستخدام وظيفةinitials(full_name: str) -> strالتي تجعل كل اختبار ناجحًا. ثم قم بتشغيلpytestوأريني النتيجة.
اقرأ الكود الذي ينشئه قبل أن تثق في الشيكات الخضراء.
الحل
قد تبدو اختباراتك (الجزء المهم) كما يلي:
from initials import initials
def test_two_names() -> None:
assert initials("ada lovelace") == "AL"
def test_many_names() -> None:
assert initials("grace brewster murray hopper") == "GBMH"
def test_single_name() -> None:
assert initials("alan") == "A"
def test_extra_spaces() -> None:
assert initials(" extra spaces ") == "ES"
تطبيق مرجعي يمررهم جميعًا:
def initials(full_name: str) -> str:
return "".join(part[0].upper() for part in full_name.split())
full_name.split() يقسم النص على المسافات (ويتجاهل المسافات الإضافية)، part[0] يأخذ الحرف الأول من كل قطعة، .upper() يكتبه بالأحرف الكبيرة، و"".join(...) يلصقهما معًا. إذا كتبت اختبار extra_spaces، فقد قمت بالتحقق من الحالة الصعبة بنفسك: وهذا هو بالضبط الحكم الذي لا يستطيع الوكيل تقديمه.
المشروع 3 — TDG في فصل دراسي (صنع)
المهارات: قراءة وتحديد الفصل الدراسي، وطرق تغيير حالة الكائن، واختبار سلوك الكائن.
الهدف. أنشئ TaskList صغيرًا: بذرة شيء مثل SmartNotes الموجود في الكتاب. يمكنه إضافة المهام، ووضع علامة على واحدة مكتملة، والإبلاغ عن عدد المهام التي لا تزال غير مكتملة.
بداية. اكتب الاختبارات أولا. قرر ما تسمى الأساليب وماذا تعود. هذا * هو * التصميم. نقطة البداية:
def test_task_flow() -> None:
tasks = TaskList()
tasks.add("write tests")
tasks.add("call agent")
assert tasks.remaining() == 2
tasks.complete("write tests")
assert tasks.remaining() == 1
ثم اطلب من الوكيل تنفيذ فئة TaskList التي تنجح في اجتياز الاختبار والتحقق.
الحل
التنفيذ المرجعي:
class TaskList:
def __init__(self) -> None:
self.tasks: dict[str, bool] = {} # task name -> done?
def add(self, name: str) -> None:
self.tasks[name] = False
def complete(self, name: str) -> None:
if name in self.tasks: # only complete a task that exists
self.tasks[name] = True
def remaining(self) -> int:
return sum(1 for done in self.tasks.values() if not done)
يقوم الفصل بتخزين المهام في إملاء يعين كل اسم إلى علامة done. remaining() يحسب العناصر التي لا تزال مضبوطة على False. لاحظ كيف قاد الاختبار التصميم: لقد قرر أسماء الطرق وما يُرجعه remaining()، قبل وجود أي كود.
اكتشف الخطأ الصامت بنفسك. ما الذي يجب على complete("a task I never added") فعله؟ غالبًا ما يكتب الإصدار الأول للوكيل self.tasks[name] = True بدون حارس if name in self.tasks، والذي يضيف بصمت مهمة جديدة تمامًا تم وضع علامة "تم إنجازها"، مما يخترع بيانات لم يطلبها أحد. هذا هو بالضبط السلوك المعقول ولكن الخاطئ الذي يكشفه الاختبار:
def test_completing_unknown_task_does_nothing() -> None:
tasks = TaskList()
tasks.add("real task")
tasks.complete("typo task") # not a real task
assert tasks.remaining() == 1 # still one; nothing was invented
ملحق: اطلب من وكيلك أن يجعل كل مهمة نموذج Pydantic Task مع طابع زمني created_at، ثم أضف اختبارًا يحتوي عليه TaskList الجديد تمامًا remaining() == 0.
المشروع 4 — ابحث عن الخطأ في رمز الوكيل (التحقيق · التعديل)
المهارات: التعرف على مفاهيم الطاقة (Pydantic، المولدات)، والقراءة النقدية، وكتابة اختبار يكشف عن خطأ حقيقي.
هذا هو أقرب شيء إلى وظيفتك الفعلية: يسلمك الوكيل تعليمات برمجية تبدو معقولة، وعليك اكتشاف الخطأ. من المفترض أن تقوم الوظيفة أدناه بإرجاع عناوين الملاحظات التي تم الانتهاء منها فقط. ولكن لديها خلل.
from collections.abc import Iterator
from pydantic import BaseModel
class Note(BaseModel):
title: str
done: bool
def completed_titles(notes: list[Note]) -> Iterator[str]:
for note in notes:
yield note.title
** مهمتك بالترتيب: **
- تحقيق. بلغة إنجليزية بسيطة، اذكر الهدف من كل سطر. (ما هو النموذج
Note؟ ماذا يخبركIterator[str]؟ ماذا يفعلyield؟) - اختبار. اكتب اختبارًا
pytestيثبت السلوك المقصود: فقط الملاحظات المكتملة هي التي تعود. تشغيله. يجب أن تفشل، وتلتقط الخطأ. - تعديل. الآن بعد أن حدد اختبارك الصحيح، اطلب من الوكيل إصلاح
completed_titles، وأعد التشغيل حتى يتحول لون الاختبار إلى اللون الأخضر.
الحل
الخطأ: تنتج الوظيفة عنوان كل ملاحظة، متجاهلة done بالكامل. لا يتحقق العلم أبدًا.
الاختبار الذي يمسك به:
def test_only_completed() -> None:
notes = [Note(title="a", done=True), Note(title="b", done=False)]
assert list(completed_titles(notes)) == ["a"]
يفشل هذا في مقابل رمز الأخطاء، لأنه يُرجع ["a", "b"]: دليل على أن الخطأ حقيقي، وليس حدسًا.
الإصلاح هو سطر واحد: تحقق done قبل الخضوع:
def completed_titles(notes: list[Note]) -> Iterator[str]:
for note in notes:
if note.done:
yield note.title
الهدف من هذا المشروع: كان الخطأ غير مرئي للوهلة الأولى وواضحًا للاختبار. "يبدو صحيحًا" كان سيشحنه. اختبار كتبته اشتعلت فيه. هذه هي الدورة بأكملها في تمرين واحد.
المشروع 5 — التتويج المصغر: أداة الملاحظات، من البداية إلى النهاية (الحلقة بأكملها)
المهارات: كل شيء، نموذج Pydantic، فئة ذات حالة، مولد، تلميحات الكتابة، ملخص f-string، ومجموعة اختبار، تم إنشاؤها كدورة TDG واحدة. هذه بروفة مصغرة ل SmartNotes الخاصة بالكتاب.
الهدف. أنشئ NoteBook صغيرًا يحتوي على ملاحظات (كل منها بعنوان ونص وعلامة تم)، ويتيح لك إضافة ملاحظات ووضع علامة عليها كاملة، وبث الملاحظات التي لا تزال معلقة، وطباعة ملخص من سطر واحد مثل 2 notes, 1 done, pending: call sara.
المبتدئ: قم بتصميمه من خلال الاختبارات. هذا هو التمرين الحقيقي: قبل كتابة أو إنشاء أي تنفيذ، اكتب test_notebook.py الذي يحدد السلوك الذي تريده. حدد أسماء الطرق وما يجب أن يقوله summary(). هيكل عظمي للرد على:
from notebook import NoteBook
def test_summary_flow() -> None:
nb = NoteBook()
nb.add("groceries", "milk, eggs")
nb.add("call sara", "about the trip")
assert nb.summary() == "2 notes, 0 done, pending: groceries, call sara"
nb.complete("groceries")
assert nb.summary() == "2 notes, 1 done, pending: call sara"
def test_pending_is_a_stream() -> None:
nb = NoteBook()
nb.add("a", "x")
nb.add("b", "y")
nb.complete("a")
assert [n.title for n in nb.pending()] == ["b"] # pending() yields, like a generator
ثم قم بتشغيل الحلقة الكاملة:
- قم بتشغيل
pytest. لقد فشل (لا يوجدnotebook.pyحتى الآن). جيد: هذه هي المواصفات الخاصة بك، والفشل عمدا. - اطلب من وكيلك: "اقرأ
test_notebook.py. قم بإنشاءnotebook.pyباستخدام نموذج PydanticNote(العنوان، النص، الانتهاء) وفئةNoteBookالتي تجعل كل اختبار ناجحًا:add،complete، ومولدpending()، ومولدsummary()إرجاع السلسلة الدقيقة التي تتوقعها الاختبارات، ثم قم بتشغيل pytest وأريني النتيجة." - اقرأ ما تم إنشاؤه. هل يمكنك الإشارة إلى نموذج Pydantic، وحالة الفصل، و
yieldفيpending()، والسلسلة f فيsummary()؟ إذا كانت الإجابة بنعم، فقد قرأت جزءًا حقيقيًا من رمز الوكيل. قم بتشغيلpyrightوruffعليه أيضًا. - كرر أي فشل من خلال قراءته، وليس إعادة المطالبة بشكل أعمى.
الحل
تطبيق مرجعي يجتاز كلا الاختبارين:
from collections.abc import Iterator
from pydantic import BaseModel
class Note(BaseModel):
title: str
body: str
done: bool = False
class NoteBook:
def __init__(self) -> None:
self.notes: list[Note] = []
def add(self, title: str, body: str) -> None:
self.notes.append(Note(title=title, body=body))
def complete(self, title: str) -> None:
for note in self.notes:
if note.title == title:
note.done = True
def pending(self) -> Iterator[Note]: # a generator: yields one note at a time
for note in self.notes:
if not note.done:
yield note
def summary(self) -> str:
total = len(self.notes)
done = sum(1 for n in self.notes if n.done)
pending_titles = [n.title for n in self.pending()]
return f"{total} notes, {done} done, pending: {', '.join(pending_titles)}"
يظهر هنا كل مفهوم من الدورة التدريبية: نموذج Pydantic (Note) مع تلميحات الكتابة والقيمة الافتراضية، class (NoteBook) حالة الاحتفاظ، طريقة التي تعدل سمة الكائن (complete)، المولد (pending) الذي yields، والفهم وsum(...)، وf-string لبناء الملخص.
**اجعلها واجهة سطر الأوامر (CLI) حقيقية (الجسر إلى دورة البرمجة في عصر الذكاء الاصطناعي). ** الجزء الأساسي الذي تم اختباره أعلاه هو الجزء الصعب؛ إن تحويلها إلى أداة سطر أوامر هو عبارة عن غلاف رقيق في الأعلى. اطلب من وكيلك إضافة main() الذي يقرأ أوامر مثل add وdone وlist من الوحدة الطرفية ويستدعي هذه الأساليب. ثم اطلب منه كتابة اختبار إضافي لأي منطق جديد. هذه الخطوة الأخيرة، وهي واجهة سطر الأوامر (CLI) العاملة مع مجموعة اختبار النجاح خلفها، هي بالضبط شكل SmartNotes، ولكنها أصغر حجمًا.
قرر حالة الحافة بنفسك. ما الذي يجب أن يقوله summary() عندما لا يوجد شيء معلق؟ يترك المرجع زائدة pending: . هل هذا صحيح؟ اكتب اختبارًا يشفر قرارك، ثم اجعل الوكيل يرضي به. إن تحديد ما تعنيه كلمة "صحيح" هنا، وتثبيته باختبار، هو المهمة بأكملها.
المشروع 6 - بمفردك: لا يوجد بداية ولا اختبارات (قم بتحليله بنفسك)
المهارات: المفهوم 17 مع إيقاف عجلات التدريب. كل مشروع حتى الآن قدم لك هيكلًا اختباريًا أو توقيعًا. هذا واحد يعطيك جملة واحدة فقط.
الهدف. "إنشاء أداة تأخذ فقرة من النص وتطبع الكلمات الثلاث الأكثر شيوعًا."
هذا كل ما تحصل عليه. لا توقيعات ولا اختبارات ولا تلميحات حول القطع. عملك هو الجبهة كلها 10٪:
- تحلل. اكتب الخطوات التي ستنفذها يدويًا، وحوّل كل منها إلى توقيع مكتوب. (كيف يمكنك تقسيم النص إلى كلمات؟ إزالة علامات الترقيم؟ عدّها؟ ترتيبها؟)
- المواصفات. لكل قطعة، اكتب اختبار
pytestفاشلًا يحدد "الصحيح": قبل إنشاء أي شيء. - التوليد والتحقق. اطلب من الوكيل تنفيذ كل وحدة وفقًا لاختباراتك؛ تشغيل
pytest،pyright،ruff؛ أعاد. - ** قم بتوصيلهما معًا ** في
report()واختبر التدفق بالكامل.
لا توجد إجابة واحدة صحيحة: فقط التحليلات حيث تكون كل قطعة صغيرة وقابلة للاختبار. هذه هي المهارة التي يتم تصنيفها.
الحل (تحليل واحد صالح - قد يختلف تحليلك)
تقسيم معقول إلى أربع وحدات صغيرة قابلة للاختبار بشكل مستقل:
def clean(text: str) -> list[str]:
# lowercase, drop punctuation, split into words
cleaned = "".join(c.lower() if c.isalnum() or c.isspace() else " " for c in text)
return cleaned.split()
def tally(words: list[str]) -> dict[str, int]:
counts: dict[str, int] = {}
for word in words:
counts[word] = counts.get(word, 0) + 1
return counts
def top_n(counts: dict[str, int], n: int) -> list[tuple[str, int]]:
return sorted(counts.items(), key=lambda pair: pair[1], reverse=True)[:n]
def report(text: str, n: int) -> str:
pairs = top_n(tally(clean(text)), n)
return ", ".join(f"{word} ({count})" for word, count in pairs)
ونوع الاختبارات التي يجب أن تكتبها أولاً:
def test_clean_strips_punctuation() -> None:
assert clean("Hi, hi! Bye.") == ["hi", "hi", "bye"]
def test_tally_counts() -> None:
assert tally(["hi", "hi", "bye"]) == {"hi": 2, "bye": 1}
def test_report_picks_top_two() -> None:
assert report("the cat sat on the mat the cat", 2) == "the (3), cat (2)"
قرار واحد يستحق اتخاذه عن قصد: عندما يكون لكلمتين نفس العدد، أيهما يأتي أولاً؟ إن sorted الخاص ب Python مستقر، لذا تعود الروابط إلى ترتيب الظهور الأول هنا. لكن عليك أن تقرر ذلك عمداً وتثبته بالاختبار، لا أن تكتشفه بالصدفة. (إذا لم تفكر في ربطات العنق على الإطلاق، فهذه فجوة كان من المفترض أن تظهرها اختباراتك.)
إذا كان تحليلك يحتوي على أجزاء مختلفة (على سبيل المثال، دالة word_frequencies واحدة) ولكن كل قطعة كانت قابلة للاختبار بشكل مستقل وتم اجتياز اختباراتك، لقد قمت بذلك بشكل صحيح. الدرجة لا تتطابق مع هذا الرمز: إنها ما إذا كنت قد حولت هدفًا من جملة واحدة إلى عقود قابلة للاختبار بنفسك. هذه هي بالضبط المهارة التي ستعتمد عليها في كل مشروع تقوم بإنشائه باستخدام الذكاء الاصطناعي بعد ذلك.
هذه هي عمليات الاحماء. المشروع التدريبي الحقيقي هو SmartNotes في البرمجة في عصر الذكاء الاصطناعي. يمكنك تطوير تطبيق واحد عبر تسع مراحل باستخدام هذه الحلقة بالضبط، ثم إنشاء تطبيق آخر (QuizForge) من الصفر دون أي توجيه لإثبات ملكيتك للمهارة.
ما هي الخطوة التالية
يمكنك الآن قراءة الأشكال الأساسية التي تشكل معظم لغة بايثون (القيم، والوظائف، والمجموعات، وتدفق التحكم، والفئات)، والتعرف على مفاهيم القوة الخمسة التي تملأ كود الوكيل وتعلم الآلة، وتشغيل دورة توليد كاملة مبنية على الاختبار في Claude Code أو OpenCode، وتقسيم هدف غامض إلى أجزاء محددة، وقد تدربت على كل ذلك في ستة مشاريع. هذه هي معرفة القراءة والكتابة التي يفترضها بقية الكتاب.
هل مررت؟ فحص ذاتي
لقد نجحت هذه الدورة التدريبية إذا كان بإمكانك الآن القيام بكل هذه الأمور السبعة بدون ملاحظات. إذا كان هناك أي شعور بالاهتزاز، قم بإعادة النظر في المفهوم الموجود بين قوسين قبل المضي قدمًا:
- اقرأ توقيع الوظيفة واشرح مدخلاتها ومخرجاتها باللغة الإنجليزية البسيطة. (المفهوم 5)
- اكتب ثلاثة اختبارات
pytestعلى الأقل للوحدة قبل وجود أي رمز. (المفاهيم 5، 16) - اطلب من وكيل الذكاء الاصطناعي تنفيذ تعليمات برمجية مقابل تلك الاختبارات باستخدام التوقيع المكتوب. (المفاهيم 10، 16)
- قم بتشغيل
pytestوPyright وRuff، واقرأ ما يخبرك به كل منهم. (المفهوم 3) - اقرأ تتبع التتبع من الأسفل إلى الأعلى ووصف المشكلة بدقة. (المفهوم 16)
- اكتشف خطأً معقولاً ولكنه خاطئ في التعليمات البرمجية التي أنشأها الذكاء الاصطناعي. (المشروع 4)
- قم بتحليل هدف غامض مكون من جملة واحدة إلى وحدات قابلة للاختبار. (الفكرة 17، المشروع 6)
إذا كنت تستطيع القيام بكل هذه الأمور السبعة، فيمكنك الجلوس مع Claude Code أو OpenCode وإرسال برنامج بايثون صغير تم اختباره وتفهمه بالفعل. هذا هو الهدف كله.
من هنا:
- إنشاء وكلاء الذكاء الاصطناعي: حيث تصبح نماذج Pydantic،
async/await، وتلميحات الكتابة التي تعلمت للتو كيفية التعرف عليها هي المادة اليومية لرمز الوكيل. - Postgres for AI: قراءة الكود الذي يقوم بتخزين واسترداد بيانات وكلائك.
- بناء FTE رقمي: تجميع كل شيء في عامل يعمل بالذكاء الاصطناعي.
- البرمجة في عصر الذكاء الاصطناعي: الدورة التدريبية العميقة الكاملة. يمكنك إنشاء SmartNotes عبر تسع مراحل، بدءًا من القارئ إلى المهندس المعماري، وامتلاك المزيد من دورة TDG في كل خطوة.
أنت لا تتعلم كتابة التعليمات البرمجية. أنت تتعلم توجيه والتحقق من الأنظمة التي تكتبها. كانت القراءة دائمًا هي النصف الأصعب. وقد بدأت للتو.
معجم مدته 60 ثانية
| شرط | الإنجليزية عادي |
|---|---|
| PRIMM-AI+ | توقع · تشغيل · تحقيق · تعديل · إنشاء — طريقة القراءة أولاً لهذه الدورة التدريبية، حيث يكون الوكيل شريكًا والاختبارات هي الحقيقة |
| TDD → TDG | التطوير القائم على الاختبار: اكتب الاختبار الفاشل، ثم اكتب الكود. في عصر الذكاء الاصطناعي، أصبح الجيل القائم على الاختبار - تكتب الاختبار الفاشل، ويكتب الوكيل الكود، وتتحقق من اجتيازه |
| تدج | الجيل القائم على الاختبار — اكتب الاختبار الفاشل أولاً، ثم يقوم الوكيل بإنشاء الكود، ثم تقوم أنت بالتحقق منه |
assert | المطالبة المكونة من سطر واحد مثل assert x == 5 — لا تفعل شيئًا إذا كانت صحيحة، وتتعطل بصوت عالٍ إذا كانت خاطئة. الذرة التي يُبنى منها كل اختبار |
| اكتب/اكتب تلميح | تسمية لنوع البيانات (str، int، float، bool) - وتعليمات دقيقة للوكيل |
| ** الوظيفة / التوقيع ** | كتلة مسماة قابلة لإعادة الاستخدام؛ خطها الأول هو عقد المدخلات والمخرجات |
| ** قائمة / إملاء / مجموعة / صف ** | الحاويات الأربع التي تقوم بتجميع البيانات؛ dict (الأزواج المسماة) هو الذي ستقرأه أكثر |
| الفئة/الكائن | الفصل عبارة عن مخطط. الكائن (المثيل) هو الشيء المبني منه. object.attribute يقرأ بياناته؛ object.method() يطلب منه التصرف |
**self / السمة / الطريقة ** | self هو "هذا الكائن" داخل الفصل الدراسي؛ السمة هي البيانات التي تحملها؛ الطريقة هي دالة تعيش في الفصل وتعمل على الكائن |
| فهم | حلقة for مطوية في سطر واحد لإنشاء قائمة جديدة |
| ** سلسلة و ** | أرسل رسالة نصية تحتوي على f في المقدمة؛ أي شيء داخل {...} يتم استبداله بقيمته (f"{name}") |
المولد / yield | قم بإعادة العناصر واحدًا تلو الآخر لإبقاء الذاكرة مسطحة - تدفقًا وليس كومة |
with (مدير السياق) | يفتح شيئًا ويستخدمه ويغلقه بأمان حتى في حالة الخطأ |
async / await | تم تصميم التعليمات البرمجية للقيام بالعديد من الأشياء البطيئة (مثل استدعاءات واجهة برمجة التطبيقات) في نفس الوقت |
** دوندر (__call__، __init__)** | أساليب الشرطة السفلية المزدوجة التي تجعل كائناتك تتصرف مثل أشياء Python الأصلية |
مصمم الديكور (@name) | تسمية فوق دالة أو فئة تضيف سلوكًا إليها |
| بيانتيك | التحقق من صحة البيانات المنظمة وإنشاء مخططات JSON التي تستخدمها LLMs لاستدعاء الأدوات تلقائيًا |
| **بيتيست / بيرايت / راف / أوف ** | أدوات التحقق الخاصة بك: إجراء الاختبارات / التحقق من الأنواع / التحقق من النمط / إدارة لغة Python |
| تتبع | تقرير خطأ بايثون - اقرأه من الأسفل إلى الأعلى؛ السطر الأخير يسمي المشكلة |