Skip to content

5. Resolución de los tres problemas con ChatGPT

5.1. Introducción

Aquí hay una primera captura de pantalla de una sesión de ChatGPT:

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

ChatGPT es un producto de OpenAI disponible en la URL [https://chatgpt.com/]. Para ver un historial de tus sesiones de preguntas y respuestas como la anterior, debes crear una cuenta. Además, al igual que todas las demás IA probadas, ChatGPT 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 continuar más tarde. Los límites impuestos por ChatGPT se alcanzan muy rápidamente. Para crear este tutorial, tuve que adquirir una suscripción de pago de un mes.

La interfaz de ChatGPT es la siguiente:

 
  • En [1], para adjuntar archivos a la pregunta formulada;
  • En [2], la pregunta formulada;
  • En [3], para ejecutar la IA;

5.2. Problema 1

La pregunta para ChatGPT:

  
 

ChatGPT responde correctamente.

5.3. Problema 2

Esto implica calcular el impuesto utilizando el PDF. Para ser sinceros, utilizaremos el PDF generado por Gemini, que corrige los errores del PDF original.

 
  • En [1], proporcionamos el PDF generado por Gemini;
  • En [2], añadimos la prueba unitaria mediante la cual Gemini demostró su superioridad:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Ejecutamos ChatGPT. Tarda unos 3 minutos en generar su respuesta. A diferencia de Gemini, sí proporciona un enlace válido para recuperar el script generado. Lo cargamos en PyCharm:

 

El script [chatGPT1] funciona a la primera. No hay duda: en este problema, ChatGPT ha superado a Gemini.

El script [chatGPT1] proporcionado por ChatGPT es el siguiente:

#  -*- coding: utf-8 -*-
"""
Calcul de l'impôt 2019 (revenus 2018) en Python.

Hypothèses et règles (d'après le PDF fourni) :
- Tous les revenus sont portés sur le déclarant 1 (même pour un couple).
- Abattement forfaitaire de 10 % encadré par un minimum et un maximum.
- Barème progressif 2018 appliqué au quotient familial (QF).
- Plafonnement du quotient familial : avantage par demi-part enfant limité.
- Décote et réduction de 20 % pour faibles revenus, avec la règle de repêchage.
- Ordre des correctifs : plafonnement QF -> décote -> réduction 20 %.
- Arrondis : abattement (↑), décote (↑), réduction (↑), impôt final (↓).
- On renvoie l'impôt à payer (arrondi à l'euro inf.), la décote et la réduction
  (arrondies à l'euro sup.).

Les nombres “magiques” sont regroupés en constantes, et chaque étape est isolée
dans des fonctions pour la clarté.
"""

from dataclasses import dataclass
from math import ceil, floor

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

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

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

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

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

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


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

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


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

def arrondi_sup(euros: float) -> int:
    """Arrondi à l'euro supérieur (ceil)."""
    return int(ceil(euros))


def arrondi_inf(euros: float) -> int:
    """Arrondi à l'euro inférieur (floor)."""
    return int(floor(euros))


#  ---------- Step 1: Net taxable income (RNI) ----------

def calcul_abattement(revenus_bruts: float) -> int:
    """
    Calcule l'abattement de 10 %, plafonné/planche, ARRONDI À L'EURO SUPÉRIEUR.
    """
    abattement_calcule = revenus_bruts * ABATTEMENT_TAUX
    abattement_calcule = max(ABATTEMENT_MIN, min(ABATTEMENT_MAX, abattement_calcule))
    return arrondi_sup(abattement_calcule)


def revenu_net_imposable(revenus_bruts: float) -> int:
    """RNI = revenus bruts - abattement (abattement arrondi ↑)."""
    return int(round(revenus_bruts - calcul_abattement(revenus_bruts)))


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

def nombre_parts(adultes: int, enfants: int) -> float:
    """
    Règles : 1 part si 1 adulte, 2 parts si 2 adultes.
    Enfants : +0,5 pour les 2 premiers, +1 par enfant à partir du 3e.
    (On ignore ici tout cas particulier type parent isolé, conformément au PDF.)
    """
    parts = 1.0 if adultes == 1 else 2.0
    if enfants <= 0:
        return parts
    if enfants == 1:
        return parts + 0.5
    #  children >= 2
    parts += 1.0  #  +0.5 +0.5 for the first two
    if enfants > 2:
        parts += (enfants - 2) * 1.0  #  +1 per child from the 3rd
    return parts


def demi_parts_enfants(enfants: int) -> int:
    """Nombre de DEMI-parts dû aux enfants (utile pour le plafonnement QF)."""
    if enfants <= 0:
        return 0
    if enfants == 1:
        return 1  #  0.5 part
    #  First two children = 2 * half-parts, then +2 half-parts per child from the 3rd onwards
    return 2 + (enfants - 2) * 2


#  ---------- Step 3: Gross tax via QF ----------

def impot_brut_par_parts(qf: float) -> float:
    """Impôt par part via barème progressif 2018 (sans arrondi)."""
    imp = 0.0
    previous = 0.0
    for borne_sup, taux in BAREME_2018:
        montant_tranche = max(0.0, min(qf, borne_sup) - previous)
        imp += montant_tranche * taux
        if qf <= borne_sup:
            break
        previous = borne_sup
    return imp


def impot_brut_total(rni: float, parts: float) -> float:
    """Impôt brut total = impôt par part * nombre de parts (sans arrondi)."""
    if parts <= 0:
        return 0.0
    qf = rni / parts
    return impot_brut_par_parts(qf) * parts


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

def apply_plafonnement_qf(impot_avec_enfants: float, rni: float, adultes: int, enfants: int) -> float:
    """
    Calcule l'impôt après plafonnement QF si l'avantage des enfants dépasse le plafond.
    - Impôt A : avec parts incluant enfants
    - Impôt B : avec seulement parts des adultes (1 ou 2)
    - Avantage réel = B - A
    - Avantage maximal = 1551 € par 1/2 part ENFANT
    Si avantage réel > avantage max, impôt = B - avantage max, sinon impôt = A.
    """
    parts_adultes = 1.0 if adultes == 1 else 2.0
    imp_b = impot_brut_total(rni, parts_adultes)
    imp_a = impot_avec_enfants
    avantage_reel = max(0.0, imp_b - imp_a)
    avantage_max = PF_QF_AVANTAGE_PAR_DEMI_PART * demi_parts_enfants(enfants)
    if avantage_reel > avantage_max:
        return imp_b - avantage_max
    return imp_a


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

def seuil_decote(adultes: int) -> int:
    return DECOTE_SEUIL_COUPLE if adultes >= 2 else DECOTE_SEUIL_CELIB


def seuil_reduction_20(rni: int, adultes: int, parts: float) -> int:
    """
    Seuil d'éligibilité à la réduction de 20 % :
    - Base : 18 985 € pour 1 part, 37 970 € pour 2 parts
    - + 3 803 € par 1/2 part supplémentaire au-delà de la base correspondante.
    """
    base = REDUCTION_BASE_2_PARTS if adultes >= 2 else REDUCTION_BASE_1_PART
    base_parts = 2.0 if adultes >= 2 else 1.0
    demi_sup = max(0, int(round((parts - base_parts) * 2)))
    return base + demi_sup * REDUCTION_PAR_DEMI_PART_SUP


def calc_decote_reduction(impot_apres_plaf: float, rni: int, adultes: int, parts: float):
    """
    Calcule (decote_arrondie, reduction_arrondie, impot_apres_correctifs).
    Règle d'interaction : la décote peut s'appliquer via repêchage si, après
    application THÉORIQUE de la réduction de 20 %, l'impôt passerait sous le seuil.
    Ordre : décote -> réduction.
    """
    imp = max(0.0, impot_apres_plaf)
    decote = 0
    reduction = 0

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

    #  Eligible for discount: directly below threshold, or if reduction would cause the threshold to be lowered
    s_dec = seuil_decote(adultes)
    direct_decote = imp <= s_dec
    repechage = False
    if not direct_decote and eligible_reduction:
        imp_theorique_apres_red = imp * 0.80  #  theoretical 20% reduction
        if imp_theorique_apres_red <= s_dec:
            repechage = True

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

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

    #  Then apply the reduction if eligible
    if eligible_reduction and imp > 0:
        montant_reduction = imp * 0.20
        reduction = max(0, arrondi_sup(montant_reduction))
        imp = max(0.0, imp - reduction)

    return decote, reduction, imp


#  ---------- API main ----------

def calcule_impot(adultes: int, enfants: int, revenus_bruts: float) -> ResultatImpot:
    """
    Calcule l'impôt 2019 (revenus 2018) pour un foyer.
    Retourne ResultatImpot(impot, decote, reduction).
    """
    #  1) RNI
    rni = revenu_net_imposable(revenus_bruts)

    #  2) Units
    parts = nombre_parts(adultes, enfants)

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

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

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

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

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


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

def _nearly_equal(a: int, b: int, tol: int = 1) -> bool:
    return abs(a - b) <= tol


def tests_unitaires():
    """
    Renvoie une liste de tuples (inputs, attendu, obtenu, ok) pour chaque test.
    Tolérance : ±1 € sur chaque valeur (impôt, décote, réduction).
    """
    cas = [
        #  (adults, children, income) -> (tax, discount, reduction)
        ((2, 2, 55555), (2815, 0, 0)),
        ((2, 2, 50000), (1385, 384, 346)),
        ((2, 3, 50000), (0, 720, 0)),
        ((1, 2, 100000), (19884, 0, 0)),
        ((1, 3, 100000), (16782, 0, 0)),
        ((2, 3, 100000), (9200, 0, 0)),
        ((2, 5, 100000), (4230, 0, 0)),
        ((1, 0, 100000), (22986, 0, 0)),
        ((2, 2, 30000), (0, 0, 0)),
        ((1, 0, 200000), (64211, 0, 0)),
        ((2, 3, 200000), (42843, 0, 0)),
        ((2, 2, 49500), (1297, 431, 324)),
    ]

    resultats = []
    for (adultes, enfants, revenus), attendu in cas:
        res = calcule_impot(adultes, enfants, revenus)
        obtenu = (res.impot, res.decote, res.reduction)
        ok = _nearly_equal(obtenu[0], attendu[0]) and _nearly_equal(obtenu[1], attendu[1]) and _nearly_equal(obtenu[2],
                                                                                                             attendu[2])
        resultats.append(((adultes, enfants, revenus), attendu, obtenu, ok))
    return resultats


if __name__ == "__main__":
    for inputs, attendu, obtenu, ok in tests_unitaires():
        print(f"{inputs} -> attendu={attendu}, obtenu={obtenu} : {'OK' if ok else 'ECHEC'}")

5.4. Problema 3

Ahora le pedimos a ChatGPT que busque en Internet las reglas de cálculo de impuestos:

 

Esta vez, no proporcionamos el PDF que contenía las reglas de cálculo a seguir. Solo proporcionamos nuestras instrucciones en el archivo de texto. Tenga en cuenta que este archivo de texto contiene ahora 12 pruebas unitarias tras añadir, a las 11 pruebas iniciales, la que utilizó Gemini para demostrar que mi PDF inicial era incorrecto.

ChatGPT responde en 8 minutos, proporcionando un enlace para descargar el script generado. Una vez cargado en PyCharm, este script supera las 12 pruebas. Así pues, en ambos problemas planteados, ChatGPT acertó las respuestas a la primera, superando así a Gemini.

ChatGPT proporciona sus fuentes en su respuesta:

 

No hay nada más que decir: es un trabajo bien hecho.

Ahora podemos pedirle, igual que hicimos con Gemini, que genere un PDF para los alumnos.

 

La respuesta de ChatGPT llegó tras varios intercambios de mensajes, ya que el PDF generado utilizaba una fuente que sustituía los caracteres por cuadrados. Pero, finalmente, generó el PDF. Lo comparto porque ofrece reglas diferentes a las del PDF de Gemini, y me preguntaba cuál de los dos era el correcto. Vamos a investigarlo.

 
 
 

La diferencia con respecto al PDF de Gemini radica en cómo se calcula el descuento. Las dos IA adoptan enfoques diferentes. Gemini había escrito:

 
 
 

Las dos IA tienen dos enfoques diferentes. ¿Cuál es el correcto?

5.5. Problema 4

Le pediremos a ChatGPT que utilice su PDF para calcular el impuesto:

Al igual que en ocasiones anteriores, genera un script de Python que funciona a la primera. Habíamos añadido una prueba adicional a las instrucciones:

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

Las 13 pruebas se superaron con éxito.

5.6. Volver a Gemini

Ahora volvemos a Gemini, donde le presentaremos el PDF de ChatGPT. Dado que las reglas aplicadas en este PDF difieren de las del PDF de Gemini, nos preguntamos qué sucederá:

 

Gemini generó primero un script en Python que falló en algunas pruebas. Le presentamos los registros:

Pregunta 2

 

Pregunta 3

Todavía hay errores. Sigamos.

 

Pregunta 4

Sigue habiendo errores durante la ejecución:

 

Esta vez es correcto.

Nos sigue intrigando que, incluso con archivos PDF que tienen reglas de cálculo bastante diferentes, ambas IA generen resultados correctos.

Le hacemos la siguiente pregunta a Gemini:

 

La pregunta completa es la siguiente:

Après tout ce qu'on a fait, compare les deux approches, la tienne et celle de ChatGPT dans les pDF que je te joins [.. selon Gemini] pour toi, [.. selon ChatGPT] pour ChatGPT. Dis-moi laquelle on doit préférer vu que sur les 13 tests générés tu les réussis tous avec l'un ou avec l'autre PDF.

La respuesta de Gemini es inequívoca:

 
 
 
 
 
 

5.7. ¿Qué opina ChatGPT?

Le hacemos a ChatGPT la misma pregunta que le hicimos a Gemini.

 

La respuesta de ChatGPT es la siguiente:

 
 

Así pues, ChatGPT sugiere una prueba unitaria para decidir entre los dos métodos. Duplicamos:

  • El script [gemini3] generado por Gemini utilizando su PDF [El problema según Gemini] como fuente se duplica en el script [gemini4];
  • El script [chatGPT3] generado por ChatGPT utilizando su PDF [El problema según ChatGPT] como fuente se duplica en el script [chatGPT4];

Además, añadimos la prueba unitaria propuesta por ChatGPT a cada uno de los scripts [gemini4, chatGPT4] para distinguir entre las dos IA.

Al ejecutar [gemini4] se obtienen los siguientes resultados:


C:\Data\st-2025\dev\python\code\python-flask-2025-cours\.venv\Scripts\python.exe "C:/Program Files/JetBrains/PyCharm 2025.2.1.1/plugins/python-ce/helpers/pycharm/_jb_unittest_runner.py" --path "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini4.py" 
Testing started at 17:45 ...
Launching unittests with arguments python -m unittest C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini4.py in C:\Data\st-2025\dev\python\code\python-flask-2025-cours
 
SubTest failure: Traceback (most recent call last):
  File "C:\Program Files\Python313\Lib\unittest\case.py", line 58, in testPartExecutor
    yield
  File "C:\Program Files\Python313\Lib\unittest\case.py", line 556, in subTest
    yield
  File "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\gemini\gemini4.py", line 234, in test_cas_verifies_simulateur_officiel
    self.assertAlmostEqual(calcul_impot, attendu_impot, delta=1, msg="Échec sur le montant de l'impôt")
    ~~~~~~~~~~~~~~~~~~~~~~^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
AssertionError: 2669 != 2270 within 1 delta (399 difference) : Échec sur le montant de l'impôt
 
 
 
 
Ran 1 test in 0.010s
 
FAILED (failures=1)
 
One or more subtests failed
Failed subtests list: [Test 'test12' avec entrée (2, 0, 43333)]
 
Process finished with exit code 1

Así pues, Gemini no supera la prueba añadida por ChatGPT.

Al ejecutar [chatGPT4] se obtienen los siguientes resultados:


C:\Data\st-2025\dev\python\code\python-flask-2025-cours\.venv\Scripts\python.exe "C:\Data\st-2025\dev\python\code\python-flask-2025-cours\outils ia\chatGPT\chatGPT4.py" 
Test (2, 2, 55555) -> obtenu (impôt=2814, décote=0, réduction=0) | attendu (2815, 0, 0) | OK
Test (2, 2, 50000) -> obtenu (impôt=1384, décote=384, réduction=347) | attendu (1385, 384, 346) | OK
Test (2, 3, 50000) -> obtenu (impôt=0, décote=721, réduction=0) | attendu (0, 720, 0) | OK
Test (1, 2, 100000) -> obtenu (impôt=19884, décote=0, réduction=0) | attendu (19884, 0, 0) | OK
Test (1, 3, 100000) -> obtenu (impôt=16782, décote=0, réduction=0) | attendu (16782, 0, 0) | OK
Test (2, 3, 100000) -> obtenu (impôt=9200, décote=0, réduction=0) | attendu (9200, 0, 0) | OK
Test (2, 5, 100000) -> obtenu (impôt=4230, décote=0, réduction=0) | attendu (4230, 0, 0) | OK
Test (1, 0, 100000) -> obtenu (impôt=22986, décote=0, réduction=0) | attendu (22986, 0, 0) | OK
Test (2, 2, 30000) -> obtenu (impôt=0, décote=0, réduction=0) | attendu (0, 0, 0) | OK
Test (1, 0, 200000) -> obtenu (impôt=64210, décote=0, réduction=0) | attendu (64211, 0, 0) | OK
Test (2, 3, 200000) -> obtenu (impôt=42842, décote=0, réduction=0) | attendu (42843, 0, 0) | OK
Test (2, 2, 49500) -> obtenu (impôt=1296, décote=431, réduction=325) | attendu (1297, 431, 324) | OK
Test (1, 0, 18535) -> obtenu (impôt=359, décote=491, réduction=90) | attendu (359, 491, 90) | OK
Test (2, 0, 43333) -> obtenu (impôt=2268, décote=0, réduction=401) | attendu (2270, 0, 400) | ECHEC
 Détails tolérance ±1€ : impôt ok? False, décote ok? True, réduction ok? True
 
Résultat global : AU MOINS UN TEST ÉCHOUE ❌
 
Process finished with exit code 0

ChatGPT también suspende la prueba añadida, pero no por las mismas razones que Gemini. ChatGPT encontró los resultados correctos, pero se desvió en 2 euros en lugar del 1 euro requerido.

Así que, a partir de ahora, utilizaremos el PDF generado por ChatGPT con las siguientes IA. Cabe señalar que es debido a la falta de pruebas unitarias en mis instrucciones que ambas IA superaron las primeras pruebas. De ahí, en este ejemplo concreto, la importancia de incluir pruebas unitarias para casos extremos en el cálculo de impuestos. Dado que es bastante difícil idear estas pruebas por uno mismo, pediremos a las IA que las añadan ellas mismas.

5.8. Problema 3 con pruebas unitarias generadas por las IA

Los resultados obtenidos con Gemini y ChatGPT dejan lugar a dudas. ¿Encontraron las IA una solución general que supera todas las pruebas imaginables, o encontraron una solución que solo supera las pruebas requeridas? Empezaremos de nuevo con una solución sin PDF para obligar a las IA a conectarse a Internet y buscar la información que necesitan. Y modificaremos nuestras instrucciones de la siguiente manera:

 

El archivo de texto [instructionsSansPDF4.txt] ya contiene 14 pruebas obligatorias. A estas pruebas añadimos las siguientes instrucciones:


7 - tu ajouteras autant de tests unitaires que nécessaires pour vérifier les cas limites du calcul de l'impôt.
 
Pour le code tu complèteras le script suivant auquel tu auras rajouté tes propres tests.
 
# =========================
# Tests unitaires (tolérance de ±1 €)
# =========================
 
TESTS = [
    # (adultes, enfants, revenus) -> (impot, decote, reduction)
    ((2, 2, 55555), (2815, 0, 0)),
    ((2, 2, 50000), (1385, 384, 346)),
    ((2, 3, 50000), (0, 720, 0)),
    ((1, 2, 100000), (19884, 0, 0)),
    ((1, 3, 100000), (16782, 0, 0)),
    ((2, 3, 100000), (9200, 0, 0)),
    ((2, 5, 100000), (4230, 0, 0)),
    ((1, 0, 100000), (22986, 0, 0)),
    ((2, 2, 30000), (0, 0, 0)),
    ((1, 0, 200000), (64211, 0, 0)),
    ((2, 3, 200000), (42843, 0, 0)),
    ((2, 2, 49500), (1297, 431, 324)),
    ((1, 0, 18535), (359, 491, 90)),
    ((2, 0, 43333), (2270, 0, 400)),
]
 
 
def _ok(a, b, tol=1):
    return abs(a - b) <= tol
 
 
def run_tests(verbose: bool = True) -> bool:
    all_ok = True
    for (params, expected) in TESTS:
        a, e, r = params
        exp_impot, exp_decote, exp_reduc = expected
        res = calcul_impot_2019(a, e, r)
        ok_impot = _ok(res.impot, exp_impot)
        ok_decote = _ok(res.decote, exp_decote)
        ok_reduc = _ok(res.reduction, exp_reduc)
        test_ok = ok_impot and ok_decote and ok_reduc
        if verbose:
            print(
                f"Test {params} -> obtenu (impôt={res.impot}, décote={res.decote}, réduction={res.reduction}) | attendu {expected} | {'OK' if test_ok else 'ECHEC'}")
            if not test_ok:
                print(
                    f" Détails tolérance ±1€ : impôt ok? {ok_impot}, décote ok? {ok_decote}, réduction ok? {ok_reduc}")
        all_ok &= test_ok
    if verbose:
        print("\nRésultat global :", "TOUS LES TESTS PASSENT ✅" if all_ok else "AU MOINS UN TEST ÉCHOUE ❌")
    return all_ok
 
 
if __name__ == "__main__":
    run_tests()
  • Líneas 11-24: las 14 pruebas requeridas;
  • Líneas 5-55: este código procede del script generado por ChatGPT. Le pediremos a Gemini que utilice este código para facilitar las comparaciones entre los dos scripts generados.

Empezaremos con ChatGPT:

 

Su primera respuesta es incorrecta. Se lo indico proporcionándole los registros de ejecución:

Su segunda respuesta es correcta. ChatGPT añadió las siguientes 11 pruebas a las 14 pruebas requeridas:

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

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

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

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

Ahora hay 25 pruebas unitarias. He verificado manualmente las 11 pruebas nuevas utilizando el simulador oficial de DGIP, y han superado las pruebas.

Ahora pasamos a Gemini. Esto va a ser mucho más complicado. Conseguirá generar un script que supere las 25 pruebas de ChatGPT, pero solo tras un largo proceso de depuración.

 

A continuación se muestra el registro de depuración:

 

Curiosamente, la mayoría de las pruebas fallaron, incluso entre las 14 obligatorias, mientras que en el pasado Gemini había generado código que las superaba todas.

La siguiente respuesta de Gemini sigue siendo incorrecta:

 

Tampoco lo es la siguiente respuesta:

 

Tampoco lo es la siguiente respuesta. Así que voy a cambiar de estrategia. Le voy a pedir que supere las 25 pruebas que superó ChatGPT, adjuntando los registros de ChatGPT:

 

Gemini falla. Sí que añadió las pruebas de ChatGPT. Adjunto los registros de su ejecución:

 

Sigue sin funcionar:

 

Todavía no:

 

Todavía no:

 

Todavía no, pero está mejor:

 

Gemini está cometiendo nuevos errores:

 

Vuelve a mejorar:

 

Esta vez está bien:

Sin duda, en este ejemplo concreto de cálculo del impuesto de 2019 con las restricciones especificadas en el archivo de instrucciones, ChatGPT fue más preciso que Gemini. Pero esto es solo un ejemplo.

Podemos ir más allá. Podemos pedirle a Gemini que vuelva a generar un PDF basándose en las reglas de cálculo que utilizó para superar las 25 pruebas. Queremos ver si ha cambiado su razonamiento inicial con respecto a los cálculos del descuento y la reducción del 20 %:

Esta vez, Gemini generó un archivo Markdown que luego convertí a PDF [El problema según Gemini, versión 2]. Y, efectivamente, Gemini ha cambiado su razonamiento:

 
 

Podemos ver que el cálculo específico del descuento y la regla de transferencia ya no están presentes. Gemini ha adoptado ahora el razonamiento de ChatGPT.