Skip to content

4. Rozwiązanie trzech problemów za pomocą Google Gemini

Przedstawimy zrzuty ekranu z trzech sesji Gemini, które pozwoliły rozwiązać trzy postawione problemy. Omówimy je dość szczegółowo. Po wykonaniu tej czynności nie będziemy powtarzać procesu dla pozostałych testowanych IA. Działają one w podobny sposób. Podamy jedynie najważniejsze szczegóły.

4.1. Wprowadzenie

Przypominamy pierwszy zrzut ekranu z Gemini przedstawiony wcześniej:

 
  • W [1], URL z Gemini;
  • W pliku [2] – wersja programu Gemini, z której korzystano;
  • W [3-5] – trzy zadania postawione Gemini;

Gemini to produkt firmy Google dostępny pod adresem URL [https://gemini.google.com/]. Aby uzyskać historię sesji pytań i odpowiedzi, taką jak powyżej, należy założyć konto. Ponadto, podobnie jak wszystkie inne przetestowane IA, Gemini ogranicza liczbę zadawanych pytań oraz liczbę pobranych plików. Po osiągnięciu tego limitu sesja zostaje zakończona i pojawia się propozycja jej kontynuowania w późniejszym terminie. Ponieważ przerywanie sesji w połowie jest dość frustrujące, wykupiłem subskrypcję. Na szczęście pierwszy miesiąc subskrypcji Gemini jest bezpłatny. Postąpiłem tak samo w przypadku innych narzędzi IA, które miały te ograniczenia, a mianowicie ChatGPT, MistralAI, ClaudeAI. Wykupiłem miesięczną subskrypcję, ale w tym przypadku pierwszy miesiąc był płatny. W przypadku Grok nie napotkałem żadnych ograniczeń. DeepSeek nie informuje o żadnych ograniczeniach, ale czasami odpowiada [Server busy] i przerywa sesję. Sprowadza się to do nakładania ograniczeń bez informowania o tym.

W dalszej części będę mówił o sesjach pytań i odpowiedzi, nazywanych dalej po prostu sesjami. IA najczęściej używa angielskiego terminu „chat” (rozmowa) lub „conversation” (rozmowa).

Interfejs serwisu Gemini służący do zadawania pytań wygląda następująco:

  • W [1] – Twoje pytanie;
  • W polu [2] znajduje się ikona uruchamiająca IA w celu obliczenia odpowiedzi;
  • W [3-4] można załączać pliki;

4.2. Zadanie 1

Sesja dotycząca zadania 1 wygląda następująco:

 
  • W pliku [1] znajduje się pytanie;
  • W pliku [2] znajduje się początek odpowiedzi Gemini;

Dalsza część odpowiedzi wygląda następująco:

 
 
 
 

Odpowiedź jest poprawna. Pozostałe pięć ciągów znaków IA również da prawidłową odpowiedź w podobnej formie.

4.3. Zadanie 2

4.3.1. Wprowadzenie

Przypomnijmy tutaj początkowe zadanie z kursu [python3-flask-2020]. Jest to tekst przekazany studentom w formacie TD.

 

Powyższa tabela pozwala obliczyć podatek w uproszczonym przypadku podatnika, który ma do zadeklarowania wyłącznie swoje wynagrodzenie. Jak wskazuje uwaga (1), tak obliczony podatek jest podatkiem przed zastosowaniem trzech mechanizmów:

  • ograniczenia kwoty ulgi rodzinnej, które ma zastosowanie w przypadku wysokich dochodów;
  • ulgi i obniżki podatkowe, które mają zastosowanie w przypadku niskich dochodów;

W związku z tym obliczenie podatku obejmuje następujące etapy [http://impotsurlerevenu.org/comprendre-le-calcul-de-l-impot/1217-calcul-de-l-impot-2019.php]:

 

Proponujemy napisanie programu umożliwiającego obliczenie podatku podatnika w 2019 r. w uproszczonym przypadku, gdy podatnik ma do zadeklarowania wyłącznie swoje wynagrodzenie.

4.3.1.1. Obliczanie podatku brutto

Podatek brutto można obliczyć w następujący sposób:

Najpierw oblicza się liczbę udziałów podatnika:

  • Każdy z rodziców wnosi 1 część;
  • Dwoje pierwszych dzieci wnosi po 1/2 części;
  • Kolejne dzieci wnoszą po jednej części:

Liczba udziałów wynosi zatem:

  • nbParts=1+nbEnfants*0,5+(liczbaDzieci-2)*0,5, jeśli pracownik nie jest żonaty;
  • nbParts = 2 + nbEnfants * 0,5 + (nbEnfants – 2) * 0,5, jeśli jest żonaty;
  • gdzie nbEnfants oznacza liczbę dzieci;
  • Oblicza się dochód podlegający opodatkowaniu R = 0,9 * S, gdzie S to roczne wynagrodzenie;
  • Oblicza się współczynnik rodzinny QF = R / nbParts;
  • Podatek brutto I oblicza się na podstawie następujących danych (2019):
9964
0
0
27519
0,14
1394,96
73779
0,3
5798
156 244
0,4
13913,69
0
0,45
20163,45

Każdy wiersz zawiera 3 pola: champ1, champ2, champ3. Aby obliczyć podatek I, szukamy pierwszego wiersza, w którym QF ≤ pole1, i pobieramy wartości z tego wiersza. Na przykład dla pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:

Dochód podlegający opodatkowaniu: R = 0,9 * S = 45 000

Liczba udziałów: nbParts = 2 + 2 * 0,5 = 3

Współczynnik rodzinny: QF = 45 000 / 3 = 15 000

Pierwszy wiersz, w którym QF <= pole1, wygląda następująco:

    27519    0.14    1394.96

Podatek I wynosi zatem 0,14*R – 1394,96*nbParts=[0,14*45000-1394,96*3]=2115. Podatek jest zaokrąglany w dół do najbliższego euro.

Jeśli już w pierwszym wierszu zachodzi relacja QF <= pole1, to podatek wynosi zero.

Jeśli QF jest takie, że relacja QF<=pole1 nigdy nie jest spełniona, wówczas stosuje się współczynniki z ostatniego wiersza. W tym przypadku:

    0    0.45    20163.45

co daje podatek brutto I = 0,45*R – 20163,45*nbParts.

4.3.1.2. Ograniczenie ilorazu rodzinnego

 

Aby sprawdzić, czy ma zastosowanie limit ilorazu rodzinnego QF, ponownie obliczamy podatek brutto bez uwzględnienia dzieci. Nadal dla pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:

Dochód podlegający opodatkowaniu: R = 0,9 * S = 45 000

Liczba udziałów: nbParts=2 (nie uwzględnia się już dzieci)

Współczynnik rodzinny: QF = 45 000 / 2 = 22 500

Pierwszy wiersz, w którym QF <= pole1, wygląda następująco:

    27519    0.14    1394.96

Podatek I wynosi zatem 0,14*R – 1394,96*nbParts = [0,14*45000-1394,96*2] = 3510.

Maksymalna ulga na dzieci: 1551 * 2 = 3102 euro

Podatek minimalny: 3510 – 3102 = 408 euro

Podatek brutto z uwzględnieniem 2 udziałów, obliczony już w poprzednim akapicie (2115 euro), jest wyższy od podatku minimalnego (408 euro), więc limit rodzinny nie ma tutaj zastosowania.

Ogólnie rzecz biorąc, podatek brutto jest większy niż (podatek1, podatek2), gdzie:

  • [impôt1]: to podatek brutto obliczony z uwzględnieniem dzieci;
  • [impôt2]: to podatek brutto obliczony bez uwzględnienia dzieci i pomniejszony o maksymalną ulgę (w tym przypadku 1551 euro na pół części) związaną z dziećmi;

4.3.1.3. Obliczanie ulgi

 

Wciąż w przypadku pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:

Podatek brutto (2115 euro) uzyskany w poprzednim etapie jest niższy niż 2627 euro dla pary (1595 euro dla osoby samotnej): w związku z tym ma zastosowanie ulga podatkowa. Oblicza się ją w następujący sposób:

ulga = próg (dla pary = 1970 / dla osoby samotnej = 1196) – 0,75 * podatek brutto

ulga = 1970 – 0,75 * 2115 = 383,75, zaokrąglone do 384 euro.

Nowy podatek brutto = 2115 – 384 = 1731 euro

Przy obliczaniu obniżki należy przestrzegać dwóch zasad (niektóre narzędzia IA napotkały trudności w tej kwestii):

  • Odliczenie nie może być ujemne;
  • Odliczenie nie może być wyższe niż już obliczony podatek;

4.3.1.4. Obliczanie ulgi podatkowej

 

Poniżej określonego progu od podatku brutto wynikającego z poprzednich obliczeń przyznaje się ulgę w wysokości 20%. W 2019 r. progi te wynoszą:

  • osoba samotna: 21 037 euro;
  • para: 42 074 euro; (liczba 37 968 użyta w powyższym przykładzie wydaje się błędna);

Próg ten zwiększa się o wartość: 3797 * (liczba pół-udziałów przypisanych dzieciom).

Wciąż w przypadku pracownika pozostającego w związku małżeńskim, mającego dwoje dzieci i roczne wynagrodzenie S w wysokości 50 000 euro:

  • Jego dochód podlegający opodatkowaniu (45 000 euro) jest niższy od progu (42 074 + 2 * 3797) = 49 668 euro;
  • Ma zatem prawo do 20-procentowej ulgi podatkowej: 1731 * 0,2 = 346,2 euro, zaokrąglone do 347 euro;
  • Podatek brutto podatnika wynosi: 1731 – 347 = 1384 euro;

4.3.1.5. Obliczenie podatku netto

Na tym zakończymy nasze obliczenia: podatek netto do zapłaty wyniesie 1384 euro. W rzeczywistości podatnik może skorzystać z innych ulg, w szczególności z tytułu darowizn na rzecz organizacji pożytku publicznego lub ogólnego.

4.3.1.6. Przypadek osób o wysokich dochodach

Nasz poprzedni przykład dotyczy większości pracowników. Jednak w przypadku osób o wysokich dochodach obliczenie podatku przebiega inaczej.

4.3.1.6.1. Ograniczenie 10-procentowej ulgi od rocznych dochodów

W większości przypadków dochód podlegający opodatkowaniu oblicza się według wzoru: R = 0,9 × S, gdzie S oznacza roczne wynagrodzenie. Nazywa się to 10-procentową ulgą. Ulga ta podlega ograniczeniu. W 2019 r.:

  • nie może przekraczać 12 502 euro;
  • nie może być niższa niż 437 euro;

Weźmy na przykład pracownika niebędącego w związku małżeńskim, bezdzietnego, o rocznym wynagrodzeniu w wysokości 200 000 euro:

  • 10-procentowa redukcja wynosi 200 000 euro > 12 502 euro. Zostaje zatem ograniczona do 12 502 euro;

4.3.1.6.2. Ograniczenie ilorazu rodzinnego

Rozważmy przypadek, w którym ma zastosowanie limit rodzinny przedstawiony w paragrafie |Plafonnement du quotient familial|. Weźmy pod uwagę parę z trójką dzieci i rocznym dochodem w wysokości 100 000 euro. Przeanalizujmy kolejne etapy obliczeń:

  • Odliczenie w wysokości 10% wynosi 100 000 euro < 12 502 euro. Dochód podlegający opodatkowaniu R wynosi zatem 100 000 – 10 000 = 90 000 euro;
  • Para posiada nbParts = 2 + 0,5 × 2 + 1 = 4 części;
  • Ich współczynnik rodzinny wynosi zatem QF = R / nbParts = 90 000 / 4 = 22 500 euro;
  • Ich podatek brutto I1 z uwzględnieniem dzieci wynosi I1 = 0,14 × 90 000 – 1 394,96 × 4 = 7 020 euro;
  • Jego podatek brutto I2 bez dzieci wynosi:
    • QF = 90 000 / 2 = 45 000 euro;
    • I2 = 0,3 × 90 000 – 5 798 × 2 = 15 404 euro;
  • Zgodnie z zasadą ograniczenia ilorazu rodzinnego korzyść wynikająca z posiadania dzieci nie może przekroczyć (1551 × 4 półczęści) = 6204 euro. W tym przypadku wynosi ona jednak I2-I1 = 15404 – 7020 = 8384 euro, a więc jest wyższa niż 6204 euro;
  • Podatek brutto jest zatem przeliczany jako I3 = I2-6204 = 15 404 – 6 204 = 9 200 euro;
  • Ponieważ I3 > I1, zachowany zostanie podatek I3;

Para ta nie otrzyma ani ulgi, ani obniżki, a jej ostateczny podatek wyniesie 9200 euro.

4.3.1.7. Oficjalne dane

Obliczanie podatku jest skomplikowane. W całym dokumencie analizy będą przeprowadzane na podstawie poniższych przykładów. Wyniki pochodzą z symulatora urzędu skarbowego |https://www3.impots.gouv.fr/simulateur/calcul_impot/2019/simplifie/index.htm|:

Podatnik
Oficjalne wyniki
Wyniki algorytmu przedstawionego w dokumencie
Para z dwójką dzieci i rocznym dochodem w wysokości 55 555 euro
Podatek = 2815 euro
Stawka podatkowa = 14 %
Podatek = 2814 euro
Stawka podatkowa = 14 %
Para z dwójką dzieci i rocznym dochodem w wysokości 50 000 euro
Podatek = 1385 euro
Ulga = 384 euro
Ulga = 346 euro
Stawka podatkowa = 14 %
Podatek = 1384 euro
Odliczenie = 384 euro
Ulga = 347 euro
Stawka podatkowa = 14 %
Para z trójką dzieci i rocznym dochodem w wysokości 50 000 euro
Podatek = 0 euro
Odliczenie = 720 euro
Ulga = 0 euro
Stawka podatkowa = 14 %
Podatek = 0 euro
odliczenie = 720 euro
Ulga = 0 euro
Stawka podatkowa = 14 %
Osoba samotna z dwójką dzieci i rocznym dochodem w wysokości 100 000 euro
Podatek = 19 884 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Podatek = 19 884 euro
Dodatek = 4480 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Osoba samotna z trójką dzieci i rocznym dochodem w wysokości 100 000 euro
Podatek = 16 782 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Podatek = 16 782 euro
Dodatek = 7176 euro
obniżka = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Para z trójką dzieci i rocznym dochodem w wysokości 100 000 euro
Podatek = 9200 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 30 %
Podatek = 9200 euro
Dodatek = 2180 euro
obniżka = 0 euro
Ulga = 0 euro
Stawka podatkowa = 30 %
Para z pięciorgiem dzieci i rocznym dochodem w wysokości 100 000 euro
Podatek = 4230 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 14 %
Podatek = 4230 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 14 %
Osoba samotna bez dzieci, z rocznym dochodem w wysokości 100 000 euro
Podatek = 22 986 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Podatek = 22 986 euro
Dodatek = 0 euro
obniżka = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %
Para z dwójką dzieci i rocznym dochodem w wysokości 30 000 euro
Podatek = 0 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 0 %
Podatek = 0 euro
odliczenie = 0 euro
Ulga = 0 euro
Stawka podatkowa = 0 %
Osoba samotna bez dzieci, z rocznym dochodem w wysokości 200 000 euro
Podatek = 64 211 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 45 %
Podatek = 64 210 euro
Dodatek = 7498 euro
obniżka = 0 euro
Ulga = 0 euro
Stawka podatkowa = 45 %
Para z trójką dzieci i rocznym dochodem w wysokości 200 000 euro
Podatek = 42 843 euro
ulga = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41%
Podatek = 42 842 euro
Dodatek = 17 283 euro
obniżka = 0 euro
Ulga = 0 euro
Stawka podatkowa = 41 %

W powyższym przykładzie jako dopłatę określa się kwotę, którą osoby o wysokich dochodach płacą dodatkowo z powodu dwóch czynników:

  • ograniczenie ulgi podatkowej w wysokości 10% od rocznych dochodów;
  • ograniczenie wysokości ilorazu rodzinnego;

Wskaźnika tego nie udało się zweryfikować, ponieważ symulator urzędu skarbowego go nie podaje.

Widać, że algorytm opisany w dokumencie za każdym razem oblicza prawidłową kwotę podatku, choć z marginesem błędu wynoszącym 1 euro. Margines ten wynika z zaokrągleń. Wszystkie kwoty pieniężne są czasami zaokrąglane w górę do pełnego euro, a czasami w dół. Ponieważ nie znałem oficjalnych zasad, kwoty w algorytmie zawartym w dokumencie zostały zaokrąglone:

  • w górę do pełnego euro w przypadku zniżek i ulg;
  • W dół o 1 euro w przypadku dopłat i podatku końcowego;

Poprosimy IA o wykonanie tego obliczenia podatku.

4.3.2. Konfiguracja sesji Gemini

Do zapytania skierowanego do Gemini dołączone są dwa pliki:

 
  • W pliku [1] obliczenia, które właśnie zostały szczegółowo opisane, zostały umieszczone w pliku PDF, który jest przekazywany do Gemini. Gemini znajdzie w nim dokładne zasady uproszczonego obliczania podatku za rok 2019 od dochodów za rok 2018;
  • W pliku [2] znajdują się nasze instrukcje;
  • W pliku [3] znajdują się instrukcje dotyczące uruchomienia pliku IA;

Nasze instrukcje w pliku tekstowym [instructionsAvecPDF.txt] są następujące:

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/symulator/calcul_impot/2019/uproszczenie/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é.

Instrukcje te są wynikiem bardzo wielu pytań zadanych Gemini. Dość szybko zdajemy sobie sprawę, że plik IA musi być bardzo precyzyjny, jeśli chcemy uzyskać to, czego oczekujemy. To właśnie z powodu tych wszystkich prób i błędów sesja Gemini została ostatecznie przerwana z powodu przekroczenia limitów. Przyjrzyjmy się dalszej części tych instrukcji:

  • Wiersz 1: prosimy, aby rozmowa odbywała się w języku francuskim. Ta wskazówka dotyczy DeepSeek, który miał tendencję do mówienia po angielsku;
  • Wiersz 3: to, czego oczekujemy;
  • Wiersz 5: nakazujemy IA, aby użył PDF, który mu przekazaliśmy;
  • Wiersze 7–14: kilka przydatnych wskazówek, zwłaszcza dotyczących zadania 3 bez PDF. Kilka IA zaginęło podczas obliczania podatku;
  • Wiersze 15–44: 11 testów jednostkowych, które chcemy uwzględnić w wygenerowanym skrypcie. Po wygenerowaniu skryptu uruchomimy go w PyCharm i sprawdzimy, czy wszystkie 11 testów zakończy się powodzeniem;
  • Wiersze 46–53: bez tych instrukcji skrypt IA generowałby testy jednostkowe sprawdzające dokładne wyniki, które kończyłyby się niepowodzeniem;
  • Wiersze 55–56: nakazuję IA, aby nie łączył się z Internetem. Najprostszym rozwiązaniem jest użycie PDF;
  • Wiersze 58–59: ta instrukcja nie została wykonana przez IA. Byłem zmuszony zapisać ją wyraźnie w pytaniu, gdy zauważyłem, że test zakończył się niepowodzeniem;
  • Wiersze 61–65: wskazuję, jakiego rodzaju skrypt w języku Python chcę uzyskać;
  • Wiersze 67–69: wolałbym otrzymać link do pobrania wygenerowanego skryptu, ponieważ wyświetlanie kodu na ekranie zajmuje dużo czasu. Okazało się, że większość IA nie potrafi tego zrobić. Podane linki nie działały;
  • Wiersze 71–72: Chciałbym poznać czas, jaki IA poświęcił na udzielenie odpowiedzi na pytanie. Jedynie Gemini był w stanie podać mi tę informację. Pozostałe modele IA albo nie reagowały na tę instrukcję, albo podawały zmyślone liczby, co wskazuje, że nie zrozumiały tej instrukcji;

4.3.3. Odpowiedź Gemini

Pierwsza odpowiedź Gemini brzmi następująco:

 
  • W [1-4] Gemini podaje linki do części PDF lub pliku tekstowego z instrukcjami, z których korzysta w danym momencie;

Dalej następuje:

 
  • W pliku [1] Gemini potwierdza, że pomyślnie wykonał 11 testów jednostkowych. Większość plików IA potwierdzała to zarówno w przypadku zadania 2, jak i zadania 3, a często po załadowaniu wygenerowanego skryptu okazywało się, że nie działa. Należy więc podchodzić do tego stwierdzenia z rezerwą. W przypadku Gemini okaże się to prawdą;
  • W przypadku [2] link, który okaże się niedziałający;
  • W [3] tylko Gemini podało realistyczny czas wykonania;

Zatem link [2] nie działa. Informujemy o tym Gemini:

 

Odpowiedź Gemini:

 
  • W [1] znajduje się skrypt w języku Python wygenerowany przez Gemini;

Wczytujemy ten skrypt do PyCharm i uruchamiamy go:

 
  • W pliku [1] skrypt [gemini1] został wygenerowany przez Gemini;

Podczas wykonywania skryptu pojawiają się błędy kompilacji:


"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
  • Wiersz 30 – błąd kompilacji. [cite_start] to znacznik służący do wygenerowania określonej formy tekstu;

Umieszczamy powyższe logi w pliku [logs gemini1.txt] i przekazujemy go do Gemini:

 

Odpowiedź Gemini wygląda wówczas następująco:

 

Po załadowaniu do pliku PyCharm wykonanie nowego skryptu generuje dokładnie ten sam błąd. Informujemy o tym Gemini, ponownie dostarczając mu logi z wykonania:

 

Odpowiedź Gemini brzmi następująco:

 

Tym razem wszystko działa poprawnie. Wszystkie 11 testów jednostkowych zakończyło się powodzeniem. Informujemy o tym Gemini:

 

A Gemini odpowiada:

 

Skrypt wygenerowany przez Gemini postępował zgodnie z instrukcjami zawartymi w pliku tekstowym [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 ---
# Wszystkie wartości pochodzą z dostarczonego dokumentu PDF.

# Limity dotyczące 10-procentowej ulgi od rocznych dochodów
PLAFOND_ABATTEMENT_10_POURCENT = 12502.0
MINIMUM_ABATTEMENT_10_POURCENT = 437.0

# Tabela stawek podatku dochodowego na rok 2019
# Struktura: (maksymalny próg przedziału, stawka podatkowa, kwota odliczenia na osobę)
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}
]

# Limit ilorazu rodzinnego
GAIN_MAX_DEMI_PART = 1551.0  # Maksymalny zysk na każdą dodatkową połowę udziału

# Progi i kwoty uprawniające do obniżki podatku
SEUIL_APPLICATION_DECOTE = {"celibataire": 1595.0, "couple": 2627.0}
SEUIL_CALCUL_DECOTE = {"celibataire": 1196.0, "couple": 1970.0}

# Progi dla 20-procentowej ulgi podatkowej
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
     # Odliczenie jest ograniczone między wartością minimalną a maksymalną
    abattement = min(PLAFOND_ABATTEMENT_10_POURCENT, max(MINIMUM_ABATTEMENT_10_POURCENT, abattement_calcule))

     # Zadanie wymaga zaokrąglenia ulgi w górę do pełnego euro
    abattement_arrondi = math.ceil(abattement)

    return revenus - abattement_arrondi


def calculer_impot_brut_base(revenu_imposable: float, nombre_parts: float) -> float:
    """
    Calcule l'impôt brut selon le barème progressif.
    """
    if nombre_parts <= 0:
        return 0.0

    quotient_familial = revenu_imposable / nombre_parts

    tranche_applicable = None
    for tranche in BAREME_IMPOT_2019:
        if quotient_familial <= tranche["seuil"]:
            tranche_applicable = tranche
            break

     # Jeśli wartość QF przekracza wszystkie progi, stosuje się ostatni przedział
    if tranche_applicable is None:
        tranche_applicable = BAREME_IMPOT_2019[-1]

     # Wzór na obliczenie podatku brutto
    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

     # Obliczanie podatku bez uwzględnienia udziałów dzieci
    parts_sans_enfants = calculer_nombre_parts(adultes, 0)
    impot_sans_enfants = calculer_impot_brut_base(revenu_imposable, parts_sans_enfants)

     # Obliczanie maksymalnego zysku związanego z dziećmi
    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

     # Podatek potrącony jest wyższy z obu obliczeń
    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
     # Obniżka ma zastosowanie, jeśli podatek jest poniżej progu zastosowania
    if impot_avant_decote < seuil_application:
         # Wzór na obniżkę: próg – 0,75 * podatek brutto
        decote_calculee = seuil_calcul - (0.75 * impot_avant_decote)

         # Zaokrąglenie w górę do pełnego euro
        decote = math.ceil(decote_calculee)

         # Obniżka nie może być ujemna ani wyższa od kwoty podatku
        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]

     # Próg jest zwiększany o każdą połowę udziału związaną z dziećmi
    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
     # Ulga ma zastosowanie, jeśli dochód podlegający opodatkowaniu jest poniżej progu
    if revenu_imposable < seuil_final:
        reduction_calculee = impot_avant_reduction * 0.20

         # Zaokrąglenie w górę do pełnego euro
        reduction = math.ceil(reduction_calculee)

    impot_apres_reduction = impot_avant_reduction - reduction
    return max(0.0, impot_apres_reduction), reduction


