Skip to content

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

A continuación, presentaremos las capturas de pantalla de las tres sesiones de Gemini que permitieron resolver los tres problemas planteados. Entraremos en bastante detalle. Una vez hecho esto, no repetiremos el proceso para las otras IA probadas. Funcionan de manera similar. Solo daremos los detalles más destacados.

4.1. Introducción

Recordamos la primera captura de pantalla de Gemini que se presentó anteriormente:

 
  • En [1], el 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 URL [https://gemini.google.com/]. Para tener un historial de tus sesiones de preguntas y respuestas como el anterior, debes crear una cuenta. Además, al igual que todas las demás IA probadas, Gemini limita el número de preguntas y el número de archivos descargados. Cuando se alcanza este límite, la sesión finaliza y se te ofrece la opción de continuarla más adelante. Como es bastante frustrante tener que detenerse en medio de una sesión, me suscribí. Por suerte, el primer mes de la suscripción a Gemini es gratis. Hice lo mismo con los otros IA que tenían estas limitaciones, es decir, ChatGPT, MistralAI y ClaudeAI. Me suscribí por un mes, pero en este caso el primer mes era de pago. No he encontrado límites con Grok. DeepSeek no anuncia límites, pero a veces responde [Server busy] e interrumpe la sesión. Es como poner límites sin decirlo.

En lo que sigue, me referiré a las sesiones de preguntas y respuestas, que abreviaré simplemente como «sesiones». Los usuarios de IA suelen utilizar el término inglés «chat» (charla) o «conversación».

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

  • En [1], tu pregunta;
  • En [2], el ícono que inicia el IA para calcular la respuesta;
  • En [3-4], puede adjuntar archivos;

4.2. El problema 1

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

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

El resto de la respuesta es el siguiente:

 
 
 
 

La respuesta es correcta. Los otros cinco IA también darán la respuesta correcta de manera similar.

4.3. El problema 2

4.3.1. Introducción

Recordemos aquí el problema inicial del curso [python3-flask-2020]. Se trata de un texto que se les dio a los estudiantes en TD.

 

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

  • El límite máximo del coeficiente familiar, que se aplica a los ingresos altos;
  • La bonificación y la reducción de impuestos que se aplican a los ingresos bajos;

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

 

Nos proponemos escribir un programa que permita calcular el impuesto de un contribuyente en 2019 en el caso simplificado de un contribuyente que solo tenga que declarar su salario.

4.3.1.1. Cálculo del impuesto bruto

El impuesto bruto se puede calcular de la siguiente manera:

Primero se calcula el número de partes del contribuyente:

  • Cada padre aporta 1 parte;
  • Los dos primeros hijos aportan cada uno 1/2 parte;
  • Los hijos siguientes aportan una parte cada uno:

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

  • nbParts = 1 + nbEnfants * 0,5 + (n.º de hijos - 2) * 0,5 si el empleado no está casado;
  • nbParts = 2 + nbEnfants * 0,5 + (nbEnfants - 2) * 0,5 si está casado;
  • donde nbEnfants es su número de hijos;
  • Se calcula el ingreso imponible R = 0,9 * S, donde S es el salario anual;
  • Se calcula el cociente familiar QF = R / nbParts;
  • Se calcula el impuesto bruto I a partir de los siguientes datos (2019):
9964
0
0
27519
0,14
1394,96
73 779
0,3
5798
156 244
0,4
13 913,69
0
0,45
20163,45

Cada línea tiene 3 campos: champ1, champ2, champ3. Para calcular el impuesto I, se busca la primera línea en la que QF <= campo1 y se toman los valores de esa línea. Por ejemplo, para un empleado casado con dos hijos y un salario anual S de 50 000 euros:

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

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

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

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

    27519    0.14    1394.96

El impuesto I es, por lo tanto, igual a 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*3] = 2115. El impuesto se redondea al euro inferior.

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

Si QF es tal que la relación QF <= campo1 nunca se cumple, entonces se utilizan los coeficientes de la última línea. En este caso:

    0    0.45    20163.45

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

