Skip to content

5. Lösen der drei Probleme mit ChatGPT

5.1. Einführung

Hier ist ein erster Screenshot einer ChatGPT-Sitzung:

 
  • In [1-3] werden die drei Probleme, die ChatGPT ;
  • In [4] wurde der URL von ChatGPT ;
  • In [5] wurde die verwendete Version von ChatGPT ;

ChatGPT ist ein Produkt von OpenAI, das unter URL [https://chatgpt.com/] erhältlich ist. Um einen Verlauf Ihrer Frage-/Antwort-Sitzungen wie oben beschrieben zu haben, müssen Sie ein Konto einrichten. Außerdem begrenzt ChatGPT, wie alle anderen getesteten IA, die Anzahl Ihrer Fragen und die Anzahl der hochgeladenen Dateien. Wenn dieses Limit erreicht ist, ist die Sitzung beendet und Ihnen wird angeboten, sie zu einem späteren Zeitpunkt fortzusetzen. Die von ChatGPT gesetzten Grenzen werden sehr schnell erreicht. Um dieses Tutorial zu erstellen, musste ich ai ein kostenpflichtiges Abonnement für einen Monat abschließen.

Die Schnittstelle von ChatGPT ist wie folgt:

 
  • In [1], um Dateien an die gestellte Frage anzuhängen ;
  • In [2] wurde die gestellte Frage ;
  • In [3], um die Ausführung des IA ;

5.2. Das Problem 1

Die Frage an ChatGPT:

  
 

ChatGPT antwortet korrekt.

5.3. Das Problem 2

Das ist die Berechnung der Steuer mit dem PDF. Um ehrlich zu sein, werden wir den von Gemini erzeugten PDF verwenden, der Fehler im ursprünglichen PDF korrigiert.

 
  • In [1] wurde der von Gemini erzeugte PDF angegeben;
  • In [2] wurde der Einheitstest hinzugefügt, mit dem Gemini seine Überlegenheit demonstrierte:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Wir starten ChatGPT. Er braucht etwa drei Minuten, um seine Antwort zu generieren. Im Gegensatz zu Gemini liefert er einen funktionierenden Link, um den erzeugten script abzurufen. Wir laden ihn in PyCharm :

 

Der script [chatGPT1] funktioniert beim ersten Versuch. Hier gibt es kein Foto, bei diesem Problem war ChatGPT besser als Gemini.

Der von script [chatGPT1] bereitgestellte ChatGPT ist wie folgt:

# -*- Codierung: utf-8 -*-
"""
Steuerberechnung 2019 (Einkommen 2018) in Python.

Annahmen und Regeln (gemäß dem bereitgestellten PDF):
- Alle Einkünfte werden dem Steuerpflichtigen 1 zugerechnet (auch bei Ehepaaren).
- Pauschaler Abschlag von 10 %, der durch einen Mindest- und einen Höchstwert begrenzt ist.
- Progressiver Steuertarif 2018, angewandt auf den Familienquotienten (QF).
- Begrenzung des Familienquotienten: Begrenzung des Vorteils pro halber Kinderpauschale.
- Preisnachlass und Ermäßigung von 20 % für Geringverdiener, mit der Nachrückregelung.
- Reihenfolge der Wertberichtigungen: QF-Obergrenze -> Abschlag -> Reduzierung um 20 %.
- Rundung: Freibetrag (↑), Abschlag (↑), Ermäßigung (↑), endgültige Steuer (↓).
- Die zu zahlende Steuer (auf den nächsten Euro abgerundet), der Abschlag und die Ermäßigung werden ausgewiesen
  (auf den nächsten Euro aufgerundet).

Die „magischen“ Zahlen werden in Konstanten zusammengefasst, und jeder Schritt wird isoliert
in Funktionen zur besseren Übersichtlichkeit.
"""

from dataclasses import dataclass
from math import ceil, floor

# ---------- Regulatorische Konstanten ----------

# Pro-Kopf-Kostenfreibetrag (Untergrenze/Obergrenze) für Einkommen 2018
ABATTEMENT_TAUX = 0.10
ABATTEMENT_MIN = 437
ABATTEMENT_MAX = 12502

# Tarif 2018 (QF)
# Jede Tranche ist (borne_sup, Rate). Die erste 0%-Tranche endet bei 9.964 €.
BAREME_2018 = [
    (9964, 0.00),
    (27519, 0.14),
    (73779, 0.30),
    (156244, 0.41),
    (float("inf"), 0.45),
]

# Begrenzung des Familienquotienten
PF_QF_AVANTAGE_PAR_DEMI_PART = 1551  # max. Vorteil pro 1/2 Anteil ENFANT

# Abschlag (Schwellenwerte je nach Anzahl der Erwachsenen / Paarstatus)
DECOTE_SEUIL_CELIB = 1196
DECOTE_SEUIL_COUPLE = 1970

# 20 %-Ermäßigung: Schwellenwerte je nach Anteilen
REDUCTION_BASE_1_PART = 18985
REDUCTION_BASE_2_PARTS = 37970
REDUCTION_PAR_DEMI_PART_SUP = 3803  # hinzufügung pro 1/2 Anteil über den Sockel von 1 oder 2 Anteilen hinaus


# ---------- Datenstrukturen ----------

@dataclass
class ResultatImpot:
    impot: int  # zu zahlende Endsteuer, abgerundet auf den nächsten Euro
    decote: int  # abschlag aufgerundet auf den nächsten Euro
    reduction: int  # ermäßigung 20 % aufgerundet auf den nächsten Euro


# ---------- Utility-Funktionen ----------

def arrondi_sup(euros: float) -> int:
    """Auf den nächsten Euro aufgerundet (ceil)."""
    return int(ceil(euros))


def arrondi_inf(euros: float) -> int:
    """Auf den nächsten ganzen Euro abgerundet (Floor)."""
    return int(floor(euros))


# ---------- Schritt 1: Steuerpflichtiges Einkommen Net (RNI) ----------

def calcul_abattement(revenus_bruts: float) -> int:
    """
    Berechnet den Abschlag von 10 %, begrenzt nach oben bzw. unten, auf den nächsten Euro aufgerundet.
    """
    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)))