def calculer_impot(adultes: int, revenus: float, enfants: int) -> dict:
    """
    Orchestre le calcul complet de l'impôt net à payer.
    """
     # Krok 1: Obliczenie dochodu podlegającego opodatkowaniu
    revenu_imposable = calculer_revenu_imposable(revenus)

     # Krok 2: Obliczenie liczby udziałów
    nombre_parts = calculer_nombre_parts(adultes, enfants)

     # Krok 3: Obliczenie podatku brutto
    impot_brut_initial = calculer_impot_brut_base(revenu_imposable, nombre_parts)

     # Krok 4: Zastosowanie limitu QF
    impot_apres_plafonnement = appliquer_plafonnement_qf(impot_brut_initial, revenu_imposable, adultes, enfants)

     # Krok 5: Zastosowanie obniżki
    impot_apres_decote, decote_calculee = appliquer_decote(impot_apres_plafonnement, adultes)

     # Krok 6: Zastosowanie 20-procentowej zniżki
    impot_avant_arrondi_final, reduction_calculee = appliquer_reduction_20_pourcent(impot_apres_decote,
                                                                                    revenu_imposable, adultes, enfants)

     # Krok 7: Ostateczne zaokrąglenie podatku w dół do pełnego euro
    impot_final = math.floor(impot_avant_arrondi_final)

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