4.3.1.2. Límite máximo del cociente familiar

 

Para saber si se aplica el límite máximo del cociente familiar QF, se vuelve a calcular el impuesto bruto sin los hijos. Siguiendo con el ejemplo del empleado casado con dos hijos y un salario anual S de 50 000 euros:

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

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

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, por lo tanto, igual a 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*2] = 3510.

Ganancia máxima relacionada con los hijos: 1551 * 2 = 3102 euros

Impuesto mínimo: 3510 – 3102 = 408 euros

El impuesto bruto con 2 partes, ya calculado en el párrafo anterior (2115 euros), es mayor que el 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:

  • [impôt1]: es el impuesto bruto calculado con los hijos;
  • [impôt2]: es el impuesto bruto calculado sin los hijos y menos la deducción máxima (en este caso, 1551 euros por media parte) relacionada con los hijos;

4.3.1.3. Cálculo de la reducción

 

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

El impuesto bruto (2115 euros) obtenido en el paso anterior es inferior a 2627 euros para una pareja (1595 euros para una persona soltera): por lo tanto, se aplica la reducción. Se obtiene mediante el siguiente cálculo:

descuento = umbral (pareja = 1970 / soltero = 1196) - 0,75 * impuesto bruto

descuento = 1970 - 0,75 × 2115 = 383,75, redondeado a 384 euros.

Nuevo impuesto bruto = 2115 - 384 = 1731 euros

Hay que tener en cuenta dos reglas al calcular la reducción (algunas herramientas de IA han tenido problemas con esto):

  • El descuento no puede ser negativo;
  • La deducción no puede ser mayor que el impuesto ya calculado;

4.3.1.4. Cálculo de la reducción de impuestos

 

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

  • soltero: 21 037 euros;
  • pareja: 42 074 euros; (la cifra de 37 968 utilizada en el ejemplo anterior parece errónea);

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

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

  • Su ingreso imponible (45 000 euros) es inferior al umbral (42 074 + 2 × 3797) = 49 668 euros;
  • Por lo tanto, tiene derecho a una reducción del 20 % de su impuesto: 1731 * 0,2 = 346,2 euros, redondeado a 347 euros;
  • El impuesto bruto del contribuyente queda en: 1731 - 347 = 1384 euros;

4.3.1.5. Cálculo del impuesto neto

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

4.3.1.6. El caso de los ingresos altos

Nuestro ejemplo anterior corresponde a la mayoría de los casos de asalariados. Sin embargo, el cálculo del impuesto es diferente en el caso de los ingresos altos.

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

En la mayoría de los casos, el ingreso imponible se calcula mediante la fórmula: R = 0,9 * S, donde S es el salario anual. A esto se le llama la reducción del 10 %. Esta reducción tiene un límite máximo. En 2019:

  • No puede ser superior a 12 502 euros;
  • No puede ser inferior a 437 euros;

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

  • La reducción del 10 % es de 20 000 euros > 12 502 euros. Por lo tanto, se reduce a 12 502 euros;

4.3.1.6.2. Límite máximo del cociente familiar

Consideremos un caso en el que se aplica el límite máximo familiar mencionado en el párrafo |Límite máximo del cociente familiar|. Tomemos el caso de una pareja con tres hijos y ingresos anuales de 100 000 euros. Repasemos los pasos del cálculo:

  • La deducción del 10 % es de 100 000 euros < 12 502 euros. Por lo tanto, el ingreso 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 cociente 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 máximo del cociente familiar establece que el beneficio que aportan los hijos no puede superar (1551 × 4 medias partes) = 6204 euros. Sin embargo, en este caso es I2-I1 = 15 404 - 7 020 = 8 384 euros, por lo que es superior a 6 204 euros;
  • Por lo tanto, el impuesto bruto se vuelve a calcular como I3 = I2-6204 = 15 404 - 6 204 = 9 200 euros;
  • Como I3 > I1, se mantendrá el impuesto I3;