# ---------- Schritt 2: Anzahl der Anteile ----------

def nombre_parts(adultes: int, enfants: int) -> float:
    """
    Regeln: 1 Portion bei 1 Erwachsenen, 2 Portionen bei 2 Erwachsenen.
    Kinder: +0,5 für die ersten beiden, +1 pro Kind ab dem dritten.
    (Sonderfälle wie alleinerziehende Eltern werden hier gemäß dem PDF nicht berücksichtigt.)
    """
    parts = 1.0 if adultes == 1 else 2.0
    if enfants <= 0:
        return parts
    if enfants == 1:
        return parts + 0.5
    # kinder >= 2
    parts += 1.0  # +0,5 +0,5 für die ersten beiden
    if enfants > 2:
        parts += (enfants - 2) * 1.0  # +1 pro Kind ab dem 3
    return parts


def demi_parts_enfants(enfants: int) -> int:
    """Anzahl der den Kindern zustehenden HALBEN Anteile (relevant für die QF-Obergrenze)."""
    if enfants <= 0:
        return 0
    if enfants == 1:
        return 1  # 0,5 Anteil
    # Die ersten beiden Kinder = 2 * Halbteil, dann +2 Halbteile pro Kind ab dem 3
    return 2 + (enfants - 2) * 2


# ---------- Schritt 3: Brutto-Steuer über QF ----------

def impot_brut_par_parts(qf: float) -> float:
    """Steuer pro Anteil nach dem progressiven Steuertarif 2018 (ohne Rundung)."""
    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:
    """Gesamtsteuerbetrag = Steuer pro Anteil * Anzahl der Anteile (ohne Rundung)."""
    if parts <= 0:
        return 0.0
    qf = rni / parts
    return impot_brut_par_parts(qf) * parts


# ---------- Schritt 4.1: Begrenzung des Familienquotienten ----------

