Skip to content

4. Risoluzione dei tre problemi con Google Gemini

Forniremo gli screenshot delle tre sessioni di Gemini che hanno permesso di risolvere i tre problemi posti. Entreremo abbastanza nei dettagli. Una volta fatto ciò, non ripeteremo il processo per gli altri IA testati. Funzionano in modo analogo. Forniremo solo i dettagli salienti.

4.1. Introduzione

Ricordiamo la prima schermata di Gemini fornita in precedenza:

 
  • In [1], il URL di Gemini;
  • In [2], la versione di Gemini utilizzata;
  • In [3-5], i tre problemi sottoposti a Gemini;

Gemini è un prodotto di Google disponibile all’indirizzo URL [https://gemini.google.com/]. Per avere una cronologia delle vostre sessioni di domande e risposte come quella sopra riportata, è necessario creare un account. Inoltre, come tutte le altre IA testate, Gemini limita il numero di domande e il numero di file scaricati. Quando si raggiunge questo limite, la sessione termina e viene proposto di riprenderla in un secondo momento. Poiché è piuttosto frustrante interrompersi nel bel mezzo di una sessione, ho sottoscritto un abbonamento. Fortunatamente, il primo mese dell’abbonamento a Gemini è gratuito. Ho fatto lo stesso con gli altri IA che presentavano queste limitazioni, ovvero ChatGPT, MistralAI e ClaudeAI. Ho sottoscritto un abbonamento di un mese, ma in questo caso il primo mese era a pagamento. Con Grok non ho riscontrato alcuna limitazione. DeepSeek non dichiara alcuna limitazione, ma a volte risponde [Server busy] e interrompe la sessione. È come imporre delle limitazioni senza dirlo.

Di seguito mi riferirò alle sessioni di domande e risposte, abbreviate semplicemente in “sessioni”. Gli utenti IA utilizzano più spesso il termine inglese “chat” (discussione) o “conversazione”.

L’interfaccia di Gemini per porre una domanda è la seguente:

  • In [1], la vostra domanda;
  • In [2], l’icona che avvia il IA per il calcolo della risposta;
  • In [3-4], è possibile allegare dei file;

4.2. Problema 1

La sessione relativa al problema 1 è la seguente:

 
  • In [1], la domanda;
  • In [2], l'inizio della risposta di Gemini;

Il seguito della risposta è il seguente:

 
 
 
 

La risposta è corretta. Anche gli altri cinque IA daranno la risposta corretta in una forma analoga.

4.3. Problema 2

4.3.1. Introduzione

Ricordiamo qui il problema iniziale del corso [python3-flask-2020]. Si tratta di un testo fornito agli studenti in formato TD.

 

La tabella sopra riportata consente di calcolare l’imposta nel caso semplificato di un contribuente che abbia da dichiarare solo il proprio stipendio. Come indicato nella nota (1), l’imposta così calcolata è l’imposta al lordo di tre meccanismi:

  • il tetto massimo del quoziente familiare, che si applica ai redditi elevati;
  • Lo sconto e la riduzione d’imposta che si applicano ai redditi bassi;

Pertanto, il calcolo dell’imposta comprende le seguenti fasi [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

Ci proponiamo di scrivere un programma che consenta di calcolare l’imposta di un contribuente nel 2019 nel caso semplificato di un contribuente che abbia da dichiarare solo il proprio stipendio.

4.3.1.1. Calcolo dell’imposta lorda

L’imposta lorda può essere calcolata nel modo seguente:

Si calcola innanzitutto il numero di quote del contribuente:

  • Ogni genitore apporta 1 quota;
  • I primi due figli contribuiscono ciascuno con 1/2 quota;
  • I figli successivi apportano una quota ciascuno:

Il numero di quote è quindi:

  • nbParts=1+nbEnfants*0,5+(nbEnfants-2)*0,5 se il dipendente non è sposato;
  • nbParts = 2 + nbEnfants * 0,5 + (nbEnfants - 2) * 0,5 se è sposato;
  • dove nbEnfants è il numero di figli;
  • Si calcola il reddito imponibile R=0,9*S dove S è lo stipendio annuale;
  • Si calcola il quoziente familiare QF = R / nbParts;
  • Si calcola l’imposta lorda I in base ai seguenti dati (2019):
9964
0
0
27519
0,14
1394,96
73779
0,3
5798
156.244
0,4
13913,69
0
0,45
20163,45

Ogni riga presenta 3 campi: champ1, champ2, champ3. Per calcolare l'imposta I, si cerca la prima riga in cui QF ≤ campo1 e si prendono i valori di quella riga. Ad esempio, per un dipendente sposato con due figli e uno stipendio annuo S di 50.000 euro:

Reddito imponibile: R=0,9*S=45000

Numero di quote: nbParts=2+2*0,5=3

Quoziente familiare: QF=45.000/3=15.000

La prima riga in cui QF <= campo1 è la seguente:

    27519    0.14    1394.96

L'imposta I è quindi pari a 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*3] = 2115. L'imposta viene arrotondata all'euro inferiore.

Se la relazione QF ≤ campo1 è vera fin dalla prima riga, allora l’imposta è pari a zero.

Se QF è tale che la relazione QF<=campo1 non è mai soddisfatta, allora vengono utilizzati i coefficienti dell’ultima riga. In questo caso:

    0    0.45    20163.45

il che dà l'imposta lorda I=0,45*R – 20163,45*nbParts.

4.3.1.2. Limite massimo del quoziente familiare

 

Per verificare se si applica il limite massimo del quoziente familiare QF, si ricalcola l’imposta lorda senza considerare i figli. Sempre per il lavoratore dipendente sposato con due figli e uno stipendio annuo S di 50.000 euro:

Reddito imponibile: R=0,9*S=45.000

Numero di quote: nbParts=2 (i figli non vengono più conteggiati)

Quoziente familiare: QF = 45.000 / 2 = 22.500

La prima riga in cui QF <= campo1 è la seguente:

    27519    0.14    1394.96

L’imposta I è quindi pari a 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*2]=3510.

Guadagno massimo legato ai figli: 1551 * 2 = 3102 euro

Imposta minima: 3510 – 3102 = 408 euro

L’imposta lorda con 2 quote, già calcolata nel paragrafo precedente (2115 euro), è superiore all’imposta minima (408 euro), pertanto in questo caso non si applica il limite massimo familiare.

In generale, l’imposta lorda è maggiore di (imposta1, imposta2), dove:

  • [impôt1]: è l’imposta lorda calcolata con i figli;
  • [impôt2]: è l’imposta lorda calcolata senza i figli e ridotta del guadagno massimo (in questo caso 1551 euro per mezza quota) relativo ai figli;

4.3.1.3. Calcolo della riduzione

 

Sempre per il dipendente sposato con due figli e uno stipendio annuo S di 50.000 euro:

L’imposta lorda (2115 euro) risultante dalla fase precedente è inferiore a 2627 euro per una coppia (1595 euro per un single): si applica quindi la riduzione. Essa si ottiene con il seguente calcolo:

sconto = soglia (coppia = 1970 / single = 1196) - 0,75 * imposta lorda

sconto = 1970 - 0,75 * 2115 = 383,75 arrotondato a 384 euro.

Nuova imposta lorda = 2115 - 384 = 1731 euro

Nel calcolo della riduzione occorre osservare due regole (alcuni strumenti di IA hanno incontrato difficoltà su questo punto):

  • La riduzione non può essere negativa;
  • La riduzione non può essere superiore all’imposta già calcolata;

4.3.1.4. Calcolo della riduzione d’imposta

 

Al di sotto di una certa soglia, viene applicata una riduzione del 20% sull’imposta lorda risultante dai calcoli precedenti. Nel 2019, le soglie sono le seguenti:

  • single: 21.037 euro;
  • coppia: 42.074 euro; (la cifra 37.968 utilizzata nell’esempio sopra riportato sembra errata);

A questa soglia va aggiunto il valore: 3.797 * (numero di mezze quote apportate dai figli).

Sempre per il lavoratore dipendente sposato con due figli e uno stipendio annuo S di 50.000 euro:

  • Il suo reddito imponibile (45.000 euro) è inferiore alla soglia (42.074 + 2 * 3.797) = 49.668 euro;
  • Ha quindi diritto a una riduzione del 20% della sua imposta: 1731 * 0,2 = 346,2 euro, arrotondati a 347 euro;
  • L’imposta lorda del contribuente risulta pari a: 1.731 - 347 = 1.384 euro;

4.3.1.5. Calcolo dell’imposta netta

Il nostro calcolo si ferma qui: l’imposta netta da pagare sarà di 1.384 euro. In realtà, il contribuente può beneficiare di altre detrazioni, in particolare per le donazioni a enti di interesse pubblico o generale.

4.3.1.6. Caso dei redditi elevati

Il nostro esempio precedente corrisponde alla maggior parte dei casi dei lavoratori dipendenti. Tuttavia, il calcolo dell’imposta è diverso nel caso dei redditi elevati.

4.3.1.6.1. Limite massimo della riduzione del 10% sul reddito annuo

Nella maggior parte dei casi, il reddito imponibile si ottiene con la formula: R=0,9*S, dove S è lo stipendio annuale. Si parla in questo caso di riduzione del 10%. Tale riduzione è soggetta a un limite massimo. Nel 2019:

  • non può superare i 12.502 euro;
  • non può essere inferiore a 437 euro;

Prendiamo il caso di un dipendente non sposato senza figli con uno stipendio annuo di 200.000 euro:

  • La riduzione del 10% è pari a 20.000 euro > 12.502 euro. Viene quindi ridotta a 12.502 euro;

4.3.1.6.2. Limite massimo del quoziente familiare

Consideriamo un caso in cui si applica il limite massimo del quoziente familiare illustrato nel paragrafo |Limite massimo del quoziente familiare|. Prendiamo il caso di una coppia con tre figli e un reddito annuo di 100.000 euro. Ripassiamo le fasi del calcolo:

  • La detrazione del 10% è pari a 100.000 euro < 12.502 euro. Il reddito imponibile R è quindi pari a 100.000 - 10.000 = 90.000 euro;
  • La coppia ha nbParts = 2 + 0,5 × 2 + 1 = 4 quote;
  • Il suo quoziente familiare è quindi QF = R / nbParts = 90.000 / 4 = 22.500 euro;
  • La sua imposta lorda I1 con figli è pari a I1=0,14*90.000-1.394,96*4= 7.020 euro;
  • La sua imposta lorda I2 senza figli:
    • QF = 90.000 / 2 = 45.000 euro;
    • I2 = 0,3 × 90.000 − 5.798 × 2 = 15.404 euro;
  • La regola del limite massimo del quoziente familiare stabilisce che il beneficio derivante dai figli non possa superare (1551*4 mezze quote)=6204 euro. In questo caso, invece, è pari a I2-I1=15404-7020= 8384 euro, quindi superiore a 6204 euro;
  • L’imposta lorda viene quindi ricalcolata come I3 = I2-6204 = 15.404 - 6.204 = 9.200 euro;
  • Poiché I3 > I1, verrà mantenuta l’imposta I3;

Questa coppia non beneficerà né di uno sconto né di una riduzione e la sua imposta finale sarà di 9200 euro.

4.3.1.7. Dati ufficiali

Il calcolo dell’imposta è complesso. Nel corso del documento, le simulazioni saranno effettuate utilizzando i seguenti esempi. I risultati provengono dal simulatore dell’amministrazione fiscale |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

Contribuente
Risultati ufficiali
Risultati dell’algoritmo del documento
Coppia con 2 figli e un reddito annuo di 55555 euro
Imposta = 2815 euro
Aliquota fiscale = 14 %
Imposta = 2814 euro
Aliquota fiscale = 14 %
Coppia con 2 figli e un reddito annuo di 50.000 euro
Imposta = 1.385 euro
Sconto = 384 euro
Riduzione = 346 euro
Aliquota fiscale = 14 %
Imposta = 1.384 euro
Sconto = 384 euro
Riduzione = 347 euro
Aliquota fiscale = 14 %
Coppia con 3 figli e un reddito annuo di 50.000 euro
Imposta = 0 euro
Sconto = 720 euro
Sconto = 0 euro
Aliquota fiscale = 14 %
Imposta = 0 euro
Sconto = 720 euro
Riduzione = 0 euro
Aliquota fiscale = 14 %
Celibe con 2 figli e un reddito annuo di 100.000 euro
Imposta = 19.884 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 41 %
Imposta = 19.884 euro
Maggiorazione = 4.480 euro
sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41 %
Celibe con 3 figli e un reddito annuo di 100.000 euro
Imposta = 16.782 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 41 %
Imposta = 16.782 euro
Maggiorazione = 7.176 euro
sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41 %
Coppia con 3 figli e un reddito annuo di 100.000 euro
Imposta = 9.200 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 30 %
Imposta = 9.200 euro
Maggiorazione = 2180 euro
sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 30 %
Coppia con 5 figli e un reddito annuo di 100.000 euro
Imposta = 4.230 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 14 %
Imposta = 4.230 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 14 %
Celibe senza figli e con un reddito annuo di 100.000 euro
Imposta = 22.986 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 41%
Imposta = 22.986 euro
Maggiorazione = 0 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41 %
Coppia con 2 figli e un reddito annuo di 30.000 euro
Imposta = 0 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 0%
Imposta = 0 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 0 %
Celibe senza figli e con un reddito annuo di 200.000 euro
Imposta = 64.211 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 45 %
Imposta = 64.210 euro
Maggiorazione = 7.498 euro
sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 45 %
Coppia con 3 figli e un reddito annuo di 200.000 euro
Imposta = 42.843 euro
Sconto = 0 euro
Sconto = 0 euro
Aliquota fiscale = 41%
Imposta = 42.842 euro
Maggiorazione = 17.283 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41 %

In questo caso, si definisce «sovrattassa» l’importo aggiuntivo che i redditi elevati devono versare a causa di due fenomeni:

  • il limite massimo del 10% sulla detrazione sui redditi annuali;
  • Il limite massimo del quoziente familiare;

Questo indicatore non ha potuto essere verificato poiché il simulatore dell’amministrazione fiscale non lo fornisce.

Si nota che l’algoritmo del documento fornisce ogni volta un’imposta corretta, con tuttavia un margine di errore di 1 euro. Questo margine di errore deriva dagli arrotondamenti. Tutte le somme di denaro vengono arrotondate a volte all’euro superiore, a volte all’euro inferiore. Poiché non conoscevo le regole ufficiali, gli importi dell’algoritmo del documento sono stati arrotondati:

  • all’euro superiore per gli sconti e le riduzioni;
  • All’euro inferiore per i sovrapprezzi e l’imposta finale;

Chiederemo a IA di eseguire questo calcolo dell’imposta.

4.3.2. Configurazione della sessione Gemini

La richiesta inviata a Gemini è accompagnata da due file:

 
  • Nel file [1], il calcolo appena descritto è stato inserito in un file PDF che viene fornito a Gemini. Gemini vi troverà le regole esatte per il calcolo semplificato dell’imposta 2019 sui redditi del 2018;
  • In [2], le nostre istruzioni;
  • Nel file [3] per avviare il file IA;

Le nostre istruzioni nel file di testo [instructionsAvecPDF.txt] sono le seguenti:

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/simulatore/calcul_impot/2019/semplifica/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é.

Queste istruzioni sono il risultato di moltissime domande poste a Gemini. Ci si rende conto abbastanza rapidamente che il IA deve essere molto ben definito se si vuole ottenere ciò che si desidera. È a causa di tutti questi tentativi che la sessione Gemini è stata infine interrotta per superamento dei limiti. Esaminiamo il seguito di queste istruzioni:

  • Riga 1: si richiede che la conversazione sia in francese. Questa indicazione è rivolta a DeepSeek, che tendeva a parlare in inglese;
  • Riga 3: ciò che si desidera;
  • Riga 5: si dice a IA di utilizzare il PDF che gli è stato fornito;
  • Righe 7-14: una serie di consigli utili soprattutto per il problema 3 senza PDF. Diversi IA sono andati persi nel calcolo dell’imposta;
  • Righe 15-44: gli 11 test unitari che si desidera includere nello script generato. Una volta generato lo script, lo si eseguirà in PyCharm e si verificherà se gli 11 test vengono superati;
  • Righe 46-53: senza queste istruzioni, IA generava test unitari che cercavano risultati esatti e fallivano;
  • Righe 55-56: indico a IA di non connettersi a Internet. La soluzione più semplice è utilizzare PDF;
  • Righe 58-59: questa istruzione non è stata seguita da IA. Sono stato costretto a scriverla esplicitamente in una domanda, quando ho constatato che un test era fallito;
  • Righe 61-65: indico quale tipo di script Python desidero;
  • Righe 67-69: avrei preferito un link per scaricare lo script generato, poiché la visualizzazione del codice sullo schermo richiede tempo. È emerso che la maggior parte dei IA non è in grado di farlo. I link forniti non funzionavano;
  • Righe 71-72: avrei voluto conoscere il tempo impiegato dall’IA per rispondere alla domanda. Solo Gemini è stato in grado di fornirmi questa informazione. Gli altri IA o non rispondevano a questa istruzione, oppure fornivano cifre inventate, dimostrando di non averla compresa;

4.3.3. La risposta di Gemini

La prima risposta di Gemini è la seguente:

 
  • In [1-4], Gemini fornisce dei link alla parte del PDF o del file di testo contenente le istruzioni che sta utilizzando in quel momento;

Il seguito è il seguente:

 
  • In [1], Gemini afferma di aver eseguito con successo tutti gli 11 test unitari. La maggior parte dei IA lo ha affermato sia per il problema 2 che per il problema 3 e spesso, quando si caricava lo script generato, non funzionava. Bisogna quindi diffidare di questa affermazione. Per Gemini si rivelerà vera;
  • In [2], un link che si rivelerà non funzionante;
  • Nel [3 solo Gemini ha fornito un tempo di esecuzione realistico;

Quindi il link [2] non funziona. Lo segnaliamo a Gemini:

 

La risposta di Gemini:

 
  • In [1], lo script Python generato da Gemini;

Carichiamo questo script in PyCharm e lo eseguiamo:

 
  • In [1], [gemini1] è lo script generato da Gemini;

All’esecuzione dello script compaiono errori di compilazione:


"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
  • Riga 30, l’errore di compilazione. [cite_start] è un marcatore per generare una determinata forma di testo;

Si inseriscono i log sopra riportati in un file [logs gemini1.txt] e lo si fornisce a Gemini:

 

La risposta di Gemini è quindi la seguente:

 

Una volta caricato in PyCharm, l’esecuzione del nuovo script genera esattamente lo stesso errore. Lo si comunica a Gemini fornendogli nuovamente i log dell’esecuzione:

 

La risposta di Gemini è la seguente:

 

Questa volta funziona. Tutti gli 11 test unitari hanno esito positivo. Lo comunichiamo a Gemini:

 

Che risponde:

 

Lo script generato da Gemini ha seguito le istruzioni fornite nel file di testo [instructionsAvecPDF.txt]:

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

import math
import unittest

# --- CONSTANTES DE CALCUL POUR L'IMPÔT 2019 ---
# Tutti i valori sono estratti dal documento PDF fornito.

# Limiti massimi per la detrazione del 10% sul reddito annuo
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# Tabella delle aliquote dell’imposta sul reddito 2019
# Struttura: (Soglia massima della fascia, Aliquota fiscale, Importo da dedurre per quota)
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}
]