# --- TESTS UNITAIRES ---
# Sprawdzenie poprawności skryptu na podstawie podanych przykładów
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)

     # Przykład wykorzystania kalkulatora w konkretnym przypadku
    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']}€")

Nie sprawdziłem tego kodu. Ponieważ wszystkie 11 testów jednostkowych zakończyło się powodzeniem, uważam, że jest on „prawdopodobnie poprawny”. Nie zrobiłem nic więcej w odniesieniu do mojego własnego kodu poza sprawdzeniem tych 11 testów.

4.4. Zadanie 3

Zadanie 3 jest identyczne z zadaniem 2, z tą różnicą, że w pliku IA nie podano już pliku PDF, który zawierał zasady obliczeń, których należy przestrzegać.

Pierwotne zapytanie skierowane do Gemini brzmi następująco:

 

Plik z instrukcjami o nazwie [1] jest niemal identyczny jak w przypadku zadania 2, z następującymi różnicami:


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 :

  • W zadaniu [3] ma on znaleźć w Internecie zasady obliczania podatku za rok 2019 od dochodów uzyskanych w 2018 roku. Jest to zadanie trudniejsze niż poprzednie;

Poniżej przytaczam jedynie fragmenty pierwszej odpowiedzi Gemini:

 
 

Szacowany czas jest wiarygodny. Długo czekamy na odpowiedź Gemini.

