Skip to content

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

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

4.1. مقدمة

نرجع إلى لقطة الشاشة الأولى لـ Gemini التي تم تقديمها سابقًا:

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

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

من الآن فصاعدًا، سأشير إلى جلسات الأسئلة والأجوبة ببساطة باسم "الجلسات". غالبًا ما تستخدم أنظمة الذكاء الاصطناعي المصطلح الإنجليزي "chat" أو "conversation".

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

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

4.2. المسألة 1

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

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

وباقي الإجابة كما يلي:

 
 
 
 

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

4.3. المسألة 2

4.3.1. مقدمة

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

 

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

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

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

 

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

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

يمكن حساب الضريبة الإجمالية على النحو التالي:

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

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

وبالتالي، فإن عدد الأسهم هو:

  • nbParts=1+nbChildren*0.5+(nbChildren-2)*0.5 إذا كان الموظف غير متزوج؛
  • nbParts=2+nbChildren*0.5+(nbChildren-2)*0.5 إذا كان متزوجًا؛
  • حيث nbChildren هو عدد الأطفال؛
  • نحسب الدخل الخاضع للضريبة R = 0.9 * S، حيث S هو الراتب السنوي؛
  • يُحسب معامل الأسرة QF على النحو التالي: QF = R / nbParts؛
  • نحسب الضريبة الإجمالية I بناءً على البيانات التالية (2019):
9964
0
0
27,519
0.14
1,394.96
73,779
0.3
5,798
156,244
0.4
13,913.69
0
0.45
20163.45

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

الدخل الخاضع للضريبة: R=0.9*S=45,000

عدد الحصص: nbParts=2+2*0.5=3

نسبة الأسرة: QF=45,000/3=15,000

الصف الأول الذي يكون فيه QF <= field1 هو كما يلي:

    27519    0.14    1394.96

وبالتالي، فإن الضريبة I تساوي 0.14*R – 1394.96*عدد_الأسهم=[0.14*45000-1394.96*3]=2115. يتم تقريب الضريبة لأسفل إلى أقرب يورو.

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

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

    0    0.45    20163.45

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

4.3.1.2. الحد الأقصى لنسبة الأسرة

 

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

الدخل الخاضع للضريبة: R = 0.9 * S = 45,000

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

معدل الأسرة: QF = 45,000 / 2 = 22,500

السطر الأول حيث QF <= field1 هو كما يلي:

    27519    0.14    1394.96

وبالتالي، فإن الضريبة الأولى تساوي 0.14*R – 1394.96*عدد الأسهم = [0.14*45,000 – 1394.96*2] = 3,510.

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

الضريبة الدنيا: 3,510 – 3,102 = 408 يورو

الضريبة الإجمالية مع سهمين، التي تم حسابها بالفعل في الفقرة السابقة (2,115 يورو)، أكبر من الحد الأدنى للضريبة (408 يورو)، لذا لا ينطبق سقف الأسرة هنا.

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

  • [الضريبة 1]: هي الضريبة الإجمالية المحسوبة بما في ذلك الأطفال؛
  • [الضريبة 2]: هي الضريبة الإجمالية المحسوبة بدون الأطفال والمخفضة بالائتمان الأقصى (هنا 1,551 يورو لكل نصف حصة) المتعلق بالأطفال؛

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

 

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

الضريبة الإجمالية (2,115 يورو) من الخطوة السابقة أقل من 2,627 يورو للزوجين (1,595 يورو للشخص العازب): وبالتالي ينطبق التخفيض الضريبي. ويتم حسابه على النحو التالي:

الائتمان الضريبي = الحد الأدنى (للزوجين = 1,970 / للشخص العازب = 1,196) – 0.75 * الضريبة الإجمالية

الخصم = 1,970 – 0.75 * 2,115 = 383.75، مقربًا إلى 384 يورو.

