Skip to content

4. حل المشكلات الثلاث باستخدام Google Gemini

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

4.1. مقدمة

نذكر لقطة الشاشة الأولى لـ Gemini التي تم عرضها سابقًا:

 
  • في [1]، نسخة Gemini من URL؛
  • في [2]، نسخة Gemini المستخدمة؛
  • في [3-5]، المشاكل الثلاث التي طُرحت على Gemini؛

Gemini هو منتج من Google متاح على URL [https://gemini.google.com/]. للحصول على سجل لجلسات الأسئلة والأجوبة الخاصة بك كما هو موضح أعلاه، يتعين عليك إنشاء حساب. بالإضافة إلى ذلك، مثل جميع الخدمات الأخرى التي تم اختبارها، يحدد Gemini عدد الأسئلة وعدد الملفات التي يمكن تحميلها. وعند بلوغ هذا الحد، تنتهي الجلسة ويُعرض عليك متابعتها لاحقًا. ونظرًا لأن التوقف في منتصف الجلسة أمر مثير للإحباط إلى حد ما، فقد اشتركت في الخدمة. لحسن الحظ، الشهر الأول من اشتراك Gemini مجاني. وقد فعلت الشيء نفسه مع أدوات IA الأخرى التي كانت تفرض هذه القيود، وهي ChatGPT وMistralAI وClaudeAI. اشتركت في اشتراك لمدة شهر واحد، لكن في هذه الحالة كان الشهر الأول مدفوعًا. لم أواجه أي قيود مع Grok. لا يعلن DeepSeek عن أي قيود، لكنه يرد أحيانًا بـ [Server busy] ويقطع الجلسة. وهذا يعادل فرض قيود دون الإعلان عنها.

فيما يلي، سأتحدث عن «جلسة الأسئلة والأجوبة»، والتي سأختصرها إلى «جلسة» فقط. غالبًا ما يستخدم مستخدمو IA المصطلح الإنجليزي «chat» (محادثة) أو «conversation» (حوار).

واجهة Gemini لطرح سؤال هي كما يلي:

  • في [1]، سؤالك؛
  • في [2]، الرمز الذي يطلق IA لحساب الإجابة؛
  • في [3-4]، يمكنك إرفاق الملفات؛

4.2. المسألة 1

الجلسة الخاصة بالمشكلة 1 هي كما يلي:

 
  • في [1]، السؤال؛
  • في [2]، بداية إجابة Gemini؛

وتأتي بقية الإجابة على النحو التالي:

 
 
 
 

الإجابة صحيحة. وستعطي الإجابات الخمس الأخرى IA الإجابة الصحيحة أيضًا بشكل مشابه.

4.3. المسألة 2

4.3.1. مقدمة

نذكر هنا المسألة الأصلية للدورة التدريبية [python3-flask-2020]. وهو نص مُقدَّم في TD للطلاب.

 

يسمح الجدول أعلاه بحساب الضريبة في الحالة المبسطة للمكلف الذي لا يعلن سوى راتبه الوحيد. وكما تشير الملاحظة (1)، فإن الضريبة المحسوبة بهذه الطريقة هي الضريبة قبل تطبيق الآليات الثلاث التالية:

  • تحديد سقف للحصة العائلية الذي ينطبق على ذوي الدخل المرتفع؛
  • الخصم والتخفيض الضريبي اللذين ينطبقان على الدخل المنخفض؛

وبالتالي، يتضمن حساب الضريبة الخطوات التالية [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

نقترح كتابة برنامج يسمح بحساب الضريبة المستحقة على المكلف في عام 2019 في الحالة المبسطة للمكلف الذي لا يعلن سوى راتبه الوحيد.

4.3.1.1. حساب الضريبة الإجمالية

يمكن حساب الضريبة الإجمالية بالطريقة التالية:

نحسب أولاً عدد الحصص الخاصة بالمكلف:

  • يُحسب لكل والد حصة واحدة؛
  • يُحسب لكل من الطفلين الأولين نصف حصة؛
  • يحصل كل طفل من الأطفال التاليين على حصة واحدة:

وبالتالي، فإن عدد الحصص هو:

  • nbParts=1+nbEnfants*0,5+(عدد_الأطفال-2)*0,5 إذا كان الموظف غير متزوج؛
  • nbParts=2+nbEnfants*0,5+(nbEnfants-2)*0,5 إذا كان متزوجًا؛
  • حيث nbEnfants هو عدد أطفاله؛
  • يُحسب الدخل الخاضع للضريبة R=0.9*S حيث S هو الراتب السنوي؛
  • يُحسب الحصّة العائلية QF=R/nbParts؛
  • يُحسب الضريبة الإجمالية I بناءً على البيانات التالية (2019):
9964
0
0
27519
0.14
1394.96
73779
0.3
5798
156244
0.4
13913.69
0
0.45
20163.45

يحتوي كل سطر على 3 حقول: champ1، champ2، champ3. لحساب الضريبة I، نبحث عن السطر الأول الذي يكون فيه QF <= الحقل 1 ونأخذ قيم هذا السطر. على سبيل المثال، بالنسبة لموظف متزوج ولديه طفلان وراتب سنوي S يبلغ 50000 يورو:

الدخل الخاضع للضريبة: R=0,9*S=45000

عدد الحصص: nbParts=2+2*0,5=3

الحصة العائلية: QF=45000/3=15000

السطر الأول الذي يكون فيه QF <= الحقل 1 هو التالي:

    27519    0.14    1394.96

وبالتالي، فإن الضريبة I تساوي 0.14*R – 1394,96*nbParts = [0,14*45000-1394,96*3] = 2115. ويتم تقريب الضريبة إلى أقرب يورو أقل.

إذا كانت العلاقة QF <= حقل1 صحيحة منذ السطر الأول، فإن الضريبة تكون صفرًا.

إذا كان QF بحيث لا تتحقق العلاقة QF<=champ1 أبدًا، فسيتم استخدام معاملات السطر الأخير. هنا:

    0    0.45    20163.45

مما يعطي الضريبة الإجمالية I=0.45*R – 20163,45*nbParts.

4.3.1.2. الحد الأقصى للمنحة العائلية

 

لمعرفة ما إذا كان الحد الأقصى لحصة الأسرة QF ينطبق، نعيد حساب الضريبة الإجمالية بدون احتساب الأطفال. وبالنسبة للموظف المتزوج الذي لديه طفلان وراتب سنوي S يبلغ 50000 يورو:

الدخل الخاضع للضريبة: R=0,9*S=45000

عدد الحصص: nbParts=2 (لم يعد يتم احتساب الأطفال)

الحصة العائلية: QF=45000/2=22500

السطر الأول الذي يكون فيه QF <= الحقل 1 هو التالي:

    27519    0.14    1394.96

وبالتالي، فإن الضريبة I تساوي 0.14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.

الربح الأقصى المرتبط بالأطفال: 1551 * 2 = 3102 يورو

الضريبة الدنيا: 3510 - 3102 = 408 يورو

الضريبة الإجمالية مع حصتين، التي تم حسابها بالفعل في الفقرة السابقة، وهي 2115 يورو، تزيد عن الضريبة الدنيا البالغة 408 يورو، وبالتالي لا ينطبق سقف الضريبة العائلية في هذه الحالة.

بشكل عام، الضريبة الإجمالية تساوي (الضريبة 1، الضريبة 2) حيث:

  • [impôt1]: هو الضريبة الإجمالية المحسوبة مع الأطفال؛
  • [impôt2]: هو الضريبة الإجمالية المحسوبة بدون احتساب الأطفال، مطروحًا منها الحد الأقصى للزيادة (وهو هنا 1551 يورو لكل نصف حصة) المرتبطة بالأطفال؛

4.3.1.3. حساب الخصم

 

بالنسبة للموظف المتزوج ولديه طفلان ويبلغ راتبه السنوي S 50000 يورو:

الضريبة الإجمالية (2115 يورو) الناتجة عن الخطوة السابقة أقل من 2627 يورو للزوجين (1595 يورو للعازب): وبالتالي يُطبق الخصم. ويتم حسابه بالصيغة التالية:

التخفيض = الحد الأدنى (للزوجين = 1970 / للعازب = 1196) - 0,75 * الضريبة الإجمالية

الخصم = 1970 - 0,75 × 2115 = 383,75، مقربًا إلى 384 يورو.

الضريبة الإجمالية الجديدة = 2115 - 384 = 1731 يورو

يجب مراعاة قاعدتين عند حساب الخصم (واجهت بعض أدوات IA صعوبة في هذه المسألة):

  • لا يمكن أن يكون الخصم سالبًا؛
  • لا يمكن أن يكون الخصم أكبر من الضريبة المحسوبة بالفعل؛

4.3.1.4. حساب التخفيض الضريبي

 

تحت حد معين، يتم تطبيق تخفيض بنسبة 20% على الضريبة الإجمالية الناتجة عن الحسابات السابقة. في عام 2019، كانت الحدود كما يلي:

  • الأعزب: 21037 يورو؛
  • الزوجان: 42074 يورو؛ (يبدو أن الرقم 37968 المستخدم في المثال أعلاه خاطئ)؛

يُضاف إلى هذا الحد القيمة التالية: 3797 * (عدد نصف الحصص التي يضيفها الأبناء).

وبالنسبة للموظف المتزوج ولديه طفلان وراتب سنوي S يبلغ 50000 يورو:

  • دخله الخاضع للضريبة (45000 يورو) أقل من الحد الأدنى (42074+2*3797)=49668 يورو؛
  • وبالتالي يحق له الحصول على تخفيض بنسبة 20٪ من ضريبته: 1731 * 0,2 = 346,2 يورو، مقربًا إلى 347 يورو؛
  • يصبح الضريبة الإجمالية للمكلف: 1731 - 347 = 1384 يورو؛

4.3.1.5. حساب الضريبة الصافية

سيتوقف حسابنا عند هذا الحد: سيكون صافي الضريبة المستحقة الدفع 1384 يورو. في الواقع، يمكن للمكلف الاستفادة من تخفيضات أخرى، لا سيما تلك المتعلقة بالتبرعات المقدمة إلى المنظمات ذات المنفعة العامة أو العامة.

4.3.1.6. حالة ذوي الدخل المرتفع

المثال السابق ينطبق على غالبية حالات الموظفين. ومع ذلك، يختلف حساب الضريبة في حالة أصحاب الدخل المرتفع.

4.3.1.6.1. الحد الأقصى للتخفيض بنسبة 10% على الدخل السنوي

في معظم الحالات، يتم حساب الدخل الخاضع للضريبة بالصيغة التالية: R=0,9*S حيث S هو الراتب السنوي. ويُطلق على ذلك اسم التخفيض بنسبة 10٪. ويخضع هذا التخفيض لسقف أقصى. في عام 2019:

  • لا يمكن أن يتجاوز 12502 يورو؛
  • لا يمكن أن يقل عن 437 يورو؛

لنأخذ مثالاً لموظف غير متزوج وليس لديه أطفال ويبلغ راتبه السنوي 200000 يورو:

  • يبلغ التخفيض بنسبة 10% ما مقداره 20000 يورو > 12502 يورو. وبالتالي، يتم تخفيضه إلى 12502 يورو؛

4.3.1.6.2. الحد الأقصى للمعامل الأسري

لنأخذ حالة ينطبق فيها الحد الأقصى للمعامل الأسري المذكور في الفقرة |الحد الأقصى للمعامل الأسري|. لنأخذ مثال زوجين لديهما ثلاثة أطفال ودخل سنوي قدره 100000 يورو. لنستعرض خطوات الحساب:

  • الخصم بنسبة 10٪ هو 100000 يورو < 12502 يورو. وبالتالي، فإن الدخل الخاضع للضريبة R يساوي 100000 - 10000 = 90000 يورو؛
  • يمتلك الزوجان nbParts = 2 + 0,5*2 + 1 = 4 حصص؛
  • وبالتالي، فإن نصيب الأسرة هو QF = R/nbParts = 90000/4 = 22500 يورو؛
  • ضريبه الإجمالي I1 مع وجود أطفال هو I1=0,14*90000-1394,96*4= 7020 يورو؛
  • ضريبته الإجمالية I2 بدون أطفال:
    • QF = 90000/2 = 45000 يورو؛
    • I2=0,3*90000-5798*2=15404 يورو؛
  • تنص قاعدة الحد الأقصى للحصة العائلية على أن المكسب الناتج عن وجود أطفال لا يمكن أن يتجاوز (1551 × 4 نصف حصص) = 6204 يورو. لكن في هذه الحالة، يبلغ المكسب I2-I1 = 15404 - 7020 = 8384 يورو، وهو بذلك أعلى من 6204 يورو؛
  • وبالتالي، يُعاد حساب الضريبة الإجمالية على النحو التالي: I3 = I2-6204 = 15404 - 6204 = 9200 يورو؛
  • وبما أن I3 > I1، فسيتم الاحتفاظ بالضريبة I3؛

لن يحصل هذا الزوج على أي خصم أو تخفيض، وستكون ضريبتهما النهائية 9200 يورو.

4.3.1.7. الأرقام الرسمية

يعد حساب الضريبة أمرًا معقدًا. وستُجرى الاختبارات طوال هذا المستند باستخدام الأمثلة التالية. النتائج مأخوذة من أداة المحاكاة الخاصة بإدارة الضرائب |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

المكلف
النتائج الرسمية
نتائج الخوارزمية الواردة في الوثيقة
زوجان ولديهما طفلان ودخلهما السنوي 55555 يورو
الضريبة = 2815 يورو
معدل الضريبة = 14%
الضريبة = 2814 يورو
معدل الضريبة = 14%
زوجان ولديهما طفلان ودخلهما السنوي 50000 يورو
الضريبة = 1385 يورو
الخصم = 384 يورو
التخفيض = 346 يورو
معدل الضريبة = 14%
الضريبة = 1384 يورو
الخصم = 384 يورو
الخصم = 347 يورو
معدل الضريبة = 14%
زوجان لديهما 3 أطفال ودخل سنوي يبلغ 50000 يورو
الضريبة = 0 يورو
الخصم = 720 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
الضريبة = 0 يورو
الخصم = 720 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
أعزب ولديه طفلان ودخله السنوي 100000 يورو
الضريبة = 19884 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 19884 يورو
الزيادة = 4480 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
أعزب ولديه 3 أطفال ودخل سنوي قدره 100000 يورو
الضريبة = 16782 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 16782 يورو
الزيادة = 7176 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
زوجان لديهما 3 أطفال ودخل سنوي قدره 100000 يورو
الضريبة = 9200 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 30%
الضريبة = 9200 يورو
الزيادة = 2180 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 30%
زوجان لديهما 5 أطفال ودخل سنوي قدره 100000 يورو
الضريبة = 4230 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
الضريبة = 4230 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
أعزب بدون أطفال ودخل سنوي قدره 100000 يورو
الضريبة = 22986 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 22986 يورو
الزيادة = 0 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
زوجان ولديهما طفلان ودخلهما السنوي 30000 يورو
الضريبة = 0 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 0%
الضريبة = 0 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 0%
أعزب بدون أطفال ودخل سنوي قدره 200000 يورو
الضريبة = 64211 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 45%
الضريبة = 64210 يورو
الزيادة = 7498 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 45%
زوجان لديهما 3 أطفال ودخل سنوي يبلغ 200000 يورو
الضريبة = 42843 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 42842 يورو
الزيادة = 17283 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%

فيما سبق، يُطلق مصطلح «الزيادة» على المبلغ الإضافي الذي يدفعه أصحاب الدخل المرتفع بسبب ظاهرتين:

  • تحديد سقف للخصم بنسبة 10% على الدخل السنوي؛
  • وضع حد أقصى للمعامل الأسري؛

لم يتسن التحقق من هذا المؤشر لأن أداة المحاكاة الخاصة بإدارة الضرائب لا توفره.

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

  • إلى اليورو الأعلى بالنسبة للخصومات والتخفيضات؛
  • إلى اليورو الأدنى بالنسبة للزيادة الضريبية والضريبة النهائية؛

سنطلب من IA إجراء حساب الضريبة هذا.

4.3.2. إعداد جلسة عمل Gemini

يُرفق بالسؤال الموجه إلى Gemini ملفان:

 
  • في ملف [1]، تم إدراج الحساب الذي تم تفصيله للتو في ملف PDF الذي تم تقديمه إلى Gemini. سيجد Gemini فيه القواعد الدقيقة للحساب المبسط للضريبة لعام 2019 على دخل عام 2018؛
  • في ملف [2]، توجد تعليماتنا؛
  • في ملف [3] لتشغيل ملف IA؛

تعليماتنا الواردة في الملف النصي [instructionsAvecPDF.txt] هي كما يلي:

1 - Exprime-toi en français.

2 - Peux-tu générer un script Python permettant de calculer l'impôt payé par les familles en 2019 sur leurs revenus de 2018.

3 - Tu t'aideras du document PDF que j'ai joint et qui explique les calculs à faire.

4 - Tu dois faire attention aux points suivants :

- plafonnement du quotient familial. Il y a des seuils à vérifier.
- calcul de la décote dans certains cas. Il y a des seuils à vérifier.
- calcul de la réduction de 20% dans certains cas. Il y a des seuils à vérifier.
- plafonnement de l'abattement de 10% sur les revenus annuels dans certains cas.
- tu considèreras que tous les revenus sont à déclarer pour le déclarant 1 même si le couple est marié.

5 - Tu ajouteras au script généré des tests unitaires pour les cas suivants.

Dans ces tests on appelle :

adultes :nombre d'adultes du foyer fiscal
enfants : nombre d'enfants du foyer fiscal
revenus : revenus nets annuels avant impôt, ç-à-d avant le 1er calcul de l'abattement.
impot : l'impôt à payer
decote :la décote éventuelle du foyer fiscal
reduction : la réduction de 20% pour les faibles revenus

Voici les 11 tests à vérifier. Ils ont tous été vérifiés manuellement sur le simulateur officiel
du calcul de l'impôt 2019 [https://www3.impots.gouv.fr/محاكي/calcul_impot/2019/simplifie/index.html].
Si tu utilises ce simulateur les revenus doivent être associés au seul déclarant 1 dans le cas d'un couple, le déclarant 2 étant alors ignoré. Lorsqu'on répartit les revenus
sur deux déclarants, on n'obtient pas le même résultat.

On utilise la syntaxe (adultes, enfants, revenus) -> (impot, decote, reduction) pour dire que le script reçoit les entrées
(adultes, enfants, revenus) et produit les résultats (impot, decote, reduction)

test1 : (2,2,55555) -> (2815, 0, 0)
test2 : (2, 2, 50000) -> (1385, 384, 346)
test3 : (2,3,50000) -> (0, 720, 0)
test4: (1,2,100000) -> (19884, 0, 0)
test5: (1,3,100000) -> (16782, 0, 0)
test6 : (2, 3, 100000) -> (9200, 0, 0)
test7 : (2, 5, 100000) -> (4230, 0, 0)
test8 : (1, 0, 100000) -> (22986, 0, 0)
test9 : (2, 2, 30000) -> (0, 0, 0)
test10 : (1,0,200000) -> (64211, 0, 0)
test11  : (2, 3, 200000) -> (42843, 0, 0)

6 - Il peut y avoir des problèmes d'arrondis. Tu procèderas comme suit
- l'impôt à payer sera arrondi à l'euro inférieur,
- la décote sera arrondie à l'euro supérieur,
- la réduction de 20 % sera arrondie à l'euro supérieur.
- l'abattement de 10% sera arrondi à l'euro supérieur

Fais tous les tests unitaires à l'euro près à cause de ces éventuelles erreurs d'arrondi.
Ne cherche pas à avoir les valeurs exactes ci-dessus mais ces valeurs à 1 euro près.

7 - Evite de chercher sur internet. Le PDF que je te donne est correct.
Ne donne ton résultat que lorsque tu as passé les 11 tests unitaires avec succès.

8 - si l'un des tests échoue et que tu es bloqué, affiche ton raisonnement pour ce test
afin que je puisse t'aider.

9- Mets des commentaires détaillés dans le script que tu génères.
Mets le barème progressif dans une liste ou dictionnaire puis utilise cette liste ou dictionnaire
Mets les nombres en dur (nombres magiques) que tu utilises dans des constantes
Utilise des fonctions pour séparer les étapes du calcul.
Ecris le tout en français

9 - n'affiche pas le code généré à l'écran. Donne-moi simplement un lien pour le récupérer.
Si les tests unitaires échouent, je te donnerai les logs de l'exécution du script pour que
tu voies tes erreurs.

10 - si c'est possible, indique le temps en minutes et secondes que tu as mis pour produire
le script demandé.

هذه التعليمات هي نتيجة لعدد كبير جدًا من الأسئلة التي طُرحت على Gemini. وسرعان ما ندرك أن ملف IA يجب أن يكون محددًا بدقة إذا أردنا الحصول على ما نريد. وبسبب كل هذه المحاولات المتعثرة، تم إيقاف جلسة Gemini في النهاية لتجاوزها الحدود المسموح بها. دعونا ندرس بقية هذه التعليمات:

  • السطر 1: نطلب أن تكون المحادثة باللغة الفرنسية. هذه الإشارة موجهة إلى DeepSeek الذي كان يميل إلى التحدث باللغة الإنجليزية؛
  • السطر 3: ما نريده؛
  • السطر 5: يُطلب من IA استخدام PDF الذي تم تزويده به؛
  • الأسطر 7-14: عدد من النصائح المفيدة خاصةً للمشكلة 3 بدون PDF. فقدت عدة IA أثناء حساب الضريبة؛
  • الأسطر 15-44: الاختبارات الفردية الـ 11 التي نريد تضمينها في البرنامج النصي الذي سيتم إنشاؤه. وعندما يتم إنشاء البرنامج النصي، سنقوم بتنفيذه في PyCharm وسنرى ما إذا كانت الاختبارات الـ 11 ستنجح أم لا؛
  • الأسطر 46-53: بدون هذه التعليمات، كان IA يُنشئ اختبارات وحدة تبحث عن نتائج محددة بالضبط، مما يؤدي إلى فشل الاختبارات؛
  • الأسطر 55-56: أطلب من IA عدم الاتصال بالإنترنت. الحل الأبسط هو استخدام PDF؛
  • السطران 58-59: لم يتبع IA هذه التعليمات. اضطررت إلى كتابتها صراحةً في سؤال، عندما لاحظت أن أحد الاختبارات قد فشل؛
  • الأسطر 61-65: أشير إلى نوع البرنامج النصي لـ Python الذي أريده؛
  • الأسطر 67-69: كنتُ أفضّل وجود رابط لتنزيل البرنامج النصي الذي تم إنشاؤه لأن عرض الكود على الشاشة يستغرق وقتًا. وتبين أن معظم IA لا يعرفون كيفية القيام بذلك. فالروابط المقدمة لم تكن تعمل؛
  • الأسطر 71-72: كنت أرغب في معرفة الوقت الذي استغرقته أداة IA للإجابة على السؤال. لم تتمكن سوى أداة Gemini من تزويدي بهذه المعلومة. أما أجهزة IA الأخرى، فإما أنها لم تستجب لهذه التعليمات، أو أنها قدمت أرقامًا عشوائية مما يدل على أنها لم تفهم هذه التعليمات؛

4.3.3. رد «جيميني»

كان الرد الأول من Gemini كما يلي:

 
  • في [1-4]، يقدم Gemini روابط إلى الجزء من PDF أو الملف النصي للتعليمات التي يستخدمها في وقت معين؛

ويأتي ما يلي بعد ذلك:

 
  • في [1]، يؤكد Gemini أنه نفذ الاختبارات الوحدوية الـ 11 بنجاح. وقد أكد معظم ملفات IA ذلك أيضًا فيما يتعلق بالمشكلة 2 والمشكلة 3، وغالبًا ما كان البرنامج النصي الذي تم إنشاؤه لا يعمل عند تحميله. لذا يجب توخي الحذر من هذا التأكيد. بالنسبة لـ Gemini، سيثبت صحة هذا الأمر؛
  • في [2]، رابط سيتبين أنه لا يعمل؛
  • في [3]، لم تقدم سوى Gemini وقتًا واقعيًّا للتنفيذ؛

لذا فإن الرابط [2] لا يعمل. نبلغ Gemini بذلك:

 

رد Gemini:

 
  • في [1]، البرنامج النصي Python الذي أنشأته Gemini؛

نقوم بتحميل هذا البرنامج النصي في PyCharm ونقوم بتنفيذه:

 
  • في [1]، [gemini1] هو البرنامج النصي الذي أنشأته Gemini؛

عند تشغيل البرنامج النصي، تظهر أخطاء في الترجمة:


"C:\Program Files\Python313\python.exe" "C:/Program Files/JetBrains/PyCharm 2025.2.0.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --path "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py" 
Testing started at 17:12 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours

Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm 2025.2.0.1\plugins\python-ce\helpers\pycharm\_jb_unittest_runner.py", line 38, in <module>
    sys.exit(main(argv=args, module=None, testRunner=unittestpy.TeamcityTestRunner,
             ~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
                  buffer=not JB_DISABLE_BUFFERING))
                  ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 103, in __init__
    self.parseArgs(argv)
    ~~~~~~~~~~~~~~^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 142, in parseArgs
    self.createTests()
    ~~~~~~~~~~~~~~~~^^
  File "C:\Program Files\Python313\Lib\unittest\main.py", line 153, in createTests
    self.test = self.testLoader.loadTestsFromNames(self.testNames,
                ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^
                                                   self.module)
                                                   ^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\loader.py", line 207, in loadTestsFromNames
    suites = [self.loadTestsFromName(name, module) for name in names]
              ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^
  File "C:\Program Files\Python313\Lib\unittest\loader.py", line 137, in loadTestsFromName
    module = __import__(module_name)
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT1.py", line 28, in <module>
    [cite_start]
     ^^^^^^^^^^
NameError: name 'cite_start' is not defined

Process finished with exit code 1

Empty suite
  • السطر 30، خطأ الترجمة. [cite_start] هو علامة لإنشاء شكل معين من النص؛

نضع السجلات المذكورة أعلاه في ملف [logs gemini1.txt] ونقدمه إلى Gemini:

 

وتكون استجابة Gemini كما يلي:

 

عند تحميله في ملف PyCharm، يؤدي تشغيل البرنامج النصي الجديد إلى ظهور نفس الخطأ بالضبط. نبلغ Gemini بذلك من خلال تزويده مرة أخرى بسجلات التشغيل:

 

وكانت استجابة Gemini كما يلي:

 

هذه المرة نجح الأمر. اجتازت الاختبارات الفردية الـ 11 بنجاح. أبلغنا Gemini بذلك:

 

فأجاب:

 

اتبع البرنامج النصي الذي أنشأه جيميني التعليمات الواردة في الملف النصي [instructionsAvecPDF.txt]:

# -*- coding: utf-8 -*-
"""
Script pour le calcul de l'impôt 2019 sur les revenus de 2018 en France,
basé sur les règles simplifiées fournies dans le document PDF.
"""

import math
import unittest

# --- CONSTANTES DE CALCUL POUR L'IMPÔT 2019 ---
# جميع القيم مستخرجة من المستند PDF المرفق.

# الحدود القصوى للخصم بنسبة 10% على الدخل السنوي
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# جدول ضريبة الدخل لعام 2019
# الهيكل: (الحد الأقصى للشريحة، معدل الضريبة، المبلغ المخصوم لكل حصة)
BAREME_IMPOT_2019 = [
    {"seuil": 9964, "taux": 0.0, "deduction_par_part": 0},
    {"seuil": 27519, "taux": 0.14, "deduction_par_part": 1394.96},
    {"seuil": 73779, "taux": 0.30, "deduction_par_part": 5798},
    {"seuil": 156244, "taux": 0.41, "deduction_par_part": 13913.69},
    {"seuil": float('inf'), "taux": 0.45, "deduction_par_part": 20163.45}
]

# الحد الأقصى للمعامل الأسري
GAIN_MAX_DEMI_PART = 1551.0  # الربح الأقصى لكل نصف حصة إضافية

# الحدود والمبالغ الخاصة بالخصم
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# الحدود الدنيا لتخفيض الضريبة بنسبة 20%
SEUIL_BASE_REDUCTION_20 = {"celibataire": 21037.0, "couple": 42074.0}
MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART = 3797.0


def calculer_nombre_parts(adultes: int, enfants: int) -> float:
    """
    Calcule le nombre de parts fiscales du foyer.
    - Chaque parent amène 1 part.
    - Les deux premiers enfants amènent chacun 1/2 part.
    - Les enfants suivants amènent une part chacun.
    """
    if adultes not in [1, 2]:
        raise ValueError("Le nombre d'adultes doit être 1 ou 2.")

    parts = float(adultes)
    if enfants > 0:
        parts += 0.5 * min(enfants, 2)
    if enfants > 2:
        parts += 1.0 * (enfants - 2)

    return parts


def calculer_revenu_imposable(revenus: float) -> float:
    """
    Calcule le revenu net imposable après l'abattement de 10%.
    Cet abattement est plafonné.
    L'abattement est arrondi à l'euro supérieur.
    """
    abattement_calcule = revenus * 0.1
     # يتم تحديد الحد الأقصى للخصم بين حد أدنى وحد أقصى
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # تطلب المسألة تقريب الخصم إلى اليورو الأعلى
    abattement_arrondi = math.ceil(abattement)

    return revenus - abattement_arrondi


def calculer_impot_brut_base(revenu_imposable: float, nombre_parts: float) -> float:
    """
    Calcule l'impôt brut selon le barème progressif.
    """
    if nombre_parts <= 0:
        return 0.0

    quotient_familial = revenu_imposable / nombre_parts

    tranche_applicable = None
    for tranche in BAREME_IMPOT_2019:
        if quotient_familial <= tranche["seuil"]:
            tranche_applicable = tranche
            break

     # إذا كان QF أكبر من جميع الحدود، يتم استخدام الشريحة الأخيرة
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # صيغة حساب الضريبة الإجمالية
    impot_brut = (revenu_imposable * tranche_applicable["taux"]) - (
            tranche_applicable["deduction_par_part"] * nombre_parts)

    return max(0.0, impot_brut)


def appliquer_plafonnement_qf(impot_brut_avec_enfants: float, revenu_imposable: float, adultes: int,
                              enfants: int) -> float:
    """
    Applique le plafonnement du quotient familial si nécessaire.
    L'impôt final ne peut être inférieur à l'impôt calculé sans les enfants,
    diminué d'un gain maximal par demi-part.
    """
    if enfants == 0:
        return impot_brut_avec_enfants

     # حساب الضريبة دون حصص الأبناء
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # حساب الحد الأقصى للربح المرتبط بالأطفال
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    demi_parts_enfants = (parts_avec_enfants - parts_sans_enfants) * 2
    gain_maximal = demi_parts_enfants * GAIN_MAX_DEMI_PART

    impot_minimal_plafone = impot_sans_enfants - gain_maximal

     # الضريبة المقتطعة هي الأعلى من بين الحسابين
    return max(impot_brut_avec_enfants, impot_minimal_plafone)


def appliquer_decote(impot_avant_decote: float, adultes: int) -> tuple[float, float]:
    """
    Applique la décote si l'impôt est inférieur à certains seuils.
    La décote est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_application = SEUIL_APPLICATION_DECOTE[statut]
    seuil_calcul = SEUIL_CALCUL_DECOTE[statut]

    decote = 0.0
     # يُطبق الخصم إذا كان الضريبة أقل من الحد الأدنى للتطبيق
    if impot_avant_decote < seuil_application:
         # صيغة الخصم: الحد الأدنى - 0.75 * الضريبة الإجمالية
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # يُقرب إلى اليورو الأعلى
        decote = math.ceil(decote_calculee)

         # لا يمكن أن يكون الخصم سالبًا أو أكبر من الضريبة
        decote = max(0.0, min(decote, impot_avant_decote))

    impot_apres_decote = impot_avant_decote - decote
    return impot_apres_decote, decote


def appliquer_reduction_20_pourcent(impot_avant_reduction: float, revenu_imposable: float, adultes: int,
                                    enfants: int) -> tuple[float, float]:
    """
    Applique la réduction de 20% sous condition de revenus.
    La réduction est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_base = SEUIL_BASE_REDUCTION_20[statut]

     # يتم زيادة الحد الأدنى عن كل نصف حصة مرتبطة بالأطفال
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    parts_adultes = calculer_nombre_parts(adultes, 0)
    demi_parts_enfants = (parts_avec_enfants - parts_adultes) * 2
    seuil_final = seuil_base + (demi_parts_enfants * MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART)

    reduction = 0.0
     # يُطبق التخفيض إذا كان الدخل الخاضع للضريبة أقل من الحد الأدنى
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # التقريب إلى اليورو الأعلى
        reduction = math.ceil(reduction_calculee)

    impot_apres_reduction = impot_avant_reduction - reduction
    return max(0.0, impot_apres_reduction), reduction


def calculer_impot(adultes: int, revenus: float, enfants: int) -> dict:
    """
    Orchestre le calcul complet de l'impôt net à payer.
    """
     # الخطوة 1: حساب الدخل الخاضع للضريبة
    revenu_imposable = calculer_revenu_imposable(revenus)

     # الخطوة 2: حساب عدد الحصص
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # الخطوة 3: حساب الضريبة الإجمالية
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # الخطوة 4: تطبيق الحد الأقصى لـ QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # الخطوة 5: تطبيق الخصم
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # الخطوة 6: تطبيق التخفيض بنسبة 20%
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # الخطوة 7: التقريب النهائي للضريبة إلى اليورو الأقل
    impot_final = math.floor(impot_avant_arrondi_final)

    return {
        "impot": impot_final,
        "decote": decote_calculee,
        "reduction": reduction_calculee
    }


# --- TESTS UNITAIRES ---
# التحقق من صحة البرنامج النصي باستخدام الحالات المقدمة
class TestCalculImpot2019(unittest.TestCase):

    def _verifier_resultat(self, adultes, enfants, revenus, impot_attendu, decote_attendue, reduction_attendue):
        """Fonction utilitaire pour exécuter un test et vérifier les résultats à 1 euro près."""
        resultat = calculer_impot(adultes=adultes, revenus=revenus, enfants=enfants)

        self.assertAlmostEqual(resultat["impot"], impot_attendu, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Impôt attendu {impot_attendu}, obtenu {resultat['impot']}")

        self.assertAlmostEqual(resultat["decote"], decote_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Décote attendue {decote_attendue}, obtenue {resultat['decote']}")

        self.assertAlmostEqual(resultat["reduction"], reduction_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Réduction attendue {reduction_attendue}, obtenue {resultat['reduction']}")

    def test_cas_1(self):
        # (2, 2, 55555) -> (2815, 0, 0)
        self._verifier_resultat(2, 2, 55555, 2815, 0, 0)

    def test_cas_2(self):
        # (2, 2, 50000) -> (1385, 384, 346)
        self._verifier_resultat(2, 2, 50000, 1385, 384, 346)

    def test_cas_3(self):
        # (2, 3, 50000) -> (0, 720, 0)
        self._verifier_resultat(2, 3, 50000, 0, 720, 0)

    def test_cas_4(self):
        # (1, 2, 100000) -> (19884, 0, 0)
        self._verifier_resultat(1, 2, 100000, 19884, 0, 0)

    def test_cas_5(self):
        # (1, 3, 100000) -> (16782, 0, 0)
        self._verifier_resultat(1, 3, 100000, 16782, 0, 0)

    def test_cas_6(self):
        # (2, 3, 100000) -> (9200, 0, 0)
        self._verifier_resultat(2, 3, 100000, 9200, 0, 0)

    def test_cas_7(self):
        # (2, 5, 100000) -> (4230, 0, 0)
        self._verifier_resultat(2, 5, 100000, 4230, 0, 0)

    def test_cas_8(self):
        # (1, 0, 100000) -> (22986, 0, 0)
        self._verifier_resultat(1, 0, 100000, 22986, 0, 0)

    def test_cas_9(self):
        # (2, 2, 30000) -> (0, 0, 0)
        self._verifier_resultat(2, 2, 30000, 0, 0, 0)

    def test_cas_10(self):
        # (1, 0, 200000) -> (64211, 0, 0)
        self._verifier_resultat(1, 0, 200000, 64211, 0, 0)

    def test_cas_11(self):
        # (2, 3, 200000) -> (42843, 0, 0)
        self._verifier_resultat(2, 3, 200000, 42843, 0, 0)


if __name__ == '__main__':
    print("Exécution des tests unitaires...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

     # مثال على استخدام الآلة الحاسبة لحالة محددة
    print("\n--- Exemple de calcul ---")
    revenus_annuels = 50000
    nombre_adultes = 2
    nombre_enfants = 2

    resultat_calcul = calculer_impot(adultes=nombre_adultes, revenus=revenus_annuels, enfants=nombre_enfants)

    print(f"Pour un couple ({nombre_adultes} adultes) avec {nombre_enfants} enfants et {revenus_annuels}€ de revenus :")
    print(f"  - Impôt à payer : {resultat_calcul['impot']}€")
    print(f"  - Montant de la décote : {resultat_calcul['decote']}€")
    print(f"  - Montant de la réduction : {resultat_calcul['reduction']}€")

لم أتحقق من هذا الكود. وبما أن الاختبارات الفردية الـ 11 قد نجحت، فأنا أعتبره «صحيحًا على الأرجح». لم أفعل شيئًا أكثر من التحقق من هذه الاختبارات الـ 11 بالنسبة لكودي الخاص.

4.4. المشكلة 3

المشكلة 3 مطابقة للمشكلة 2، باستثناء أنه لم يعد يتم تزويد IA بـ PDF الذي كان يحدد قواعد الحساب التي يجب اتباعها.

السؤال الأولي الموجه إلى Gemini هو التالي:

 

ملف التعليمات [1] مطابق تقريبًا لملف المشكلة رقم 2، مع الاختلافات التالية:


1 - Exprime-toi en français.

2 - Peux-tu générer un script Python permettant de calculer l'impôt payé par les familles en 2019 sur leurs revenus de 2018.

3 - Tu t'aideras des sources que tu trouveras sur internet. Dans ta réponse indique-moi ces sources.

4 - Tu dois faire attention aux points suivants :

  • في السؤال [3]، يُطلب منه البحث على الإنترنت عن قواعد حساب ضريبة عام 2019 على دخل عام 2018. وهذا تمرين أصعب من السابق؛

ولن أورد أدناه سوى أجزاء من الإجابة الأولى التي قدمها «جيميني»:

 
 

الوقت المقدر معقول. ننتظر إجابة جيميني لفترة طويلة.

وكما في المرة السابقة، قدم «جيميني» رابطًا لتنزيل البرنامج النصي الذي تم إنشاؤه، وهو رابط لا يعمل. وقد أبلغناه بذلك:

 

رد «جيميني»:

 

نقوم بتحميل البرنامج النصي في PyCharm تحت اسم [gemini2]:

 

نقوم بتنفيذه و... لا يعمل. سجلات التنفيذ هي كما يلي:


"C:\Program Files\Python313\python.exe" "C:/Program Files/JetBrains/PyCharm 2025.2.0.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --path "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py" 
Testing started at 17:23 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours


Failure
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 278, in test_cas_2
    self.assertAlmostEqual(impot, 1385, delta=1)
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 1691 != 1385 within 1 delta (306 difference)


Error
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 291, in test_cas_3
    impot, _, _ = calculer_impot_final(2, 3, 50000)
                  ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 187, in calculer_impot_final
    decote, impot_apres_decote = calculer_decote(impot_apres_plafonnement, adultes)
                                 ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 134, in calculer_decote
    decote = seuil_decote - (impot_avant_decote * COFFICIENT_DECOTE)
                                                  ^^^^^^^^^^^^^^^^^
NameError: name 'COFFICIENT_DECOTE' is not defined. Did you mean: 'COEFFICIENT_DECOTE'?


Error
Traceback (most recent call last):
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 316, in test_cas_9
    self._verifier_cas(2, 2, 30000, (0, 0, 0))
    ~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 216, in _verifier_cas
    impot_calcule, decote_calculee, reduction_calculee = calculer_impot_final(adultes, enfants, revenus)
                                                         ~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 187, in calculer_impot_final
    decote, impot_apres_decote = calculer_decote(impot_apres_plafonnement, adultes)
                                 ~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini2.py", line 134, in calculer_decote
    decote = seuil_decote - (impot_avant_decote * COFFICIENT_DECOTE)
                                                  ^^^^^^^^^^^^^^^^^
NameError: name 'COFFICIENT_DECOTE' is not defined. Did you mean: 'COEFFICIENT_DECOTE'?



Ran 11 tests in 0.038s

FAILED (failures=1, errors=2)

Process finished with exit code 1
  • السطر 11، فشل أحد الاختبارات؛
  • السطران 25 و42: نفس خطأ الترجمة؛

نضع سجلات التشغيل هذه في ملف نصي ونرسله إلى Gemini:

 

رد Gemini:

 
 

نقوم بتحميل البرنامج النصي في PyCharm ونقوم بتنفيذه. تظهر أخطاء مرة أخرى. نبلغ Gemini بذلك مع إرفاق سجلات التنفيذ مرة أخرى:

 

رد Gemini:

 
 
 

نقوم بتحميل هذا البرنامج النصي الجديد في PyCharm ونقوم بتنفيذه. هذه المرة، نجحت الاختبارات الفردية الـ 11:

الرمز الذي أنشأته Gemini هو التالي:

# -*- coding: utf-8 -*-
import math
import unittest

# ==============================================================================
# CONSTANTES QZXW2HTMLCUE9VUgZQXQZXW2HTMLCSU1QT1NJVElPTgZQX DES REVENUS 2018 (PAYÉE EN 2019)
# ==============================================================================
BAREME_IMPOT_2019 = [
    {'seuil': 0, 'taux': 0.0},
    {'seuil': 9964, 'taux': 0.14},
    {'seuil': 27519, 'taux': 0.30},
    {'seuil': 73779, 'taux': 0.41},
    {'seuil': 156244, 'taux': 0.45},
]
ABATTEMENT_FORFAITAIRE_TAUX = 0.10
ABATTEMENT_FORFAITAIRE_MINIMUM = 437
ABATTEMENT_FORFAITAIRE_PLAFOND = 12502
PLAFONNEMENT_AVANTAGE_QF_DEMI_PART = 1551
SEUIL_DECOTE_CELIBATAIRE = 1196
SEUIL_DECOTE_COUPLE = 1970
COEFFICIENT_DECOTE = 0.75
TAUX_REDUCTION_20_POURCENT = 0.20
SEUIL_RFR_REDUCTION_CELIBATAIRE = 18985
SEUIL_RFR_REDUCTION_COUPLE = 37970
MAJORATION_RFR_REDUCTION_PAR_DEMI_PART = 3803


# ==============================================================================
# FONCTIONS DE CALCUL
# ==============================================================================

def calculer_nombre_parts(adultes: int, enfants: int) -> float:
    """Calcule le nombre de parts du foyer fiscal."""
    if adultes < 1: return 0
    parts = float(adultes)
    if enfants >= 1: parts += 0.5
    if enfants >= 2: parts += 0.5
    if enfants >= 3: parts += (enfants - 2) * 1.0
    return parts


def calculer_revenu_imposable(revenus_bruts: float) -> float:
    """Calcule le revenu net imposable après abattement de 10%."""
    if revenus_bruts == 0: return 0.0
    abattement = revenus_bruts * ABATTEMENT_FORFAITAIRE_TAUX
    if abattement < ABATTEMENT_FORFAITAIRE_MINIMUM: abattement = ABATTEMENT_FORFAITAIRE_MINIMUM
    if abattement > ABATTEMENT_FORFAITAIRE_PLAFOND: abattement = ABATTEMENT_FORFAITAIRE_PLAFOND
    abattement = math.ceil(abattement)
    revenu_imposable = revenus_bruts - abattement
    return revenu_imposable if revenu_imposable > 0 else 0


def calculer_impot_brut_par_tranche(revenu_imposable: float, nombre_parts: float) -> float:
    """Calcule l'impôt brut en appliquant le barème progressif."""
    if nombre_parts <= 0 or revenu_imposable <= 0: return 0.0
    quotient_familial = revenu_imposable / nombre_parts
    impot_par_part = 0.0
    for i in range(len(BAREME_IMPOT_2019)):
        tranche = BAREME_IMPOT_2019[i]
        seuil_actuel = tranche['seuil']
        taux = tranche['taux']
        if quotient_familial > seuil_actuel:
            base_imposable_tranche = 0
            if i < len(BAREME_IMPOT_2019) - 1:
                seuil_suivant = BAREME_IMPOT_2019[i + 1]['seuil']
                base_imposable_tranche = min(quotient_familial, seuil_suivant) - seuil_actuel
            else:
                base_imposable_tranche = quotient_familial - seuil_actuel
            impot_par_part += base_imposable_tranche * taux
    return impot_par_part * nombre_parts


def appliquer_plafonnement_qf(revenu_imposable: float, nombre_parts: float, adultes: int,
                              impot_brut_famille: float) -> float:
    """Applique le plafonnement des effets du quotient familial."""
    if nombre_parts <= adultes: return impot_brut_famille
    impot_base = calculer_impot_brut_par_tranche(revenu_imposable, float(adultes))
    demi_parts_enfants = (nombre_parts - adultes) * 2
    avantage_qf_max = PLAFONNEMENT_AVANTAGE_QF_DEMI_PART * demi_parts_enfants
    avantage_qf_reel = impot_base - impot_brut_famille
    if avantage_qf_reel > avantage_qf_max:
        return impot_base - avantage_qf_max
    return impot_brut_famille


def calculer_impot_final(adultes: int, enfants: int, revenus: float) -> (int, int, int):
    """Orchestre le calcul complet de l'impôt."""
    revenu_imposable = calculer_revenu_imposable(revenus)
    nombre_parts = calculer_nombre_parts(adultes, enfants)
    impot_brut = calculer_impot_brut_par_tranche(revenu_imposable, nombre_parts)
    impot_apres_plafonnement = appliquer_plafonnement_qf(revenu_imposable, nombre_parts, adultes, impot_brut)

     # CORRECTION: إذا كانت الضريبة تساوي 0 بالفعل، فلا يوجد خصم أو تخفيض.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

     # --- LOGIQUE CORRIGÉE POUR L'INTERACTION DÉCOTE / RÉDUCTION ---
    decote_calculee = 0.0
    reduction_calculee = 0.0
    impot_a_traiter = impot_apres_plafonnement

     # 1. تحديد الأهلية للحصول على الخصم بنسبة 20%
    est_eligible_reduction = False
    if adultes >= 2:
        seuil_rfr = SEUIL_RFR_REDUCTION_COUPLE + max(0, (nombre_parts - 2) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    else:
        seuil_rfr = SEUIL_RFR_REDUCTION_CELIBATAIRE + max(0, (
                nombre_parts - 1) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    if revenu_imposable < seuil_rfr:
        est_eligible_reduction = True

     # 2. تحديد الأهلية للحصول على الخصم
    seuil_decote_foyer = SEUIL_DECOTE_COUPLE if adultes >= 2 else SEUIL_DECOTE_CELIBATAIRE
    reduction_theorique = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT) if est_eligible_reduction else 0

    if (impot_a_traiter - reduction_theorique) < seuil_decote_foyer:
        decote_calculee = seuil_decote_foyer - (impot_a_traiter * COEFFICIENT_DECOTE)
        decote_calculee = math.ceil(decote_calculee)
         # CORRECTION: لا يجوز أن يتجاوز الخصم المُردَّد الضريبة التي يلغيها
        decote_calculee = min(decote_calculee, math.ceil(impot_a_traiter))
        impot_a_traiter -= decote_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

     # 3. تطبيق التخفيض بنسبة 20% في حالة الأهلية
    if est_eligible_reduction:
        reduction_calculee = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT)
        impot_a_traiter -= reduction_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

    impot_final = math.floor(impot_a_traiter)

    return impot_final, int(decote_calculee), int(reduction_calculee)


# ==============================================================================
# TESTS UNITAIRES
# ==============================================================================

class TestCalculImpot2019(unittest.TestCase):
    def _verifier_cas(self, adultes, enfants, revenus, attendu):
        impot_attendu, decote_attendue, reduction_attendue = attendu
        impot_calcule, decote_calculee, reduction_calculee = calculer_impot_final(adultes, enfants, revenus)
        self.assertAlmostEqual(impot_calcule, impot_attendu, delta=1, msg=f"Impôt ({impot_calcule} vs {impot_attendu})")
        self.assertAlmostEqual(decote_calculee, decote_attendue, delta=1,
                               msg=f"Décote ({decote_calculee} vs {decote_attendue})")
        self.assertAlmostEqual(reduction_calculee, reduction_attendue, delta=1,
                               msg=f"Réduction ({reduction_calculee} vs {reduction_attendue})")

    def test_cas_1(self): self._verifier_cas(2, 2, 55555, (2815, 0, 0))

    def test_cas_2(self): self._verifier_cas(2, 2, 50000, (1385, 384, 346))

    def test_cas_3(self): self._verifier_cas(2, 3, 50000, (0, 720, 0))

    def test_cas_4(self): self._verifier_cas(1, 2, 100000, (19884, 0, 0))

    def test_cas_5(self): self._verifier_cas(1, 3, 100000, (16782, 0, 0))

    def test_cas_6(self): self._verifier_cas(2, 3, 100000, (9200, 0, 0))

    def test_cas_7(self): self._verifier_cas(2, 5, 100000, (4230, 0, 0))

    def test_cas_8(self): self._verifier_cas(1, 0, 100000, (22986, 0, 0))

    def test_cas_9(self): self._verifier_cas(2, 2, 30000, (0, 0, 0))

    def test_cas_10(self): self._verifier_cas(1, 0, 200000, (64211, 0, 0))

    def test_cas_11(self): self._verifier_cas(2, 3, 200000, (42843, 0, 0))


if __name__ == '__main__':
    print("Lancement des tests unitaires pour le calcul de l'impôt 2019...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

مرة أخرى، لم أتفحص هذا الكود. لقد لاحظت ببساطة أنه اجتاز الاختبارات الـ 11 بنجاح.

ولكن قد يثير الفضول معرفة المنطق الكامن وراءه، لا سيما في الحالات الخاصة بحساب الضريبة. نسأله عن ذلك:

 

هذه حالة دخل مرتفع تتضمن في آن واحد حدًا أقصى محتملًا للخصم بنسبة 10٪ وحدًا أقصى محتملًا للحصّة العائلية.

إجابة «جيميني» هي كما يلي:

 
 
 
 
 

هاتان اللقطتان الأخيرتان مثيرتان للاهتمام. يستخدم Gemini طريقة حسابية مختلفة عن تلك الموضحة في PDF. ويمكن بالفعل العثور على هذه الطريقة الحسابية على الإنترنت. والطريقتان متكافئتان.

 
 
 

يتميز الشرح بوضوحه المذهل. يمكن تقديمه كما هو للطلاب لشرح طريقة حساب الضريبة.

والآن لنأخذ مثالاً آخر، هذه المرة مع دخل منخفض. في هذه الحالة، قد يكون هناك خصم وتخفيض:

 

إجابة Gemini هي كما يلي:

 
 
 
 
 

هنا، نلاحظ أن Gemini يطبق قاعدة غير موجودة في PDF. ربما عثر عليها على الإنترنت، لكن هل المصدر موثوق؟

 

هنا، يواصل Gemini تطبيق قاعدة غير معروفة (القاعدة الخاصة المذكورة أعلاه).

 
 
 

إذن، تتطابق نتائج Gemini مع نتيجة المحاكي الرسمي للضرائب. لكنه استخدم قاعدة غير موجودة في PDF. أين يكمن الخطأ؟ نسأله عن ذلك مع إرفاق PDF:

 

رد Gemini:

 
 
 
 

أعتقد أن Gemini على حق وأن رمزي PDF خاطئ. للتحقق من ذلك، أطلب منه إجراء اختبار:

  • حيث يعطي استنتاجه نفس النتائج التي يعطيها محاكي الضرائب الرسمي؛
  • حيث ستؤدي طريقة حساب PDF إلى نتائج مختلفة عن تلك التي يقدمها المحاكي؛
 

إجابة Gemini هي كما يلي:

 

هنا يخطئ Gemini. لقد قمت بتشغيل المحاكي على هذا المثال ووجدت ما يلي:

 

ومع ذلك، سنكتشف أن طريقة التفكير التي اتبعتها Gemini تؤدي بالفعل إلى النتائج المذكورة أعلاه. لنكمل:

 
 
 
 

حسناً. تم تدوين ذلك. لنكمل:

 
 
 
 

إذن، توصل برنامج Gemini إلى النتائج التالية: (الضريبة، الخصم، التخفيض) = (431، 325، 1296)، في حين أن المحاكي الذي استخدمته أنا أعطى النتائج التالية: (431، 324، 1297). لذا، فقد توصل برنامج Gemini إلى النتائج الصحيحة بفارق يورو واحد فقط، لكنه لا يدرك ذلك. لذا نخبره بذلك:

 

يرد Gemini:

 
 

والآن، نتساءل عما إذا كان بإمكان «جيميني» توليد قيمة «PDF» مصححة:

 

رد جيميني:

 

لذلك لم يقدم لي Gemini رابطًا إلى ملف PDF، لكنه أنشأ نصًا لتمكيني من إنشاء ملف PDF بنفسي. على الرغم من أن عرض لقطات شاشة لـ PDF هنا قد يكون مرهقًا، إلا أنني أفعل ذلك حتى يرى القارئ الجانب التوليدي لـ IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

بصراحة، لم أتحقق مما إذا كان كل ما ورد في هذا الملف PDF صحيحًا. على أي حال، إنه مستند مثالي لملف TD، تم إنشاؤه في بضع ثوانٍ.

ومع ذلك، يمكننا أن نطلب من «جيميني» نفسه التحقق من صحة ملفه PDF. نبدأ محادثة جديدة:

 
  • في [1]، وضعنا PDF الذي أنشأه Gemini [Le problème selon Gemini.pdf
  • في [2]، [instructionsAvecPDF2.txt] مطابق لتعليمات [instructionsAvecPDF.txt]، باستثناء إضافة اختبار وحدة ثاني عشر، وهو نفس الاختبار الذي أظهر أن PDF الأصلي كان خاطئًا:
test12 : (2, 2, 49500) -> (1297, 431, 324)

ومن الغريب أنه استغرق الأمر عدة محاولات ذهابًا وإيابًا قبل أن يقوم Gemini بإنشاء البرنامج النصي الصحيح:

السؤال 2

 

السؤال 3

 

وكما تم فعله عدة مرات حتى الآن، عندما يفشل البرنامج النصي الذي تم إنشاؤه وتحميله في PyCharm، نقدم لـ Gemini ملف نصي يحتوي على سجلات التنفيذ. ويفهم Gemini هذه السجلات جيدًا.

السؤال 4

 

السؤال 5

 

السؤال 6 والنهاية

الآن نحن مطمئنون إلى صحة الرمز PDF الذي أنشأته Gemini. قواعد الحساب الواردة فيه صحيحة.

سنقوم الآن بنفس الإجراء بالنسبة للملفات الخمسة الأخرى IA، لكننا سنكون موجزين جدًّا في تفسيراتنا، باستثناء ChatGPT وIA، اللذين يتصدران القائمة حاليًّا. ما يهمنا هو معرفة ما إذا كان IA يحل أم لا المشاكل الثلاث المطروحة عليه. في الواقع، واجهات جميع ملفات IA هذه متشابهة جدًّا، وقد تعاملت معها بنفس الطريقة التي تعاملت بها مع Gemini. نشجع القارئ على إعادة تشغيل محادثات Gemini باستخدام نموذج IA الذي يختاره.