Podobnie jak poprzednio, Gemini podało link do pobrania wygenerowanego skryptu, który jednak nie działa. Poinformowaliśmy o tym Gemini:

 

Odpowiedź Gemini:

 

Wgrywamy skrypt do PyCharm pod nazwą [gemini2]:

 

Uruchamiamy go i… nie działa. Oto logi z wykonania:


"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
  • Wiersz 11 – test zakończył się niepowodzeniem;
  • Wiersze 25, 42: ten sam błąd kompilacji;

Umieszczamy te logi w pliku tekstowym, który przekazujemy Gemini:

 

Odpowiedź Gemini:

 
 

Wczytujemy skrypt o nazwie PyCharm i uruchamiamy go. Znowu pojawiają się błędy. Informujemy o tym Gemini, ponownie załączając logi z wykonania:

 

Odpowiedź firmy Gemini:

 
 
 

Wczytujemy ten nowy skrypt do PyCharm i uruchamiamy go. Tym razem wszystkie 11 testów jednostkowych zakończyło się powodzeniem:

Kod wygenerowany przez Gemini wygląda następująco:

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

# ==============================================================================
# CONSTANTES QZXW2HTMLCUE9VUgZQXQZXW2HTMLCSU1QT1NJVElPTgZQX 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: Jeśli podatek wynosi już 0, nie ma żadnej zniżki ani ulgi.
    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. Ustalenie uprawnień do 20-procentowej zniżki
    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. Ustalenie uprawnień do obniżki
    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: Zwracana ulga nie może przekroczyć kwoty podatku, który znosi
        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. Zastosowanie 20-procentowej ulgi, jeśli przysługuje
    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)