الضريبة الإجمالية الجديدة = 2,115 – 384 = 1,731 يورو

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

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

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

 

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

  • الأفراد: 21,037 يورو؛
  • للزوجين: 42,074 يورو؛ (يبدو أن الرقم 37,968 المستخدم في المثال أعلاه غير صحيح)؛

يتم زيادة هذا الحد بقيمة: 3,797 * (عدد نصف الحصص التي يساهم بها الأبناء).

مرة أخرى، بالنسبة للموظف المتزوج الذي لديه طفلان وراتب سنوي S قدره 50,000 يورو:

  • دخله الخاضع للضريبة (45,000 يورو) أقل من الحد الأدنى (42,074 + 2 × 3,797) = 49,668 يورو؛
  • وبالتالي، يحق له الحصول على تخفيض بنسبة 20% في ضريبته: 1,731 * 0.2 = 346.2 يورو، مقربة إلى 347 يورو؛
  • يصبح إجمالي الضريبة المستحقة على المكلف: 1,731 – 347 = 1,384 يورو؛

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

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

4.3.1.6. حالات الدخل المرتفع

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

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

في معظم الحالات، يتم حساب الدخل الخاضع للضريبة باستخدام الصيغة: R = 0.9 × S، حيث S هو الراتب السنوي. يُعرف هذا باسم التخفيض بنسبة 10٪. هذا التخفيض محدد بسقف. في عام 2019:

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

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

  • يبلغ الفرق الناتج عن التخفيض بنسبة 10% 200,000 يورو > 12,502 يورو. وبالتالي، فإن الحد الأقصى هو 12,502 يورو؛

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

لننظر إلى حالة ينطبق عليها الحد الأقصى للأسرة الموصوف في القسم |الحد الأقصى لمعدل الأسرة|. لنأخذ مثال زوجين لديهما ثلاثة أطفال ودخل سنوي قدره 100,000 يورو. لنراجع خطوات الحساب مرة أخرى:

  • الخصم بنسبة 10% هو 100,000 يورو < 12,502 يورو. وبالتالي، فإن الدخل الخاضع للضريبة R هو 100,000 - 10,000 = 90,000 يورو؛
  • لدى الزوجين nbParts = 2 + 0.5 × 2 + 1 = 4 حصص؛
  • وبالتالي، فإن معامل الأسرة هو QF = R / nbParts = 90,000 / 4 = 22,500 يورو؛
  • ضريبته الإجمالية I1 مع أطفال هي I1 = 0.14 × 90,000 – 1,394.96 × 4 = 7,020 يورو؛
  • ضريبته الإجمالية I2 بدون أطفال:
    • QF = 90,000 / 2 = 45,000 يورو؛
    • I2 = 0.3 × 90,000 – 5,798 × 2 = 15,404 يورو؛
  • تنص قاعدة سقف الحصة العائلية على أن المزايا المستمدة من الأطفال لا يمكن أن تتجاوز (1,551 × 4 نصف حصص) = 6,204 يورو. ومع ذلك، في هذه الحالة، فإن I2 – I1 = 15,404 – 7,020 = 8,384 يورو، وهو مبلغ أكبر من 6,204 يورو؛
  • وبالتالي، يُعاد حساب الضريبة الإجمالية على النحو التالي: I3 = I2 - 6,204 = 15,404 - 6,204 = 9,200 يورو؛
  • وبما أن I3 > I1، فسيتم الاحتفاظ بالضريبة I3؛

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

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

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

