Skip to content

5. Risolvere i tre problemi con ChatGPT

5.1. Introduzione

Ecco una prima schermata di una sessione di ChatGPT:

 
  • In [1-3], i tre problemi sottoposti a ChatGPT;
  • In [4], l'URL di ChatGPT;
  • In [5], la versione di ChatGPT utilizzata;

ChatGPT è un prodotto di OpenAI disponibile all'URL [https://chatgpt.com/]. Per visualizzare una cronologia delle sessioni di domande e risposte come quella sopra riportata, è necessario creare un account. Inoltre, come tutte le altre IA testate, ChatGPT limita il numero di domande che è possibile porre e il numero di file che è possibile caricare. Quando si raggiunge questo limite, la sessione termina e viene offerta la possibilità di continuare in un secondo momento. I limiti imposti da ChatGPT vengono raggiunti molto rapidamente. Per creare questo tutorial, ho dovuto acquistare un abbonamento a pagamento di un mese.

L'interfaccia di ChatGPT è la seguente:

 
  • In [1], per allegare file alla domanda posta;
  • Al punto [2], la domanda posta;
  • Al punto [3], per eseguire l'IA;

5.2. Problema 1

La domanda per ChatGPT:

  
 

ChatGPT risponde correttamente.

5.3. Problema 2

Si tratta di calcolare l'imposta utilizzando il PDF. A dire il vero, useremo il PDF generato da Gemini, che corregge gli errori presenti nel PDF originale.

 
  • In [1] abbiamo fornito il PDF generato da Gemini;
  • In [2], abbiamo aggiunto il test unitario attraverso il quale Gemini ha dimostrato la sua superiorità:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Eseguiamo ChatGPT. Ci vogliono circa 3 minuti per generare la risposta. A differenza di Gemini, fornisce un link funzionante per recuperare lo script generato. Lo carichiamo in PyCharm:

 

Lo script [chatGPT1] funziona al primo tentativo. Non c'è storia: su questo problema, ChatGPT ha superato Gemini.

Lo script [chatGPT1] fornito da ChatGPT è il seguente:

#  -*- 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

#  ---------- Regulatory constants ----------

#  Professional expense allowance (floor/ceiling) for 2018 income
ABATTEMENT_TAUX = 0.10
ABATTEMENT_MIN = 437
ABATTEMENT_MAX = 12502

#  Scale 2018 (QF)
#  Each band is (borne_sup, rate). The first 0% bracket ends at €9,964.
BAREME_2018 = [
    (9964, 0.00),
    (27519, 0.14),
    (73779, 0.30),
    (156244, 0.41),
    (float("inf"), 0.45),
]

#  Family allowance ceiling
PF_QF_AVANTAGE_PAR_DEMI_PART = 1551  #  max benefit per 1/2 share ENFANT

#  Discount (thresholds according to number of adults / couple status)
DECOTE_SEUIL_CELIB = 1196
DECOTE_SEUIL_COUPLE = 1970

#  20% reduction: share-based thresholds
REDUCTION_BASE_1_PART = 18985
REDUCTION_BASE_2_PARTS = 37970
REDUCTION_PAR_DEMI_PART_SUP = 3803  #  addition of 1/2 share above the base of 1 or 2 shares


#  ---------- Data structures ----------

@dataclass
class ResultatImpot:
    impot: int  #  final tax payable, rounded down to the nearest euro
    decote: int  #  discount rounded up to the nearest euro
    reduction: int  #  20% discount rounded up to the nearest euro


#  ---------- Utility functions ----------

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))


#  ---------- Step 1: Net taxable income (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)))


#  ---------- Step 2: Number of units ----------

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
    #  children >= 2
    parts += 1.0  #  +0.5 +0.5 for the first two
    if enfants > 2:
        parts += (enfants - 2) * 1.0  #  +1 per child from the 3rd
    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 part
    #  First two children = 2 * half-parts, then +2 half-parts per child from the 3rd onwards
    return 2 + (enfants - 2) * 2


#  ---------- Step 3: Gross tax 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


#  ---------- Step 4.1: Capping the family quotient ----------

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