Esta pareja no tendrá ni descuento ni reducción, y su impuesto final será de 9200 euros.

4.3.1.7. Cifras oficiales

El cálculo del impuesto es complejo. A lo largo de este documento, los ejemplos se basarán en los siguientes casos. Los resultados provienen 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 2 hijos y ingresos anuales de 55 555 euros
Impuesto = 2815 euros
Tasa impositiva = 14 %
Impuesto = 2814 euros
Tasa impositiva = 14 %
Pareja con dos hijos y ingresos anuales de 50 000 euros
Impuesto = 1385 euros
Descuento = 384 euros
Reducción = 346 euros
Tasa impositiva = 14 %
Impuesto = 1384 euros
Descuento = 384 euros
Descuento = 347 euros
Tasa impositiva = 14 %
Pareja con 3 hijos y ingresos anuales de 50 000 euros
Impuesto = 0 euros
Descuento = 720 euros
Descuento = 0 euros
Tasa impositiva = 14 %
Impuesto = 0 euros
Descuento = 720 euros
Descuento = 0 euros
Tasa impositiva = 14 %
Soltero con 2 hijos y ingresos anuales de 100 000 euros
Impuesto = 19 884 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 41 %
Impuesto = 19 884 euros
Recargo = 4480 euros
descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 41 %
Soltero con 3 hijos y ingresos anuales de 100 000 euros
Impuesto = 16 782 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 41 %
Impuesto = 16 782 euros
Recargo = 7176 euros
descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 41 %
Pareja con 3 hijos y ingresos anuales de 100 000 euros
Impuesto = 9200 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 30 %
Impuesto = 9200 euros
Recargo = 2180 euros
descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 30 %
Pareja con 5 hijos y ingresos anuales de 100 000 euros
Impuesto = 4230 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 14 %
Impuesto = 4230 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 14 %
Soltero sin hijos y con ingresos anuales de 100 000 euros
Impuesto = 22 986 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 41 %
Impuesto = 22 986 euros
Recargo = 0 euros
Descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 41 %
Pareja con dos hijos y ingresos anuales de 30 000 euros
Impuesto = 0 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 0 %
Impuesto = 0 euros
Descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 0 %
Soltero sin hijos y con ingresos anuales de 200 000 euros
Impuesto = 64 211 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 45 %
Impuesto = 64 210 euros
Recargo = 7498 euros
descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 45 %
Pareja con 3 hijos y ingresos anuales de 200 000 euros
Impuesto = 42 843 euros
Descuento = 0 euros
Descuento = 0 euros
Tasa impositiva = 41 %
Impuesto = 42 842 euros
Recargo = 17 283 euros
Descuento = 0 euros
Reducción = 0 euros
Tasa impositiva = 41 %

En lo anterior, se denomina recargo a lo que pagan de más las personas con ingresos altos debido a dos fenómenos:

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

Este indicador no se pudo verificar porque el simulador de la administración tributaria no lo proporciona.

Se observa que el algoritmo del documento calcula el impuesto correcto en todos los casos, aunque con un margen de error de 1 euro. Este margen de error se debe a los redondeos. Todas las cantidades de dinero se redondean a veces al euro superior y otras al euro inferior. Como no conocía las reglas oficiales, las cantidades del algoritmo del documento se redondearon:

  • Al euro superior para los descuentos y rebajas;
  • Al euro inferior para los recargos y el impuesto final;

Le pediremos al IA que realice este cálculo de impuestos.

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