المكلف
النتائج الرسمية
النتائج المستمدة من خوارزمية الوثيقة
زوجان لديهما طفلان ودخلهما السنوي 55,555 يورو
الضريبة = 2,815 يورو
معدل الضريبة = 14%
الضريبة = 2,814 يورو
معدل الضريبة = 14%
زوجان ولديهما طفلان ودخلهما السنوي 50,000 يورو
الضريبة = 1,385 يورو
الائتمان الضريبي = 384 يورو
التخفيض = 346 يورو
معدل الضريبة = 14%
الضريبة = 1,384 يورو
الخصم = 384 يورو
الائتمان = 347 يورو
معدل الضريبة = 14%
زوجان ولديهما 3 أطفال ودخلهما السنوي 50,000 يورو
الضريبة = 0 يورو
الائتمان الضريبي = 720 يورو
التخفيض = 0 يورو
معدل الضريبة = 14%
الضريبة = 0 يورو
الخصم = 720 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
أعزب ولديه طفلان ودخله السنوي 100,000 يورو
الضريبة = 19,884 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 19,884 يورو
الرسوم الإضافية = 4,480 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 41%
أعزب ولديه 3 أطفال ودخله السنوي 100,000 يورو
الضريبة = 16,782 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 16,782 يورو
الرسوم الإضافية = 7,176 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 41%
زوجان ولديهما 3 أطفال ودخلهما السنوي 100,000 يورو
الضريبة = 9,200 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 30%
الضريبة = 9,200 يورو
الرسوم الإضافية = 2,180 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 30%
زوجان لديهما 5 أطفال ودخل سنوي قدره 100,000 يورو
الضريبة = 4,230 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
الضريبة = 4,230 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 14%
أعزب، بدون أطفال، ودخل سنوي قدره 100,000 يورو
الضريبة = 22,986 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 22,986 يورو
الرسوم الإضافية = 0 يورو
الخصم = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
زوجان ولديهما طفلان ودخلهما السنوي 30,000 يورو
الضريبة = 0 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 0%
الضريبة = 0 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 0%
أعزب بدون أطفال ودخل سنوي قدره 200,000 يورو
الضريبة = 64,211 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 45%
الضريبة = 64,210 يورو
الرسوم الإضافية = 7,498 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 45%
زوجان ولديهما 3 أطفال ودخلهما السنوي 200,000 يورو
الضريبة = 42,843 يورو
الائتمان الضريبي = 0 يورو
الخصم = 0 يورو
معدل الضريبة = 41%
الضريبة = 42,842 يورو
الرسوم الإضافية = 17,283 يورو
الخصم = 0 يورو
التخفيض = 0 يورو
معدل الضريبة = 41%

في المثال أعلاه، تشير "الرسوم الإضافية" إلى المبلغ الإضافي الذي يدفعه أصحاب الدخل المرتفع بسبب عاملين:

  • الحد الأقصى للخصم البالغ 10% من الدخل السنوي؛
  • الحد الأقصى للبدل العائلي؛

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

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

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

سنطلب من الذكاء الاصطناعي إجراء هذا الحساب الضريبي.

4.3.2. تكوين جلسة Gemini

السؤال المطروح على Gemini مصحوب بملفين:

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

تعليماتنا في الملف النصي [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/simulateur/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. وسرعان ما يتضح أن الذكاء الاصطناعي يحتاج إلى توجيه دقيق للغاية إذا أردنا الحصول على ما نريد. وبسبب كل هذه التجارب والأخطاء، تم إنهاء جلسة Gemini في النهاية لتجاوزها الحدود المسموح بها. دعونا ندرس بقية هذه التعليمات:

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

4.3.3. رد Gemini

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

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

والباقي كما يلي:

 
  • في [1]، يذكر Gemini أنه نفذ بنجاح جميع الاختبارات الوحدوية البالغ عددها 11 اختبارًا. وقد ادعت معظم أنظمة الذكاء الاصطناعي ذلك بالنسبة لكل من المشكلة 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:

 

فأجاب:

 

اتبع البرنامج النصي الذي أنشأته Gemini التعليمات الواردة في الملف النصي [instructionsWithPDF.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 ---
#  All values are taken from the PDF document supplied.

#  Ceilings for the 10% allowance on annual income
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

#  2019 income tax scale
#  Structure: (Maximum bracket threshold, Tax rate, Amount to be deducted per share)
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}
]