#  ---------- Step 4.2: Discount and 20% reduction ----------

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

    #  Eligible for 20% discount based on RNI
    seuil_red = seuil_reduction_20(rni, adultes, parts)
    eligible_reduction = rni < seuil_red  #  "below a threshold" in PDF

    #  Eligible for discount: directly below threshold, or if reduction would cause the threshold to be lowered
    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  #  theoretical 20% reduction
        if imp_theorique_apres_red <= s_dec:
            repechage = True

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

    #  Apply discount first (if eligible)
    if elig_decote:
        montant_decote = s_dec - (imp * 0.75)
        decote_calc = max(0, arrondi_sup(montant_decote))
        #  The discount cannot exceed the remaining tax: it is limited to
        decote = min(decote_calc, arrondi_sup(imp))
        imp = max(0.0, imp - decote)

    #  Then apply the reduction if eligible
    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 main ----------

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) Units
    parts = nombre_parts(adultes, enfants)

    #  3) Gross tax via tax scale (with all shares)
    imp_brut_A = impot_brut_total(rni, parts)

    #  4.1) Family allowance ceiling
    imp_apres_plaf = apply_plafonnement_qf(imp_brut_A, rni, adultes, enfants)

    #  4.2) Discount then 20% reduction (with draft)
    decote, reduction, imp_corrige = calc_decote_reduction(imp_apres_plaf, rni, adultes, parts)

    #  5) Final tax rounding (↓) and bounding to 0
    imp_final = max(0, arrondi_inf(imp_corrige))

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