def apply_plafonnement_qf(impot_avec_enfants: float, rni: float, adultes: int, enfants: int) -> float:
    """
    Berechnet die Steuer nach Anrechnung der Kinderfreibeträge, wenn der Kinderfreibetrag die Obergrenze überschreitet.
    - Steuer A: mit Anteilen einschließlich Kinder
    - Steuer B: nur mit Anteilen der Erwachsenen (1 oder 2)
    - Tatsächlicher Gewinn = B – A
    - Maximaler Zuschuss = 1551 € pro 1/2 Kinderanteil
    Wenn tatsächlicher Vorteil > maximaler Vorteil, dann Steuer = B – maximaler Vorteil; andernfalls Steuer = 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


# ---------- Schritt 4.2: Abschlag und Ermäßigung 20 % ----------

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:
    """
    Schwellenwert für den Anspruch auf den Rabatt von 20 %:
    - Grundpreis: 18.985 € für 1 Anteil, 37.970 € für 2 Anteile
    - + 3.803 € pro zusätzlichem halben Anteil über die entsprechende Grundmenge hinaus.
    """
    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).
    Interaktionsregel: Der Abschlag kann im Rahmen der Nachrückregelung angewendet werden, wenn nach
    Bei einer THEORETISCHEN Anwendung des 20-prozentigen Abschlags würde die Steuer unter die Schwelle fallen.
    Reihenfolge: Preisnachlass -> Rabatt.
    """
    imp = max(0.0, impot_apres_plaf)
    decote = 0
    reduction = 0

    # Anspruchsberechtigung 20 % Ermäßigung basierend auf RNI
    seuil_red = seuil_reduction_20(rni, adultes, parts)
    eligible_reduction = rni < seuil_red  # "unterhalb eines Schwellenwerts" in PDF

    # Anspruchsberechtigung mit Abschlägen: direkt unterhalb des Schwellenwerts, oder "repêchage", wenn eine Verringerung zu einer Unterschreitung des Schwellenwerts führen würde
    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  # theoretische Ermäßigung 20%
        if imp_theorique_apres_red <= s_dec:
            repechage = True

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

    # Zuerst den Abschlag anwenden (wenn berechtigt)
    if elig_decote:
        montant_decote = s_dec - (imp * 0.75)
        decote_calc = max(0, arrondi_sup(montant_decote))
        # Der Freibetrag darf die verbleibende Steuer nicht übersteigen: Man begrenzt ihn
        decote = min(decote_calc, arrondi_sup(imp))
        imp = max(0.0, imp - decote)

    # Dann die Ermäßigung anwenden, wenn berechtigt
    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 Haupt ----------

def calcule_impot(adultes: int, enfants: int, revenus_bruts: float) -> ResultatImpot:
    """
    Berechnet die Steuer für 2019 (Einkommen 2018) für einen Haushalt.
    Gibt das Ergebnis von „ResultatImpot(impot, decote, reduction)“ zurück.
    """
    # 1) RNI
    rni = revenu_net_imposable(revenus_bruts)

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

    # 3) Bruttosteuer über Tarif (mit allen Anteilen)
    imp_brut_A = impot_brut_total(rni, parts)

    # 4.1) Begrenzung des Familienquotienten
    imp_apres_plaf = apply_plafonnement_qf(imp_brut_A, rni, adultes, enfants)

    # 4.2) Abschlag und dann 20 % Kürzung (mit repêchage)
    decote, reduction, imp_corrige = calc_decote_reduction(imp_apres_plaf, rni, adultes, parts)

    # 5) Abschließende Rundung Steuer (↓) und Verrundung auf 0
    imp_final = max(0, arrondi_inf(imp_corrige))

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


# ---------- Kleine ausführbare Datei zum Testen ----------

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


