Skip to content

5. Oplossing van de drie problemen met ChatGPT

5.1. Inleiding

Hier is een eerste schermafbeelding van een ChatGPT-sessie:

 
  • In [1-3] zijn de drie problemen die in ChatGPT aan de orde kwamen;
  • In [4], de URL van ChatGPT;
  • In [5], de gebruikte versie van ChatGPT;

ChatGPT is een product van OpenAI dat beschikbaar is in URL en [https://chatgpt.com/]. Om een overzicht te krijgen van uw vraag- en antwoordsessies zoals hierboven, moet u een account aanmaken. Bovendien geldt, net als bij alle andere geteste IA-versies, dat ChatGPT het aantal vragen en het aantal gedownloade bestanden beperkt. Wanneer deze limiet is bereikt, wordt de sessie beëindigd en wordt u gevraagd deze later voort te zetten. De limieten die door ChatGPT worden opgelegd, zijn zeer snel bereikt. Om deze tutorial te kunnen maken, moest ik een betaald abonnement van een maand afsluiten.

De interface van ChatGPT ziet er als volgt uit:

 
  • In [1], om bestanden bij de gestelde vraag toe te voegen;
  • In [2], de gestelde vraag;
  • In [3], om de uitvoering van IA te starten;

5.2. Probleem 1

De vraag in ChatGPT:

  
 

ChatGPT geeft het juiste antwoord.

5.3. Probleem 2

Dit betreft de berekening van de belasting met het PDF-formulier. Eerlijk gezegd gaan we het door Gemini gegenereerde PDF-formulier gebruiken, dat de fouten in het oorspronkelijke PDF-formulier corrigeert.

 
  • In [1] hebben we het door Gemini gegenereerde PDF opgegeven;
  • In [2] is de unit-test toegevoegd waarmee Gemini zijn superioriteit heeft aangetoond:
test12 : (2, 2, 49500) -> (1297, 431, 324)

We starten ChatGPT. Het duurt ongeveer 3 minuten om het antwoord te genereren. In tegenstelling tot Gemini levert het wel een werkende link om het gegenereerde script op te halen. We laden dit in PyCharm:

 

Het script [chatGPT1] werkt meteen. Hier is geen twijfel mogelijk: bij dit probleem presteerde ChatGPT beter dan Gemini.

Het script [chatGPT1], geleverd door ChatGPT, is als volgt:

# -*- coding: utf-8 -*-
"""
Calcul de l'impôt 2019 (revenus 2018) en Python.

Hypothèses et règles (d'après le PDF fourni) :
- Tous les revenus sont portés sur le déclarant 1 (même pour un couple).
- Abattement forfaitaire de 10 % encadré par un minimum et un maximum.
- Barème progressif 2018 appliqué au quotient familial (QF).
- Plafonnement du quotient familial : avantage par demi-part enfant limité.
- Décote et réduction de 20 % pour faibles revenus, avec la règle de repêchage.
- Ordre des correctifs : plafonnement QF -> décote -> réduction 20 %.
- Arrondis : abattement (↑), décote (↑), réduction (↑), impôt final (↓).
- On renvoie l'impôt à payer (arrondi à l'euro inf.), la décote et la réduction
  (arrondies à l'euro sup.).

Les nombres “magiques” sont regroupés en constantes, et chaque étape est isolée
dans des fonctions pour la clarté.
"""

from dataclasses import dataclass
from math import ceil, floor

# ---------- Wettelijke constanten ----------

# Aftrek voor beroepskosten (minimum/maximum) voor inkomsten 2018
ABATTEMENT_TAUX = 0.10
ABATTEMENT_MIN = 437
ABATTEMENT_MAX = 12502

# Tariefschema 2018 (QF)
# Elke schijf is (borne_sup, tarief). De eerste schijf van 0% eindigt bij 9 964 €.
BAREME_2018 = [
    (9964, 0.00),
    (27519, 0.14),
    (73779, 0.30),
    (156244, 0.41),
    (float("inf"), 0.45),
]

# Maximum voor het gezinsquotiënt
PF_QF_AVANTAGE_PAR_DEMI_PART = 1551  # Maximaal voordeel per 1/2 deel ENFANT

# Korting (drempels afhankelijk van het aantal volwassenen / relatiestatus)
DECOTE_SEUIL_CELIB = 1196
DECOTE_SEUIL_COUPLE = 1970

# Korting van 20 %: drempels afhankelijk van het aantal delen
REDUCTION_BASE_1_PART = 18985
REDUCTION_BASE_2_PARTS = 37970
REDUCTION_PAR_DEMI_PART_SUP = 3803  # toeslag per 1/2 aandeel boven de basis van 1 of 2 aandelen


# ---------- Gegevensstructuren ----------

@dataclass
class ResultatImpot:
    impot: int  # te betalen definitieve belasting, afgerond naar beneden op de euro
    decote: int  # korting afgerond naar de hogere euro
    reduction: int  # korting van 20 %, afgerond naar boven op de euro


# ---------- Hulpfuncties ----------

def arrondi_sup(euros: float) -> int:
    """Arrondi à l'euro supérieur (ceil)."""
    return int(ceil(euros))


def arrondi_inf(euros: float) -> int:
    """Arrondi à l'euro inférieur (floor)."""
    return int(floor(euros))


# ---------- Stap 1: Netto belastbaar inkomen (RNI) ----------

def calcul_abattement(revenus_bruts: float) -> int:
    """
    Calcule l'abattement de 10 %, plafonné/planche, ARRONDI À L'EURO SUPÉRIEUR.
    """
    abattement_calcule = revenus_bruts * ABATTEMENT_TAUX
    abattement_calcule = max(ABATTEMENT_MIN, min(ABATTEMENT_MAX, abattement_calcule))
    return arrondi_sup(abattement_calcule)


def revenu_net_imposable(revenus_bruts: float) -> int:
    """RNI = revenus bruts - abattement (abattement arrondi ↑)."""
    return int(round(revenus_bruts - calcul_abattement(revenus_bruts)))


# ---------- Stap 2: Aantal aandelen ----------

def nombre_parts(adultes: int, enfants: int) -> float:
    """
    Règles : 1 part si 1 adulte, 2 parts si 2 adultes.
    Enfants : +0,5 pour les 2 premiers, +1 par enfant à partir du 3e.
    (On ignore ici tout cas particulier type parent isolé, conformément au PDF.)
    """
    parts = 1.0 if adultes == 1 else 2.0
    if enfants <= 0:
        return parts
    if enfants == 1:
        return parts + 0.5
     # kinderen >= 2
    parts += 1.0  # +0,5 +0,5 voor de eerste twee
    if enfants > 2:
        parts += (enfants - 2) * 1.0  # +1 per kind vanaf het derde
    return parts


def demi_parts_enfants(enfants: int) -> int:
    """Nombre de DEMI-parts dû aux enfants (utile pour le plafonnement QF)."""
    if enfants <= 0:
        return 0
    if enfants == 1:
        return 1  # 0,5 deel
     # Eerste twee kinderen = 2 * halve deel, daarna +2 halve delen per kind vanaf het derde
    return 2 + (enfants - 2) * 2


# ---------- Stap 3: Bruto belasting via QF ----------

def impot_brut_par_parts(qf: float) -> float:
    """Impôt par part via barème progressif 2018 (sans arrondi)."""
    imp = 0.0
    previous = 0.0
    for borne_sup, taux in BAREME_2018:
        montant_tranche = max(0.0, min(qf, borne_sup) - previous)
        imp += montant_tranche * taux
        if qf <= borne_sup:
            break
        previous = borne_sup
    return imp


def impot_brut_total(rni: float, parts: float) -> float:
    """Impôt brut total = impôt par part * nombre de parts (sans arrondi)."""
    if parts <= 0:
        return 0.0
    qf = rni / parts
    return impot_brut_par_parts(qf) * parts


# ---------- Stap 4.1: Maximering van het gezinsquotiënt ----------

def apply_plafonnement_qf(impot_avec_enfants: float, rni: float, adultes: int, enfants: int) -> float:
    """
    Calcule l'impôt après plafonnement QF si l'avantage des enfants dépasse le plafond.
    - Impôt A : avec parts incluant enfants
    - Impôt B : avec seulement parts des adultes (1 ou 2)
    - Avantage réel = B - A
    - Avantage maximal = 1551 € par 1/2 part ENFANT
    Si avantage réel > avantage max, impôt = B - avantage max, sinon impôt = A.
    """
    parts_adultes = 1.0 if adultes == 1 else 2.0
    imp_b = impot_brut_total(rni, parts_adultes)
    imp_a = impot_avec_enfants
    avantage_reel = max(0.0, imp_b - imp_a)
    avantage_max = PF_QF_AVANTAGE_PAR_DEMI_PART * demi_parts_enfants(enfants)
    if avantage_reel > avantage_max:
        return imp_b - avantage_max
    return imp_a


# ---------- Stap 4.2: Korting en vermindering van 20 % ----------

def seuil_decote(adultes: int) -> int:
    return DECOTE_SEUIL_COUPLE if adultes >= 2 else DECOTE_SEUIL_CELIB


def seuil_reduction_20(rni: int, adultes: int, parts: float) -> int:
    """
    Seuil d'éligibilité à la réduction de 20 % :
    - Base : 18 985 € pour 1 part, 37 970 € pour 2 parts
    - + 3 803 € par 1/2 part supplémentaire au-delà de la base correspondante.
    """
    base = REDUCTION_BASE_2_PARTS if adultes >= 2 else REDUCTION_BASE_1_PART
    base_parts = 2.0 if adultes >= 2 else 1.0
    demi_sup = max(0, int(round((parts - base_parts) * 2)))
    return base + demi_sup * REDUCTION_PAR_DEMI_PART_SUP


def calc_decote_reduction(impot_apres_plaf: float, rni: int, adultes: int, parts: float):
    """
    Calcule (decote_arrondie, reduction_arrondie, impot_apres_correctifs).
    Règle d'interaction : la décote peut s'appliquer via repêchage si, après
    application THÉORIQUE de la réduction de 20 %, l'impôt passerait sous le seuil.
    Ordre : décote -> réduction.
    """
    imp = max(0.0, impot_apres_plaf)
    decote = 0
    reduction = 0

     # Recht op korting van 20 % op basis van RNI
    seuil_red = seuil_reduction_20(rni, adultes, parts)
    eligible_reduction = rni < seuil_red  # "onder een drempel" in PDF

     # In aanmerking komen voor korting: direct onder de drempel, of herkansing als de korting ertoe zou leiden dat de drempel wordt onderschreden
    s_dec = seuil_decote(adultes)
    direct_decote = imp <= s_dec
    repechage = False
    if not direct_decote and eligible_reduction:
        imp_theorique_apres_red = imp * 0.80  # theoretische korting 20%
        if imp_theorique_apres_red <= s_dec:
            repechage = True

    elig_decote = (direct_decote or repechage) and (imp > 0)

     # Pas eerst de korting toe (indien van toepassing)
    if elig_decote:
        montant_decote = s_dec - (imp * 0.75)
        decote_calc = max(0, arrondi_sup(montant_decote))
         # De korting mag de resterende belasting niet overschrijden: deze wordt begrensd
        decote = min(decote_calc, arrondi_sup(imp))
        imp = max(0.0, imp - decote)

     # Vervolgens de korting toepassen indien van toepassing
    if eligible_reduction and imp > 0:
        montant_reduction = imp * 0.20
        reduction = max(0, arrondi_sup(montant_reduction))
        imp = max(0.0, imp - reduction)

    return decote, reduction, imp


# ---------- API hoofd ----------

def calcule_impot(adultes: int, enfants: int, revenus_bruts: float) -> ResultatImpot:
    """
    Calcule l'impôt 2019 (revenus 2018) pour un foyer.
    Retourne ResultatImpot(impot, decote, reduction).
    """
     # 1) RNI
    rni = revenu_net_imposable(revenus_bruts)

     # 2) Aandelen
    parts = nombre_parts(adultes, enfants)

     # 3) Bruto belasting volgens tariefschema (met alle aandelen)
    imp_brut_A = impot_brut_total(rni, parts)

     # 4.1) Maximumbedrag van de gezinsquotiënt
    imp_apres_plaf = apply_plafonnement_qf(imp_brut_A, rni, adultes, enfants)

     # 4.2) Afkorting en vervolgens vermindering met 20 % (met herstel)
    decote, reduction, imp_corrige = calc_decote_reduction(imp_apres_plaf, rni, adultes, parts)

     # 5) Definitieve afronding van de belasting (↓) en begrenzing op 0
    imp_final = max(0, arrondi_inf(imp_corrige))

    return ResultatImpot(impot=imp_final, decote=decote, reduction=reduction)


# ---------- Klein testprogramma ----------

def _nearly_equal(a: int, b: int, tol: int = 1) -> bool:
    return abs(a - b) <= tol


def tests_unitaires():
    """
    Renvoie une liste de tuples (inputs, attendu, obtenu, ok) pour chaque test.
    Tolérance : ±1 € sur chaque valeur (impôt, décote, réduction).
    """
    cas = [
         # (volwassenen, kinderen, inkomsten) -> (belasting, korting, vermindering)
        ((2, 2, 55555), (2815, 0, 0)),
        ((2, 2, 50000), (1385, 384, 346)),
        ((2, 3, 50000), (0, 720, 0)),
        ((1, 2, 100000), (19884, 0, 0)),
        ((1, 3, 100000), (16782, 0, 0)),
        ((2, 3, 100000), (9200, 0, 0)),
        ((2, 5, 100000), (4230, 0, 0)),
        ((1, 0, 100000), (22986, 0, 0)),
        ((2, 2, 30000), (0, 0, 0)),
        ((1, 0, 200000), (64211, 0, 0)),
        ((2, 3, 200000), (42843, 0, 0)),
        ((2, 2, 49500), (1297, 431, 324)),
    ]

    resultats = []
    for (adultes, enfants, revenus), attendu in cas:
        res = calcule_impot(adultes, enfants, revenus)
        obtenu = (res.impot, res.decote, res.reduction)
        ok = _nearly_equal(obtenu[0], attendu[0]) and _nearly_equal(obtenu[1], attendu[1]) and _nearly_equal(obtenu[2],
                                                                                                             attendu[2])
        resultats.append(((adultes, enfants, revenus), attendu, obtenu, ok))
    return resultats


if __name__ == "__main__":
    for inputs, attendu, obtenu, ok in tests_unitaires():
        print(f"{inputs} -> attendu={attendu}, obtenu={obtenu} : {'OK' if ok else 'ECHEC'}")

5.4. Probleem 3

Nu vragen we ChatGPT om de regels voor de berekening van de belasting op internet op te zoeken:

 

Deze keer wordt het bestand PDF, dat de te volgen berekeningsregels bevatte, niet meegeleverd. We geven alleen onze instructies in het tekstbestand. Ter herinnering: dit tekstbestand bevat nu 12 unit-tests, nadat we aan de oorspronkelijke 11 tests de test hebben toegevoegd die Gemini gebruikte om aan te tonen dat mijn oorspronkelijke PDF onjuist was.

ChatGPT reageert binnen 8 minuten en geeft een link om het gegenereerde script te downloaden. Zodra dit script in PyCharm is geladen, doorstaat het alle 12 tests. ChatGPT heeft de twee gestelde problemen dus in één keer correct opgelost en daarmee Gemini overtroffen.

ChatGPT vermeldt zijn bronnen in zijn antwoord:

 

Er valt niets op aan te merken, het is prachtig werk.

Nu kunnen we hem, net zoals we bij Gemini hebben gedaan, vragen om een PDF voor studenten te genereren.

 

Het antwoord van ChatGPT werd na een aantal heen-en-weer-gesprekken verkregen, omdat de gegenereerde PDF een lettertype gebruikte dat tekens verving door een vierkantje. Maar uiteindelijk genereerde het de PDF. Ik geef dit aan omdat het andere regels oplevert dan de PDF van Gemini en ik me toen afvroeg wie er gelijk had. We gaan dit onderzoeken.

 
 
 

Het verschil met de PDF van Gemini zit in de berekening van de korting. De twee IA hanteren niet dezelfde aanpak. Gemini had geschreven:

 
 
 

De twee IA-codes hebben twee verschillende benaderingen. Wie heeft gelijk?

5.5. Opgave 4

We gaan ChatGPT vragen om op basis van zijn PDF de belasting te berekenen:

Net als de vorige keren genereert hij een Python-script dat meteen werkt. We hadden in de instructies een extra test toegevoegd:

test13 : (1, 0, 18535) -> (359, 491, 90)

Alle 13 tests zijn met succes doorlopen.

5.6. Terug naar Gemini

Nu keren we terug naar Gemini, waaraan we de PDF van ChatGPT gaan voorleggen. Aangezien de regels die in dit PDF zijn geïmplementeerd, verschillen van die in het PDF van Gemini, is het de vraag wat er gaat gebeuren:

 

Gemini heeft eerst een Python-script gegenereerd dat de tests niet doorstond. We hebben de logbestanden aan Gemini getoond:

Vraag 2

 

Vraag 3

Er zijn nog steeds fouten. We gaan verder.

 

Vraag 4

Er treden nog steeds fouten op tijdens de uitvoering:

 

Deze keer is het goed.

We vinden het toch wel opmerkelijk dat, hoewel de PDF vrij verschillende berekeningsregels hebben, de IA beide correcte resultaten opleveren.

We stellen de volgende vraag aan Gemini:

 

De volledige vraag luidt als volgt:

Après tout ce qu'on a fait, compare les deux approches, la tienne et celle de ChatGPT dans les pDF que je te joins [.. selon Gemini] pour toi, [.. selon ChatGPT] pour ChatGPT. Dis-moi laquelle on doit préférer vu que sur les 13 tests générés tu les réussis tous avec l'un ou avec l'autre PDF.

Het antwoord van Gemini is ondubbelzinnig:

 
 
 
 
 
 

5.7. Wat vindt ChatGPT ervan?

We stellen ChatGPT dezelfde vraag die we aan Gemini hebben gesteld.

 

Het antwoord van ChatGPT luidt als volgt:

 
 

Daarom stelt ChatGPT ons een unit-test voor om de twee methoden te vergelijken. We kopiëren:

  • Het door Gemini gegenereerde script [gemini3], waarbij PDF als bron wordt gebruikt; [Le problème selon Gemini] wordt gekopieerd naar het script [gemini4];
  • Het script [chatGPT3], gegenereerd door ChatGPT met als bron PDF en [Le problème selon ChatGPT], wordt gedupliceerd in het script [chatGPT4];

Bovendien wordt in elk van de scripts [gemini4, chatGPT4] de door ChatGPT voorgestelde unit-test toegevoegd om de twee IA-scripts van elkaar te onderscheiden.

De uitvoering van [gemini4] levert de volgende resultaten op:


C:\Data\st-2025\dev\python\code\python-flask-2025-cours\.venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm 2025.2.1.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\gemini4.py" 
Testing started at 17:45 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini4.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours

SubTest failure: Traceback (most recent call last):
  File "C:\Program Files\Python313\Lib\unittest\case.py", line 58, in testPartExecutor
    yield
  File "C:\Program Files\Python313\Lib\unittest\case.py", line 556, in subTest
    yield
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini4.py", line 234, in test_cas_verifies_simulateur_officiel
    self.assertAlmostEqual(calcul_impot, attendu_impot, delta=1, msg="Échec sur le montant de l'impôt")
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 2669 != 2270 within 1 delta (399 difference) : Échec sur le montant de l'impôt




Ran 1 test in 0.010s

FAILED (failures=1)

One or more subtests failed
Failed subtests list: [Test 'test12' avec entrée (2, 0, 43333)]

Process finished with exit code 1

Gemini faalt dus voor de test die door ChatGPT is toegevoegd.

Het uitvoeren van [chatGPT4] levert de volgende resultaten op:


C:\Data\st-2025\dev\python\code\python-flask-2025-cours\.venv\Scripts\python.exe "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT4.py" 
Test (2, 2, 55555) -> obtenu (impôt=2814, décote=0, réduction=0) | attendu (2815, 0, 0) | OK
Test (2, 2, 50000) -> obtenu (impôt=1384, décote=384, réduction=347) | attendu (1385, 384, 346) | OK
Test (2, 3, 50000) -> obtenu (impôt=0, décote=721, réduction=0) | attendu (0, 720, 0) | OK
Test (1, 2, 100000) -> obtenu (impôt=19884, décote=0, réduction=0) | attendu (19884, 0, 0) | OK
Test (1, 3, 100000) -> obtenu (impôt=16782, décote=0, réduction=0) | attendu (16782, 0, 0) | OK
Test (2, 3, 100000) -> obtenu (impôt=9200, décote=0, réduction=0) | attendu (9200, 0, 0) | OK
Test (2, 5, 100000) -> obtenu (impôt=4230, décote=0, réduction=0) | attendu (4230, 0, 0) | OK
Test (1, 0, 100000) -> obtenu (impôt=22986, décote=0, réduction=0) | attendu (22986, 0, 0) | OK
Test (2, 2, 30000) -> obtenu (impôt=0, décote=0, réduction=0) | attendu (0, 0, 0) | OK
Test (1, 0, 200000) -> obtenu (impôt=64210, décote=0, réduction=0) | attendu (64211, 0, 0) | OK
Test (2, 3, 200000) -> obtenu (impôt=42842, décote=0, réduction=0) | attendu (42843, 0, 0) | OK
Test (2, 2, 49500) -> obtenu (impôt=1296, décote=431, réduction=325) | attendu (1297, 431, 324) | OK
Test (1, 0, 18535) -> obtenu (impôt=359, décote=491, réduction=90) | attendu (359, 491, 90) | OK
Test (2, 0, 43333) -> obtenu (impôt=2268, décote=0, réduction=401) | attendu (2270, 0, 400) | ECHEC
 Détails tolérance ±1€ : impôt ok? False, décote ok? True, réduction ok? True

Résultat global : AU MOINS UN TEST ÉCHOUE ❌

Process finished with exit code 0

Ook ChatGPT faalt bij de toegevoegde test, maar niet om dezelfde redenen als Gemini. ChatGPT heeft de juiste resultaten gevonden, maar met een afwijking van 2 euro in plaats van de voorgeschreven 1 euro.

Daarom zullen we voortaan de door ChatGPT gegenereerde PDF gebruiken in combinatie met de volgende IA-varianten. Opgemerkt moet worden dat het door het ontbreken van unit-tests in mijn instructies komt dat de twee IA-codes beide de eerste tests hebben doorstaan. Vandaar dat het in dit specifieke voorbeeld belangrijk is om unit-tests op te stellen voor de grensgevallen bij de belastingberekening. Aangezien het nogal moeilijk is om deze tests zelf te bedenken. We gaan de IA vragen om deze zelf toe te voegen.

5.8. Probleem 3 met unit-tests die zijn gegenereerd door de IA

De resultaten die met Gemini en ChatGPT zijn behaald, laten twijfel bestaan. Hebben de IA een algemene oplossing gevonden die alle denkbare tests doorstaat, of hebben ze een oplossing gevonden die alleen de opgelegde tests doorstaat? We gaan opnieuw beginnen met een oplossing zonder PDF, om de IA te dwingen op internet te zoeken naar de informatie die ze nodig hebben. En we passen onze instructies als volgt aan:

 

Het tekstbestand [instructionsSansPDF4.txt] bevat al 14 voorgeschreven tests. Aan deze tests voegen we de volgende instructies toe:


7 - tu ajouteras autant de tests unitaires que nécessaires pour vérifier les cas limites du calcul de l'impôt.

Pour le code tu complèteras le script suivant auquel tu auras rajouté tes propres tests.

# =========================
# Unit-tests (tolerantie van ±1 €)
# =========================

TESTS = [
     # (volwassenen, kinderen, inkomsten) -> (belasting, korting, vermindering)
    ((2, 2, 55555), (2815, 0, 0)),
    ((2, 2, 50000), (1385, 384, 346)),
    ((2, 3, 50000), (0, 720, 0)),
    ((1, 2, 100000), (19884, 0, 0)),
    ((1, 3, 100000), (16782, 0, 0)),
    ((2, 3, 100000), (9200, 0, 0)),
    ((2, 5, 100000), (4230, 0, 0)),
    ((1, 0, 100000), (22986, 0, 0)),
    ((2, 2, 30000), (0, 0, 0)),
    ((1, 0, 200000), (64211, 0, 0)),
    ((2, 3, 200000), (42843, 0, 0)),
    ((2, 2, 49500), (1297, 431, 324)),
    ((1, 0, 18535), (359, 491, 90)),
    ((2, 0, 43333), (2270, 0, 400)),
]


def _ok(a, b, tol=1):
    return abs(a - b) <= tol


def run_tests(verbose: bool = True) -> bool:
    all_ok = True
    for (params, expected) in TESTS:
        a, e, r = params
        exp_impot, exp_decote, exp_reduc = expected
        res = calcul_impot_2019(a, e, r)
        ok_impot = _ok(res.impot, exp_impot)
        ok_decote = _ok(res.decote, exp_decote)
        ok_reduc = _ok(res.reduction, exp_reduc)
        test_ok = ok_impot and ok_decote and ok_reduc
        if verbose:
            print(
                f"Test {params} -> obtenu (impôt={res.impot}, décote={res.decote}, réduction={res.reduction}) | attendu {expected} | {'OK' if test_ok else 'ECHEC'}")
            if not test_ok:
                print(
                    f" Détails tolérance ±1€ : impôt ok? {ok_impot}, décote ok? {ok_decote}, réduction ok? {ok_reduc}")
        all_ok &= test_ok
    if verbose:
        print("\nRésultat global :", "TOUS LES TESTS PASSENT ✅" if all_ok else "AU MOINS UN TEST ÉCHOUE ❌")
    return all_ok


if __name__ == "__main__":
    run_tests()
  • Regels 11-24: de 14 voorgeschreven tests;
  • Regels 5-55: deze code is afkomstig uit het script dat is gegenereerd door ChatGPT. We gaan Gemini opdragen deze code te gebruiken om de vergelijkingen tussen de twee gegenereerde scripts te vergemakkelijken.

We beginnen met ChatGPT:

 

Zijn eerste antwoord is onjuist. Ik laat hem dat weten en stuur hem de uitvoerlogs:

Zijn tweede antwoord is juist. ChatGPT heeft de volgende 11 tests toegevoegd aan de 14 verplichte tests:

# Extra grensgevallen (trapsgewijze overgangen/afrondingen)
TESTS += [
     # Korting van 10 %: ondergrens en bovengrens
    ((1, 0, 3000), (0, 0, 0)),  # 10 % = 300 < ondergrens 437 => RNI laag -> geen belasting
    ((1, 0, 200000), (64211, 0, 0)),  # Maximum van de aftrek is al meegenomen in de eerste tests

     # Korting: net onder / boven de drempels
    ((1, 0, 25000), None),  # diagnose
    ((2, 0, 35000), None),  # diagnose

     # Korting van 20 %: volledig recht versus afplatting
    ((1, 0, 17000), None),  # diagnose
    ((2, 0, 34000), None),  # diagnose
    ((1, 0, 20000), None),  # diagnose
    ((2, 0, 40000), None),  # diagnose

     # Wijziging van aandelen (plafond QF)
    ((2, 1, 80000), None),
    ((2, 2, 80000), None),
    ((2, 3, 80000), None),
]

Er zijn nu 25 unit-tests. Ik heb de 11 nieuwe tests handmatig gecontroleerd met de officiële simulator van DGIP en ze zijn in orde.

Nu gaan we verder met Gemini. Dat wordt een stuk ingewikkelder. Hij zal erin slagen een script te genereren dat de 25 tests van ChatGPT doorstaat, maar pas na een langdurige foutopsporing.

 

Hieronder volgt de lijst met foutopsporingsresultaten:

 

Vreemd genoeg is het merendeel van de tests mislukt, zelfs onder de 14 verplichte tests, terwijl Gemini in het verleden code had gegenereerd die ze allemaal doorstond.

Het volgende antwoord van Gemini is nog steeds niet correct:

 

Het volgende antwoord ook niet:

 

Het volgende antwoord ook niet. Daarom verander ik van tactiek. Ik vraag hem om de 25 tests te doorstaan die ChatGPT heeft doorstaan, en voeg daarbij de logs van ChatGPT toe:

 

Gemini faalt. Hij heeft de tests van ChatGPT wel toegevoegd. Ik voeg de logs van zijn uitvoering toe:

 

Nog steeds niet:

 

Nog steeds niet:

 

Nog steeds niet:

 

Nog steeds niet, maar het is al beter:

 

Gemini maakt weer nieuwe fouten:

 

Het gaat weer beter:

 

Deze keer is het goed:

Het staat buiten kijf dat, in dit specifieke voorbeeld van de berekening van de belasting voor 2019 met de beperkingen die in het instructiebestand zijn opgenomen, ChatGPT relevanter was dan Gemini. Maar dit is slechts een voorbeeld.

We kunnen nog een stap verder gaan. We kunnen Gemini vragen om een PDF opnieuw te genereren volgens de berekeningsregels die het heeft gebruikt om de 25 tests te doorstaan. We willen zien of het zijn oorspronkelijke redenering over de berekening van de korting en de vermindering van 20% heeft gewijzigd:

Deze keer heeft Gemini een bestand MarkDown gegenereerd, dat ik vervolgens heb omgezet in PDF [Le problème selon Gemini version 2]. En Gemini heeft zijn redenering inderdaad aangepast:

 
 

We zien dat de specifieke berekening van de korting en de herplaatsingsregel niet meer voorkomen. Gemini volgt nu de redenering van ChatGPT.