#  ---------- Small test executable ----------

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 = [
        #  (adults, children, income) -> (tax, discount, reduction)
        ((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. Problema 3

Ora chiediamo a ChatGPT di cercare su Internet le regole di calcolo delle imposte:

 

Questa volta non forniamo il PDF contenente le regole di calcolo da seguire. Forniamo solo le nostre istruzioni nel file di testo. Si noti che questo file di testo ora contiene 12 test unitari dopo aver aggiunto, agli 11 test iniziali, quello utilizzato da Gemini per dimostrare che il mio PDF iniziale era errato.

ChatGPT risponde in 8 minuti, fornendo un link per scaricare lo script generato. Una volta caricato in PyCharm, questo script supera tutti e 12 i test. Quindi, per entrambi i problemi posti, ChatGPT ha dato le risposte giuste al primo tentativo, superando così Gemini.

ChatGPT fornisce le sue fonti nella risposta:

 

Non c'è altro da dire: è un lavoro ben fatto.

Ora possiamo chiedergli, proprio come abbiamo fatto con Gemini, di generare un PDF per gli studenti.

 

La risposta di ChatGPT è arrivata dopo diversi scambi perché il PDF generato utilizzava un font che sostituiva i caratteri con dei quadrati. Ma alla fine ha generato il PDF. Lo condivido perché presenta regole diverse rispetto al PDF di Gemini e mi chiedevo quale fosse quello corretto. Analizziamolo.

 
 
 

La differenza rispetto al PDF di Gemini sta nel modo in cui viene calcolato lo sconto. Le due IA adottano approcci diversi. Gemini aveva scritto:

 
 
 

Le due IA hanno due approcci diversi. Qual è quello giusto?

5.5. Problema 4

Chiederemo a ChatGPT di utilizzare il suo PDF per calcolare l'imposta:

Come nei casi precedenti, genera uno script Python che funziona al primo tentativo. Avevamo aggiunto un test aggiuntivo alle istruzioni:

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

Tutti i 13 test sono stati superati con successo.

5.6. Torna a Gemini

Torniamo ora a Gemini, al quale presenteremo il PDF di ChatGPT. Poiché le regole implementate in questo PDF differiscono da quelle del PDF di Gemini, ci chiediamo cosa succederà:

 

Gemini ha prima generato uno script Python che non ha superato alcuni test. Gli abbiamo presentato i log:

Domanda 2

 

Domanda 3

Ci sono ancora degli errori. Continuiamo.

 

Domanda 4

Si verificano ancora errori durante l'esecuzione:

 

Questa volta è corretto.

Ci incuriosisce ancora il fatto che, anche con PDF che hanno regole di calcolo piuttosto diverse, entrambe le IA generino risultati corretti.

Poniamo a Gemini la seguente domanda:

 

La domanda completa è la seguente:

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.

La risposta di Gemini è inequivocabile:

 
 
 
 
 
 

5.7. Cosa ne pensa ChatGPT?

Poniamo a ChatGPT la stessa domanda che abbiamo posto a Gemini.

 

La risposta di ChatGPT è la seguente:

 
 

Quindi, ChatGPT suggerisce un test unitario per scegliere tra i due metodi. Duplichiamo:

  • Lo script [gemini3] generato da Gemini utilizzando il suo PDF [Il problema secondo Gemini] come fonte viene duplicato nello script [gemini4];
  • Lo script [chatGPT3] generato da ChatGPT utilizzando il proprio PDF [Il problema secondo ChatGPT] come fonte viene duplicato nello script [chatGPT4];

Inoltre, aggiungiamo il test unitario proposto da ChatGPT a ciascuno degli script [gemini4, chatGPT4] per distinguere tra le due IA.

L'esecuzione di [gemini4] produce i seguenti risultati:


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

Quindi Gemini non supera il test aggiunto da ChatGPT.

L'esecuzione di [chatGPT4] produce i seguenti risultati:


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

Anche ChatGPT fallisce il test aggiunto, ma non per gli stessi motivi di Gemini. ChatGPT ha trovato i risultati corretti, ma con uno scarto di 2 euro invece dell'1 euro richiesto.

Quindi, d'ora in poi, useremo il PDF generato da ChatGPT con le seguenti IA. Vale la pena notare che è proprio a causa della mancanza di test unitari nelle mie istruzioni che entrambe le IA hanno superato i primi test. Da qui, in questo specifico esempio, l'importanza di includere test unitari per i casi limite nel calcolo delle imposte. Dato che è piuttosto difficile ideare questi test da soli, chiederemo alle IA di aggiungerli autonomamente.

5.8. Problema 3 con i test unitari generati dalle IA

I risultati ottenuti con Gemini e ChatGPT lasciano spazio a dubbi. Le IA hanno trovato una soluzione generale che supera ogni test immaginabile, o hanno trovato una soluzione che supera solo i test richiesti? Ricominceremo da capo con una soluzione senza PDF per costringere le IA a collegarsi a Internet e cercare le informazioni di cui hanno bisogno. E modificheremo le nostre istruzioni come segue:

 

Il file di testo [instructionsSansPDF4.txt] contiene già 14 test richiesti. A questi test aggiungiamo le seguenti istruzioni:


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.
 
# =========================
# Tests unitaires (tolérance de ±1 €)
# =========================
 
TESTS = [
    # (adultes, enfants, revenus) -> (impot, decote, reduction)
    ((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()
  • Righe 11–24: i 14 test richiesti;
  • Righe 5-55: questo codice proviene dallo script generato da ChatGPT. Chiederemo a Gemini di utilizzare questo codice per facilitare il confronto tra i due script generati.

Inizieremo con ChatGPT:

 

La sua prima risposta è errata. Glielo faccio notare fornendo i log di esecuzione:

La sua seconda risposta è corretta. ChatGPT ha aggiunto i seguenti 11 test ai 14 test richiesti:

# Cas limites supplémentaires (bords de paliers/arrondis)
TESTS += [
    # Abattement 10 % : plancher et plafond
    ((1, 0, 3000), (0, 0, 0)),  # 10 % = 300 < plancher 437 => RNI faible -> impôt nul
    ((1, 0, 200000), (64211, 0, 0)),  # plafond abattement déjà couvert dans tests initiaux

    # Décote : juste en dessous / au-dessus des seuils
    ((1, 0, 25000), None),  # diagnostique
    ((2, 0, 35000), None),  # diagnostique

    # Réduction 20 % : plein droit vs écrêtement
    ((1, 0, 17000), None),  # diagnostique
    ((2, 0, 34000), None),  # diagnostique
    ((1, 0, 20000), None),  # diagnostique
    ((2, 0, 40000), None),  # diagnostique

    # Changement de parts (plafonnement QF)
    ((2, 1, 80000), None),
    ((2, 2, 80000), None),
    ((2, 3, 80000), None),
]

Ora ci sono 25 test unitari. Ho verificato manualmente gli 11 nuovi test utilizzando il simulatore DGIP ufficiale e sono stati superati.

Ora passiamo a Gemini. Sarà molto più complicato. Riuscirà a generare uno script che superi tutti i 25 test di ChatGPT, ma solo dopo un lungo processo di debug.

 

Di seguito è riportato il log di debug:

 

Stranamente, la maggior parte dei test ha dato esito negativo, anche tra i 14 richiesti, mentre in passato Gemini aveva generato un codice che li superava tutti.

La seguente risposta di Gemini è ancora errata:

 

E nemmeno la seguente risposta:

 

E nemmeno la seguente risposta. Quindi sto cambiando approccio. Gli sto chiedendo di superare i 25 test che ChatGPT ha superato, allegando i log di ChatGPT:

 

Gemini fallisce. Ha aggiunto i test di ChatGPT. Allego i log della sua esecuzione:

 

Ancora no:

 

Ancora no:

 

Ancora no:

 

Ancora no, ma va meglio:

 

Gemini sta commettendo nuovi errori:

 

La situazione sta migliorando di nuovo:

 

Questa volta è corretto:

Indubbiamente, in questo specifico esempio di calcolo delle imposte del 2019 con i vincoli specificati nel file di istruzioni, ChatGPT è stato più accurato di Gemini. Ma questo è solo un esempio.

Possiamo andare oltre. Possiamo chiedere a Gemini di rigenerare un PDF basato sulle regole di calcolo che ha utilizzato per superare i 25 test. Vogliamo vedere se ha modificato il suo ragionamento iniziale riguardo ai calcoli per lo sconto e la riduzione del 20%:

Questa volta, Gemini ha generato un file Markdown che ho poi convertito in PDF [Il problema secondo Gemini Versione 2]. E Gemini ha effettivamente cambiato il suo ragionamento:

 
 

Possiamo notare che il calcolo specifico dello sconto e la regola del riporto non sono più presenti. Gemini ha ora adottato il ragionamento di ChatGPT.