#  Family allowance ceiling
GAIN_MAX_DEMI_PART = 1551.0  #  Maximum gain per additional half-share

#  Thresholds and amounts for the discount
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

#  Thresholds for the 20% tax reduction
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
    #  The allowance is capped between a minimum and a maximum
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

    #  The statement requires the abatement to be rounded up to the nearest euro
    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

    #  If the QF is greater than all the thresholds, the last slice is used
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

    #  Gross tax calculation formula
    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

    #  Tax calculation without children's shares
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

    #  Calculation of maximum child-related earnings
    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

    #  The tax used is the higher of the two calculations
    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
    #  The discount applies if the tax is below the application threshold
    if impot_avant_decote < seuil_application:
        #  Discount formula: threshold - 0.75 * gross tax
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

        #  Rounded up to the nearest euro
        decote = math.ceil(decote_calculee)

        #  The discount cannot be negative or greater than the tax due
        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]

    #  The threshold is increased for each half-percentage point for children
    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
    #  The reduction applies if taxable income is below the threshold
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

        #  Rounded up to the nearest euro
        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.
    """
    #  Step 1: Calculating taxable income
    revenu_imposable = calculer_revenu_imposable(revenus)

    #  Step 2: Calculating the number of shares
    nombre_parts = calculer_nombre_parts(adultes, enfants)

    #  Step 3: Gross tax calculation
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

    #  Step 4: Applying the QF cap
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

    #  Step 5: Applying the discount
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

    #  Step 6: Apply the 20% discount
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

    #  Step 7: Final tax rounding down to the nearest euro
    impot_final = math.floor(impot_avant_arrondi_final)

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


#  --- TESTS UNITAIRES ---
#  Validation of the script with the cases provided
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)

    #  Example of how to use the calculator for a specific case
    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، باستثناء أننا لم نعد نزود الذكاء الاصطناعي بملف 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 عبر الإنترنت. هذا تمرين أصعب من التمرين السابق؛

فيما يلي، أقدم فقط أجزاء من إجابة Gemini الأولى:

 
 

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

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

 

رد Gemini:

 

نقوم بتحميل البرنامج النصي في 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 POUR L'IMPOSITION 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: If tax is already 0, no discount or reduction.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

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

    #  1. Determining eligibility for the 20% discount
    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. Determining eligibility for the discount
    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: The discount returned cannot exceed the tax it cancels out
        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. Apply the 20% discount if eligible
    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 هو كما يلي:

 
 
 
 
 

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

 
 
 

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

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

 

رد Gemini هو كما يلي:

 
 
 
 
 

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

 

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

 
 
 

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

 

رد Gemini:

 
 
 
 

أعتقد أن جيميني محق وأن ملف PDF الخاص بي غير صحيح. للتحقق من ذلك، أطلب منه إجراء اختبار:

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

فإن رد Gemini هو كما يلي:

 

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

 

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

 
 
 
 

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

 
 
 
 

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

 

يستجيب Gemini:

 
 

والآن، نتساءل عما إذا كان بإمكان Gemini إنشاء ملف PDF مصحح:

 

رد Gemini:

 

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

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

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

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

 
  • في [1]، قمنا بتضمين ملف PDF الذي أنشأه Gemini [The Problem According to Gemini.pdf
  • في [2]، ملف [instructionsWithPDF2.txt] مطابق للتعليمات الواردة في [instructionsWithPDF.txt]، باستثناء أننا أضفنا اختبار وحدة ثاني عشر — وهو بالذات الاختبار الذي أظهر أن ملف PDF الأولي كان غير صحيح:
test12 : (2, 2, 49500) -> (1297, 431, 324)

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

السؤال 2

 

السؤال 3

 

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

السؤال 4

 

السؤال 5

 

السؤال السادس والخاتمة

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

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