La consulta enviada a Gemini va acompañada de dos archivos:

 
  • En [1], el cálculo que acabamos de detallar se ha incluido en un PDF que se le entrega a Gemini. Gemini encontrará allí las reglas exactas para el cálculo simplificado del impuesto de 2019 sobre los ingresos de 2018;
  • En [2], nuestras instrucciones;
  • En [3] para ejecutar el archivo 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/simulador/calcul_impot/2019/simplifica/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. Muy pronto nos damos cuenta de que el IA debe estar muy bien definido si queremos obtener lo que buscamos. Fue debido a todos estos intentos a ciegas que la sesión de Gemini finalmente se detuvo por exceder los límites. Analicemos el resto de estas instrucciones:

  • Línea 1: se solicita que la conversación sea en francés. Esta indicación es para DeepSeek, que tendía a hablar en inglés;
  • Línea 3: lo que queremos;
  • Línea 5: se le indica a IA que utilice el PDF que se le ha proporcionado;
  • Líneas 7-14: una serie de consejos útiles, sobre todo para el problema 3 sin PDF. Varios IA se perdieron en el cálculo del impuesto;
  • Líneas 15-44: las 11 pruebas unitarias que queremos que se incluyan en el script generado. Cuando se genere el script, lo ejecutaremos en PyCharm y veremos si las 11 pruebas pasan;
  • Líneas 46-53: sin estas instrucciones, IA generaba pruebas unitarias que buscaban resultados exactos y fallaban;
  • Líneas 55-56: le indico a IA que no se conecte a Internet. La solución más sencilla es utilizar PDF;
  • Líneas 58-59: esta instrucción no fue seguida por los IA. Me vi obligado a escribirla explícitamente en una pregunta, cuando me di cuenta de que una prueba había fallado;
  • Líneas 61-65: indico qué tipo de script de Python deseo;
  • 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 los IA no saben hacer eso. Los enlaces proporcionados no funcionaban;
  • Líneas 71-72: me hubiera gustado saber el tiempo que tardó el IA en responder la pregunta. Solo Gemini pudo darme esa información. Los demás IA o bien no respondían a esta instrucción, o bien daban cifras inventadas, lo que demostraba que no entendían la instrucción;

4.3.3. La 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 de las instrucciones que utiliza en un momento dado;

Lo que sigue es lo siguiente:

 
  • En [1], Gemini afirma que ha ejecutado con éxito las 11 pruebas unitarias. La mayoría de los IA afirmaron lo mismo tanto en el problema 2 como en el problema 3, y a menudo, al cargar el script generado, este no funcionaba. Por lo tanto, hay que desconfiar de esta afirmación. En el caso de Gemini, esto resultará ser cierto;
  • En [2], un enlace que resultará no funcionar;
  • En [3], solo Gemini proporcionó un tiempo de ejecución realista;

Así que el enlace [2] no funciona. Se le informa a Gemini:

 

La 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 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 para generar un determinado formato de texto;

Colocamos los registros anteriores en un archivo llamado [logs gemini1.txt] y se lo entregamos a Gemini:

 

La respuesta de Gemini es entonces la siguiente:

 

Al cargarlo en PyCharm, la ejecución del nuevo script genera exactamente el mismo error. Se le informa a Gemini proporcionándole nuevamente los registros de la ejecución:

 

La respuesta de Gemini es la siguiente:

 

Esta vez sí. Las 11 pruebas unitarias se han superado. Se le informa a Gemini:

 

Quien responde:

 

El script generado por Gemini siguió las instrucciones dadas en el archivo de texto [instructionsAvecPDF.txt]:

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

import math
import unittest

# --- CONSTANTES DE CALCUL POUR L'IMPÔT 2019 ---
# Todos los valores se extraen del documento PDF proporcionado.

# Límites máximos para la deducción del 10 % sobre los ingresos anuales
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# Escala de impuestos sobre la renta 2019
# Estructura: (Umbral máximo del tramo, tasa impositiva, monto a deducir por parte)
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}
]

# Límite máximo del coeficiente familiar
GAIN_MAX_DEMI_PART = 1551.0  # Ganancia máxima por media parte adicional

# Umbrales e importes para la reducción
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# Umbrales para la reducción del impuesto del 20 %
SEUIL_BASE_REDUCTION_20 = {"celibataire": 21037.0, "couple": 42074.0}
MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART = 3797.0


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

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

    return parts