Ponownie nie sprawdzałem tego kodu. Po prostu stwierdziłem, że pomyślnie przeszedł wszystkie 11 testów.

Można jednak być ciekawym, na jakiej logice opiera się ten kod, zwłaszcza w konkretnych przypadkach obliczania podatku. Zapytamy o to:

 

Jest to przypadek wysokich dochodów, w którym występuje zarówno możliwe ograniczenie ulgi w wysokości 10%, jak i możliwe ograniczenie współczynnika rodzinnego.

Odpowiedź Gemini brzmi następująco:

 
 
 
 
 

Te dwa ostatnie zrzuty ekranu są interesujące. Gemini stosuje metodę obliczeniową odmienną od tej opisanej w PDF. Metodę tę można rzeczywiście znaleźć w Internecie. Obie metody są równoważne.

 
 
 

Wyjaśnienie to wyróżnia się niezwykłą przejrzystością. Można by je w tej postaci przekazać studentom w celu wyjaśnienia metody obliczania podatku.

Teraz weźmy inny przykład, tym razem dotyczący niskich dochodów. W tym przypadku mogą wystąpić ulgi i obniżki:

 

Odpowiedź Gemini brzmi następująco:

 
 
 
 
 

Widać tutaj, że Gemini stosuje regułę, której nie ma w pliku PDF. Prawdopodobnie znalazł ją w Internecie, ale czy źródło to jest wiarygodne?

 

