Skip to content

5. Вирішення трьох проблем за допомогою ChatGPT

5.1. Вступ

Ось перший знімок екрана сеансу ChatGPT:

 
  • У [1-3] — три проблеми, поставлені в ChatGPT;
  • У [4] — URL із ChatGPT;
  • У [5] — версія, що використовується в ChatGPT;

ChatGPT є похідним від OpenAI, доступним у URL та [https://chatgpt.com/]. Щоб отримати історію ваших сесій запитань/відповідей, як показано вище, вам потрібно створити обліковий запис. Крім того, як і всі інші протестовані IA, ChatGPT обмежує кількість ваших запитань та кількість завантажених файлів. Коли цей ліміт вичерпано, сесія завершується, і вам пропонується продовжити її пізніше. Обмеження, встановлені ChatGPT, досягаються дуже швидко. Щоб підготувати цей посібник, мені довелося оформити платну підписку на один місяць.

Інтерфейс ChatGPT виглядає так:

 
  • У [1], щоб додати файли до поставленого запитання;
  • У [2] — поставлене запитання;
  • У [3] — для запуску виконання IA;

5.2. Задача 1

Запитання у файлі ChatGPT:

  
 

ChatGPT відповідає правильно.

5.3. Завдання 2

Це розрахунок податку за допомогою PDF. Чесно кажучи, ми будемо використовувати PDF, згенерований Gemini, який виправляє помилки початкового PDF.

 
  • У файлі [1] ми вказали файл PDF, згенерований Gemini;
  • У файлі [2] було додано модульний тест, за допомогою якого Gemini продемонстрував свою перевагу:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Запускаємо ChatGPT. На генерацію відповіді йому потрібно приблизно 3 хвилини. На відміну від Gemini, він надає дійсне посилання для завантаження згенерованого скрипта. Завантажуємо його в PyCharm:

 

Скрипт [chatGPT1] працює з першого разу. Тут навіть немає про що говорити: у вирішенні цієї проблеми ChatGPT виявився ефективнішим за Gemini.

Скрипт [chatGPT1], наданий ChatGPT, має такий вигляд:

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

# ---------- Нормативні константи ----------

# Відрахування на професійні витрати (мінімальний/максимальний розмір) для доходів 2018 року
ABATTEMENT_TAUX = 0.10
ABATTEMENT_MIN = 437
ABATTEMENT_MAX = 12502

# Шкала 2018 року (QF)
# Кожна ставка становить (borne_sup, ставка). Перша ставка 0 % закінчується на рівні 9 964 €.
BAREME_2018 = [
    (9964, 0.00),
    (27519, 0.14),
    (73779, 0.30),
    (156244, 0.41),
    (float("inf"), 0.45),
]

# Максимальний розмір сімейного коефіцієнта
PF_QF_AVANTAGE_PAR_DEMI_PART = 1551  # максимальна пільга на 1/2 частки ENFANT

# Знижка (пороги залежно від кількості дорослих / сімейного стану)
DECOTE_SEUIL_CELIB = 1196
DECOTE_SEUIL_COUPLE = 1970

# Знижка 20 %: порогові значення залежно від часток
REDUCTION_BASE_1_PART = 18985
REDUCTION_BASE_2_PARTS = 37970
REDUCTION_PAR_DEMI_PART_SUP = 3803  # додаток за кожну 1/2 частки понад базову кількість у 1 або 2 частки


# ---------- Структури даних ----------

@dataclass
class ResultatImpot:
    impot: int  # остаточний податок до сплати, округлений до найближчого євро в меншу сторону
    decote: int  # знижка, округлена до вищого євро
    reduction: int  # знижка 20 %, округлена до вищого євро


# ---------- Допоміжні функції ----------

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


# ---------- Етап 1: Чистий оподатковуваний дохід (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)))


# ---------- Етап 2: Кількість часток ----------

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
     # дітей >= 2
    parts += 1.0  # +0,5 +0,5 за перших двох
    if enfants > 2:
        parts += (enfants - 2) * 1.0  # +1 на кожну дитину, починаючи з третьої
    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 частки
     # Двоє перших дітей = 2 * півчастини, потім +2 півчастини на кожну дитину, починаючи з третьої
    return 2 + (enfants - 2) * 2


# ---------- Етап 3: Валовий податок за допомогою 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


# ---------- Етап 4.1: Обмеження сімейного коефіцієнта ----------

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


# ---------- Етап 4.2: Знижка та зменшення на 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:
    """
    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

     # Право на 20 % знижку на основі RNI
    seuil_red = seuil_reduction_20(rni, adultes, parts)
    eligible_reduction = rni < seuil_red  # «нижче порогового значення» у PDF

     # Право на знижку: безпосередньо нижче порогового значення або додаткова можливість, якщо знижка призведе до опускання нижче порогового значення
    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  # теоретичне зниження на 20 %
        if imp_theorique_apres_red <= s_dec:
            repechage = True

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

     # Спочатку застосувати знижку (якщо є право на неї)
    if elig_decote:
        montant_decote = s_dec - (imp * 0.75)
        decote_calc = max(0, arrondi_sup(montant_decote))
         # Знижка не може перевищувати залишок податку: її обмежують
        decote = min(decote_calc, arrondi_sup(imp))
        imp = max(0.0, imp - decote)

     # Потім застосувати знижку, якщо вона передбачена
    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 основна ----------

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) Частки
    parts = nombre_parts(adultes, enfants)

     # 3) Валовий податок за шкалою (з урахуванням усіх часткок)
    imp_brut_A = impot_brut_total(rni, parts)

     # 4.1) Обмеження сімейного коефіцієнта
    imp_apres_plaf = apply_plafonnement_qf(imp_brut_A, rni, adultes, enfants)

     # 4.2) Знижка, а потім зменшення на 20 % (з перерахунком)
    decote, reduction, imp_corrige = calc_decote_reduction(imp_apres_plaf, rni, adultes, parts)

     # 5) Остаточне округлення податку (↓) та обмеження до 0
    imp_final = max(0, arrondi_inf(imp_corrige))

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


# ---------- Невелика тестова програма ----------

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 = [
         # (дорослі, діти, доходи) -> (податок, знижка, зменшення)
        ((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. Проблема 3

Тепер ми просимо ChatGPT знайти в Інтернеті правила розрахунку податку:

 

Цього разу ми не надаємо PDF, який містив правила розрахунку, яких слід дотримуватися. Ми лише надаємо наші інструкції у текстовому файлі. Нагадаємо, що цей текстовий файл тепер містить 12 одиничних тестів після того, як до 11 початкових тестів було додано той, який використовував Gemini, щоб продемонструвати, що мій початковий PDF був помилковим.

ChatGPT відповідає за 8 хвилин, надає посилання для завантаження згенерованого скрипта. Після завантаження в PyCharm цей скрипт проходить усі 12 тестів. Отже, на обидві поставлені задачі ChatGPT відповів правильно з першої спроби, тим самим перевершивши Gemini.

ChatGPT наводить свої джерела у своїй відповіді:

 

Немає що додати, це чудова робота.

Тепер можна попросити його, як ми це робили з Gemini, згенерувати PDF для студентів.

 

Відповідь ChatGPT було отримано після кількох спроб, оскільки у згенерованому PDF використовувався шрифт, який замінював символи на квадратики. Але врешті-решт він згенерував PDF. Я наводжу його, оскільки він дає правила, що відрізняються від PDF від Gemini, і я замислився, хто ж має рацію. Давайте розберемося.

 
 
 

Відмінність від PDF від Gemini полягає в розрахунку знижки. Ці два IA мають різний підхід. Gemini написав:

 
 
 

Ці два IA мають два різних підходи. Хто має рацію?

5.5. Задача 4

Попросимо ChatGPT скористатися своїм PDF для розрахунку податку:

Як і раніше, він генерує скрипт на Python, який працює з першого разу. Ми додали до інструкцій додатковий тест:

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

Усі 13 тестів було успішно пройдено.

5.6. Повернення до Gemini

Тепер повернемося до Gemini, якому ми представимо PDF з ChatGPT. Оскільки правила, реалізовані в цьому PDF, відрізняються від тих, що реалізовані в PDF Gemini, можна задатися питанням, що ж станеться:

 

Спочатку Gemini згенерував скрипт на Python, який не пройшов тестування. Йому надали логи:

Питання 2

 

Питання 3

Помилки все ще залишаються. Продовжуємо.

 

Питання 4

Все ще є помилки під час виконання:

 

Цього разу все гаразд.

Проте нас все ж цікавить, чому при PDF, що мають досить різні правила обчислення, обидва IA дають правильні результати.

Ми задаємо Gemini таке запитання:

 

Повне формулювання питання таке:

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.

Відповідь Gemini є категоричною:

 
 
 
 
 
 

5.7. Що про це думає ChatGPT

Ми задаємо ChatGPT те саме питання, що й Gemini.

 

Відповідь ChatGPT така:

 
 

Отже, ChatGPT пропонує нам модульний тест, щоб визначити, який із двох методів кращий. Ми дублюємо:

  • Скрипт [gemini3], згенерований Gemini на основі його PDF [Le problème selon Gemini], дублюється у скрипті [gemini4];
  • Скрипт [chatGPT3], згенерований ChatGPT на основі PDF та [Le problème selon ChatGPT], дублюється у скрипті [chatGPT4];

Крім того, до кожного зі скриптів [gemini4, chatGPT4] додається модульний тест, запропонований ChatGPT, щоб розрізнити два IA.

Виконання [gemini4] дає такі результати:


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

Отже, Gemini не пройшов тест, доданий ChatGPT.

Виконання [chatGPT4] дає такі результати:


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 також не пройшов доданий тест, але не з тих самих причин, що й Gemini. ChatGPT знайшов правильні результати, але з похибкою у 2 євро замість встановленого 1 євро.

Отже, відтепер ми будемо використовувати PDF, згенерований ChatGPT, разом із наступними IA. Слід зазначити, що саме через відсутність модульних тестів у моїх інструкціях обидва IA успішно пройшли перші тести. Звідси в цьому конкретному прикладі випливає важливість додавання модульних тестів для граничних випадків розрахунку податку. Адже самостійно придумати такі тести досить складно. Ми попросимо IA самостійно додати їх.

5.8. Завдання 3 з модульними тестами, згенерованими IA

Результати, отримані за допомогою Gemini та ChatGPT, викликають сумніви. Чи знайшли IA загальне рішення, яке проходить усі можливі тести, чи вони знайшли рішення, яке проходить лише ті тести, що були задані? Ми повернемося до рішення без PDF, щоб змусити IA звернутися до Інтернету та знайти необхідну інформацію. І ми змінюємо наші інструкції наступним чином:

 

Текстовий файл [instructionsSansPDF4.txt] вже містить 14 обов’язкових тестів. До цих тестів додаємо такі інструкції:


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.

# =========================
# Одиничні тести (допуск ±1 €)
# =========================

TESTS = [
     # (дорослі, діти, доходи) -> (податок, знижка, пільга)
    ((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()
  • Рядки 11–24 — 14 заданих тестів;
  • Рядки 5–55: цей код походить зі скрипта, згенерованого ChatGPT. Ми змусимо Gemini використовувати цей код, щоб полегшити порівняння між двома згенерованими скриптами.

Почнемо з ChatGPT:

 

Його перша відповідь є неправильною. Я повідомляю йому про це, надаючи логи виконання:

Його друга відповідь правильна. ChatGPT додав наступні 11 тестів до 14 обов’язкових:

# Додаткові граничні випадки (межі ступенів/округлення)
TESTS += [
     # Знижка 10 %: мінімальний та максимальний поріг
    ((1, 0, 3000), (0, 0, 0)),  # 10 % = 300 < мінімальний поріг 437 => RNI низький -> податок дорівнює нулю
    ((1, 0, 200000), (64211, 0, 0)),  # Верхня межа знижки вже врахована в початкових тестах

     # Знижка: трохи нижче / вище порогових значень
    ((1, 0, 25000), None),  # діагностика
    ((2, 0, 35000), None),  # діагностика

     # Знижка 20 %: повне право проти обмеження
    ((1, 0, 17000), None),  # діагностика
    ((2, 0, 34000), None),  # діагностика
    ((1, 0, 20000), None),  # діагностика
    ((2, 0, 40000), None),  # діагностика

     # Зміна частки (обмеження QF)
    ((2, 1, 80000), None),
    ((2, 2, 80000), None),
    ((2, 3, 80000), None),
]

Тепер є 25 модульних тестів. Я вручну перевірив 11 нових тестів за допомогою офіційного симулятора DGIP, і все гаразд.

Тепер переходимо до Gemini. Це буде набагато складніше. Йому вдасться згенерувати скрипт, який пройде 25 тестів ChatGPT, але лише після тривалого налагодження.

 

Нижче наведено список результатів налагодження:

 

Дивно, але більшість тестів не пройшла навіть серед тих 14, що були задані, хоча раніше Gemini генерував код, який проходив їх усі.

Наступна відповідь Gemini все ще не є правильною:

 

Наступна відповідь теж неправильна:

 

Наступна відповідь теж неправильна. Тому я змінюю тактику. Я прошу його пройти ті 25 тестів, які пройшов ChatGPT, додавши до запиту логи ChatGPT:

 

Gemini зазнав невдачі. Він дійсно додав тести від ChatGPT. Я додаю йому логи їх виконання:

 

Все ще не працює:

 

Все ще не працює:

 

Все ще немає:

 

Все ще ні, але вже краще:

 

Gemini робить нові помилки:

 

Знову покращується:

 

Цього разу все гаразд:

Безперечно, у цьому конкретному прикладі розрахунку податку за 2019 рік з урахуванням обмежень, заданих у файлі інструкцій, ChatGPT виявився точнішим за Gemini. Але це лише приклад.

Можна піти далі. Можна попросити Gemini перегенерувати PDF відповідно до правил розрахунку, які він використовував для успішного проходження 25 тестів. Ми хочемо перевірити, чи змінив він своє початкове міркування щодо розрахунку знижки та зменшення на 20 %:

Цього разу Gemini згенерував файл MarkDown, який я потім перетворив на PDF [Le problème selon Gemini version 2]. І Gemini дійсно змінив свій підхід:

 
 

Можна помітити, що більше немає ні окремого розрахунку знижки, ні правила перерозподілу. Gemini тепер використовує логіку ChatGPT.