def calculer_revenu_imposable(revenus: float) -> float:
    """
    Calcule le revenu net imposable après l'abattement de 10%.
    Cet abattement est plafonné.
    L'abattement est arrondi à l'euro supérieur.
    """
    abattement_calcule = revenus * 0.1
     # La deducción tiene un límite entre un mínimo y un máximo
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # La pregunta pide redondear la deducción al euro superior
    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

     # Si el valor de QF supera todos los umbrales, se utiliza el último tramo
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # Fórmula para calcular el impuesto bruto
    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

     # Cálculo del impuesto sin las participaciones de los hijos
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # Cálculo de la ganancia máxima relacionada con los hijos
    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

     # El impuesto retenido es el más alto de los dos cálculos
    return max(impot_brut_avec_enfants, impot_minimal_plafone)


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

    decote = 0.0
     # La reducción se aplica si el impuesto está por debajo del umbral de aplicación
    if impot_avant_decote < seuil_application:
         # Fórmula de la reducción: umbral - 0,75 * Impuesto bruto
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # Redondeo al euro superior
        decote = math.ceil(decote_calculee)

         # La reducción no puede ser negativa ni superar el impuesto
        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]

     # El umbral se incrementa por cada media parte relacionada con los hijos
    parts_avec_enfants = calculer_nombre_parts(adultes, enfants)
    parts_adultes = calculer_nombre_parts(adultes, 0)
    demi_parts_enfants = (parts_avec_enfants - parts_adultes) * 2
    seuil_final = seuil_base + (demi_parts_enfants * MAJORATION_SEUIL_REDUCTION_PAR_DEMI_PART)

    reduction = 0.0
     # La reducción se aplica si el ingreso imponible está por debajo del umbral
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # Redondeado al euro superior
        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.
    """
     # Paso 1: Cálculo de la renta imponible
    revenu_imposable = calculer_revenu_imposable(revenus)

     # Paso 2: Cálculo del número de participaciones
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # Paso 3: Cálculo del impuesto bruto
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # Paso 4: Aplicación del límite máximo de QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # Paso 5: Aplicación del descuento
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # Paso 6: Aplicación de la reducción del 20 %
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # Paso 7: Redondeo final del impuesto al euro inferior
    impot_final = math.floor(impot_avant_arrondi_final)

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


# --- TESTS UNITAIRES ---
# Validación del script con los casos proporcionados
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)

     # Ejemplo de uso de la calculadora para un caso específico
    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, considero que es «probablemente correcto». Lo único que hice con mi propio código fue verificar estas 11 pruebas.

4.4. El problema 3

El problema 3 es idéntico al problema 2, salvo que ya no se le proporciona al IA el PDF que indicaba las reglas de cálculo que se debían seguir.

La pregunta inicial a Gemini es la siguiente:

 

El archivo de instrucciones en [1] es prácticamente igual 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 le pide que busque en Internet las reglas 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 presento partes de la primera respuesta de Gemini:

 
 

El tiempo estimado es plausible. Se espera mucho tiempo la respuesta de Gemini.

Al igual que antes, Gemini proporcionó un enlace para descargar el script generado, enlace que no funciona. Se le informa de ello:

 

La respuesta de Gemini:

 

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

 

Lo ejecutamos y… no funciona. Los registros de la 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 falló;
  • Líneas 25 y 42: el mismo error de compilación;

Ponemos estos registros en un archivo de texto que le entregamos a Gemini:

 

La respuesta de Gemini:

 
 

Cargamos el script en PyCharm y lo ejecutamos. Vuelven a aparecer errores. Se lo informamos a Gemini adjuntando nuevamente los registros de la ejecución:

 

La respuesta de Gemini:

 
 
 

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

El código generado por Gemini es el siguiente:

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

# ==============================================================================
# CONSTANTES POUR ElIMPOSITION 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: Si el impuesto ya es 0, no hay descuento ni reducción.
    if impot_apres_plafonnement == 0:
        return 0, 0, 0

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

     # 1. Determinar la elegibilidad para el descuento del 20 %
    est_eligible_reduction = False
    if adultes >= 2:
        seuil_rfr = SEUIL_RFR_REDUCTION_COUPLE + max(0, (nombre_parts - 2) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    else:
        seuil_rfr = SEUIL_RFR_REDUCTION_CELIBATAIRE + max(0, (
                nombre_parts - 1) * 2) * MAJORATION_RFR_REDUCTION_PAR_DEMI_PART
    if revenu_imposable < seuil_rfr:
        est_eligible_reduction = True

     # 2. Determinar la elegibilidad para el descuento
    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: El descuento devuelto no puede exceder el impuesto que anula
        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. Aplicar la reducción del 20 % si es elegible
    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 superó las 11 pruebas con éxito.

Pero tal vez nos interese conocer su razonamiento, especialmente en los casos particulares del cálculo del impuesto. Se le pregunta:

 

Se trata de un caso de ingresos elevados con un posible límite máximo de la deducción del 10 % y un posible límite máximo 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 explicado en el PDF. De hecho, este método de cálculo se puede encontrar en Internet. Ambos métodos son equivalentes.

 
 
 

La explicación destaca por su claridad. Se podría entregar tal cual a los estudiantes para explicar el método de cálculo del impuesto.

Ahora veamos otro ejemplo, esta vez con ingresos bajos. En este caso, puede haber un descuento y una reducción:

 

La respuesta de Gemini es la siguiente:

 
 
 
 
 

Aquí se observa que Gemini aplica una regla que no figura en el PDF. Probablemente la haya encontrado en Internet, pero ¿es confiable la fuente?

 

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

 
 
 

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

 

La respuesta de Gemini:

 
 
 
 

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

  • Donde su razonamiento daría los mismos resultados que el simulador oficial de impuestos;
  • Donde el razonamiento del PDF le daría resultados diferentes a los del simulador;
 

La respuesta de Gemini es la siguiente:

 

Aquí Gemini se equivoca. Ejecuté el simulador con este ejemplo y obtuve lo siguiente:

 

Sin embargo, veremos que el razonamiento de Gemini sí da los resultados anteriores. Sigamos:

 
 
 
 

Muy bien. Anotado. Sigamos:

 
 
 
 

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

 

Gemini responde:

 
 

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

 

La respuesta de Gemini:

 

Así que Gemini no me dio un enlace a un PDF, pero generó un texto para que yo mismo pudiera generar este PDF. Aunque resulta un poco engorroso incluir aquí las capturas de pantalla del PDF, lo hago para que el lector pueda apreciar el aspecto generativo del IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Sinceramente, no he verificado si todo lo que se dice en este PDF es cierto. En cualquier caso, es un documento perfecto para un TD, generado en unos segundos.

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

 
  • en [1], hemos puesto el PDF generado por Gemini [Le problème selon Gemini.pdf];
  • en [2], [instructionsAvecPDF2.txt] es idéntico a las instrucciones de [instructionsAvecPDF.txt], salvo que se ha agregado una duodécima prueba unitaria, la misma que demostró que el PDF inicial era erróneo:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Curiosamente, se necesitaron varios intentos antes de que Gemini generara el script correcto:

Pregunta 2

 

Pregunta 3

 

Como ya se ha hecho varias veces, cuando el script generado y cargado en PyCharm falla, le proporcionamos a Gemini el archivo de texto con los registros de la ejecución. Gemini los entiende muy bien.

Pregunta 4

 

Pregunta 5

 

Pregunta 6 y fin

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

Ahora haremos lo mismo con los otros cinco IA, pero seremos muy breves en nuestras explicaciones, excepto en el caso del ChatGPT, el IA líder actual. Lo que nos interesa es saber si el IA resuelve o no los tres problemas que se le plantean. De hecho, las interfaces de todos estos IA son muy similares y he procedido con ellos de la misma manera que con Gemini. Se invita al lector a volver a reproducir las conversaciones de Gemini con el modelo IA de su elección.