Skip to content

4. Oplossen van de drie problemen met Google Gemini

We zullen screenshots tonen van de drie Gemini-sessies waarmee de drie gestelde problemen zijn opgelost. We zullen vrij gedetailleerd te werk gaan. Zodra dit is gebeurd, zullen we het proces niet herhalen voor de andere geteste IA. Deze werken op dezelfde manier. We zullen alleen de opvallende details vermelden.

4.1. Inleiding

We herinneren aan de eerste, eerder getoonde schermafbeelding van Gemini:

 
  • In [1], de URL van Gemini;
  • In [2], de gebruikte versie van Gemini;
  • In [3-5], de drie vragen die aan Gemini zijn gesteld;

Gemini is een product van Google dat beschikbaar is op de URL [https://gemini.google.com/]. Om een overzicht te krijgen van uw vraag- en antwoordsessies zoals hierboven, moet u een account aanmaken. Bovendien geldt voor Gemini, net als voor alle andere geteste IA, een limiet op het aantal vragen dat je kunt stellen en het aantal bestanden dat je kunt uploaden. Wanneer deze limiet is bereikt, wordt de sessie beëindigd en krijg je de mogelijkheid om deze later voort te zetten. Omdat het nogal frustrerend is om midden in een sessie te moeten stoppen, heb ik een abonnement genomen. De eerste maand van het Gemini-abonnement is gelukkig gratis. Ik heb hetzelfde gedaan met de andere IA-diensten die deze beperkingen hadden, namelijk ChatGPT, MistralAI en ClaudeAI. Ik heb een abonnement van één maand genomen, maar in dit geval moest ik voor de eerste maand betalen. Bij Grok ben ik geen beperkingen tegengekomen. DeepSeek vermeldt geen beperkingen, maar antwoordt soms met [Server busy] en verbreekt dan de sessie. Dat komt neer op het instellen van beperkingen zonder dat dit wordt vermeld.

Hierna zal ik het hebben over vraag-en-antwoordsessies, kortweg ‘sessies’ genoemd. De IA gebruiken meestal de Engelse term ‘chat’ (discussie) of ‘gesprek’.

De interface van Gemini om een vraag te stellen ziet er als volgt uit:

  • In [1], uw vraag;
  • In [2], het pictogram waarmee u de IA start voor het berekenen van het antwoord;
  • In [3-4] kunt u bestanden bijvoegen;

4.2. Opgave 1

De sessie voor probleem 1 is als volgt:

 
  • In [1] staat de vraag;
  • In [2], het begin van het antwoord van Gemini;

Het vervolg van het antwoord luidt als volgt:

 
 
 
 

Het antwoord is correct. De vijf andere IA-codes zullen eveneens het juiste antwoord opleveren in een vergelijkbare vorm.

4.3. Opgave 2

4.3.1. Inleiding

We herhalen hier het oorspronkelijke probleem van de cursus [python3-flask-2020]. Het betreft een tekst die in TD aan studenten is gegeven.

 

Aan de hand van bovenstaande tabel kan de belasting worden berekend in het vereenvoudigde geval van een belastingplichtige die alleen zijn salaris hoeft aan te geven. Zoals aangegeven in voetnoot (1), is de aldus berekende belasting de belasting vóór drie mechanismen:

  • De plafonnering van het gezinsquotiënt die geldt voor hoge inkomens;
  • De belastingvermindering en -korting die van toepassing zijn op lage inkomens;

De berekening van de belasting omvat dus de volgende stappen [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

We willen een programma schrijven waarmee de belasting van een belastingplichtige in 2019 kan worden berekend in het vereenvoudigde geval dat de belastingplichtige alleen zijn salaris hoeft aan te geven.

4.3.1.1. Berekening van de bruto-belasting

De bruto belasting kan als volgt worden berekend:

Eerst wordt het aantal aandelen van de belastingplichtige berekend:

  • Elke ouder levert 1 aandeel op;
  • De eerste twee kinderen leveren elk een half aandeel op;
  • De volgende kinderen dragen elk 1 aandeel bij:

Het aantal aandelen is dus:

  • nbParts=1+nbEnfants*0,5+(aantalKinderen-2)*0,5 als de werknemer ongehuwd is;
  • aantal delen = 2 + aantal kinderen * 0,5 + (aantal kinderen - 2) * 0,5 als hij getrouwd is;
  • waarbij nbEnfants het aantal kinderen is;
  • Het belastbaar inkomen R wordt berekend als R = 0,9 * S, waarbij S het jaarsalaris is;
  • We berekenen de gezinsquotiënt QF = R / nbParts;
  • De bruto belasting I wordt berekend op basis van de volgende gegevens (2019):
9964
0
0
27519
0,14
1394,96
73779
0,3
5798
156.244
0,4
13913,69
0
0,45
20163,45

Elke regel heeft 3 velden: champ1, champ2, champ3. Om belasting I te berekenen, zoeken we de eerste regel waarbij QF <= veld1 en nemen we de waarden van die regel. Bijvoorbeeld, voor een gehuwde werknemer met twee kinderen en een jaarsalaris S van 50.000 euro:

Belastbaar inkomen: R=0,9*S=45.000

Aantal delen: nbParts = 2 + 2 * 0,5 = 3

Gezinsquotiënt: QF = 45.000 / 3 = 15.000

De eerste regel waarbij QF <= veld1 is, luidt als volgt:

    27519    0.14    1394.96

De belasting I is dan gelijk aan 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*3] = 2115. De belasting wordt naar beneden afgerond op de dichtstbijzijnde euro.

Als de relatie QF ≤ veld1 vanaf de eerste regel geldt, dan is de belasting nul.

Als QF zodanig is dat de relatie QF<=veld1 nooit wordt vervuld, dan worden de coëfficiënten van de laatste regel gebruikt. Hier:

    0    0.45    20163.45

wat de bruto belasting I = 0,45*R – 20163,45*nbParts oplevert.

4.3.1.2. Maximum van het gezinsquotiënt

 

Om te bepalen of de maximering van de gezinsquotiënt QF van toepassing is, wordt de bruto belasting opnieuw berekend zonder de kinderen. Nog steeds voor de gehuwde werknemer met twee kinderen en een jaarsalaris S van 50.000 euro:

Belastbaar inkomen: R = 0,9 * S = 45.000

Aantal delen: nbParts = 2 (de kinderen worden niet meer meegeteld)

Gezinsquotiënt: QF = 45.000 / 2 = 22.500

De eerste regel waarbij QF <= veld1 is, luidt als volgt:

    27519    0.14    1394.96

De belasting I is dan gelijk aan 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.

Maximale kinderbijslag: 1551 * 2 = 3102 euro

Minimale belasting: 3510 – 3102 = 408 euro

De bruto belasting met 2 delen, die in de vorige paragraaf al is berekend (2115 euro), is hoger dan de minimale belasting van 408 euro, dus de gezinsplafondregeling is hier niet van toepassing.

In het algemeen geldt dat de bruto belasting groter is dan (belasting1, belasting2), waarbij:

  • [impôt1]: de bruto belasting berekend met kinderen;
  • [impôt2]: de bruto belasting berekend zonder kinderen, verminderd met de maximale vrijstelling (hier 1551 euro per halve aandeel) in verband met de kinderen;

4.3.1.3. Berekening van de korting

 

Nog steeds voor de gehuwde werknemer met twee kinderen en een jaarsalaris S van 50.000 euro:

De bruto belasting (2115 euro) uit de vorige stap is lager dan 2627 euro voor een echtpaar (1595 euro voor een alleenstaande): de korting is dus van toepassing. Deze wordt verkregen met de volgende berekening:

korting = drempel (echtpaar = 1970 / alleenstaande = 1196) - 0,75 * bruto belasting

korting = 1970 – 0,75 * 2115 = 383,75, afgerond op 384 euro.

Nieuwe bruto belasting = 2115 – 384 = 1731 euro

Bij de berekening van de korting moeten twee regels in acht worden genomen (sommige tools van IA zijn op dit punt gestrand):

  • De korting mag niet negatief zijn;
  • De korting mag niet hoger zijn dan de reeds berekende belasting;

4.3.1.4. Berekening van de belastingvermindering

 

Onder een bepaalde drempel wordt een korting van 20 % toegepast op de bruto belasting die uit de voorgaande berekeningen voortvloeit. In 2019 gelden de volgende drempels:

  • alleenstaanden: 21.037 euro;
  • paar: 42.074 euro; (het cijfer 37.968 dat in het bovenstaande voorbeeld wordt gebruikt, lijkt onjuist);

Deze drempel wordt verhoogd met de waarde: 3797 * (aantal halve delen dat door de kinderen wordt bijgedragen).

Nogmaals voor de gehuwde werknemer met twee kinderen en een jaarsalaris S van 50.000 euro:

  • Zijn belastbaar inkomen (45.000 euro) ligt onder de drempel (42.074 + 2 * 3.797) = 49.668 euro;
  • Hij heeft dus recht op een vermindering van 20 % van zijn belasting: 1731 * 0,2 = 346,2 euro, afgerond op 347 euro;
  • De bruto belasting van de belastingplichtige bedraagt dan: 1731 – 347 = 1384 euro;

4.3.1.5. Berekening van de nettobelasting

Onze berekening houdt hier op: de te betalen nettobelasting bedraagt 1384 euro. In de praktijk kan de belastingplichtige profiteren van andere aftrekposten, met name voor donaties aan instellingen van algemeen nut.

4.3.1.6. Gevallen met hoge inkomens

Ons vorige voorbeeld geldt voor de meeste werknemers. Bij hoge inkomens verloopt de belastingberekening echter anders.

4.3.1.6.1. Maximum voor de korting van 10 % op het jaarinkomen

In de meeste gevallen wordt het belastbaar inkomen berekend met de formule: R = 0,9 * S, waarbij S het jaarsalaris is. Dit wordt de korting van 10 % genoemd. Aan deze korting geldt een maximum. In 2019:

  • mag deze niet hoger zijn dan 12.502 euro;
  • mag deze niet lager zijn dan 437 euro;

Laten we het geval nemen van een ongehuwde werknemer zonder kinderen met een jaarsalaris van 200.000 euro:

  • De vermindering van 10 % bedraagt 200.000 euro > 12.502 euro. Deze wordt dus teruggebracht tot 12.502 euro;

4.3.1.6.2. Maximum van het gezinsquotiënt

Laten we een geval bekijken waarin de maximale gezinsquotiënt, zoals beschreven in de paragraaf |Maximale gezinsquotiënt|, van toepassing is. Laten we het geval nemen van een echtpaar met drie kinderen en een jaarinkomen van 100.000 euro. Laten we de berekeningsstappen nog eens doornemen:

  • De aftrek van 10 % bedraagt 100.000 euro < 12.502 euro. Het belastbaar inkomen R bedraagt dus 100.000 - 10.000 = 90.000 euro;
  • Het echtpaar heeft nbParts = 2 + 0,5 × 2 + 1 = 4 delen;
  • Hun gezinsquotiënt is dus QF = R / nbParts = 90.000 / 4 = 22.500 euro;
  • Hun bruto belasting I1 met kinderen bedraagt I1 = 0,14 * 90.000 - 1.394,96 * 4 = 7.020 euro;
  • Zijn bruto belasting I2 zonder kinderen:
    • QF = 90.000 / 2 = 45.000 euro;
    • I2 = 0,3 × 90.000 – 5.798 × 2 = 15.404 euro;
  • Volgens de regel inzake de maximering van het gezinsquotiënt mag het voordeel dat kinderen opleveren niet hoger zijn dan (1551 × 4 halve delen) = 6204 euro. In dit geval is het echter I2-I1 = 15404 – 7020 = 8384 euro, dus hoger dan 6204 euro;
  • De bruto belasting wordt daarom herberekend als I3 = I2-6204 = 15.404 – 6.204 = 9.200 euro;
  • Aangezien I3 > I1, wordt de belasting I3 aangehouden;

Dit echtpaar krijgt geen korting of vermindering en hun uiteindelijke belastingbedrag bedraagt 9200 euro.

4.3.1.7. Officiële cijfers

De berekening van de belasting is complex. In dit document worden de berekeningen uitgevoerd aan de hand van de volgende voorbeelden. De resultaten zijn afkomstig van de simulator van de belastingdienst |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

Belastingplichtige
Officiële resultaten
Resultaten van het algoritme uit het document
Echtpaar met 2 kinderen en een jaarinkomen van 55555 euro
Belasting = 2815 euro
Belastingtarief = 14 %
Belasting = 2814 euro
Belastingtarief = 14 %
Echtpaar met 2 kinderen en een jaarinkomen van 50.000 euro
Belasting = 1385 euro
Korting = 384 euro
Korting = 346 euro
Belastingtarief = 14 %
Belasting = 1.384 euro
Korting = 384 euro
Korting = 347 euro
Belastingtarief = 14 %
Echtpaar met 3 kinderen en een jaarinkomen van 50.000 euro
Belasting = 0 euro
Korting = 720 euro
Korting = 0 euro
Belastingtarief = 14 %
Belasting = 0 euro
korting = 720 euro
Korting = 0 euro
Belastingtarief = 14 %
Alleenstaande met 2 kinderen en een jaarinkomen van 100.000 euro
Belasting = 19.884 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Belasting = 19.884 euro
Toeslag = 4.480 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Alleenstaande met 3 kinderen en een jaarinkomen van 100.000 euro
Belasting = 16.782 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Belasting = 16.782 euro
Toeslag = 7.176 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Echtpaar met 3 kinderen en een jaarinkomen van 100.000 euro
Belasting = 9.200 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 30 %
Belasting = 9.200 euro
Toeslag = 2180 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 30 %
Echtpaar met 5 kinderen en een jaarinkomen van 100.000 euro
Belasting = 4230 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 14 %
Belasting = 4.230 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 14 %
Alleenstaand zonder kinderen en met een jaarinkomen van 100.000 euro
Belasting = 22.986 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Belasting = 22.986 euro
Toeslag = 0 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Echtpaar met 2 kinderen en een jaarinkomen van 30.000 euro
Belasting = 0 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 0 %
Belasting = 0 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 0 %
Alleenstaand zonder kinderen en met een jaarinkomen van 200.000 euro
Belasting = 64.211 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 45 %
Belasting = 64.210 euro
Toeslag = 7498 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 45 %
Echtpaar met 3 kinderen en een jaarinkomen van 200.000 euro
Belasting = 42.843 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %
Belasting = 42.842 euro
Opwaardering = 17.283 euro
korting = 0 euro
Korting = 0 euro
Belastingtarief = 41 %

Hierboven wordt 'surcote' genoemd wat mensen met een hoog inkomen extra betalen vanwege twee fenomenen:

  • De maximering van de aftrek van 10 % op het jaarinkomen;
  • De maximering van het gezinsquotiënt;

Deze indicator kon niet worden gecontroleerd, omdat de simulator van de belastingdienst deze niet weergeeft.

We zien dat het algoritme in het document telkens een juiste belasting oplevert, zij het met een foutmarge van 1 euro. Deze foutmarge is het gevolg van afrondingen. Alle bedragen worden soms naar boven afgerond, soms naar beneden. Aangezien ik de officiële regels niet kende, zijn de bedragen in het algoritme van het document afgerond:

  • naar boven afgerond voor kortingen en kortingspercentages;
  • naar beneden afgerond voor toeslagen en de uiteindelijke belasting;

We gaan de IA vragen om deze belastingberekening uit te voeren.

4.3.2. Configuratie van de Gemini-sessie

De vraag die aan Gemini wordt gesteld, gaat vergezeld van twee bestanden:

 
  • In [1] is de zojuist beschreven berekening opgenomen in een PDF-bestand dat aan Gemini wordt verstrekt. Gemini vindt daarin de exacte regels voor de vereenvoudigde berekening van de belasting over 2019 op de inkomsten van 2018;
  • In [2] staan onze instructies;
  • In [3] om IA te starten;

Onze instructies in het tekstbestand [instructionsAvecPDF.txt] zijn als volgt:

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/simulator/calcul_impot/2019/vereenvoudigt/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é.

Deze instructies zijn het resultaat van heel veel vragen die aan Gemini zijn gesteld. Al vrij snel wordt duidelijk dat het IA heel strak moet worden ingekaderd als we willen bereiken wat we willen. Juist vanwege al dat vallen en opstaan werd de Gemini-sessie uiteindelijk stopgezet vanwege het overschrijden van de limieten. Laten we de rest van deze instructies eens bekijken:

  • Regel 1: er wordt gevraagd dat het gesprek in het Frans plaatsvindt. Deze aanwijzing is bedoeld voor DeepSeek, die de neiging had om Engels te spreken;
  • Regel 3: wat we willen;
  • Regel 5: we zeggen tegen IA dat hij de PDF moet gebruiken die we hem hebben gegeven;
  • Regels 7-14: een aantal nuttige tips, vooral voor probleem 3 zonder PDF. Er zijn verschillende IA’en verloren gegaan bij de berekening van de belasting;
  • Regels 15-44: de 11 unit-tests die we in het gegenereerde script willen opnemen. Zodra het script is gegenereerd, voeren we het uit in PyCharm en zullen we zien of de 11 tests slagen;
  • Regels 46-53: zonder deze instructies genereerde IA unit-tests die op exacte resultaten controleerden en daardoor mislukten;
  • Regels 55-56: ik geef IA de opdracht om niet op internet te gaan. De eenvoudigste oplossing is om PDF te gebruiken;
  • Regels 58-59: deze instructie werd niet opgevolgd door de IA. Ik moest dit expliciet in een vraag vermelden toen ik constateerde dat een test was mislukt;
  • Regels 61-65: ik geef aan welk type Python-script ik wil;
  • Regels 67-69: ik had liever een link gehad om het gegenereerde script op te halen, omdat het weergeven van de code op het scherm tijd kost. Het bleek dat de meeste IA dit niet kunnen. De gegeven links werkten niet;
  • Regels 71-72: ik had graag willen weten hoeveel tijd de IA nodig had om de vraag te beantwoorden. Alleen Gemini kon mij deze informatie geven. De andere IA reageerden ofwel niet op deze instructie, ofwel gaven ze willekeurige cijfers, waaruit bleek dat ze deze instructie niet begrepen;

4.3.3. Het antwoord van Gemini

Het eerste antwoord van Gemini luidt als volgt:

 
  • In [1-4] geeft Gemini links naar het gedeelte van PDF of het tekstbestand met de instructies die het op dat moment gebruikt;

Het vervolg is als volgt:

 
  • In [1] bevestigt Gemini dat het de 11 unit-tests met succes heeft uitgevoerd. De meeste IA-bestanden beweerden dit zowel bij probleem 2 als bij probleem 3, en vaak werkte het gegenereerde script niet wanneer het werd geladen. Men moet deze bewering dus met de nodige voorzichtigheid benaderen. Voor Gemini zal dit wel kloppen;
  • In [2] staat een link die niet blijkt te werken;
  • In [3 gaf alleen Gemini een realistische uitvoeringstijd;

De link [2] werkt dus niet. We melden dit aan Gemini:

 

Het antwoord van Gemini:

 
  • In [1], het door Gemini gegenereerde Python-script;

We laden dit script in PyCharm en voeren het uit:

 
  • In [1] is [gemini1] het door Gemini gegenereerde script;

Bij het uitvoeren van het script verschijnen er compilatiefouten:


"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
  • Regel 30, de compilatiefout. [cite_start] is een markering om een bepaalde tekstvorm te genereren;

We plaatsen de bovenstaande logs in een bestand [logs gemini1.txt] en geven dit aan Gemini:

 

Het antwoord van Gemini is dan als volgt:

 

Na het laden in PyCharm genereert de uitvoering van het nieuwe script precies dezelfde fout. We melden dit aan Gemini door opnieuw de uitvoeringslogs te verstrekken:

 

Het antwoord van Gemini luidt als volgt:

 

Deze keer is het gelukt. De 11 unit-tests zijn geslaagd. We melden dit aan Gemini:

 

Die antwoordt:

 

Het door Gemini gegenereerde script heeft de instructies in het tekstbestand [instructionsAvecPDF.txt] opgevolgd:

# -*- 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 ---
# Alle waarden zijn afkomstig uit het meegeleverde document PDF.

# Maximumbedragen voor de aftrek van 10% op het jaarinkomen
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# Inkomstenbelastingtarief 2019
# Structuur: (Maximale drempel van de schijf, Belastingtarief, Af te trekken bedrag per aandeel)
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}
]