W tym przypadku Gemini nadal stosuje nieznaną regułę (powyższą regułę specjalną).

 
 
 

Wyniki Gemini są więc zgodne z wynikiem oficjalnego symulatora podatków. Jednak Gemini zastosowało regułę, której nie ma w pliku PDF. Gdzie leży błąd? Zadajemy to pytanie, dołączając plik PDF:

 

Odpowiedź Gemini:

 
 
 
 

Uważam, że Gemini ma rację, a mój kod PDF jest błędny. Aby to sprawdzić, proszę o przeprowadzenie testu:

  • w którym jego obliczenia dałyby takie same wyniki jak oficjalny symulator podatkowy;
  • gdzie rozumowanie oparte na PDF dałoby wyniki inne niż te z symulatora;
 

Odpowiedź Gemini brzmi następująco:

 

W tym przypadku Gemini się myli. Uruchomiłem symulator na tym przykładzie i uzyskałem następujące wyniki:

 

Jednak okaże się, że rozumowanie Gemini rzeczywiście prowadzi do powyższych wyników. Kontynuujmy:

 
 
 
 

Bardzo dobrze. Zanotowano. Kontynuujemy:

 
 
 
 

Zatem Gemini obliczyło (podatek, ulga, obniżka) = (431, 325, 1296), podczas gdy symulator, z którego korzystam, podaje wyniki (431, 324, 1297). Gemini uzyskało więc prawidłowe wyniki z dokładnością do 1 euro, ale nie jest tego świadome. Informujemy je o tym:

 