# Limite massimo del quoziente familiare
GAIN_MAX_DEMI_PART = 1551.0  # Guadagno massimo per ogni mezza quota supplementare

# Soglie e importi per la riduzione
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# Soglie per la riduzione d’imposta del 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
     # La detrazione è soggetta a un limite compreso tra un minimo e un massimo
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # La domanda richiede di arrotondare la detrazione all’euro superiore
    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

     # Se il valore di QF è superiore a tutte le soglie, si utilizza l’ultima fascia
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # Formula per il calcolo dell’imposta lorda
    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

     # Calcolo dell’imposta senza le quote dei figli
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # Calcolo del guadagno massimo relativo ai figli
    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

     # L'imposta trattenuta è quella più alta tra i due calcoli
    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
     # La riduzione si applica se l’imposta è inferiore alla soglia di applicazione
    if impot_avant_decote < seuil_application:
         # Formula della riduzione: soglia - 0,75 * Imposta lorda
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # Arrotondamento all’euro superiore
        decote = math.ceil(decote_calculee)

         # La riduzione non può essere negativa né superiore all’imposta
        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]

     # La soglia viene aumentata per ogni mezza quota relativa ai figli
    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
     # La riduzione si applica se il reddito imponibile è inferiore alla soglia
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # Arrotondamento all'euro superiore
        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.
    """
     # Fase 1: Calcolo del reddito imponibile
    revenu_imposable = calculer_revenu_imposable(revenus)

     # Fase 2: Calcolo del numero di quote
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # Fase 3: Calcolo dell'imposta lorda
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # Fase 4: Applicazione del limite massimo del QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # Fase 5: Applicazione dello sconto
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # Fase 6: Applicazione della riduzione del 20%
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # Fase 7: Arrotondamento finale dell’imposta all’euro inferiore
    impot_final = math.floor(impot_avant_arrondi_final)

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


# --- TESTS UNITAIRES ---
# Convalida dello script con i casi forniti
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)

     # Esempio di utilizzo del calcolatore per un caso specifico
    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']}€")

Non ho verificato questo codice. Poiché gli 11 test unitari sono stati superati, lo considero «probabilmente corretto». Per quanto riguarda il mio codice, mi sono limitato a verificare questi 11 test.

4.4. Il problema 3

Il problema 3 è identico al problema 2, tranne per il fatto che a IA non viene più fornito il PDF che indicava le regole di calcolo da seguire.

La domanda iniziale a Gemini è la seguente:

 

Il file delle istruzioni in formato [1] è praticamente identico a quello del problema 2, con le seguenti differenze:


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], gli viene chiesto di trovare su Internet le regole per il calcolo dell’imposta 2019 sui redditi del 2018. Si tratta di un esercizio più difficile del precedente;

Di seguito riporto solo alcune parti della prima risposta di Gemini:

 
 

Il tempo stimato è plausibile. Si attende a lungo la risposta di Gemini.

Come in precedenza, Gemini ha fornito un link per scaricare lo script generato, link che però non funziona. Glielo facciamo notare:

 

La risposta di Gemini:

 

Carichiamo lo script in PyCharm con il nome [gemini2]:

 

Lo si esegue e… non funziona. I log dell’esecuzione sono i seguenti:


"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
  • Riga 11, un test non è andato a buon fine;
  • Righe 25, 42: lo stesso errore di compilazione;

Si inseriscono questi log in un file di testo che si invia a Gemini:

 

La risposta di Gemini:

 
 

Carichiamo lo script in PyCharm e lo eseguiamo. Ancora errori. Lo segnaliamo a Gemini allegando nuovamente i log dell’esecuzione:

 

La risposta di Gemini:

 
 
 

Carichiamo questo nuovo script in PyCharm ed eseguiamolo. Questa volta tutti gli 11 test unitari vengono superati:

Il codice generato da Gemini è il seguente:

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

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


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

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


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


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


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


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

     # CORRECTION: Se l’imposta è già pari a 0, non si applicano né sconti né riduzioni.
    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. Determinare l'idoneità alla riduzione del 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. Determinare l'idoneità allo sconto
    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: Lo sconto restituito non può superare l'imposta che annulla
        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. Applicare la riduzione del 20% se ammissibile
    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)

Anche in questo caso non ho esaminato il codice. Ho semplicemente constatato che superava con successo tutti gli 11 test.

Ma si può essere curiosi di conoscere il suo ragionamento, in particolare per i casi particolari del calcolo dell’imposta. Glielo chiediamo:

 

Si tratta di un caso di redditi elevati con un possibile limite massimo della detrazione del 10% e un possibile limite massimo del quoziente familiare.

La risposta di Gemini è la seguente:

 
 
 
 
 

Queste ultime due schermate sono interessanti. Gemini utilizza un metodo di calcolo diverso da quello spiegato nel PDF. Questo metodo di calcolo è effettivamente reperibile su Internet. I due metodi sono equivalenti.

 
 
 

La spiegazione si distingue per la sua chiarezza. Potrebbe essere fornita così com’è agli studenti per illustrare il metodo di calcolo dell’imposta.

Ora prendiamo un altro esempio, questa volta con redditi modesti. In questo caso, possono esserci uno sconto e una riduzione:

 

La risposta di Gemini è la seguente:

 
 
 
 
 

Qui si nota che Gemini applica una regola che non è presente nel PDF. Probabilmente l’ha trovata su Internet, ma la fonte è affidabile?

 

Qui Gemini continua ad applicare una regola sconosciuta (la regola speciale di cui sopra).

 
 
 

Quindi i risultati di Gemini coincidono con quelli del simulatore fiscale ufficiale. Tuttavia, ha utilizzato una regola non presente nel PDF. Dove sta l’errore? Glielo chiediamo allegando il PDF:

 

La risposta di Gemini:

 
 
 
 

Penso che Gemini abbia ragione e che il mio PDF sia errato. Per verificarlo, gli chiedo di fare un test:

  • in cui il suo ragionamento darebbe gli stessi risultati del simulatore fiscale ufficiale;
  • in cui il ragionamento del PDF darebbe risultati diversi da quelli del simulatore;
 

La risposta di Gemini è la seguente:

 

In questo caso Gemini sbaglia. Ho eseguito il simulatore su questo esempio e ho ottenuto quanto segue:

 

Tuttavia, scopriremo che il ragionamento di Gemini porta effettivamente ai risultati sopra riportati. Continuiamo:

 
 
 
 

Va bene. Ne prendo nota. Continuiamo:

 
 
 
 

Quindi Gemini ha calcolato (imposta, sconto, riduzione) = (431, 325, 1296), mentre il simulatore che ho utilizzato io restituisce (431, 324, 1297). Quindi Gemini ha ottenuto i risultati corretti con uno scarto di 1 euro, ma non lo sa. Glielo diciamo:

 

Gemini risponde:

 
 

Ora ci si chiede se Gemini potrebbe generare un PDF corretto:

 

La risposta di Gemini:

 

Gemini non mi ha quindi fornito un link a un PDF, ma ha generato un testo che mi permette di generare io stesso questo PDF. Sebbene sia un po’ ingombrante riportare qui gli screenshot del PDF, lo faccio affinché il lettore possa cogliere l’aspetto generativo del IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Onestamente non ho verificato se tutto ciò che è riportato in questo PDF sia vero. In ogni caso, è un documento perfetto per un TD, generato in pochi secondi.

Tuttavia, è possibile far verificare da Gemini stesso che il suo PDF sia corretto. Si avvia una nuova conversazione:

 
  • in [1], abbiamo inserito il PDF generato da Gemini [Le problème selon Gemini.pdf];
  • in [2], [instructionsAvecPDF2.txt] è identico alle istruzioni di [instructionsAvecPDF.txt], tranne per l’aggiunta di un dodicesimo test unitario, proprio quello che ha evidenziato l’errore nel PDF iniziale:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, ci sono voluti diversi tentativi prima che Gemini generasse lo script corretto:

Domanda 2

 

Domanda 3

 

Come già fatto più volte, quando lo script generato e caricato in PyCharm non funziona, forniamo a Gemini il file di testo contenente i log dell’esecuzione. Gemini li comprende molto bene.

Domanda 4

 

Domanda 5

 

Domanda 6 e fine

Ora siamo certi della validità del codice PDF generato da Gemini. Le regole di calcolo ivi riportate sono corrette.

Ora faremo la stessa cosa per gli altri cinque IA, ma saremo molto concisi nelle nostre spiegazioni, tranne che per il ChatGPT, l’attuale leader tra i IA. Ciò che ci interessa è sapere se l’IA risolva o meno i tre problemi che gli vengono posti. Infatti, le interfacce di tutti questi IA sono molto simili e ho proceduto con loro come con Gemini. Il lettore è invitato a riprodurre le conversazioni di Gemini con il modello IA di sua scelta.