# Maximum voor de gezinsquotiënt
GAIN_MAX_DEMI_PART = 1551.0  # Maximale winst per extra halve deel

# Drempels en bedragen voor de korting
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# Drempels voor de belastingvermindering van 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
     # De aftrek is begrensd tussen een minimum en een maximum
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # In de opgave wordt gevraagd de aftrek naar boven af te ronden op de volgende 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

     # Als de QF hoger is dan alle drempels, wordt de laatste schijf gebruikt
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # Formule voor de berekening van de brutobelasting
    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

     # Berekening van de belasting zonder de aandelen van de kinderen
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # Berekening van de maximale winst in verband met kinderen
    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

     # De ingehouden belasting is de hoogste van de twee berekeningen
    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
     # De korting is van toepassing als de belasting onder de toepassingsdrempel ligt
    if impot_avant_decote < seuil_application:
         # Formule voor de korting: drempel - 0,75 * bruto belasting
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # Afgerond naar de volgende euro
        decote = math.ceil(decote_calculee)

         # De korting mag niet negatief zijn en niet hoger zijn dan de belasting
        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]

     # De drempel wordt verhoogd voor elk halfdeel dat betrekking heeft op kinderen
    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
     # De korting is van toepassing als het belastbaar inkomen onder de drempel ligt
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # Afgerond naar de volgende 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.
    """
     # Stap 1: Berekening van het belastbaar inkomen
    revenu_imposable = calculer_revenu_imposable(revenus)

     # Stap 2: Berekening van het aantal aandelen
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # Stap 3: Berekening van de brutobelasting
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # Stap 4: Toepassing van de maximering van de QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # Stap 5: Toepassing van de korting
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # Stap 6: Toepassing van de korting van 20%
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # Stap 7: Definitieve afronding van de belasting naar beneden op de euro
    impot_final = math.floor(impot_avant_arrondi_final)

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


# --- TESTS UNITAIRES ---
# Validatie van het script met de verstrekte voorbeelden
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)

     # Voorbeeld van het gebruik van de calculator voor een specifiek geval
    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']}€")

Ik heb deze code niet gecontroleerd. Aangezien de 11 unit-tests zijn geslaagd, ga ik ervan uit dat deze "waarschijnlijk correct" is. Ik heb voor mijn eigen code niet meer gedaan dan deze 11 tests controleren.

4.4. Probleem 3

Probleem 3 is identiek aan probleem 2, behalve dat aan IA niet langer PDF wordt meegegeven, dat de te volgen berekeningsregels bevatte.

De oorspronkelijke vraag aan Gemini luidt als volgt:

 

Het instructiebestand [1] is vrijwel hetzelfde als dat voor vraag 2, met de volgende verschillen:


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 :

  • In [3] wordt hem gevraagd om op internet de regels te zoeken voor de berekening van de belasting over 2019 op de inkomsten van 2018. Dit is een moeilijkere opgave dan de vorige;

Hieronder geef ik slechts delen van het eerste antwoord van Gemini weer:

 
 

De geschatte tijd is aannemelijk. We moeten lang wachten op het antwoord van Gemini.

Net als eerder heeft Gemini een downloadlink voor het gegenereerde script verstrekt, maar die link werkt niet. We hebben hem daarop gewezen:

 

Het antwoord van Gemini:

 

We laden het script in PyCharm op onder de naam [gemini2]:

 

We voeren het uit en… het werkt niet. De uitvoerlogs zijn als volgt:


"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
  • Regel 11: een test is mislukt;
  • Regels 25 en 42: dezelfde compilatiefout;

We zetten deze logbestanden in een tekstbestand dat we aan Gemini geven:

 

Het antwoord van Gemini:

 
 

We laden het script in PyCharm en voeren het uit. Weer fouten. We melden dit aan Gemini en voegen opnieuw de logbestanden van de uitvoering toe:

 

Het antwoord van Gemini:

 
 
 

We laden dit nieuwe script in PyCharm en voeren het uit. Deze keer slagen alle 11 unit-tests:

De door Gemini gegenereerde code is als volgt:

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

# ==============================================================================
# CONSTANTES POUR DeIMPOSITION 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: Als de belasting al 0 is, geldt er geen korting of vermindering.
    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. Bepaal of men in aanmerking komt voor de korting van 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. Bepalen of men in aanmerking komt voor de korting
    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: De teruggestorte korting mag niet hoger zijn dan de belasting die hiermee wordt gecompenseerd
        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. De korting van 20% toepassen indien van toepassing
    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)

Ook deze keer heb ik de code niet nader bekeken. Ik heb alleen geconstateerd dat de 11 tests met succes zijn doorlopen.

Maar men kan nieuwsgierig zijn naar de redenering erachter, met name voor de specifieke gevallen bij de belastingberekening. We vragen het aan het systeem:

 

Dit is een geval van hoge inkomsten waarbij zowel een mogelijke maximering van de aftrek van 10% als een mogelijke maximering van het gezinsquotiënt van toepassing is.

Het antwoord van Gemini luidt als volgt:

 
 
 
 
 

Deze laatste twee schermafbeeldingen zijn interessant. Gemini gebruikt een andere berekeningsmethode dan die welke in PDF wordt uitgelegd. Deze berekeningsmethode is inderdaad op internet te vinden. Beide methoden zijn gelijkwaardig.

 
 
 

De uitleg is opmerkelijk duidelijk. Je zou deze uitleg zo aan studenten kunnen geven om de methode voor het berekenen van de belasting uit te leggen.

Laten we nu een ander voorbeeld nemen, ditmaal met een laag inkomen. In dit geval kunnen er een korting en een vermindering van toepassing zijn:

 

Het antwoord van Gemini luidt als volgt:

 
 
 
 
 

Hier zien we dat Gemini een regel toepast die niet in de PDF staat. Waarschijnlijk heeft hij deze op internet gevonden, maar is de bron wel betrouwbaar?

 

Hier blijft Gemini een onbekende regel toepassen (de hierboven genoemde speciale regel).

 
 
 

De resultaten van Gemini komen dus overeen met het resultaat van de officiële belastingsimulator. Maar het heeft een regel gebruikt die niet voorkomt in het bestand PDF. Waar zit de fout? We vragen het aan Gemini en voegen daarbij het bestand PDF toe:

 

Het antwoord van Gemini:

 
 
 
 

Ik denk dat Gemini gelijk heeft en dat mijn PDF onjuist is. Om dit te controleren, vraag ik hem om een test:

  • waarbij zijn redenering dezelfde resultaten zou opleveren als de officiële belastingsimulator;
  • waarbij de berekening van de PDF andere resultaten zou opleveren dan die van de simulator;
 

Het antwoord van Gemini luidt als volgt:

 

Hier heeft Gemini het bij het verkeerde eind. Ik heb de simulator op dit voorbeeld toegepast en het volgende gevonden:

 

We zullen echter zien dat de redenering van Gemini inderdaad de bovenstaande resultaten oplevert. We gaan verder:

 
 
 
 

Oké. Dat is genoteerd. We gaan verder:

 
 
 
 

Gemini heeft dus (belasting, korting, vermindering) = (431, 325, 1296) berekend, terwijl de simulator die ik heb gebruikt (431, 324, 1297) oplevert. Gemini heeft dus de juiste resultaten berekend, op 1 euro na, maar dat weet het zelf niet. We vertellen het hem:

 

Gemini antwoordt:

 
 

Nu vragen we ons af of Gemini een gecorrigeerde PDF zou kunnen genereren:

 

Het antwoord van Gemini:

 

Gemini heeft me dus geen link naar een PDF gegeven, maar het heeft wel een tekst gegenereerd waarmee ik zelf deze PDF kan genereren. Hoewel het wat omslachtig is om hier de schermafbeeldingen van de PDF weer te geven, doe ik dit toch zodat de lezer de generatieve kant van de IA kan zien:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Eerlijk gezegd heb ik niet gecontroleerd of alles wat in dit PDF staat, waar is. Het is in ieder geval een perfect document voor een TD, dat in een paar seconden is gegenereerd.

Toch kun je door Gemini zelf laten controleren of zijn PDF in orde is. We starten een nieuw gesprek:

 
  • in [1] hebben we de door Gemini gegenereerde PDF geplaatst [Le problème selon Gemini.pdf];
  • in [2], [instructionsAvecPDF2.txt] is identiek aan de instructies van [instructionsAvecPDF.txt], behalve dat er een twaalfde unit-test is toegevoegd, precies die test die aantoonde dat het oorspronkelijke PDF fout was:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Vreemd genoeg waren er meerdere pogingen nodig voordat Gemini het juiste script genereerde:

Vraag 2

 

Vraag 3

 

Zoals nu al meerdere keren is gebeurd: wanneer het gegenereerde en in PyCharm geladen script mislukt, geven we Gemini het tekstbestand met de uitvoeringslogs. Gemini begrijpt deze heel goed.

Vraag 4

 

Vraag 5

 

Vraag 6 en einde

Nu zijn we ervan overtuigd dat de door Gemini gegenereerde PDF geldig is. De berekeningsregels die erin staan, kloppen.

We gaan nu hetzelfde doen voor de vijf andere IA-bestanden, maar we zullen onze uitleg zeer beknopt houden, behalve voor ChatGPT, het IA-bestand dat momenteel aan kop gaat. Wat ons interesseert, is of de IA de drie problemen die we hem voorleggen al dan niet oplost. De interfaces van al deze IA-en zijn namelijk zeer vergelijkbaar en ik ben ermee te werk gegaan zoals met Gemini. De lezer wordt aangemoedigd om de Gemini-gesprekken opnieuw te spelen met de IA van zijn keuze.