Gemini odpowiada:

 
 

Teraz zastanawiamy się, czy Gemini mogłoby wygenerować poprawiony kod PDF:

 

Odpowiedź Gemini:

 

Gemini nie podało mi więc linku do pliku PDF, ale wygenerowało tekst, dzięki któremu sam mogłem stworzyć ten plik PDF. Chociaż zamieszczenie tutaj zrzutów ekranu z pliku PDF jest nieco uciążliwe, robię to, aby czytelnik mógł dostrzec generatywny charakter pliku IA:

 

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Image

Szczerze mówiąc, nie sprawdziłem, czy wszystko, co jest napisane w tym pliku PDF, jest prawdą. W każdym razie jest to idealny dokument dla pliku TD, wygenerowany w ciągu kilku sekund.

Można jednak poprosić samego Gemini o sprawdzenie, czy jego plik PDF jest poprawny. Rozpoczynamy nową rozmowę:

 
  • w [1] umieściliśmy plik PDF wygenerowany przez Gemini [Le problème selon Gemini.pdf];
  • w pliku [2] plik [instructionsAvecPDF2.txt] jest identyczny z instrukcjami zawartymi w pliku [instructionsAvecPDF.txt], z tą różnicą, że dodano dwunasty test jednostkowy – ten sam, który wykazał, że początkowy plik PDF był błędny:
test12 : (2, 2, 49500) -> (1297, 431, 324)

Co ciekawe, potrzeba było kilku prób, zanim Gemini wygenerowało poprawny skrypt:

Pytanie 2

 

Pytanie 3

 

Jak już wielokrotnie robiono, gdy skrypt wygenerowany i załadowany do PyCharm nie działa, przekazujemy Gemini plik tekstowy z logami wykonania. Gemini bardzo dobrze je rozumie.

Pytanie 4

 

Pytanie 5

 

Pytanie 6 i koniec

Teraz mamy pewność co do poprawności kodu PDF wygenerowanego przez Gemini. Podane w nim zasady obliczeń są prawidłowe.

Teraz zrobimy to samo dla pozostałych pięciu plików IA, ale nasze wyjaśnienia będą bardzo zwięzłe, z wyjątkiem pliku ChatGPT, który jest obecnie liderem wśród plików IA. Interesuje nas, czy IA rozwiązuje trzy postawione mu problemy, czy też nie. Interfejsy wszystkich tych IA są bowiem bardzo podobne i postąpiłem z nimi tak samo jak z Gemini. Zachęcam czytelnika do ponownego prześledzenia rozmów z Gemini przy użyciu wybranego przez siebie modelu IA.