Skip to content

4. Resolución de los tres problemas con Google Gemini

Proporcionaremos capturas de pantalla de las tres sesiones de Gemini que se utilizaron para resolver los tres problemas planteados. Entraremos en bastante detalle. Una vez hecho esto, no repetiremos el proceso con las demás IA probadas. Funcionan de manera similar. Solo proporcionaremos los detalles más destacados.

4.1. Introducción

Nos remitimos a la primera captura de pantalla de Gemini proporcionada anteriormente:

 
  • En [1], la URL de Gemini;
  • En [2], la versión de Gemini utilizada;
  • En [3-5], los tres problemas planteados a Gemini;

Gemini es un producto de Google disponible en la URL [https://gemini.google.com/]. Para ver un historial de tus sesiones de preguntas y respuestas como se muestra arriba, debes crear una cuenta. Además, al igual que todas las demás IA probadas, Gemini limita el número de preguntas que puedes hacer y el número de archivos que puedes subir. Cuando se alcanza este límite, la sesión finaliza y se te ofrece la opción de continuarla más tarde. Dado que resulta bastante frustrante tener que parar en mitad de una sesión, me suscribí. Afortunadamente, el primer mes de la suscripción a Gemini es gratuito. Hice lo mismo con las otras IA que tenían estos límites, concretamente ChatGPT, MistralAI y ClaudeAI. Me suscribí por un mes, pero en esos casos el primer mes era de pago. No encontré ningún límite con Grok. DeepSeek no anuncia ningún límite, pero a veces responde con [Servidor ocupado] e interrumpe la sesión. Eso es, en esencia, establecer límites sin decirlo.

A partir de ahora, me referiré a las sesiones de preguntas y respuestas simplemente como «sesiones». Las IA suelen utilizar los términos ingleses «chat» o «conversation».

La interfaz de Gemini para formular una pregunta es la siguiente:

  • En [1], tu pregunta;
  • En [2], el icono que activa la IA para calcular la respuesta;
  • En [3-4], puedes adjuntar archivos;

4.2. Problema 1

La sesión para el Problema 1 es la siguiente:

 
  • En [1], la pregunta;
  • En [2], el comienzo de la respuesta de Gemini;

El resto de la respuesta es el siguiente:

 
 
 
 

La respuesta es correcta. Las otras cinco IA también darán la respuesta correcta de forma similar.

4.3. Problema 2

4.3.1. Introducción

Aquí recordamos el problema inicial del curso [python3-flask-2020]. Se trata de un texto que se entrega a los alumnos en una clase práctica.

 

La tabla anterior nos permite calcular el impuesto en el caso simplificado de un contribuyente que solo tiene que declarar su salario. Como se indica en la nota (1), el impuesto calculado de esta manera es el impuesto antes de aplicar tres mecanismos:

  • La limitación del coeficiente familiar, que se aplica a las rentas elevadas;
  • La deducción fiscal y la reducción de impuestos que se aplican a las personas con rentas bajas;

Por lo tanto, el cálculo del impuesto implica los siguientes pasos [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

Proponemos escribir un programa para calcular la deuda tributaria de un contribuyente correspondiente al año 2019, en el caso simplificado de un contribuyente que solo tiene que declarar su salario.

4.3.1.1. Cálculo del impuesto bruto

El impuesto bruto se puede calcular de la siguiente manera:

En primer lugar, calcula el número de acciones del contribuyente:

  • Cada progenitor aporta 1 participación;
  • Los dos primeros hijos aportan cada uno 1/2 parte;
  • Los hijos siguientes aportan cada uno una participación:

Por lo tanto, el número de acciones es:

  • nbParts=1+nbChildren*0,5+(nbChildren-2)*0,5 si el empleado no está casado;
  • nbParts=2+nbChildren*0,5+(nbChildren-2)*0,5 si está casado;
  • donde nbChildren es el número de hijos;
  • Calculamos la base imponible R = 0,9 * S, donde S es el salario anual;
  • El coeficiente familiar QF se calcula como QF = R / nbParts;
  • Calculamos el impuesto bruto I basándonos en los siguientes datos (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

Cada fila tiene 3 campos: campo1, campo2, campo3. Para calcular el impuesto I, buscamos la primera fila en la que QF <= campo1 y tomamos los valores de esa fila. Por ejemplo, para un empleado casado con dos hijos y un salario anual S de 50 000 euros:

Renta imponible: R=0,9*S=45 000

Número de partes: nbParts = 2 + 2 * 0,5 = 3

Coeficiente familiar: QF = 45 000 / 3 = 15 000

La primera fila en la que QF <= campo1 es la siguiente:

    27519    0.14    1394.96

El impuesto I es entonces igual a 0,14*R – 1394,96*númeroDeAcciones=[0,14*45000-1394,96*3]=2115. El impuesto se redondea al euro inferior.

Si se cumple la condición QF <= campo1 en la primera línea, el impuesto es cero.

Si QF es tal que la condición QF <= campo1 nunca se cumple, se utilizan los coeficientes de la última línea. Aquí:

    0    0.45    20163.45

lo que da como resultado el impuesto bruto I = 0,45*R – 20163,45*nbParts.

4.3.1.2. Límite del coeficiente familiar

 

Para determinar si se aplica el límite del coeficiente familiar (QF), volvemos a calcular el impuesto bruto sin tener en cuenta a los hijos. De nuevo, en el caso del empleado casado con dos hijos y un salario anual S de 50 000 euros:

Base imponible: R = 0,9 * S = 45 000

Número de partes: nbParts=2 (los hijos ya no se cuentan)

Cotiente familiar: QF = 45 000 / 2 = 22 500

La primera línea en la que QF <= campo1 es la siguiente:

    27519    0.14    1394.96

El impuesto I es entonces igual a 0,14*R – 1394,96*número de acciones = [0,14*45 000 – 1394,96*2] = 3510.

Prestación máxima por hijos: 1551 * 2 = 3102 euros

Impuesto mínimo: 3.510 – 3.102 = 408 euros

El impuesto bruto con 2 acciones, ya calculado en el párrafo anterior (2.115 euros), es superior al impuesto mínimo (408 euros), por lo que el límite familiar no se aplica en este caso.

En general, el impuesto bruto es mayor que (impuesto1, impuesto2), donde:

  • [impuesto1]: es el impuesto bruto calculado incluyendo a los hijos;
  • [impuesto2]: es el impuesto bruto calculado sin hijos y reducido por la deducción máxima (en este caso, 1.551 euros por media cuota) relacionada con los hijos;

4.3.1.3. Cálculo de la reducción fiscal

 

Siguiendo con el ejemplo del empleado casado con dos hijos y un salario anual S de 50 000 euros:

El impuesto bruto (2.115 euros) del paso anterior es inferior a 2.627 euros para una pareja (1.595 euros para una persona soltera): por lo tanto, se aplica la reducción fiscal. Se calcula de la siguiente manera:

crédito fiscal = umbral (pareja = 1.970 / soltero = 1.196) – 0,75 * impuesto bruto

descuento = 1.970 – 0,75 * 2.115 = 383,75, redondeado a 384 euros.

Nuevo impuesto bruto = 2.115 – 384 = 1.731 euros

Hay que tener en cuenta dos reglas al calcular el descuento (algunas herramientas de IA han tenido problemas con esto):

  • El descuento no puede ser negativo;
  • El descuento no puede superar el impuesto ya calculado;

4.3.1.4. Cálculo de la reducción del impuesto

 

Por debajo de un determinado umbral, se aplica una reducción del 20 % al impuesto bruto resultante de los cálculos anteriores. En 2019, los umbrales son los siguientes:

  • Solteros: 21 037 euros;
  • pareja: 42 074 euros; (la cifra de 37 968 utilizada en el ejemplo anterior parece ser incorrecta);

Este umbral se incrementa en el valor: 3.797 * (número de medias participaciones aportadas por los hijos).

De nuevo, para el empleado casado con dos hijos y un salario anual S de 50 000 euros:

  • Su base imponible (45 000 euros) es inferior al umbral (42 074 + 2 × 3 797) = 49 668 euros;
  • Por lo tanto, tiene derecho a una reducción del 20 % en su impuesto: 1.731 * 0,2 = 346,2 euros, redondeado a 347 euros;
  • El impuesto bruto del contribuyente queda en: 1 731 – 347 = 1 384 euros;

4.3.1.5. Cálculo del impuesto neto

Nuestro cálculo termina aquí: el impuesto neto a pagar será de 1.384 euros. En realidad, el contribuyente puede tener derecho a otras deducciones, en particular por donaciones a organizaciones de interés público o general.

4.3.1.6. Casos de rentas elevadas

Nuestro ejemplo anterior se aplica a la mayoría de los empleados. Sin embargo, el cálculo del impuesto difiere para las personas con ingresos elevados.

4.3.1.6.1. Límite de la reducción del 10 % sobre los ingresos anuales

En la mayoría de los casos, la base imponible se calcula mediante la fórmula: R = 0,9 × S, donde S es el salario anual. Esto se conoce como la reducción del 10 %. Esta reducción tiene un límite máximo. En 2019:

  • No puede superar los 12 502 euros;
  • No puede ser inferior a 437 euros;

Consideremos el caso de un empleado soltero sin hijos y con un salario anual de 200 000 euros:

  • La reducción del 10 % es de 200 000 euros > 12 502 euros. Por lo tanto, tiene un límite máximo de 12 502 euros;

4.3.1.6.2. Límite del coeficiente familiar

Consideremos un caso en el que se aplica el límite familiar descrito en la sección |Límite del coeficiente familiar|. Tomemos el caso de una pareja con tres hijos y unos ingresos anuales de 100 000 euros. Repasemos de nuevo los pasos del cálculo:

  • La deducción del 10 % es de 100 000 euros < 12 502 euros. Por lo tanto, la renta imponible R es de 100 000 - 10 000 = 90 000 euros;
  • La pareja tiene nbParts = 2 + 0,5 × 2 + 1 = 4 partes;
  • Por lo tanto, su coeficiente familiar es QF = R / nbParts = 90 000 / 4 = 22 500 euros;
  • Su impuesto bruto I1 con hijos es I1 = 0,14 × 90 000 – 1 394,96 × 4 = 7 020 euros;
  • Su impuesto bruto I2 sin hijos:
    • QF = 90 000 / 2 = 45 000 euros;
    • I2 = 0,3 × 90 000 – 5 798 × 2 = 15 404 euros;
  • La regla del límite del coeficiente familiar establece que la ventaja derivada de los hijos no puede superar (1.551 × 4 medias partes) = 6.204 euros. Sin embargo, en este caso, es I2 – I1 = 15.404 – 7.020 = 8.384 euros, lo que es superior a 6.204 euros;
  • Por lo tanto, el impuesto bruto se recalcula como I3 = I2 - 6.204 = 15.404 - 6.204 = 9.200 euros;
  • Dado que I3 > I1, se retendrá el impuesto I3;

Esta pareja no recibirá ni crédito fiscal ni reducción, y su impuesto final será de 9.200 euros.

4.3.1.7. Cifras oficiales

El cálculo del impuesto es complejo. A lo largo de este documento, se realizarán pruebas utilizando los siguientes ejemplos. Los resultados proceden del simulador de la administración tributaria |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

Contribuyente
Resultados oficiales
Resultados del algoritmo del documento
Pareja con dos hijos y unos ingresos anuales de 55 555 euros
Impuesto = 2.815 euros
Tipo impositivo = 14 %
Impuesto = 2.814 euros
Tipo impositivo = 14 %
Pareja con dos hijos y unos ingresos anuales de 50 000 euros
Impuesto = 1.385 euros
Desgravación fiscal = 384 €
Reducción = 346 euros
Tipo impositivo = 14 %
Impuesto = 1.384 euros
Descuento = 384 euros
Crédito = 347 euros
Tipo impositivo = 14 %
Pareja con 3 hijos y unos ingresos anuales de 50 000 euros
Impuesto = 0 euros
Crédito fiscal = 720 euros
Reducción = 0 euros
Tipo impositivo = 14 %
Impuesto = 0 euros
Descuento = 720 euros
Deducción = 0 euros
Tipo impositivo = 14 %
Soltero con 2 hijos y unos ingresos anuales de 100 000 euros
Impuesto = 19 884 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 41 %
Impuesto = 19 884 euros
Recargo = 4.480 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 41 %
Soltero con 3 hijos y unos ingresos anuales de 100 000 euros
Impuesto = 16 782 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 41 %
Impuesto = 16 782
Recargo = 7.176 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 41 %
Pareja con 3 hijos y unos ingresos anuales de 100 000 euros
Impuesto = 9.200 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 30 %
Impuesto = 9.200 euros
Recargo = 2.180 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 30 %
Pareja con 5 hijos y unos ingresos anuales de 100 000 euros
Impuesto = 4.230 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 14 %
Impuesto = 4.230
Descuento = 0 euros
Deducción = 0 euros
Tipo impositivo = 14 %
Soltero, sin hijos y con unos ingresos anuales de 100 000 euros
Impuesto = 22 986 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 41 %
Impuesto = 22 986 euros
Recargo = 0 euros
Descuento = 0 euros
Deducción = 0 euros
Tipo impositivo = 41 %
Pareja con 2 hijos y unos ingresos anuales de 30 000 euros
Impuesto = 0 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 0 %
Impuesto = 0 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 0 %
Soltero sin hijos y con unos ingresos anuales de 200 000 euros
Impuesto = 64 211 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 45 %
Impuesto = 64 210 euros
Recargo = 7.498 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 45 %
Pareja con 3 hijos y unos ingresos anuales de 200 000 euros
Impuesto = 42 843 euros
Crédito fiscal = 0 euros
Deducción = 0 euros
Tipo impositivo = 41 %
Impuesto = 42 842 euros
Recargo = 17 283 euros
Descuento = 0 euros
Reducción = 0 euros
Tipo impositivo = 41 %

En el ejemplo anterior, el «recargo» se refiere al importe adicional que pagan las personas con ingresos elevados debido a dos factores:

  • El límite máximo de la deducción del 10 % de los ingresos anuales;
  • El límite máximo de la prestación familiar;

Este indicador no se ha podido verificar porque el simulador de la autoridad fiscal no lo proporciona.

Podemos observar que el algoritmo del documento calcula el importe correcto del impuesto en todas las ocasiones, aunque con un margen de error de 1 euro. Este margen de error se debe al redondeo. Todos los importes monetarios se redondean al euro superior en algunos casos y al euro inferior en otros. Como no estaba familiarizado con las normas oficiales, los importes monetarios del algoritmo del documento se redondearon:

  • Al euro superior para los descuentos y reducciones;
  • Hacia abajo, al euro más cercano, para los recargos y el impuesto final;

Le pediremos a la IA que realice este cálculo del impuesto.

4.3.2. Configuración de la sesión de Gemini

La pregunta planteada a Gemini va acompañada de dos archivos:

 
  • En [1], el cálculo que acabamos de detallar se ha incluido en un archivo PDF que se ha facilitado a Gemini. Gemini encontrará allí las normas exactas para el cálculo simplificado del impuesto de 2019 sobre los ingresos de 2018;
  • En [2], nuestras instrucciones;
  • En [3], el comando para iniciar la IA;

Nuestras instrucciones en el archivo de texto [instructionsAvecPDF.txt] son las siguientes:

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é.

Estas instrucciones son el resultado de numerosas preguntas formuladas a Gemini. Pronto queda claro que la IA necesita una orientación muy precisa si queremos obtener lo que buscamos. Fue debido a todo este proceso de prueba y error que la sesión de Gemini terminó finalmente por exceder los límites. Examinemos el resto de estas instrucciones:

  • Línea 1: Especificamos que la conversación debe ser en francés. Esta instrucción es para DeepSeek, que tendía a hablar en inglés;
  • Línea 3: lo que queremos;
  • Línea 5: Le indicamos a la IA que utilice el PDF que le hemos proporcionado;
  • Líneas 7-14: varios consejos útiles, especialmente para el Problema 3 sin el PDF. Varias IA se perdieron en el cálculo de los impuestos;
  • Líneas 15-44: las 11 pruebas unitarias que queremos incluir en el script generado. Una vez generado el script, lo ejecutaremos en PyCharm y comprobaremos si las 11 pruebas se superan;
  • Líneas 46-53: sin estas instrucciones, las IA generarían pruebas unitarias que buscarían resultados exactos y que fallarían;
  • Líneas 55-56: le digo a la IA que no se conecte a Internet. La solución más sencilla es utilizar el PDF;
  • Líneas 58–59: La IA no siguió esta instrucción. Tuve que escribirla explícitamente en un comando cuando me di cuenta de que una prueba había fallado;
  • Líneas 61-65: Especifico qué tipo de script de Python quiero;
  • Líneas 67-69: Hubiera preferido un enlace para descargar el script generado, ya que mostrar el código en pantalla lleva tiempo. Resultó que la mayoría de las IA no pueden hacer esto. Los enlaces proporcionados no funcionaban;
  • Líneas 71-72: Me hubiera gustado saber el tiempo que tardó la IA en responder a la pregunta. Solo Gemini fue capaz de proporcionar esta información. Las demás IA o bien no respondieron a esta instrucción o bien proporcionaron números arbitrarios, lo que indica que no la entendieron;

4.3.3. Respuesta de Gemini

La primera respuesta de Gemini es la siguiente:

 
  • En [1-4], Gemini proporciona enlaces a la parte del PDF o del archivo de texto que contiene las instrucciones que está utilizando en un momento dado;

El resto es el siguiente:

 
  • En [1], Gemini afirma que ejecutó con éxito las 11 pruebas unitarias. La mayoría de las IA hicieron esta afirmación tanto para el Problema 2 como para el Problema 3, y a menudo, cuando se cargaba el script generado, este no funcionaba. Por lo tanto, esta afirmación debe tomarse con cautela. En el caso de Gemini, sin embargo, esto resultará ser cierto;
  • En [2], un enlace que resulta no funcionar;
  • En [3], solo Gemini proporcionó un tiempo de ejecución realista;

Así que el enlace [2] no funciona. Le decimos a Gemini:

 

Respuesta de Gemini:

 
  • En [1], el script de Python generado por Gemini;

Cargamos este script en PyCharm y lo ejecutamos:

 
  • En [1], [gemini1] es el script generado por Gemini;

Al ejecutar el script, aparecen los siguientes errores de compilación:


"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
  • Línea 30, el error de compilación. [cite_start] es un marcador que se utiliza para generar un tipo específico de texto;

Guardamos los registros anteriores en un archivo [logs gemini1.txt] y se lo pasamos a Gemini:

 

La respuesta de Gemini es entonces la siguiente:

 

Al cargarlo en PyCharm, al ejecutar el nuevo script se produce exactamente el mismo error. Se lo comunicamos a Gemini proporcionando de nuevo los registros de ejecución:

 

La respuesta de Gemini es la siguiente:

 

Esta vez funciona. Las 11 pruebas unitarias se superan. Le decimos a Gemini:

 

A lo que responde:

 

El script generado por Gemini siguió las instrucciones dadas en el archivo de texto [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']}€")

No he verificado este código. Dado que las 11 pruebas unitarias han pasado, lo considero «probablemente correcto». No he hecho nada más con mi propio código aparte de verificar estas 11 pruebas.

4.4. Problema 3

El problema 3 es idéntico al problema 2, salvo que ya no proporcionamos a la IA el PDF que contiene las reglas de cálculo a seguir.

La pregunta inicial a Gemini es la siguiente:

 

El archivo de instrucciones de [1] es casi idéntico al del Problema 2, con las siguientes diferencias:


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 :

  • En [3], se pide al alumno que busque en Internet las normas para calcular el impuesto de 2019 sobre los ingresos de 2018. Este ejercicio es más difícil que el anterior;

A continuación, solo incluyo partes de la primera respuesta de Gemini:

 
 

El tiempo estimado es plausible. Llevamos mucho tiempo esperando la respuesta de Gemini.

Como antes, Gemini nos ha proporcionado un enlace de descarga para el script generado, pero el enlace no funciona. Le decimos:

 

Respuesta de Gemini:

 

Cargamos el script en PyCharm con el nombre [gemini2]:

 

Lo ejecutamos y… no funciona. Los registros de ejecución son los siguientes:


"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
  • Línea 11, una prueba ha fallado;
  • Líneas 25 y 42: el mismo error de compilación;

Guardamos estos registros en un archivo de texto que enviamos a Gemini:

 

Respuesta de Gemini:

 
 

Cargamos el script en PyCharm y lo ejecutamos. Más errores. Se lo comunicamos a Gemini, adjuntando de nuevo los registros de ejecución:

 

Respuesta de Gemini:

 
 
 

Cargamos este nuevo script en PyCharm y lo ejecutamos. Esta vez, las 11 pruebas unitarias se superan:

El código generado por Gemini es el siguiente:

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

Una vez más, no he revisado este código. Simplemente he comprobado que ha superado con éxito las 11 pruebas.

Pero quizá alguien sienta curiosidad por conocer su razonamiento, especialmente en casos específicos del cálculo de impuestos. Vamos a preguntárselo:

 

Este es un caso de ingresos elevados en el que se aplica tanto un posible límite de deducción del 10 % como un posible límite del coeficiente familiar.

La respuesta de Gemini es la siguiente:

 
 
 
 
 

Estas dos últimas capturas de pantalla son interesantes. Gemini utiliza un método de cálculo diferente al que se explica en el PDF. De hecho, este método de cálculo se puede encontrar en Internet. Los dos métodos son equivalentes.

 
 
 

La explicación es muy clara. Se podría entregar tal cual a los alumnos para explicarles el método de cálculo de los impuestos.

Veamos ahora otro ejemplo, esta vez con ingresos bajos. En este caso, puede haber una deducción fiscal y una reducción:

 

La respuesta de Gemini es la siguiente:

 
 
 
 
 

Aquí vemos que Gemini aplica una regla que no aparece en el PDF. Probablemente la haya encontrado en Internet, pero ¿es fiable la fuente?

 

Aquí, Gemini sigue aplicando una regla desconocida (la regla especial mencionada anteriormente).

 
 
 

Así pues, los resultados de Gemini coinciden con los del simulador fiscal oficial. Pero ha utilizado una regla que no aparece en el PDF. ¿Dónde está el error? Se lo preguntamos a Gemini, adjuntando el PDF:

 

Respuesta de Gemini:

 
 
 
 

Creo que Gemini tiene razón y que mi PDF es incorrecto. Para verificarlo, le pido que haga una prueba:

  • En qué casos su razonamiento daría los mismos resultados que el simulador fiscal oficial;
  • en los que el razonamiento del PDF daría resultados diferentes a los del simulador;
 

la respuesta de Gemini es la siguiente:

 

En este caso, Gemini se equivoca. He ejecutado el simulador con este ejemplo y he obtenido lo siguiente:

 

Sin embargo, veremos que el razonamiento de Gemini efectivamente arroja los resultados anteriores. Continuemos:

 
 
 
 

Muy bien. Entendido. Sigamos:

 
 
 
 

Así pues, Gemini obtuvo (impuesto, descuento, reducción) = (431, 325, 1296), mientras que el simulador que utilicé da (431, 324, 1297). Por lo tanto, Gemini obtuvo los resultados correctos con una diferencia de 1 euro, pero no lo sabe. Se lo indicamos:

 

Gemini responde:

 
 

Ahora nos preguntamos si Gemini podría generar un PDF corregido:

 

Respuesta de Gemini:

 

Así que Gemini no me proporcionó un enlace a un PDF, sino que generó texto para que yo mismo pudiera crear el PDF. Aunque resulta engorroso incluir capturas de pantalla del PDF aquí, lo hago para mostrar a los lectores el aspecto generativo de la IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Para ser sincero, no he comprobado si todo lo que aparece en este PDF es cierto. En cualquier caso, es un documento perfecto para un tutorial, generado en solo unos segundos.

Sin embargo, podemos hacer que el propio Gemini verifique que su PDF es correcto. Iniciamos una nueva conversación:

 
  • en [1], incluimos el PDF generado por Gemini [The Problem According to Gemini.pdf];
  • en [2], [instructionsWithPDF2.txt] es idéntico a las instrucciones de [instructionsWithPDF.txt], salvo que hemos añadido una duodécima prueba unitaria, precisamente la que reveló que el PDF inicial era incorrecto:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, fueron necesarias varias iteraciones de ida y vuelta antes de que Gemini generara el script correcto:

Pregunta 2

 

Pregunta 3

 

Como ya se ha hecho en varias ocasiones, cuando el script generado y cargado en PyCharm falla, proporcionamos a Gemini el archivo de texto que contiene los registros de ejecución. Gemini los entiende perfectamente.

Pregunta 4

 

Pregunta 5

 

Pregunta 6 y conclusión

Ahora estamos seguros de la validez del PDF generado por Gemini. Las reglas de cálculo que figuran en él son correctas.

Ahora haremos lo mismo con las otras cinco IA, pero nuestras explicaciones serán muy breves, salvo en el caso de ChatGPT, el líder actual en IA. Lo que nos interesa es si la IA resuelve o no los tres problemas que le planteamos. De hecho, las interfaces de todas estas IA son muy similares, y procedí con ellas de la misma manera que con Gemini. Animamos a los lectores a reproducir las conversaciones de Gemini con la IA que elijan.