Skip to content

4. Risoluzione dei tre problemi con Google Gemini

Forniremo screenshot delle tre sessioni di Gemini utilizzate per risolvere i tre problemi proposti. Entreremo in notevole dettaglio. Una volta fatto ciò, non ripeteremo il processo per le altre IA testate. Esse operano in modo simile. Forniremo solo i dettagli più rilevanti.

4.1. Introduzione

Facciamo riferimento alla prima schermata di Gemini fornita in precedenza:

 
  • In [1], l'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'URL [https://gemini.google.com/]. Per visualizzare la cronologia delle sessioni di domande e risposte come mostrato sopra, è necessario creare un account. Inoltre, come tutte le altre IA testate, Gemini 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 continuarla in un secondo momento. Dato che è 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 le altre IA che presentavano questi limiti, ovvero ChatGPT, MistralAI e ClaudeAI. Ho sottoscritto un abbonamento di un mese, ma in quei casi il primo mese era a pagamento. Con Grok non ho riscontrato alcun limite. DeepSeek non annuncia alcun limite, ma a volte risponde con [Server occupato] e interrompe la sessione. In sostanza, si tratta di impostare dei limiti senza dirlo.

D'ora in poi, mi riferirò alle sessioni di domande e risposte semplicemente come "sessioni". Le IA usano molto spesso il termine inglese "chat" o "conversazione".

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

  • In [1], la tua domanda;
  • In [2], l'icona che avvia l'IA per calcolare la risposta;
  • In [3-4], puoi allegare dei file;

4.2. Problema 1

La sessione per il Problema 1 è la seguente:

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

Il resto della risposta è il seguente:

 
 
 
 

La risposta è corretta. Anche le altre cinque IA daranno la risposta corretta in una forma simile.

4.3. Problema 2

4.3.1. Introduzione

Qui ricordiamo il problema iniziale del corso [python3-flask-2020]. Si tratta di un testo fornito agli studenti durante un esercitazione.

 

La tabella sopra riportata ci permette di calcolare l'imposta nel caso semplificato di un contribuente che ha solo lo stipendio da dichiarare. Come indicato nella nota (1), l'imposta calcolata in questo modo è l'imposta prima dell'applicazione di tre meccanismi:

  • Il tetto massimo del quoziente familiare, che si applica ai redditi elevati;
  • Il credito d'imposta e la riduzione d'imposta che si applicano ai redditi bassi;

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

 

Proponiamo di scrivere un programma per calcolare l'imposta dovuta da un contribuente per il 2019 nel caso semplificato di un contribuente che deve dichiarare solo il proprio stipendio.

4.3.1.1. Calcolo dell'imposta lorda

L'imposta lorda può essere calcolata come segue:

In primo luogo, calcolare il numero di quote del contribuente:

  • Ciascun genitore contribuisce con 1 quota;
  • I primi due figli contribuiscono ciascuno con 1/2 quota;
  • I figli successivi contribuiscono ciascuno con una quota:

Il numero di azioni è quindi:

  • nbParts=1+nbChildren*0,5+(nbChildren-2)*0,5 se il dipendente è celibe/nubile;
  • nbParts=2+nbChildren*0,5+(nbChildren-2)*0,5 se è sposato;
  • dove nbChildren è il numero di figli;
  • Calcoliamo il reddito imponibile R = 0,9 * S, dove S è lo stipendio annuale;
  • Il quoziente familiare QF è calcolato come QF = R / nbParts;
  • Calcoliamo l'imposta lorda I sulla base dei seguenti dati (2019):
9964
0
0
27.519
0,14
1.394,96
73.779
0,3
5.798
156.244
0,4
13.913,69
0
0,45
20163,45

Ogni riga ha 3 campi: campo1, campo2, campo3. Per calcolare l'imposta I, cerchiamo la prima riga in cui QF <= campo1 e prendiamo i valori da 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=45.000

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*numeroDiAzioni=[0,14*45000-1394,96*3]=2115. L'imposta viene arrotondata per difetto all'euro più vicino.

Se nella prima riga si verifica la condizione QF <= campo1, l'imposta è pari a zero.

Se QF è tale che la condizione QF <= campo1 non è mai soddisfatta, vengono utilizzati i coefficienti dell'ultima riga. Qui:

    0    0.45    20163.45

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

4.3.1.2. Limite del quoziente familiare

 

Per determinare se si applica il limite massimo del quoziente familiare (QF), ricalcoliamo l'imposta lorda senza i figli. Ancora una volta, per il 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*numero di azioni = [0,14*45.000 – 1394,96*2] = 3.510.

Indennità massima per figli a carico: 1551 * 2 = 3102 euro

Imposta minima: 3.510 – 3.102 = 408 euro

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

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

  • [imposta1]: è l'imposta lorda calcolata includendo i figli;
  • [imposta2]: è l'imposta lorda calcolata senza i figli e ridotta del credito massimo (in questo caso 1.551 euro per mezza quota) relativo ai figli;

4.3.1.3. Calcolo della riduzione d'imposta

 

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

L'imposta lorda (2.115 euro) del passaggio precedente è inferiore a 2.627 euro per una coppia (1.595 euro per una persona single): si applica quindi la riduzione d'imposta. Essa viene calcolata come segue:

credito d'imposta = soglia (coppia = 1.970 / single = 1.196) – 0,75 * imposta lorda

sconto = 1.970 – 0,75 * 2.115 = 383,75, arrotondato a 384 euro.

Nuova imposta lorda = 2.115 – 384 = 1.731 euro

Nel calcolo dello sconto devono essere rispettate due regole (alcuni strumenti di IA hanno avuto difficoltà su questo punto):

  • Lo sconto non può essere negativo;
  • Lo sconto non può superare l'imposta già calcolata;

4.3.1.4. Calcolo della riduzione fiscale

 

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

  • Single: 21.037 euro;
  • coppia: 42.074 euro; (la cifra di 37.968 utilizzata nell'esempio sopra riportato sembra essere errata);

Questa soglia viene aumentata del valore: 3.797 * (numero di quote pari alla metà apportate dai figli).

Ancora una volta, per il 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: 1.731 * 0,2 = 346,2 euro, arrotondati a 347 euro;
  • L'imposta lorda del contribuente diventa: 1.731 – 347 = 1.384 euro;

4.3.1.5. Calcolo dell'imposta netta

Il nostro calcolo termina qui: l'imposta netta dovuta sarà di 1.384 euro. In realtà, il contribuente potrebbe avere diritto ad altre detrazioni, in particolare per le donazioni a organizzazioni pubbliche o di interesse generale.

4.3.1.6. Casi di redditi elevati

Il nostro esempio precedente si applica alla maggior parte dei dipendenti. Tuttavia, il calcolo dell'imposta è diverso per i redditi elevati.

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

Nella maggior parte dei casi, il reddito imponibile viene calcolato utilizzando la formula: R = 0,9 × S, dove S è lo stipendio annuale. Questa è nota come 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;

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

  • La riduzione del 10% è pari a 200.000 euro > 12.502 euro. Il limite massimo è quindi fissato a 12.502 euro;

4.3.1.6.2. Limite del quoziente familiare

Consideriamo un caso in cui si applica il limite familiare descritto nella sezione |Limite del quoziente familiare|. Prendiamo il caso di una coppia con tre figli e un reddito annuo di 100.000 euro. Ripassiamo i passaggi 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 è 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 del quoziente familiare stabilisce che il beneficio derivante dai figli non può superare (1.551 × 4 mezze quote) = 6.204 euro. Tuttavia, in questo caso, si ha I2 – I1 = 15.404 – 7.020 = 8.384 euro, che è superiore a 6.204 euro;
  • L'imposta lorda viene quindi ricalcolata come I3 = I2 - 6.204 = 15.404 - 6.204 = 9.200 euro;
  • Poiché I3 > I1, verrà mantenuta l'imposta I3;

Questa coppia non riceverà né un credito d'imposta né una riduzione, e la loro imposta finale sarà di 9.200 euro.

4.3.1.7. Dati ufficiali

Il calcolo dell'imposta è complesso. Nel corso di questo documento, verranno effettuati dei test 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 55.555 euro
Imposta = 2.815 euro
Aliquota fiscale = 14%
Imposta = 2.814 euro
Aliquota fiscale = 14%
Coppia con 2 figli e un reddito annuo di 50.000 euro
Imposta = 1.385 euro
Credito d'imposta = 384 euro
Riduzione = 346 euro
Aliquota fiscale = 14%
Imposta = 1.384 euro
Sconto = 384 euro
Credito = 347 euro
Aliquota fiscale = 14%
Coppia con 3 figli e un reddito annuo di 50.000 euro
Imposta = 0 euro
Credito d'imposta = 720 euro
Riduzione = 0 euro
Aliquota fiscale = 14%
Imposta = 0 euro
Sconto = 720 euro
Detrazione = 0 euro
Aliquota fiscale = 14%
Single con 2 figli e un reddito annuo di 100.000 euro
Imposta = 19.884 euro
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 41%
Imposta = 19.884 euro
Sovrattassa = 4.480 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41%
Single con 3 figli e un reddito annuo di 100.000 euro
Imposta = 16.782 euro
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 41%
Imposta = 16.782
Sovrattassa = 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
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 30%
Imposta = 9.200 euro
Sovrattassa = 2.180 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
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 14%
Imposta = 4.230
Sconto = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 14%
Celibe, senza figli e reddito annuo di 100.000 euro
Imposta = 22.986 euro
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 41%
Imposta = 22.986 euro
Sovrattassa = 0 euro
Sconto = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 41%
Coppia con 2 figli e un reddito annuo di 30.000 euro
Imposta = 0 euro
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 0%
Imposta = 0 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 0%
Single senza figli e con un reddito annuo di 200.000 euro
Imposta = 64.211 euro
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 45%
Imposta = 64.210 euro
Sovrattassa = 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
Credito d'imposta = 0 euro
Detrazione = 0 euro
Aliquota fiscale = 41%
Imposta = 42.842 euro
Sovrattassa = 17.283 euro
Sconto = 0 euro
Riduzione = 0 euro
Aliquota fiscale = 41%

Nell'esempio sopra riportato, il termine "sovrapprezzo" si riferisce all'importo aggiuntivo pagato dai redditi più elevati a causa di due fattori:

  • Il limite massimo del 10% sulla detrazione dal reddito annuo;
  • Il limite massimo dell'assegno familiare;

Questo indicatore non ha potuto essere verificato poiché il simulatore dell'autorità fiscale non lo fornisce.

Si può notare che l’algoritmo del documento calcola ogni volta l’importo corretto dell’imposta, sebbene con un margine di errore di 1 euro. Questo margine di errore deriva dall’arrotondamento. Tutti gli importi monetari vengono arrotondati per eccesso all’euro più vicino in alcuni casi e per difetto all’euro più vicino in altri. Poiché non conoscevo le regole ufficiali, gli importi monetari nell’algoritmo del documento sono stati arrotondati:

  • per arrotondamento per eccesso all'euro successivo per sconti e riduzioni;
  • per difetto all'euro più vicino per i supplementi e l'imposta finale;

Chiederemo all'IA di eseguire questo calcolo dell'imposta.

4.3.2. Configurazione della sessione Gemini

La domanda posta a Gemini è accompagnata da due file:

 
  • In [1], il calcolo appena illustrato è stato inserito in un documento PDF fornito a Gemini. Gemini troverà lì le regole precise per il calcolo semplificato dell'imposta del 2019 sui redditi del 2018;
  • In [2], le nostre istruzioni;
  • In [3], il comando per avviare l'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/simulateur/calcul_impot/2019/simplifie/index.html].
Si tu utilises ce simulateur les revenus doivent être associés au seul déclarant 1 dans le cas d'un couple, le déclarant 2 étant alors ignoré. Lorsqu'on répartit les revenus
sur deux déclarants, on n'obtient pas le même résultat.

On utilise la syntaxe (adultes, enfants, revenus) -> (impot, decote, reduction) pour dire que le script reçoit les entrées
(adultes, enfants, revenus) et produit les résultats (impot, decote, reduction)

test1 : (2,2,55555) -> (2815, 0, 0)
test2 : (2, 2, 50000) -> (1385, 384, 346)
test3 : (2,3,50000) -> (0, 720, 0)
test4: (1,2,100000) -> (19884, 0, 0)
test5: (1,3,100000) -> (16782, 0, 0)
test6 : (2, 3, 100000) -> (9200, 0, 0)
test7 : (2, 5, 100000) -> (4230, 0, 0)
test8 : (1, 0, 100000) -> (22986, 0, 0)
test9 : (2, 2, 30000) -> (0, 0, 0)
test10 : (1,0,200000) -> (64211, 0, 0)
test11  : (2, 3, 200000) -> (42843, 0, 0)

6 - Il peut y avoir des problèmes d'arrondis. Tu procèderas comme suit
- l'impôt à payer sera arrondi à l'euro inférieur,
- la décote sera arrondie à l'euro supérieur,
- la réduction de 20 % sera arrondie à l'euro supérieur.
- l'abattement de 10% sera arrondi à l'euro supérieur

Fais tous les tests unitaires à l'euro près à cause de ces éventuelles erreurs d'arrondi.
Ne cherche pas à avoir les valeurs exactes ci-dessus mais ces valeurs à 1 euro près.

7 - Evite de chercher sur internet. Le PDF que je te donne est correct.
Ne donne ton résultat que lorsque tu as passé les 11 tests unitaires avec succès.

8 - si l'un des tests échoue et que tu es bloqué, affiche ton raisonnement pour ce test
afin que je puisse t'aider.

9- Mets des commentaires détaillés dans le script que tu génères.
Mets le barème progressif dans une liste ou dictionnaire puis utilise cette liste ou dictionnaire
Mets les nombres en dur (nombres magiques) que tu utilises dans des constantes
Utilise des fonctions pour séparer les étapes du calcul.
Ecris le tout en français

9 - n'affiche pas le code généré à l'écran. Donne-moi simplement un lien pour le récupérer.
Si les tests unitaires échouent, je te donnerai les logs de l'exécution du script pour que
tu voies tes erreurs.

10 - si c'est possible, indique le temps en minutes et secondes que tu as mis pour produire
le script demandé.

Queste istruzioni sono il risultato di numerose domande poste a Gemini. Diventa subito chiaro che l'IA deve essere guidata in modo molto rigoroso se vogliamo ottenere ciò che desideriamo. È stato proprio a causa di tutti questi tentativi ed errori che la sessione di Gemini è stata infine interrotta per superamento dei limiti. Esaminiamo il resto di queste istruzioni:

  • Riga 1: specifichiamo che la conversazione deve essere in francese. Questa istruzione è per DeepSeek, che tendeva a parlare inglese;
  • Riga 3: ciò che vogliamo;
  • Riga 5: diciamo all'IA di utilizzare il PDF che abbiamo fornito;
  • Righe 7–14: una serie di consigli utili, in particolare per il Problema 3 senza il PDF. Diverse IA si sono confuse nel calcolo delle imposte;
  • Righe 15–44: gli 11 test unitari che vogliamo includere nello script generato. Una volta generato lo script, lo eseguiremo in PyCharm e vedremo se tutti e 11 i test vengono superati;
  • Righe 46–53: senza queste istruzioni, le IA genererebbero test unitari alla ricerca di risultati esatti che fallirebbero;
  • Righe 55–56: dico all'IA di non andare online. La soluzione più semplice è usare il PDF;
  • Righe 58–59: L'IA non ha seguito questa istruzione. Ho dovuto scriverla esplicitamente in un prompt quando ho notato che un test era fallito;
  • Righe 61–65: Specifico che tipo di script Python voglio;
  • Righe 67–69: Avrei preferito un link per recuperare lo script generato perché visualizzare il codice sullo schermo richiede tempo. È emerso che la maggior parte delle IA non è in grado di farlo. I link forniti non funzionavano;
  • Righe 71–72: Avrei voluto sapere il tempo impiegato dall'IA per rispondere alla domanda. Solo Gemini è stato in grado di fornire questa informazione. Le altre IA o non hanno risposto a questa istruzione o hanno fornito numeri arbitrari, indicando che non l'avevano compresa;

4.3.3. Risposta di Gemini

La prima risposta di Gemini è la seguente:

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

Il resto è il seguente:

 
  • In [1], Gemini afferma di aver eseguito con successo tutti gli 11 test unitari. La maggior parte delle IA ha fatto questa affermazione sia per il Problema 2 che per il Problema 3, e spesso, quando lo script generato veniva caricato, non funzionava. Questa affermazione dovrebbe quindi essere presa con le pinze. Per Gemini, tuttavia, ciò si rivelerà vero;
  • In [2], un link che risulta non funzionare;
  • In [3], solo Gemini ha fornito un tempo di esecuzione realistico;

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

 

Risposta di Gemini:

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

Carichiamo questo script in PyCharm ed eseguiamo:

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

Quando lo script viene eseguito, compaiono i seguenti 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 utilizzato per generare un tipo specifico di testo;

Inseriamo i log sopra riportati in un file [logs gemini1.txt] e lo forniamo 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 comunichiamo a Gemini fornendo nuovamente i log di esecuzione:

 

La risposta di Gemini è la seguente:

 

Questa volta funziona. Tutti gli 11 test unitari vengono superati. Diciamo a Gemini:

 

E lui risponde:

 

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

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

import math
import unittest

#  --- CONSTANTES DE CALCUL POUR L'IMPÔT 2019 ---
#  All values are taken from the PDF document supplied.

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

#  2019 income tax scale
#  Structure: (Maximum bracket threshold, Tax rate, Amount to be deducted per share)
BAREME_IMPOT_2019 = [
    {"seuil": 9964, "taux": 0.0, "deduction_par_part": 0},
    {"seuil": 27519, "taux": 0.14, "deduction_par_part": 1394.96},
    {"seuil": 73779, "taux": 0.30, "deduction_par_part": 5798},
    {"seuil": 156244, "taux": 0.41, "deduction_par_part": 13913.69},
    {"seuil": float('inf'), "taux": 0.45, "deduction_par_part": 20163.45}
]

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

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

#  Thresholds for the 20% tax reduction
SEUIL_BASE_REDUCTION_20 = {"celibataire": 21037.0, "couple": 42074.0}
MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART = 3797.0


def calculer_nombre_parts(adultes: int, enfants: int) -> float:
    """
    Calcule le nombre de parts fiscales du foyer.
    - Chaque parent amène 1 part.
    - Les deux premiers enfants amènent chacun 1/2 part.
    - Les enfants suivants amènent une part chacun.
    """
    if adultes not in [1, 2]:
        raise ValueError("Le nombre d'adultes doit être 1 ou 2.")

    parts = float(adultes)
    if enfants > 0:
        parts += 0.5 * min(enfants, 2)
    if enfants > 2:
        parts += 1.0 * (enfants - 2)

    return parts


def calculer_revenu_imposable(revenus: float) -> float:
    """
    Calcule le revenu net imposable après l'abattement de 10%.
    Cet abattement est plafonné.
    L'abattement est arrondi à l'euro supérieur.
    """
    abattement_calcule = revenus * 0.1
    #  The allowance is capped between a minimum and a maximum
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

    #  The statement requires the abatement to be rounded up to the nearest euro
    abattement_arrondi = math.ceil(abattement)

    return revenus - abattement_arrondi


def calculer_impot_brut_base(revenu_imposable: float, nombre_parts: float) -> float:
    """
    Calcule l'impôt brut selon le barème progressif.
    """
    if nombre_parts <= 0:
        return 0.0

    quotient_familial = revenu_imposable / nombre_parts

    tranche_applicable = None
    for tranche in BAREME_IMPOT_2019:
        if quotient_familial <= tranche["seuil"]:
            tranche_applicable = tranche
            break

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

    #  Gross tax calculation formula
    impot_brut = (revenu_imposable * tranche_applicable["taux"]) - (
            tranche_applicable["deduction_par_part"] * nombre_parts)

    return max(0.0, impot_brut)


def appliquer_plafonnement_qf(impot_brut_avec_enfants: float, revenu_imposable: float, adultes: int,
                              enfants: int) -> float:
    """
    Applique le plafonnement du quotient familial si nécessaire.
    L'impôt final ne peut être inférieur à l'impôt calculé sans les enfants,
    diminué d'un gain maximal par demi-part.
    """
    if enfants == 0:
        return impot_brut_avec_enfants

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

    #  Calculation of maximum child-related earnings
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    demi_parts_enfants = (parts_avec_enfants - parts_sans_enfants) * 2
    gain_maximal = demi_parts_enfants * GAIN_MAX_DEMI_PART

    impot_minimal_plafone = impot_sans_enfants - gain_maximal

    #  The tax used is the higher of the two calculations
    return max(impot_brut_avec_enfants, impot_minimal_plafone)


def appliquer_decote(impot_avant_decote: float, adultes: int) -> tuple[float, float]:
    """
    Applique la décote si l'impôt est inférieur à certains seuils.
    La décote est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_application = SEUIL_APPLICATION_DECOTE[statut]
    seuil_calcul = SEUIL_CALCUL_DECOTE[statut]

    decote = 0.0
    #  The discount applies if the tax is below the application threshold
    if impot_avant_decote < seuil_application:
        #  Discount formula: threshold - 0.75 * gross tax
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

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

        #  The discount cannot be negative or greater than the tax due
        decote = max(0.0, min(decote, impot_avant_decote))

    impot_apres_decote = impot_avant_decote - decote
    return impot_apres_decote, decote


def appliquer_reduction_20_pourcent(impot_avant_reduction: float, revenu_imposable: float, adultes: int,
                                    enfants: int) -> tuple[float, float]:
    """
    Applique la réduction de 20% sous condition de revenus.
    La réduction est arrondie à l'euro supérieur.
    """
    statut = "couple" if adultes == 2 else "celibataire"
    seuil_base = SEUIL_BASE_REDUCTION_20[statut]

    #  The threshold is increased for each half-percentage point for children
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    parts_adultes = calculer_nombre_parts(adultes, 0)
    demi_parts_enfants = (parts_avec_enfants - parts_adultes) * 2
    seuil_final = seuil_base + (demi_parts_enfants * MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART)

    reduction = 0.0
    #  The reduction applies if taxable income is below the threshold
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

        #  Rounded up to the nearest euro
        reduction = math.ceil(reduction_calculee)

    impot_apres_reduction = impot_avant_reduction - reduction
    return max(0.0, impot_apres_reduction), reduction


def calculer_impot(adultes: int, revenus: float, enfants: int) -> dict:
    """
    Orchestre le calcul complet de l'impôt net à payer.
    """
    #  Step 1: Calculating taxable income
    revenu_imposable = calculer_revenu_imposable(revenus)

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

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

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

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

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

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

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


#  --- TESTS UNITAIRES ---
#  Validation of the script with the cases provided
class TestCalculImpot2019(unittest.TestCase):

    def _verifier_resultat(self, adultes, enfants, revenus, impot_attendu, decote_attendue, reduction_attendue):
        """Fonction utilitaire pour exécuter un test et vérifier les résultats à 1 euro près."""
        resultat = calculer_impot(adultes=adultes, revenus=revenus, enfants=enfants)

        self.assertAlmostEqual(resultat["impot"], impot_attendu, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Impôt attendu {impot_attendu}, obtenu {resultat['impot']}")

        self.assertAlmostEqual(resultat["decote"], decote_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Décote attendue {decote_attendue}, obtenue {resultat['decote']}")

        self.assertAlmostEqual(resultat["reduction"], reduction_attendue, delta=1,
                               msg=f"Test ({adultes}, {enfants}, {revenus}): Réduction attendue {reduction_attendue}, obtenue {resultat['reduction']}")

    def test_cas_1(self):
        # (2, 2, 55555) -> (2815, 0, 0)
        self._verifier_resultat(2, 2, 55555, 2815, 0, 0)

    def test_cas_2(self):
        # (2, 2, 50000) -> (1385, 384, 346)
        self._verifier_resultat(2, 2, 50000, 1385, 384, 346)

    def test_cas_3(self):
        # (2, 3, 50000) -> (0, 720, 0)
        self._verifier_resultat(2, 3, 50000, 0, 720, 0)

    def test_cas_4(self):
        # (1, 2, 100000) -> (19884, 0, 0)
        self._verifier_resultat(1, 2, 100000, 19884, 0, 0)

    def test_cas_5(self):
        # (1, 3, 100000) -> (16782, 0, 0)
        self._verifier_resultat(1, 3, 100000, 16782, 0, 0)

    def test_cas_6(self):
        # (2, 3, 100000) -> (9200, 0, 0)
        self._verifier_resultat(2, 3, 100000, 9200, 0, 0)

    def test_cas_7(self):
        # (2, 5, 100000) -> (4230, 0, 0)
        self._verifier_resultat(2, 5, 100000, 4230, 0, 0)

    def test_cas_8(self):
        # (1, 0, 100000) -> (22986, 0, 0)
        self._verifier_resultat(1, 0, 100000, 22986, 0, 0)

    def test_cas_9(self):
        # (2, 2, 30000) -> (0, 0, 0)
        self._verifier_resultat(2, 2, 30000, 0, 0, 0)

    def test_cas_10(self):
        # (1, 0, 200000) -> (64211, 0, 0)
        self._verifier_resultat(1, 0, 200000, 64211, 0, 0)

    def test_cas_11(self):
        # (2, 3, 200000) -> (42843, 0, 0)
        self._verifier_resultat(2, 3, 200000, 42843, 0, 0)


if __name__ == '__main__':
    print("Exécution des tests unitaires...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

    #  Example of how to use the calculator for a specific case
    print("\n--- Exemple de calcul ---")
    revenus_annuels = 50000
    nombre_adultes = 2
    nombre_enfants = 2

    resultat_calcul = calculer_impot(adultes=nombre_adultes, revenus=revenus_annuels, enfants=nombre_enfants)

    print(f"Pour un couple ({nombre_adultes} adultes) avec {nombre_enfants} enfants et {revenus_annuels}€ de revenus :")
    print(f"  - Impôt à payer : {resultat_calcul['impot']}€")
    print(f"  - Montant de la décote : {resultat_calcul['decote']}€")
    print(f"  - Montant de la réduction : {resultat_calcul['reduction']}€")

Non ho verificato questo codice. Dato che gli 11 test unitari sono stati superati, lo considero "probabilmente corretto". Non ho fatto altro per il mio codice se non verificare questi 11 test.

4.4. Problema 3

Il problema 3 è identico al problema 2, tranne per il fatto che non forniamo più all'IA il PDF contenente le regole di calcolo da seguire.

La domanda iniziale a Gemini è la seguente:

 

Il file delle istruzioni in [1] è quasi 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 :

  • Nel punto [3], allo studente viene chiesto di trovare online le regole per calcolare l'imposta del 2019 sul reddito del 2018. Si tratta di un esercizio più difficile rispetto al precedente;

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

 
 

Il tempo stimato è plausibile. Aspettiamo da molto tempo una risposta da Gemini.

Come in precedenza, Gemini ha fornito un link per il download dello script generato, ma il link non funziona. Gli diciamo:

 

Risposta di Gemini:

 

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

 

Lo eseguiamo e… non funziona. I log di 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 è riuscito;
  • Righe 25 e 42: lo stesso errore di compilazione;

Inseriamo questi log in un file di testo che inviamo a Gemini:

 

Risposta di Gemini:

 
 

Carichiamo lo script in PyCharm e lo eseguiamo. Altri errori. Lo segnaliamo a Gemini, allegando nuovamente i log di esecuzione:

 

Risposta di Gemini:

 
 
 

Carichiamo questo nuovo script in PyCharm ed eseguiamo il codice. 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: If tax is already 0, no discount or reduction.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

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

    #  1. Determining eligibility for the 20% discount
    est_eligible_reduction = False
    if adultes >= 2:
        seuil_rfr = SEUIL_RFR_REDUCTION_COUPLE + max(0, (nombre_parts - 2) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    else:
        seuil_rfr = SEUIL_RFR_REDUCTION_CELIBATAIRE + max(0, (
                nombre_parts - 1) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    if revenu_imposable < seuil_rfr:
        est_eligible_reduction = True

    #  2. Determining eligibility for the discount
    seuil_decote_foyer = SEUIL_DECOTE_COUPLE if adultes >= 2 else SEUIL_DECOTE_CELIBATAIRE
    reduction_theorique = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT) if est_eligible_reduction else 0

    if (impot_a_traiter - reduction_theorique) < seuil_decote_foyer:
        decote_calculee = seuil_decote_foyer - (impot_a_traiter * COEFFICIENT_DECOTE)
        decote_calculee = math.ceil(decote_calculee)
        #  CORRECTION: The discount returned cannot exceed the tax it cancels out
        decote_calculee = min(decote_calculee, math.ceil(impot_a_traiter))
        impot_a_traiter -= decote_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

    #  3. Apply the 20% discount if eligible
    if est_eligible_reduction:
        reduction_calculee = math.ceil(impot_a_traiter * TAUX_REDUCTION_20_POURCENT)
        impot_a_traiter -= reduction_calculee
        impot_a_traiter = max(0.0, impot_a_traiter)

    impot_final = math.floor(impot_a_traiter)

    return impot_final, int(decote_calculee), int(reduction_calculee)


# ==============================================================================
#  TESTS UNITAIRES
# ==============================================================================

class TestCalculImpot2019(unittest.TestCase):
    def _verifier_cas(self, adultes, enfants, revenus, attendu):
        impot_attendu, decote_attendue, reduction_attendue = attendu
        impot_calcule, decote_calculee, reduction_calculee = calculer_impot_final(adultes, enfants, revenus)
        self.assertAlmostEqual(impot_calcule, impot_attendu, delta=1, msg=f"Impôt ({impot_calcule} vs {impot_attendu})")
        self.assertAlmostEqual(decote_calculee, decote_attendue, delta=1,
                               msg=f"Décote ({decote_calculee} vs {decote_attendue})")
        self.assertAlmostEqual(reduction_calculee, reduction_attendue, delta=1,
                               msg=f"Réduction ({reduction_calculee} vs {reduction_attendue})")

    def test_cas_1(self): self._verifier_cas(2, 2, 55555, (2815, 0, 0))

    def test_cas_2(self): self._verifier_cas(2, 2, 50000, (1385, 384, 346))

    def test_cas_3(self): self._verifier_cas(2, 3, 50000, (0, 720, 0))

    def test_cas_4(self): self._verifier_cas(1, 2, 100000, (19884, 0, 0))

    def test_cas_5(self): self._verifier_cas(1, 3, 100000, (16782, 0, 0))

    def test_cas_6(self): self._verifier_cas(2, 3, 100000, (9200, 0, 0))

    def test_cas_7(self): self._verifier_cas(2, 5, 100000, (4230, 0, 0))

    def test_cas_8(self): self._verifier_cas(1, 0, 100000, (22986, 0, 0))

    def test_cas_9(self): self._verifier_cas(2, 2, 30000, (0, 0, 0))

    def test_cas_10(self): self._verifier_cas(1, 0, 200000, (64211, 0, 0))

    def test_cas_11(self): self._verifier_cas(2, 3, 200000, (42843, 0, 0))


if __name__ == '__main__':
    print("Lancement des tests unitaires pour le calcul de l'impôt 2019...")
    unittest.main(argv=['first-arg-is-ignored'], exit=False)

Anche in questo caso, non ho esaminato il codice. Ho semplicemente notato che ha superato con successo tutti gli 11 test.

Ma si potrebbe essere curiosi di conoscerne il ragionamento, in particolare per casi specifici nel calcolo delle imposte. Chiediamoglielo:

 

Si tratta di un caso di reddito elevato con un possibile limite di deduzione del 10% e un possibile limite del quoziente familiare.

La risposta di Gemini è la seguente:

 
 
 
 
 

Questi ultimi due screenshot sono interessanti. Gemini utilizza un metodo di calcolo diverso da quello spiegato nel PDF. Questo metodo di calcolo è effettivamente disponibile online. I due metodi sono equivalenti.

 
 
 

La spiegazione è estremamente chiara. Potrebbe essere fornita così com'è agli studenti per illustrare il metodo di calcolo delle imposte.

Prendiamo ora un altro esempio, questa volta con un reddito basso. In questo caso, potrebbero esserci un credito d'imposta e una riduzione:

 

La risposta di Gemini è la seguente:

 
 
 
 
 

Qui vediamo che Gemini applica una regola che non è presente nel PDF. Probabilmente l'ha trovata online, ma la fonte è affidabile?

 

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

 
 
 

Quindi i risultati di Gemini corrispondono a quelli del simulatore fiscale ufficiale. Tuttavia, ha utilizzato una regola che non compare nel PDF. Dove sta l'errore? Lo chiediamo a Gemini, allegando il PDF:

 

Risposta di Gemini:

 
 
 
 

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

  • In quali casi il suo ragionamento darebbe gli stessi risultati del simulatore fiscale ufficiale;
  • In cui il ragionamento nel PDF darebbe risultati diversi da quelli del simulatore;
 

la risposta di Gemini è la seguente:

 

In questo caso Gemini ha torto. Ho eseguito il simulatore su questo esempio e ho riscontrato quanto segue:

 

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

 
 
 
 

Va bene. Preso nota. Continuiamo:

 
 
 
 

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

 

Gemini risponde:

 
 

Ora ci chiediamo se Gemini sia in grado di generare un PDF corretto:

 

Risposta di Gemini:

 

Quindi Gemini non mi ha fornito un link a un PDF, ma ha generato del testo in modo che potessi creare il PDF da solo. Anche se è complicato includere qui gli screenshot del PDF, lo faccio per mostrare ai lettori l'aspetto generativo dell'IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

A dire il vero, non ho verificato se tutto ciò che è scritto in questo PDF sia vero. In ogni caso, è un documento perfetto per un tutorial, generato in pochi secondi.

Tuttavia, possiamo chiedere a Gemini stesso di verificare che il suo PDF sia corretto. Avviamo una nuova conversazione:

 
  • in [1], abbiamo incluso il PDF generato da Gemini [The Problem According to Gemini.pdf];
  • in [2], [instructionsWithPDF2.txt] è identico alle istruzioni contenute in [instructionsWithPDF.txt], tranne per il fatto che abbiamo aggiunto un dodicesimo test unitario, proprio quello che ha evidenziato l'errore nel PDF iniziale:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, ci sono volute diverse iterazioni avanti e indietro prima che Gemini generasse lo script corretto:

Domanda 2

 

Domanda 3

 

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

Domanda 4

 

Domanda 5

 

Domanda 6 e conclusione

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

Faremo ora lo stesso con le altre cinque IA, ma manterremo le nostre spiegazioni molto brevi, ad eccezione di ChatGPT, l'attuale leader nel campo dell'IA. Ciò che ci interessa è se l'IA risolva o meno i tre problemi che le sottoponiamo. In effetti, le interfacce di tutte queste IA sono molto simili, e ho proceduto con esse allo stesso modo di quanto fatto con Gemini. I lettori sono invitati a riprodurre le conversazioni di Gemini con l'IA di loro scelta.