def tests_unitaires():
    """
    Gibt für jeden Test eine Liste von Tupeln (Eingaben, Erwartet, Erhalten, OK) zurück.
    Toleranz: ±1 € bei jedem Wert (Steuer, Abschlag, Ermäßigung).
    """
    cas = [
        # (Erwachsene, Kinder, Einkommen) -> (Steuer, Abschlag, Ermäßigung)
        ((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. Das Problem 3

Nun wird ChatGPT gebeten, die Regeln zur Berechnung der Steuer im Internet zu suchen:

 

Diesmal geben wir nicht den PDF an, der die zu beachtenden Rechenregeln vorgab. Wir geben nur unsere Anweisungen in der Textdatei an. Wir erinnern daran, dass diese Textdatei nun 12 Unit-Tests enthält, nachdem wir zu den ursprünglichen 11 Tests denjenigen hinzugefügt haben, den Gemini verwendet hat, um zu zeigen, dass mein ursprünglicher PDF fehlerhaft war.

ChatGPT antwortet innerhalb von 8 Minuten, gibt einen Link zum Herunterladen des erzeugten script an. Nach dem Hochladen in PyCharm besteht dieser script alle 12 Tests. Auf beide gestellten Probleme hat ChatGPT also gleich beim ersten Mal richtig geantwortet und damit Gemini übertroffen.

ChatGPT gibt in seiner Antwort seine Quellen an:

 

Es gibt nichts zu sagen, es ist eine gute Arbeit.

Jetzt können wir ihn bitten, wie wir es mit Gemini getan haben, einen PDF für Schüler zu generieren.

 

Die Antwort von ChatGPT erhielt er erst nach einigem Hin und Her, da der erzeugte PDF eine Schriftart verwendete, bei der Zeichen durch ein Quadrat ersetzt wurden. Aber schließlich erzeugte er PDF. Ich gebe ihn weiter, weil er andere Regeln als Geminis PDF liefert und ich mich damals fragte, wer Recht hat. Wir werden das untersuchen.

 
 
 

Der Unterschied zu Geminis PDF liegt in der Berechnung des Abschlags. Die beiden IA haben nicht denselben Ansatz. Gemini hatte geschrieben:

 
 
 

Die beiden IA haben zwei unterschiedliche Ansätze. Wer hat Recht?

5.5. Das Problem 4

Wir werden ChatGPT bitten, sich bei der Steuerberechnung auf seinen PDF zu stützen:

Wie die Male zuvor erzeugt er einen script Python, der beim ersten Versuch funktioniert. Wir hatten in den Anweisungen einen zusätzlichen Test hinzugefügt:

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

Alle 13 Tests wurden erfolgreich bestanden.

5.6. Zurück zu Gemini

Nun kehren wir zu Gemini zurück, dem wir den PDF von ChatGPT präsentieren werden. Da sich die in diesem PDF implementierten Regeln von denen unterscheiden, die in Geminis PDF implementiert sind, kann man sich fragen, was passieren wird:

 

Gemini erzeugte zunächst einen script Python, der Tests verpatzte. Wir präsentierten ihm die Logs :

Frage 2

 

Frage 3

Es gibt immer noch Fehler. Wir machen weiter.

 

Frage 4

Immer wieder Fehler bei der Ausführung :

 

Diesmal ist es gut.

Es ist dennoch faszinierend, dass bei PDF mit recht unterschiedlichen Rechenregeln die IA beide korrekte Ergebnisse liefern.

Gemini wird folgende Frage gestellt:

 

Die vollständige Frage lautet wie folgt:

Nach allem, was wir getan haben, vergleiche die beiden Ansätze – deinen und den von ChatGPT – in den PDF-Dateien, die ich dir beifüge [… laut Gemini] für dich, [… laut ChatGPT] für ChatGPT. Sag mir, welchen wir bevorzugen sollten, da du bei allen 13 generierten Tests mit dem einen oder dem anderen PDF erfolgreich bist.

Die Antwort von Gemini ist kategorisch:

 
 
 
 
 
 

5.7. Was sagt ChatGPT dazu?

Man stellt ChatGPT die gleiche Frage, die man auch Gemini gestellt hat.

 

Die Antwort von ChatGPT lautet wie folgt:

 
 

Deshalb schlägt uns ChatGPT einen Einheitentest vor, um die beiden Methoden gegeneinander abzuwägen. Wir duplizieren :

  • Der script [gemini3], den Gemini mit seinem PDF [Das Problem laut Gemini] als Quelle erzeugt, wird in den script [gemini4] dupliziert;
  • Der script [chatGPT3], der von ChatGPT unter Verwendung seines PDF [Das Problem nach ChatGPT] als Quelle erzeugt wurde, wird in script [chatGPT4] dupliziert;

Außerdem fügen wir in jedem der Skripte [gemini4, chatGPT4] den von ChatGPT vorgeschlagenen Einheitstest hinzu, um die beiden IA zu trennen.

Die Ausführung von [gemini4] führt zu folgenden Ergebnissen:


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

Also scheitert Gemini an dem von ChatGPT hinzugefügten Test.

Die Ausführung von [chatGPT4] führt zu folgenden Ergebnissen:


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 scheitert ebenfalls an dem hinzugefügten Test, aber nicht aus denselben Gründen wie Gemini. ChatGPT fand die richtigen Ergebnisse, aber mit einem Abstand von 2 Euro statt des vorgeschriebenen 1 Euro.

Von nun an ist es also der von ChatGPT erzeugte PDF, den wir mit den folgenden IA verwenden werden. Es ist anzumerken, dass beide IA die ersten Tests aufgrund der fehlenden Unit-Tests, die in meinen Anweisungen vorgeschlagen wurden, bestanden haben. Daher in diesem speziellen Beispiel die Wichtigkeit, Unit-Tests für die Grenzfälle der Steuerberechnung einzufügen. Da es ziemlich schwierig ist, sich diese Tests selbst auszudenken. Wir werden die IA bitten, selbst welche hinzuzufügen.

5.8. Problem 3 mit Unit-Tests, die von den IA generiert werden

Die Ergebnisse, die mit Gemini und ChatGPT erzielt wurden, lassen Zweifel aufkommen. Haben die IA eine allgemeine Lösung gefunden, die alle denkbaren Tests validiert, oder haben sie eine Lösung gefunden, die nur die vorgeschriebenen Tests validiert. Wir starten wieder mit eine Lösung ohne PDF, um die IA zu zwingen, im Internet nach den benötigten Informationen zu suchen. Und wir ändern unsere Anweisungen wie folgt:

 

Die Textdatei [instructionsSansPDF4.txt] enthält bereits 14 vorgeschriebene Tests. Zu diesen Tests fügen wir die folgenden Anweisungen hinzu:

7 - Du fügst so viele Unit-Tests hinzu, wie nötig sind, um die Grenzfälle der Steuerberechnung zu überprüfen.

Für den Code vervollständigst du das folgende Skript, nachdem du deine eigenen Tests hinzugefügt hast.

# =========================
# Unit-Tests (Toleranz von ±1 €)
# =========================

TESTS = [
    # (Erwachsene, Kinder, Einkommen) -> (Steuer, Abschlag, Ermäßigung)
    ((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()
  • Zeilen 11-24, die 14 vorgeschriebenen Tests ;
  • Zeilen 5-55: Dieser Code stammt aus script, der von ChatGPT generiert wurde. Wir werden Gemini auferlegen, diesen Code zu verwenden, um den Vergleich zwischen den beiden erzeugten Skripten zu erleichtern.

Wir beginnen mit ChatGPT :

 

Seine erste Antwort ist nicht korrekt. Ich mache ihn darauf aufmerksam, indem ich ihm die Logs der Ausführung gebe :

Seine zweite Antwort ist die richtige. ChatGPT hat den 14 vorgeschriebenen Tests die folgenden 11 Tests hinzugefügt:

# Weitere Grenzfälle (Stufenkanten/Rundungen)
TESTS += [
    # 10 %-Abzug: Untergrenze und Obergrenze
    ((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

    # Abschlag: knapp unterhalb / oberhalb der Schwellenwerte
    ((1, 0, 25000), None),  # diagnostique
    ((2, 0, 35000), None),  # diagnostique

    # 20 % Ermäßigung: Vollanspruch vs. Begrenzung
    ((1, 0, 17000), None),  # diagnostique
    ((2, 0, 34000), None),  # diagnostique
    ((1, 0, 20000), None),  # diagnostique
    ((2, 0, 40000), None),  # diagnostique

    # Änderung der Anteile (QF-Obergrenze)
    ((2, 1, 80000), None),
    ((2, 2, 80000), None),
    ((2, 3, 80000), None),
]

Es gibt jetzt 25 Unit-Tests. Ich ai überprüfe die 11 neuen Tests manuell mit dem offiziellen Simulator der DGIP und es ist in Ordnung.

Jetzt geht es weiter mit Gemini. Das wird viel komplizierter sein. Es wird ihm gelingen, einen script zu erzeugen, der die 25 Tests von ChatGPT besteht, aber erst nach einer langen Fehlersuche.

 

Nachfolgend die Debugging-Liste :

 

Seltsamerweise fiel eine Mehrheit der Tests durch, selbst unter den 14 vorgeschriebenen, obwohl Gemini in der Vergangenheit Code generiert hatte, der alle Tests bestanden hatte.

Die folgende Antwort von Gemini ist immer noch nicht korrekt:

 

Auch die folgende Antwort nicht:

 

Die nächste Antwort auch nicht. Daraufhin ändere ich meine Vorgehensweise. Ich bitte ihn, die 25 Tests zu bestehen, die ChatGPT bestanden hat, und lege ihm die Logs von ChatGPT bei:

 

Gemini schlägt fehl. Er hat die Tests von ChatGPT richtig hinzugefügt. Ich füge ihm die Protokolle seiner Ausführung bei :

 

Immer noch nicht :

 

Immer noch nicht :

 

Immer noch nicht :

 

Immer noch nicht, aber besser :

 

Gemini macht neue Fehler :

 

Er verbessert sich wieder :

 

Diesmal:

Unbestreitbar war ChatGPT in diesem konkreten Beispiel der Steuerberechnung für 2019 mit den in der Anweisungsdatei platzierten Beschränkungen relevanter als Gemini. Aber das ist nur ein Beispiel.

Wir können noch einen Schritt weiter gehen. Wir können Gemini bitten, einen PDF nach den Rechenregeln zu regenerieren, die er verwendet hat, um die 25 Tests zu bestehen. Wir wollen sehen, ob er seine ursprüngliche Argumentation zu den Berechnungen des Abschlags und der 20%igen Ermäßigung geändert hat :

Diesmal erzeugte Gemini eine MarkDown-Datei, die ich ai anschließend in PDF [Das Problem laut Gemini Version 2] umwandelte. Und Gemini hat tatsächlich seine Argumentation geändert:

 
 

Es ist zu erkennen, dass es die besondere Berechnung des Abschlags und die Regel des Abfischens nicht mehr gibt. Gemini hat nun die Argumentation von ChatGPT übernommen.