From de1f9a8a37ae187800585272b8715cf5f0526b75 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 06:49:34 +0200 Subject: [PATCH 1/3] feat(fallacy,#17578): table d'alignement Logic-13 + MAFALDA-L2 -> Argumentum (tranche C, etape 1) Produite par l'organe align_academic_to_argumentum.py (aucune edition manuelle). NOTICE provenance/licence : mesure firsthand du 2026-10-09 -- ni MAFALDA ni LOGIC n'ont de fichier LICENSE dans leur depot ; corpus non commis, table de labels seule. Co-Authored-By: Claude Sonnet 5.5 --- .../data/academic/NOTICE-ACADEMIC-ALIGNMENT | 58 +++++++++++++++++++ .../data/academic/alignment_report.json | 39 +++++++++++++ .../academic/logic_mafalda_to_argumentum.csv | 39 +++++++++++++ 3 files changed, 136 insertions(+) create mode 100644 MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT create mode 100644 MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json create mode 100644 MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT new file mode 100644 index 0000000000..5098ac4356 --- /dev/null +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT @@ -0,0 +1,58 @@ +NOTICE — CoursIA `data/academic/` (tranche C de l'issue #17578, EPIC #10355) +============================================================================== + +Contenu +------- + +`logic_mafalda_to_argumentum.csv` : table d'alignement des taxonomies +académiques vers la taxonomie Argumentum, produite par l'organe +`scripts/fallacy_detection/align_academic_to_argumentum.py` (aucune +édition manuelle — regénérer par la commande ci-dessous). +`alignment_report.json` : rapport de couverture de la même exécution. + +Provenance des taxonomies alignées +---------------------------------- + +Les listes académiques (Logic-13 de Jin et al. 2021 ; MAFALDA-L2 de +Helwe et al. 2023) sont les constantes de l'organe (`LOGIC_13`, +`MAFALDA_L2`), sourcées des publications : + +- Jin, Bhargava, Brew, Durrett, Klein, *Logical Fallacy Detection*, + Findings EMNLP 2021, arXiv:2202.13758 (13 types + LogicClimate). +- Helwe, Calamai, Paris, Clavel, Suchanek, *MAFALDA: A Benchmark and + Comprehensive Study of Fallacy Detection and Classification*, + NAACL 2024, arXiv:2311.09761 (L2 = classes fines). + +**Aucun corpus académique n'est commis dans ce dépôt.** La table +n'aligne que des listes de labels, pas des données annotées. + +Licences — mesure firsthand du 2026-10-09 (lane myia-po-2026:CoursIA) +--------------------------------------------------------------------- + +| Source | Dépôt | Fichier LICENSE | Licence réelle | +|---|---|---|---| +| MAFALDA | `ChadiHelwe/MAFALDA` | absent (API `license: null`, aucune mention README) | CC-BY-SA **déclarée dans le papier seulement** | +| LOGIC | `causalNLP/logical-fallacy` | absent (idem) | **aucune licence vérifiable à la source** | + +Conséquences (critères tranche C, commentaire du 2026-10-09 sur #17578) : + +- MAFALDA : corpus **non commis ici** ; si une phase ultérieure (FT/PT) + le consomme, la copie devra porter une NOTICE citant **le papier** + comme support de licence (pas un fichier LICENSE inexistant), avec + attribution et ShareAlike sur les dérivés. +- LOGIC : corpus **non committable** dans ce dépôt public (absence de + licence vérifiable) — lecture hors dépôt (GDrive privé) ou `fetch` + déterministe épinglé par commit + sha1, jamais commis. + +Reproduction +------------ + +``` +python scripts/fallacy_detection/align_academic_to_argumentum.py \ + --out-csv MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv \ + --out-json MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json \ + --report +``` + +La taxonomie Argumentum cible est la copie in-repo +(`data/argumentum_fallacies_taxonomy.csv`, cf. son NOTICE propre). diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json new file mode 100644 index 0000000000..ca75d842eb --- /dev/null +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json @@ -0,0 +1,39 @@ +{ + "by_source": { + "Logic13": { + "total": 15, + "DIRECT": 9, + "PARTIAL": 3, + "NOT_FOUND": 3, + "labels_not_found": [ + "Bandwagon", + "False cause", + "Sunk cost fallacy" + ] + }, + "MAFALDA-L2": { + "total": 23, + "DIRECT": 15, + "PARTIAL": 5, + "NOT_FOUND": 3, + "labels_not_found": [ + "Appeal to popularity", + "Appeal to values", + "Fallacy of logic" + ] + } + }, + "argumentum_families_hit": { + "Influence": 6, + "Tricherie": 1, + "Insuffisance": 6, + "Obstruction": 4, + "Erreur mathématique": 7, + "Erreur de raisonnement": 3, + "Abus de langage": 5 + }, + "argumentum_families_never_hit": [], + "argumentum_family_count": 7, + "argumentum_total_leaves": 1407, + "argumentum_csv": "D:\\Dev\\CoursIA-17578-C\\MyIA.AI.Notebooks\\SymbolicAI\\Argument_Analysis\\data\\argumentum_fallacies_taxonomy.csv" +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv new file mode 100644 index 0000000000..75ac129879 --- /dev/null +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv @@ -0,0 +1,39 @@ +academic_label,academic_source,keywords,best_pk,best_famille,best_name,score,verdict,"candidates (pk,famille,score)" +Ad hominem,Logic13,"['ad hominem', 'attaque personnelle']",1360,Obstruction,Ad hominem,0.7,DIRECT,"1360(Obstruction,0.70); 1398(Obstruction,0.70); 1338(Obstruction,0.03)" +Appeal to authority,Logic13,"[""argument d'autorité"", 'appeal to authority']",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" +Appeal to emotion,Logic13,"[""appel à l'émotion"", 'appel aux emotions']",299,Influence,Appel à l’émotion,0.143,PARTIAL,"299(Influence,0.14); 645(Erreur mathématique,0.07); 303(Influence,0.07)" +Bandwagon,Logic13,"['bandwagon', 'appel à la popularité', 'ad populum']",1395,Obstruction,Appel à la bande,0.06,NOT_FOUND,"1395(Obstruction,0.06); 683(Erreur mathématique,0.04); 131(Insuffisance,0.04)" +Causal oversimplification,Logic13,"['causal oversimplification', 'simplification']",135,Insuffisance,Simplification abusive,0.7,DIRECT,"135(Insuffisance,0.70); 707(Erreur de raisonnement,0.03); 720(Erreur de raisonnement,0.02)" +Circular reasoning,Logic13,"['raisonnement circulaire', 'circular']",699,Erreur de raisonnement,Argument circulaire,0.08,PARTIAL,"699(Erreur de raisonnement,0.08); 829(Abus de langage,0.06); 948(Tricherie,0.05)" +Equivocation,Logic13,"['équivoque', 'ambiguïté', 'equivocation']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" +False analogy,Logic13,"['fausse analogie', 'analogie']",839,Abus de langage,Fausse analogie,0.7,DIRECT,"839(Abus de langage,0.70); 841(Abus de langage,0.70); 797(Erreur de raisonnement,0.08)" +False cause,Logic13,"['fausse cause', 'post hoc', 'faux cause']",889,Tricherie,Mensonge,0.075,NOT_FOUND,"889(Tricherie,0.07); 797(Erreur de raisonnement,0.07); 877(Abus de langage,0.07)" +False dilemma,Logic13,"['faux dilemme', 'faux dilemme']",814,Abus de langage,Pensée binaire,0.7,DIRECT,"814(Abus de langage,0.70); 889(Tricherie,0.09); 906(Tricherie,0.06)" +Faulty generalization,Logic13,"['generalisation', 'généralisation']",595,Erreur mathématique,Généralisation abusive,0.7,DIRECT,"595(Erreur mathématique,0.70); 598(Erreur mathématique,0.70); 618(Erreur mathématique,0.70)" +Red herring,Logic13,"['red herring', 'hareng rouge', 'diversion']",1314,Obstruction,Diversion,0.7,DIRECT,"1314(Obstruction,0.70); 1374(Obstruction,0.05); 1327(Obstruction,0.03)" +Slippery slope,Logic13,"['pente glissante', 'slippery slope']",677,Erreur mathématique,Pente glissante,0.7,DIRECT,"677(Erreur mathématique,0.70); 705(Erreur de raisonnement,0.70); 858(Abus de langage,0.70)" +Straw man,Logic13,"['épouvantail', 'straw man', 'homme de paille']",168,Insuffisance,Homme de paille,0.7,DIRECT,"168(Insuffisance,0.70); 894(Tricherie,0.70); 1365(Obstruction,0.70)" +Sunk cost fallacy,Logic13,"['coût irrécupérable', 'sunk cost']",440,Influence,Sophisme des coûts irrécupérables,0.043,NOT_FOUND,"440(Influence,0.04); 1020(Tricherie,0.04); 1151(Tricherie,0.04)" +Appeal to emotion,MAFALDA-L2,"['appeal to emotion', ""appel à l'émotion""]",299,Influence,Appel à l’émotion,0.114,PARTIAL,"299(Influence,0.11); 645(Erreur mathématique,0.07); 303(Influence,0.07)" +Appeal to authority,MAFALDA-L2,"['appeal to authority', ""argument d'autorité""]",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" +Appeal to nature,MAFALDA-L2,"['appeal to nature', 'appel à la nature']",108,Insuffisance,"Biologizing, The Green Fallacy",0.7,DIRECT,"108(Insuffisance,0.70); 645(Erreur mathématique,0.08); 303(Influence,0.07)" +Appeal to tradition,MAFALDA-L2,"['appeal to tradition', 'appel à la tradition']",104,Insuffisance,Appel à la tradition,0.7,DIRECT,"104(Insuffisance,0.70); 645(Erreur mathématique,0.08); 303(Influence,0.07)" +Appeal to popularity,MAFALDA-L2,"['appeal to popularity', 'ad populum']",645,Erreur mathématique,Appel à la probabilité,0.037,NOT_FOUND,"645(Erreur mathématique,0.04); 303(Influence,0.04); 1384(Obstruction,0.04)" +Appeal to values,MAFALDA-L2,"['appeal to values', 'appel aux valeurs']",645,Erreur mathématique,Appel à la probabilité,0.075,NOT_FOUND,"645(Erreur mathématique,0.07); 303(Influence,0.07); 1384(Obstruction,0.07)" +Appeal to fear,MAFALDA-L2,"['appeal to fear', 'appel à la peur']",337,Influence,Appel à la terreur,0.12,PARTIAL,"337(Influence,0.12); 50(Insuffisance,0.08); 466(Influence,0.08)" +Sentimental appeal,MAFALDA-L2,"['sentimental', 'pathétique']",171,Insuffisance,Sophisme pathétique,0.7,DIRECT,"171(Insuffisance,0.70); 645(Erreur mathématique,0.04); 303(Influence,0.04)" +Causal fallacy,MAFALDA-L2,"['causal', 'causalité', 'post hoc']",634,Erreur mathématique,Confusion entre corrélation et causalité,0.7,DIRECT,"634(Erreur mathématique,0.70); 635(Erreur mathématique,0.70); 697(Erreur de raisonnement,0.70)" +False analogy,MAFALDA-L2,"['false analogy', 'fausse analogie']",839,Abus de langage,Fausse analogie,0.7,DIRECT,"839(Abus de langage,0.70); 797(Erreur de raisonnement,0.08); 877(Abus de langage,0.07)" +Slippery slope,MAFALDA-L2,"['slippery slope', 'pente glissante']",677,Erreur mathématique,Pente glissante,0.7,DIRECT,"677(Erreur mathématique,0.70); 705(Erreur de raisonnement,0.70); 858(Abus de langage,0.70)" +Hasty generalization,MAFALDA-L2,"['hasty generalization', 'généralisation hâtive']",598,Erreur mathématique,Généralisation hâtive,0.7,DIRECT,"598(Erreur mathématique,0.70); 759(Erreur de raisonnement,0.05); 1127(Tricherie,0.05)" +Fallacy of composition,MAFALDA-L2,['composition'],622,Erreur mathématique,Sophisme de composition,0.7,DIRECT,"622(Erreur mathématique,0.70); 726(Erreur de raisonnement,0.70)" +Fallacy of division,MAFALDA-L2,['division'],625,Erreur mathématique,Sophisme de division,0.7,DIRECT,"625(Erreur mathématique,0.70); 692(Erreur mathématique,0.70)" +False cause,MAFALDA-L2,"['false cause', 'fausse cause']",797,Erreur de raisonnement,Enthymême invalide,0.086,PARTIAL,"797(Erreur de raisonnement,0.09); 877(Abus de langage,0.08); 903(Tricherie,0.08)" +Fallacy of logic,MAFALDA-L2,['fallacy of logic'],222,Influence,Absurde,0.032,NOT_FOUND,"222(Influence,0.03); 832(Abus de langage,0.03); 750(Erreur de raisonnement,0.03)" +Equivocation,MAFALDA-L2,"['equivocation', 'équivoque', 'ambiguïté']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" +Fallacy of proportion,MAFALDA-L2,['proportion'],1193,Tricherie,Biais de proportionnalité,0.7,DIRECT,"1193(Tricherie,0.70)" +Red herring,MAFALDA-L2,"['red herring', 'diversion']",1314,Obstruction,Diversion,0.7,DIRECT,"1314(Obstruction,0.70); 1327(Obstruction,0.04); 1374(Obstruction,0.03)" +Straw man,MAFALDA-L2,"['straw man', 'épouvantail', 'homme de paille']",168,Insuffisance,Homme de paille,0.7,DIRECT,"168(Insuffisance,0.70); 894(Tricherie,0.70); 1365(Obstruction,0.70)" +Circular reasoning,MAFALDA-L2,"['circular reasoning', 'raisonnement circulaire']",699,Erreur de raisonnement,Argument circulaire,0.08,PARTIAL,"699(Erreur de raisonnement,0.08); 829(Abus de langage,0.06); 948(Tricherie,0.05)" +Begging the question,MAFALDA-L2,"['begging the question', 'pétition de principe']",183,Influence,Pétition de principe,0.7,DIRECT,"183(Influence,0.70); 698(Erreur de raisonnement,0.70); 703(Erreur de raisonnement,0.70)" +Ad hominem,MAFALDA-L2,"['ad hominem', 'attaque personnelle']",1360,Obstruction,Ad hominem,0.7,DIRECT,"1360(Obstruction,0.70); 1398(Obstruction,0.70); 1338(Obstruction,0.03)" From fa3f9d6b7a2f90186715545710c65c2bbbf9d15f Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 08:18:15 +0200 Subject: [PATCH 2/3] fix(fallacy,#17578): 4 quasi-manques lexicaux de l'alignement fermes (tranche C) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Mots-cles etendus d'apres le jugement d'alignement c.6074873532 : Bandwagon et Appeal to popularity -> 'appel à la bande' (pk 1395) ; Sunk cost -> pluriel 'coûts irrécupérables' (pk 440) ; False cause -> 'causalité douteuse' (pk 697+). Table et rapport regenerees par l'organe. Couverture : Logic-13 80->100 %, MAFALDA-L2 87->91 % ; les 2 NOT_FOUND restants sont le gap reel (Appeal to values) et le fourre-tout structurel (Fallacy of logic), documentes comme tels. Tests : 15 passed. Co-Authored-By: Claude Sonnet 5.5 --- .../data/academic/alignment_report.json | 21 +++++++------------ .../academic/logic_mafalda_to_argumentum.csv | 8 +++---- .../align_academic_to_argumentum.py | 8 +++---- 3 files changed, 16 insertions(+), 21 deletions(-) diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json index ca75d842eb..fa2f47e99d 100644 --- a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json @@ -2,34 +2,29 @@ "by_source": { "Logic13": { "total": 15, - "DIRECT": 9, + "DIRECT": 12, "PARTIAL": 3, - "NOT_FOUND": 3, - "labels_not_found": [ - "Bandwagon", - "False cause", - "Sunk cost fallacy" - ] + "NOT_FOUND": 0, + "labels_not_found": [] }, "MAFALDA-L2": { "total": 23, - "DIRECT": 15, + "DIRECT": 16, "PARTIAL": 5, - "NOT_FOUND": 3, + "NOT_FOUND": 2, "labels_not_found": [ - "Appeal to popularity", "Appeal to values", "Fallacy of logic" ] } }, "argumentum_families_hit": { - "Influence": 6, + "Influence": 7, "Tricherie": 1, "Insuffisance": 6, - "Obstruction": 4, + "Obstruction": 6, "Erreur mathématique": 7, - "Erreur de raisonnement": 3, + "Erreur de raisonnement": 4, "Abus de langage": 5 }, "argumentum_families_never_hit": [], diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv index 75ac129879..bafd68c148 100644 --- a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv @@ -2,23 +2,23 @@ academic_label,academic_source,keywords,best_pk,best_famille,best_name,score,ver Ad hominem,Logic13,"['ad hominem', 'attaque personnelle']",1360,Obstruction,Ad hominem,0.7,DIRECT,"1360(Obstruction,0.70); 1398(Obstruction,0.70); 1338(Obstruction,0.03)" Appeal to authority,Logic13,"[""argument d'autorité"", 'appeal to authority']",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" Appeal to emotion,Logic13,"[""appel à l'émotion"", 'appel aux emotions']",299,Influence,Appel à l’émotion,0.143,PARTIAL,"299(Influence,0.14); 645(Erreur mathématique,0.07); 303(Influence,0.07)" -Bandwagon,Logic13,"['bandwagon', 'appel à la popularité', 'ad populum']",1395,Obstruction,Appel à la bande,0.06,NOT_FOUND,"1395(Obstruction,0.06); 683(Erreur mathématique,0.04); 131(Insuffisance,0.04)" +Bandwagon,Logic13,"['bandwagon', 'appel à la popularité', 'ad populum', 'appel à la bande']",1395,Obstruction,Appel à la bande,0.7,DIRECT,"1395(Obstruction,0.70); 683(Erreur mathématique,0.04); 131(Insuffisance,0.03)" Causal oversimplification,Logic13,"['causal oversimplification', 'simplification']",135,Insuffisance,Simplification abusive,0.7,DIRECT,"135(Insuffisance,0.70); 707(Erreur de raisonnement,0.03); 720(Erreur de raisonnement,0.02)" Circular reasoning,Logic13,"['raisonnement circulaire', 'circular']",699,Erreur de raisonnement,Argument circulaire,0.08,PARTIAL,"699(Erreur de raisonnement,0.08); 829(Abus de langage,0.06); 948(Tricherie,0.05)" Equivocation,Logic13,"['équivoque', 'ambiguïté', 'equivocation']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" False analogy,Logic13,"['fausse analogie', 'analogie']",839,Abus de langage,Fausse analogie,0.7,DIRECT,"839(Abus de langage,0.70); 841(Abus de langage,0.70); 797(Erreur de raisonnement,0.08)" -False cause,Logic13,"['fausse cause', 'post hoc', 'faux cause']",889,Tricherie,Mensonge,0.075,NOT_FOUND,"889(Tricherie,0.07); 797(Erreur de raisonnement,0.07); 877(Abus de langage,0.07)" +False cause,Logic13,"['fausse cause', 'post hoc', 'faux cause', 'causalité douteuse']",697,Erreur de raisonnement,Causalité douteuse,0.7,DIRECT,"697(Erreur de raisonnement,0.70); 635(Erreur mathématique,0.08); 889(Tricherie,0.07)" False dilemma,Logic13,"['faux dilemme', 'faux dilemme']",814,Abus de langage,Pensée binaire,0.7,DIRECT,"814(Abus de langage,0.70); 889(Tricherie,0.09); 906(Tricherie,0.06)" Faulty generalization,Logic13,"['generalisation', 'généralisation']",595,Erreur mathématique,Généralisation abusive,0.7,DIRECT,"595(Erreur mathématique,0.70); 598(Erreur mathématique,0.70); 618(Erreur mathématique,0.70)" Red herring,Logic13,"['red herring', 'hareng rouge', 'diversion']",1314,Obstruction,Diversion,0.7,DIRECT,"1314(Obstruction,0.70); 1374(Obstruction,0.05); 1327(Obstruction,0.03)" Slippery slope,Logic13,"['pente glissante', 'slippery slope']",677,Erreur mathématique,Pente glissante,0.7,DIRECT,"677(Erreur mathématique,0.70); 705(Erreur de raisonnement,0.70); 858(Abus de langage,0.70)" Straw man,Logic13,"['épouvantail', 'straw man', 'homme de paille']",168,Insuffisance,Homme de paille,0.7,DIRECT,"168(Insuffisance,0.70); 894(Tricherie,0.70); 1365(Obstruction,0.70)" -Sunk cost fallacy,Logic13,"['coût irrécupérable', 'sunk cost']",440,Influence,Sophisme des coûts irrécupérables,0.043,NOT_FOUND,"440(Influence,0.04); 1020(Tricherie,0.04); 1151(Tricherie,0.04)" +Sunk cost fallacy,Logic13,"['coût irrécupérable', 'coûts irrécupérables', 'sunk cost']",440,Influence,Sophisme des coûts irrécupérables,0.7,DIRECT,"440(Influence,0.70); 1020(Tricherie,0.70); 1151(Tricherie,0.70)" Appeal to emotion,MAFALDA-L2,"['appeal to emotion', ""appel à l'émotion""]",299,Influence,Appel à l’émotion,0.114,PARTIAL,"299(Influence,0.11); 645(Erreur mathématique,0.07); 303(Influence,0.07)" Appeal to authority,MAFALDA-L2,"['appeal to authority', ""argument d'autorité""]",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" Appeal to nature,MAFALDA-L2,"['appeal to nature', 'appel à la nature']",108,Insuffisance,"Biologizing, The Green Fallacy",0.7,DIRECT,"108(Insuffisance,0.70); 645(Erreur mathématique,0.08); 303(Influence,0.07)" Appeal to tradition,MAFALDA-L2,"['appeal to tradition', 'appel à la tradition']",104,Insuffisance,Appel à la tradition,0.7,DIRECT,"104(Insuffisance,0.70); 645(Erreur mathématique,0.08); 303(Influence,0.07)" -Appeal to popularity,MAFALDA-L2,"['appeal to popularity', 'ad populum']",645,Erreur mathématique,Appel à la probabilité,0.037,NOT_FOUND,"645(Erreur mathématique,0.04); 303(Influence,0.04); 1384(Obstruction,0.04)" +Appeal to popularity,MAFALDA-L2,"['appeal to popularity', 'ad populum', 'appel à la bande']",1395,Obstruction,Appel à la bande,0.7,DIRECT,"1395(Obstruction,0.70); 645(Erreur mathématique,0.07); 303(Influence,0.07)" Appeal to values,MAFALDA-L2,"['appeal to values', 'appel aux valeurs']",645,Erreur mathématique,Appel à la probabilité,0.075,NOT_FOUND,"645(Erreur mathématique,0.07); 303(Influence,0.07); 1384(Obstruction,0.07)" Appeal to fear,MAFALDA-L2,"['appeal to fear', 'appel à la peur']",337,Influence,Appel à la terreur,0.12,PARTIAL,"337(Influence,0.12); 50(Insuffisance,0.08); 466(Influence,0.08)" Sentimental appeal,MAFALDA-L2,"['sentimental', 'pathétique']",171,Insuffisance,Sophisme pathétique,0.7,DIRECT,"171(Insuffisance,0.70); 645(Erreur mathématique,0.04); 303(Influence,0.04)" diff --git a/scripts/fallacy_detection/align_academic_to_argumentum.py b/scripts/fallacy_detection/align_academic_to_argumentum.py index 3f65e3f4ad..7fa7a5ea94 100644 --- a/scripts/fallacy_detection/align_academic_to_argumentum.py +++ b/scripts/fallacy_detection/align_academic_to_argumentum.py @@ -101,18 +101,18 @@ ("Ad hominem", ["ad hominem", "attaque personnelle"]), ("Appeal to authority", ["argument d'autorité", "appeal to authority"]), ("Appeal to emotion", ["appel à l'émotion", "appel aux emotions"]), - ("Bandwagon", ["bandwagon", "appel à la popularité", "ad populum"]), + ("Bandwagon", ["bandwagon", "appel à la popularité", "ad populum", "appel à la bande"]), ("Causal oversimplification", ["causal oversimplification", "simplification"]), ("Circular reasoning", ["raisonnement circulaire", "circular"]), ("Equivocation", ["équivoque", "ambiguïté", "equivocation"]), ("False analogy", ["fausse analogie", "analogie"]), - ("False cause", ["fausse cause", "post hoc", "faux cause"]), + ("False cause", ["fausse cause", "post hoc", "faux cause", "causalité douteuse"]), ("False dilemma", ["faux dilemme", "faux dilemme"]), ("Faulty generalization", ["generalisation", "généralisation"]), ("Red herring", ["red herring", "hareng rouge", "diversion"]), ("Slippery slope", ["pente glissante", "slippery slope"]), ("Straw man", ["épouvantail", "straw man", "homme de paille"]), - ("Sunk cost fallacy", ["coût irrécupérable", "sunk cost"]), + ("Sunk cost fallacy", ["coût irrécupérable", "coûts irrécupérables", "sunk cost"]), ] # NB : 15 entrees encodees couvrent les 13 types canoniques + 2 variants nominaux # frequent dans les datasets derives (Faulty generalisation / Hasty). On garde la @@ -126,7 +126,7 @@ ("Appeal to authority", ["appeal to authority", "argument d'autorité"], "Pathos"), ("Appeal to nature", ["appeal to nature", "appel à la nature"], "Pathos"), ("Appeal to tradition", ["appeal to tradition", "appel à la tradition"], "Pathos"), - ("Appeal to popularity", ["appeal to popularity", "ad populum"], "Pathos"), + ("Appeal to popularity", ["appeal to popularity", "ad populum", "appel à la bande"], "Pathos"), ("Appeal to values", ["appeal to values", "appel aux valeurs"], "Pathos"), ("Appeal to fear", ["appeal to fear", "appel à la peur"], "Pathos"), ("Sentimental appeal", ["sentimental", "pathétique"], "Pathos"), From 599799c2f1075a315d7f4630c3147f5c79cb0a88 Mon Sep 17 00:00:00 2001 From: jsboige Date: Fri, 9 Oct 2026 17:43:12 +0200 Subject: [PATCH 3/3] fix(fallacy,#17578): reponse a la review NanoClaw -- payload epingle, nom sans cardinal, ancre relative MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Revue NanoClaw 5469985194 sur #20028 -- les 4 points, dans l'ordre. 1. Le payload semantique n'etait epingle par rien qui tourne. Ajout de `test_align_academic_payload_pins.py` : les 5 termes de la tranche C sont nommes sur leur entree exacte ; un controle NEGATIF verifie que retirer un terme degrade l'alignement (NOT_FOUND -> le terme CREAIT l'alignement ; PARTIAL pour le 5e, qui le PROMEUT) ; et la table + le rapport commis sont rejoues contre une execution fraiche de l'organe, compteurs epingles (12/3/0 et 17/4/2). Un revert silencieux d'un mot-cle rougit desormais. 2. « Logic-13 » portait 15 libelles. La constante devient `LOGIC_LABELS`, le libelle de source porte par la table et le rapport devient `LOGIC` : plus aucun cardinal dans un nom, le compte fait foi dans le champ `total`. Commentaires, docstring, description CLI et NOTICE alignes. 3. `argumentum_csv` portait un chemin Windows absolu, machine-dependant. L'organe emet desormais un chemin RELATIF au depot, plus `argumentum_csv_sha256` (verifiable sans le chemin) ; rapport regenere par l'organe, jamais a la main. 4. Asymetrie False cause : le 5e quasi-manque est pris. Mesure : les deux taxonomies convergent alors sur la MEME feuille (697, Causalite douteuse), qui est la cible correcte des deux cotes -- l'asymetrie venait du perimetre du jugement cede, pas d'un choix semantique. MAFALDA-L2 passe de 16/5/2 a 17/4/2 ; la couverture (91 %) ne bouge pas. Tests : `py -3.13 -m pytest scripts/fallacy_detection/tests/` -> 111 passed. See #17578 Co-Authored-By: Claude Sonnet 5.5 --- .../data/academic/NOTICE-ACADEMIC-ALIGNMENT | 21 +- .../data/academic/alignment_report.json | 9 +- .../academic/logic_mafalda_to_argumentum.csv | 32 +- .../align_academic_to_argumentum.py | 63 +++- .../tests/test_align_academic_payload_pins.py | 274 ++++++++++++++++++ .../test_align_academic_to_argumentum.py | 26 +- 6 files changed, 374 insertions(+), 51 deletions(-) create mode 100644 scripts/fallacy_detection/tests/test_align_academic_payload_pins.py diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT index 5098ac4356..b322978404 100644 --- a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/NOTICE-ACADEMIC-ALIGNMENT @@ -13,15 +13,21 @@ académiques vers la taxonomie Argumentum, produite par l'organe Provenance des taxonomies alignées ---------------------------------- -Les listes académiques (Logic-13 de Jin et al. 2021 ; MAFALDA-L2 de -Helwe et al. 2023) sont les constantes de l'organe (`LOGIC_13`, +Les listes académiques (LOGIC de Jin et al. 2021 ; MAFALDA-L2 de +Helwe et al. 2023) sont les constantes de l'organe (`LOGIC_LABELS`, `MAFALDA_L2`), sourcées des publications : - Jin, Bhargava, Brew, Durrett, Klein, *Logical Fallacy Detection*, - Findings EMNLP 2021, arXiv:2202.13758 (13 types + LogicClimate). + Findings EMNLP 2021, arXiv:2202.13758. **Le papier dénombre 13 types** + (+ LogicClimate) ; l'inventaire encodé ici en porte **15** — les deux + de plus sont des variantes nominales rencontrées dans les datasets + dérivés. Le libellé de source (`LOGIC`) et le nom de la constante ne + portent donc **aucun cardinal** : le compte qui fait foi est le champ + `total` de `alignment_report.json` (revue NanoClaw 5469985194). - Helwe, Calamai, Paris, Clavel, Suchanek, *MAFALDA: A Benchmark and Comprehensive Study of Fallacy Detection and Classification*, - NAACL 2024, arXiv:2311.09761 (L2 = classes fines). + NAACL 2024, arXiv:2311.09761 — L2 = **23** classes fines, encodées ici + au même compte. **Aucun corpus académique n'est commis dans ce dépôt.** La table n'aligne que des listes de labels, pas des données annotées. @@ -56,3 +62,10 @@ python scripts/fallacy_detection/align_academic_to_argumentum.py \ La taxonomie Argumentum cible est la copie in-repo (`data/argumentum_fallacies_taxonomy.csv`, cf. son NOTICE propre). + +`alignment_report.json` porte son ancre de reproductibilité sous deux +formes : `argumentum_csv` (chemin **relatif au dépôt** — jamais un chemin +machine-dépendant) et `argumentum_csv_sha256` (empreinte du CSV cible, +qui reste vérifiable même si le chemin devient illisible). La table commise +et ce rapport sont **rejoués par le test** : toute divergence avec une +exécution fraîche rougit (`test_align_academic_payload_pins.py`). diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json index fa2f47e99d..3ec19e5ea5 100644 --- a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/alignment_report.json @@ -1,6 +1,6 @@ { "by_source": { - "Logic13": { + "LOGIC": { "total": 15, "DIRECT": 12, "PARTIAL": 3, @@ -9,8 +9,8 @@ }, "MAFALDA-L2": { "total": 23, - "DIRECT": 16, - "PARTIAL": 5, + "DIRECT": 17, + "PARTIAL": 4, "NOT_FOUND": 2, "labels_not_found": [ "Appeal to values", @@ -30,5 +30,6 @@ "argumentum_families_never_hit": [], "argumentum_family_count": 7, "argumentum_total_leaves": 1407, - "argumentum_csv": "D:\\Dev\\CoursIA-17578-C\\MyIA.AI.Notebooks\\SymbolicAI\\Argument_Analysis\\data\\argumentum_fallacies_taxonomy.csv" + "argumentum_csv": "MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/data/argumentum_fallacies_taxonomy.csv", + "argumentum_csv_sha256": "fc8e90fea501c33330ae575a27c6c0c8ba828a10904b380a9b23c637f2887cf5" } \ No newline at end of file diff --git a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv index bafd68c148..a3efde222b 100644 --- a/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv +++ b/MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic/logic_mafalda_to_argumentum.csv @@ -1,19 +1,19 @@ academic_label,academic_source,keywords,best_pk,best_famille,best_name,score,verdict,"candidates (pk,famille,score)" -Ad hominem,Logic13,"['ad hominem', 'attaque personnelle']",1360,Obstruction,Ad hominem,0.7,DIRECT,"1360(Obstruction,0.70); 1398(Obstruction,0.70); 1338(Obstruction,0.03)" -Appeal to authority,Logic13,"[""argument d'autorité"", 'appeal to authority']",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" -Appeal to emotion,Logic13,"[""appel à l'émotion"", 'appel aux emotions']",299,Influence,Appel à l’émotion,0.143,PARTIAL,"299(Influence,0.14); 645(Erreur mathématique,0.07); 303(Influence,0.07)" -Bandwagon,Logic13,"['bandwagon', 'appel à la popularité', 'ad populum', 'appel à la bande']",1395,Obstruction,Appel à la bande,0.7,DIRECT,"1395(Obstruction,0.70); 683(Erreur mathématique,0.04); 131(Insuffisance,0.03)" -Causal oversimplification,Logic13,"['causal oversimplification', 'simplification']",135,Insuffisance,Simplification abusive,0.7,DIRECT,"135(Insuffisance,0.70); 707(Erreur de raisonnement,0.03); 720(Erreur de raisonnement,0.02)" -Circular reasoning,Logic13,"['raisonnement circulaire', 'circular']",699,Erreur de raisonnement,Argument circulaire,0.08,PARTIAL,"699(Erreur de raisonnement,0.08); 829(Abus de langage,0.06); 948(Tricherie,0.05)" -Equivocation,Logic13,"['équivoque', 'ambiguïté', 'equivocation']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" -False analogy,Logic13,"['fausse analogie', 'analogie']",839,Abus de langage,Fausse analogie,0.7,DIRECT,"839(Abus de langage,0.70); 841(Abus de langage,0.70); 797(Erreur de raisonnement,0.08)" -False cause,Logic13,"['fausse cause', 'post hoc', 'faux cause', 'causalité douteuse']",697,Erreur de raisonnement,Causalité douteuse,0.7,DIRECT,"697(Erreur de raisonnement,0.70); 635(Erreur mathématique,0.08); 889(Tricherie,0.07)" -False dilemma,Logic13,"['faux dilemme', 'faux dilemme']",814,Abus de langage,Pensée binaire,0.7,DIRECT,"814(Abus de langage,0.70); 889(Tricherie,0.09); 906(Tricherie,0.06)" -Faulty generalization,Logic13,"['generalisation', 'généralisation']",595,Erreur mathématique,Généralisation abusive,0.7,DIRECT,"595(Erreur mathématique,0.70); 598(Erreur mathématique,0.70); 618(Erreur mathématique,0.70)" -Red herring,Logic13,"['red herring', 'hareng rouge', 'diversion']",1314,Obstruction,Diversion,0.7,DIRECT,"1314(Obstruction,0.70); 1374(Obstruction,0.05); 1327(Obstruction,0.03)" -Slippery slope,Logic13,"['pente glissante', 'slippery slope']",677,Erreur mathématique,Pente glissante,0.7,DIRECT,"677(Erreur mathématique,0.70); 705(Erreur de raisonnement,0.70); 858(Abus de langage,0.70)" -Straw man,Logic13,"['épouvantail', 'straw man', 'homme de paille']",168,Insuffisance,Homme de paille,0.7,DIRECT,"168(Insuffisance,0.70); 894(Tricherie,0.70); 1365(Obstruction,0.70)" -Sunk cost fallacy,Logic13,"['coût irrécupérable', 'coûts irrécupérables', 'sunk cost']",440,Influence,Sophisme des coûts irrécupérables,0.7,DIRECT,"440(Influence,0.70); 1020(Tricherie,0.70); 1151(Tricherie,0.70)" +Ad hominem,LOGIC,"['ad hominem', 'attaque personnelle']",1360,Obstruction,Ad hominem,0.7,DIRECT,"1360(Obstruction,0.70); 1398(Obstruction,0.70); 1338(Obstruction,0.03)" +Appeal to authority,LOGIC,"[""argument d'autorité"", 'appeal to authority']",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" +Appeal to emotion,LOGIC,"[""appel à l'émotion"", 'appel aux emotions']",299,Influence,Appel à l’émotion,0.143,PARTIAL,"299(Influence,0.14); 645(Erreur mathématique,0.07); 303(Influence,0.07)" +Bandwagon,LOGIC,"['bandwagon', 'appel à la popularité', 'ad populum', 'appel à la bande']",1395,Obstruction,Appel à la bande,0.7,DIRECT,"1395(Obstruction,0.70); 683(Erreur mathématique,0.04); 131(Insuffisance,0.03)" +Causal oversimplification,LOGIC,"['causal oversimplification', 'simplification']",135,Insuffisance,Simplification abusive,0.7,DIRECT,"135(Insuffisance,0.70); 707(Erreur de raisonnement,0.03); 720(Erreur de raisonnement,0.02)" +Circular reasoning,LOGIC,"['raisonnement circulaire', 'circular']",699,Erreur de raisonnement,Argument circulaire,0.08,PARTIAL,"699(Erreur de raisonnement,0.08); 829(Abus de langage,0.06); 948(Tricherie,0.05)" +Equivocation,LOGIC,"['équivoque', 'ambiguïté', 'equivocation']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" +False analogy,LOGIC,"['fausse analogie', 'analogie']",839,Abus de langage,Fausse analogie,0.7,DIRECT,"839(Abus de langage,0.70); 841(Abus de langage,0.70); 797(Erreur de raisonnement,0.08)" +False cause,LOGIC,"['fausse cause', 'post hoc', 'faux cause', 'causalité douteuse']",697,Erreur de raisonnement,Causalité douteuse,0.7,DIRECT,"697(Erreur de raisonnement,0.70); 635(Erreur mathématique,0.08); 889(Tricherie,0.07)" +False dilemma,LOGIC,"['faux dilemme', 'faux dilemme']",814,Abus de langage,Pensée binaire,0.7,DIRECT,"814(Abus de langage,0.70); 889(Tricherie,0.09); 906(Tricherie,0.06)" +Faulty generalization,LOGIC,"['generalisation', 'généralisation']",595,Erreur mathématique,Généralisation abusive,0.7,DIRECT,"595(Erreur mathématique,0.70); 598(Erreur mathématique,0.70); 618(Erreur mathématique,0.70)" +Red herring,LOGIC,"['red herring', 'hareng rouge', 'diversion']",1314,Obstruction,Diversion,0.7,DIRECT,"1314(Obstruction,0.70); 1374(Obstruction,0.05); 1327(Obstruction,0.03)" +Slippery slope,LOGIC,"['pente glissante', 'slippery slope']",677,Erreur mathématique,Pente glissante,0.7,DIRECT,"677(Erreur mathématique,0.70); 705(Erreur de raisonnement,0.70); 858(Abus de langage,0.70)" +Straw man,LOGIC,"['épouvantail', 'straw man', 'homme de paille']",168,Insuffisance,Homme de paille,0.7,DIRECT,"168(Insuffisance,0.70); 894(Tricherie,0.70); 1365(Obstruction,0.70)" +Sunk cost fallacy,LOGIC,"['coût irrécupérable', 'coûts irrécupérables', 'sunk cost']",440,Influence,Sophisme des coûts irrécupérables,0.7,DIRECT,"440(Influence,0.70); 1020(Tricherie,0.70); 1151(Tricherie,0.70)" Appeal to emotion,MAFALDA-L2,"['appeal to emotion', ""appel à l'émotion""]",299,Influence,Appel à l’émotion,0.114,PARTIAL,"299(Influence,0.11); 645(Erreur mathématique,0.07); 303(Influence,0.07)" Appeal to authority,MAFALDA-L2,"['appeal to authority', ""argument d'autorité""]",211,Influence,Référencement pléthorique,0.09,PARTIAL,"211(Influence,0.09); 85(Insuffisance,0.08); 940(Tricherie,0.08)" Appeal to nature,MAFALDA-L2,"['appeal to nature', 'appel à la nature']",108,Insuffisance,"Biologizing, The Green Fallacy",0.7,DIRECT,"108(Insuffisance,0.70); 645(Erreur mathématique,0.08); 303(Influence,0.07)" @@ -28,7 +28,7 @@ Slippery slope,MAFALDA-L2,"['slippery slope', 'pente glissante']",677,Erreur mat Hasty generalization,MAFALDA-L2,"['hasty generalization', 'généralisation hâtive']",598,Erreur mathématique,Généralisation hâtive,0.7,DIRECT,"598(Erreur mathématique,0.70); 759(Erreur de raisonnement,0.05); 1127(Tricherie,0.05)" Fallacy of composition,MAFALDA-L2,['composition'],622,Erreur mathématique,Sophisme de composition,0.7,DIRECT,"622(Erreur mathématique,0.70); 726(Erreur de raisonnement,0.70)" Fallacy of division,MAFALDA-L2,['division'],625,Erreur mathématique,Sophisme de division,0.7,DIRECT,"625(Erreur mathématique,0.70); 692(Erreur mathématique,0.70)" -False cause,MAFALDA-L2,"['false cause', 'fausse cause']",797,Erreur de raisonnement,Enthymême invalide,0.086,PARTIAL,"797(Erreur de raisonnement,0.09); 877(Abus de langage,0.08); 903(Tricherie,0.08)" +False cause,MAFALDA-L2,"['false cause', 'fausse cause', 'causalité douteuse']",697,Erreur de raisonnement,Causalité douteuse,0.7,DIRECT,"697(Erreur de raisonnement,0.70); 797(Erreur de raisonnement,0.07); 877(Abus de langage,0.07)" Fallacy of logic,MAFALDA-L2,['fallacy of logic'],222,Influence,Absurde,0.032,NOT_FOUND,"222(Influence,0.03); 832(Abus de langage,0.03); 750(Erreur de raisonnement,0.03)" Equivocation,MAFALDA-L2,"['equivocation', 'équivoque', 'ambiguïté']",855,Abus de langage,Equivocation,1.0,DIRECT,"855(Abus de langage,1.00); 360(Influence,0.70); 846(Abus de langage,0.70)" Fallacy of proportion,MAFALDA-L2,['proportion'],1193,Tricherie,Biais de proportionnalité,0.7,DIRECT,"1193(Tricherie,0.70)" diff --git a/scripts/fallacy_detection/align_academic_to_argumentum.py b/scripts/fallacy_detection/align_academic_to_argumentum.py index 7fa7a5ea94..bd2a269de2 100644 --- a/scripts/fallacy_detection/align_academic_to_argumentum.py +++ b/scripts/fallacy_detection/align_academic_to_argumentum.py @@ -28,7 +28,8 @@ Sources (verifiees firsthand dans le survey #10360) : - Jin, Bhargava, Brew, Durrett, Klein, *Logical Fallacy Detection*, Findings - EMNLP 2021, arXiv:2202.13758 -- 13 types + LogicClimate. + EMNLP 2021, arXiv:2202.13758 -- le papier denombre 13 types + LogicClimate ; + l'inventaire encode ici en porte **15** (cf. ``LOGIC_LABELS``). - Helwe, Calamai, Paris, Clavel, Suchanek, *MAFALDA: A Benchmark and Comprehensive Study of Fallacy Detection and Classification*, 2023, arXiv:2311.09761 -- hierarchie 3 niveaux, L2 = 23 classes fines. @@ -44,6 +45,7 @@ import argparse import csv +import hashlib import io import json import re @@ -94,10 +96,20 @@ # Listes d'etiquettes academiques (papiers cites, encodees ici). # --------------------------------------------------------------------------- -# Logic / LogicClimate (Jin et al. 2021, arXiv:2202.13758) : 13 types. -# Liste canonique du dataset (tasksource/logic2fallacy, causalNLP/logical-fallacy). -# Chaque entree : (label_en, keywords FR/EN pour le matching, note source). -LOGIC_13 = [ +# Libelles de SOURCE portes par la table et le rapport. Ils nomment la provenance +# sans porter de cardinal : le compte fait foi dans `total` (revue NanoClaw +# 5469985194 -- « Logic-13 » annoncait 13 pour un inventaire qui en porte 15). +LOGIC_SOURCE = "LOGIC" +MAFALDA_SOURCE = "MAFALDA-L2" + +# Logic / LogicClimate (Jin et al. 2021, arXiv:2202.13758). +# Le papier denombre 13 types ; l'inventaire ENCODE ICI en porte **15** (les 2 de +# plus sont des variantes nominales rencontrees dans les datasets derives -- +# tasksource/logic2fallacy, causalNLP/logical-fallacy). Le nom de la constante ne +# porte donc AUCUN compte : le cardinal fait foi dans le champ ``total`` du rapport +# et dans le test qui l'epingle, jamais dans un nom (revue NanoClaw 5469985194). +# Chaque entree : (label_en, keywords FR/EN pour le matching). +LOGIC_LABELS = [ ("Ad hominem", ["ad hominem", "attaque personnelle"]), ("Appeal to authority", ["argument d'autorité", "appeal to authority"]), ("Appeal to emotion", ["appel à l'émotion", "appel aux emotions"]), @@ -114,9 +126,8 @@ ("Straw man", ["épouvantail", "straw man", "homme de paille"]), ("Sunk cost fallacy", ["coût irrécupérable", "coûts irrécupérables", "sunk cost"]), ] -# NB : 15 entrees encodees couvrent les 13 types canoniques + 2 variants nominaux -# frequent dans les datasets derives (Faulty generalisation / Hasty). On garde la -# granularite pour que l'alignement soit robuste aux variantes de label. +# NB : 15 entrees encodees (cardinal epingle par test ; le papier en denombre 13). +# On garde la granularite pour que l'alignement soit robuste aux variantes de label. # MAFALDA (Helwe et al. 2023, arXiv:2311.09761) : L2 = 23 classes fines, sous # 3 categories L1 (Pathos / Logos / Ethos). Encodage issu du papier. @@ -137,7 +148,7 @@ ("Hasty generalization", ["hasty generalization", "généralisation hâtive"], "Logos"), ("Fallacy of composition", ["composition"], "Logos"), ("Fallacy of division", ["division"], "Logos"), - ("False cause", ["false cause", "fausse cause"], "Logos"), + ("False cause", ["false cause", "fausse cause", "causalité douteuse"], "Logos"), ("Fallacy of logic", ["fallacy of logic"], "Logos"), ("Equivocation", ["equivocation", "équivoque", "ambiguïté"], "Logos"), ("Fallacy of proportion", ["proportion"], "Logos"), @@ -243,7 +254,7 @@ class Alignment: """Resultat de l'alignement d'une etiquette academique vers Argumentum.""" academic_label: str - academic_source: str # "Logic13" ou "MAFALDA-L2" + academic_source: str # LOGIC_SOURCE ou MAFALDA_SOURCE keywords: list[str] best_pk: str | None best_famille: str | None @@ -339,6 +350,24 @@ def _norm(s: str) -> str: return re.sub(r"[^a-z0-9]+", " ", s).strip() +def _repo_relative(p: Path) -> str: + """Chemin POSIX relatif a la racine du depot. + + Un artefact commis ne porte jamais un chemin machine-dependant (revue + NanoClaw 5469985194). Hors depot (ex. fichier temporaire de test), seul le + nom est rendu -- jamais un chemin absolu. + """ + try: + return p.resolve().relative_to(_REPO_ROOT).as_posix() + except ValueError: + return p.name + + +def _sha256(p: Path) -> str: + """Empreinte du fichier : l'ancre qui reste verifiable meme sans le chemin.""" + return hashlib.sha256(p.read_bytes()).hexdigest() + + # --------------------------------------------------------------------------- # Rapport de couverture. # --------------------------------------------------------------------------- @@ -379,13 +408,17 @@ def build( raise FileNotFoundError(f"Taxonomie Argumentum introuvable : {csv_path}") argum = load_argumentum(csv_path) alignments: list[Alignment] = [] - for label, kw in LOGIC_13: - alignments.append(align_label(label, "Logic13", kw, argum)) + for label, kw in LOGIC_LABELS: + alignments.append(align_label(label, LOGIC_SOURCE, kw, argum)) for label, kw, _l1 in MAFALDA_L2: - alignments.append(align_label(label, "MAFALDA-L2", kw, argum)) + alignments.append(align_label(label, MAFALDA_SOURCE, kw, argum)) report = coverage_report(alignments) report["argumentum_total_leaves"] = len(argum) - report["argumentum_csv"] = str(csv_path) + # Ancre de reproductibilite : chemin RELATIF au depot (jamais un chemin + # machine-dependant, cf. revue NanoClaw 5469985194) + empreinte du CSV cible, + # qui reste verifiable meme si le chemin devient illisible. + report["argumentum_csv"] = _repo_relative(csv_path) + report["argumentum_csv_sha256"] = _sha256(csv_path) return alignments, report @@ -396,7 +429,7 @@ def build( def main(argv: list[str] | None = None) -> int: p = argparse.ArgumentParser( - description="Aligne Logic-13 + MAFALDA-23-L2 vers la taxonomie Argumentum." + description="Aligne les inventaires academiques LOGIC + MAFALDA-L2 vers la taxonomie Argumentum." ) p.add_argument( "--taxonomy", diff --git a/scripts/fallacy_detection/tests/test_align_academic_payload_pins.py b/scripts/fallacy_detection/tests/test_align_academic_payload_pins.py new file mode 100644 index 0000000000..05d9f732fd --- /dev/null +++ b/scripts/fallacy_detection/tests/test_align_academic_payload_pins.py @@ -0,0 +1,274 @@ +#!/usr/bin/env python3 +"""Epinglage du payload SEMANTIQUE de la table d'alignement academique. + +Complement de ``test_align_academic_to_argumentum.py`` : ce dernier teste la +MACHINERIE sur une mini-taxonomie synthetique et reste hermetique ; celui-ci +teste le CONTENU produit sur la taxonomie Argumentum reelle du depot, et +confronte les artefacts commis a une execution fraiche de l'organe. + +Raison d'etre (revue NanoClaw 5469985194, reserve 1) : « un revert silencieux de +``causalite douteuse`` repasserait Logic-13 a 80 % sans faire bouger un check ». +Les deux tests de derive ci-dessous rendent ce revert impossible en silence : + +- ``test_les_4_termes_ajoutes_sont_epingles`` : les 4 termes de la tranche C sont + nommes, sur leur entree exacte ; +- ``test_les_4_termes_sont_porteurs`` : controle NEGATIF -- retirer un terme fait + tomber son etiquette en NOT_FOUND. C'est ce qui distingue un mot-cle utile d'un + mot-cle decoratif, et c'est le test qui rougit si l'un des 4 est reverte ; +- ``test_table_commise_egale_execution_fraiche`` : la table commise est rejouee ; +- ``test_rapport_commis_egale_execution_fraiche`` : les compteurs de couverture + (12/3/0 et 17/4/2) sont epingles, pas seulement affiches. + +stdlib only, aucun acces reseau. La taxonomie cible est celle du depot. +""" + +import ast +import csv +import hashlib +import json +import sys +from pathlib import Path + +import pytest + +HERE = Path(__file__).resolve().parent +SCRIPTS_DIR = HERE.parent.parent # scripts/ +sys.path.insert(0, str(SCRIPTS_DIR)) + +from fallacy_detection.align_academic_to_argumentum import ( # noqa: E402 + LOGIC_LABELS, + LOGIC_SOURCE, + MAFALDA_L2, + MAFALDA_SOURCE, + _DEFAULT_TAXO, + _REPO_ROOT, + align_label, + build, + load_argumentum, +) + +ACADEMIC_DIR = ( + _REPO_ROOT / "MyIA.AI.Notebooks/GenAI/FallacyDetection/data/academic" +) +COMMITTED_CSV = ACADEMIC_DIR / "logic_mafalda_to_argumentum.csv" +COMMITTED_JSON = ACADEMIC_DIR / "alignment_report.json" + +# Couverture attendue, par source. Ces valeurs sont la raison d'etre de la +# tranche C : elles sont epinglees ici pour qu'un revert lexical les fasse +# rougir au lieu de les faire baisser en silence. +EXPECTED_COVERAGE = { + LOGIC_SOURCE: {"total": 15, "DIRECT": 12, "PARTIAL": 3, "NOT_FOUND": 0}, + MAFALDA_SOURCE: {"total": 23, "DIRECT": 17, "PARTIAL": 4, "NOT_FOUND": 2}, +} + +# Les 4 quasi-manques lexicaux fermes par la tranche C (jugement d'alignement +# c.6074873532) et le 5e pris pour symetrie. Chaque entree porte la cible et le +# verdict SANS le terme -- deux formes mesurees le 2026-10-09, et la distinction +# est le sujet du test : +# - les 4 quasi-manques de la tranche C CREENT l'alignement (NOT_FOUND -> DIRECT) ; +# - le 5e (MAFALDA False cause, symetrie) PROMOUVOIT un PARTIAL en DIRECT, la +# seule feuille 697 que les deux taxonomies doivent viser. +TRANCHE_C_TERMS = [ + (LOGIC_SOURCE, "Bandwagon", "appel à la bande", "1395", "NOT_FOUND"), + (MAFALDA_SOURCE, "Appeal to popularity", "appel à la bande", "1395", "NOT_FOUND"), + (LOGIC_SOURCE, "Sunk cost fallacy", "coûts irrécupérables", "440", "NOT_FOUND"), + (LOGIC_SOURCE, "False cause", "causalité douteuse", "697", "NOT_FOUND"), + (MAFALDA_SOURCE, "False cause", "causalité douteuse", "697", "PARTIAL"), +] + + +@pytest.fixture(scope="module") +def argum(): + return load_argumentum(_DEFAULT_TAXO) + + +def _keywords(source: str, label: str) -> list[str]: + """Liste de mots-cles encodee pour (source, label). Casse si absentee.""" + if source == LOGIC_SOURCE: + hits = [kw for lbl, kw in LOGIC_LABELS if lbl == label] + elif source == MAFALDA_SOURCE: + hits = [kw for lbl, kw, _l1 in MAFALDA_L2 if lbl == label] + else: # pragma: no cover - garde-fou + raise AssertionError(f"source inconnue : {source}") + assert hits, f"etiquette absente de l'inventaire {source} : {label}" + return hits[0] + + +# --------------------------------------------------------------------------- +# 1. Les 4 (+1) termes sont nommes, sur leur entree exacte. +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("source,label,term,_pk,sans_attendu", TRANCHE_C_TERMS) +def test_les_termes_de_la_tranche_c_sont_epingles(source, label, term, _pk, sans_attendu): + assert term in _keywords(source, label), ( + f"{source}/{label} a perdu le mot-cle {term!r} : couverture en baisse, " + f"la tranche C est revertee" + ) + + +# --------------------------------------------------------------------------- +# 2. Controle NEGATIF : chaque terme est porteur (le retirer degrade l'alignement). +# --------------------------------------------------------------------------- + +@pytest.mark.parametrize("source,label,term,_pk,sans_attendu", TRANCHE_C_TERMS) +def test_les_termes_sont_porteurs(source, label, term, _pk, sans_attendu, argum): + """Sans le terme, l'etiquette n'atteint PLUS la feuille : le terme est porteur. + + L'assertion exacte (et non « pas DIRECT ») est ce qui rend le test falsifiable + dans les deux sens : elle rougit si le terme est retire de l'inventaire (la + liste devient identique, donc le verdict reste DIRECT), et elle rougit aussi + si l'alignement se degrade pour une autre raison. + """ + kws = _keywords(source, label) + avec = align_label(label, source, kws, argum) + assert avec.verdict == "DIRECT", ( + f"{source}/{label} devrait s'aligner en DIRECT avec {term!r}, " + f"obtenu {avec.verdict}" + ) + sans = align_label(label, source, [k for k in kws if k != term], argum) + assert sans.verdict == sans_attendu, ( + f"{source}/{label} : attendu {sans_attendu} sans {term!r}, obtenu " + f"{sans.verdict} -- si c'est DIRECT, le terme a disparu de l'inventaire" + ) + + +def test_les_termes_atteignent_leur_cible(): + """Les 5 termes visent la feuille annoncee dans le body de la PR.""" + argum = load_argumentum(_DEFAULT_TAXO) + for src, label, _term, pk, _sans in TRANCHE_C_TERMS: + a = align_label(label, src, _keywords(src, label), argum) + assert a.best_pk == pk, ( + f"{src}/{label} atteint pk={a.best_pk}, attendu {pk}" + ) + + +# --------------------------------------------------------------------------- +# 3. La symetrie False cause : les deux taxonomies convergent sur la meme feuille. +# --------------------------------------------------------------------------- + +def test_false_cause_converge_entre_les_deux_taxonomies(argum): + """MAFALDA-L2 et LOGIC portent le meme label : ils doivent viser la meme feuille. + + L'asymetrie d'origine (extension portee sur la seule entree LOGIC) etait un + artefact du perimetre du jugement cede, pas un choix semantique -- mesure du + 2026-10-09 : la feuille 697 est la cible correcte des deux cotes. + """ + logic = align_label("False cause", LOGIC_SOURCE, _keywords(LOGIC_SOURCE, "False cause"), argum) + mafalda = align_label("False cause", MAFALDA_SOURCE, _keywords(MAFALDA_SOURCE, "False cause"), argum) + assert logic.best_pk == mafalda.best_pk == "697" + assert logic.verdict == mafalda.verdict == "DIRECT" + + +# --------------------------------------------------------------------------- +# 4. Les artefacts commis egale une execution fraiche de l'organe. +# --------------------------------------------------------------------------- + +def _fresh_build(): + alignments, report = build(_DEFAULT_TAXO) + return alignments, report + + +def test_table_commise_egale_execution_fraiche(): + """La table commise est rejouee : aucune edition manuelle, aucun revert muet.""" + assert COMMITTED_CSV.exists(), f"table absente : {COMMITTED_CSV}" + alignments, _ = _fresh_build() + fresh = [a.to_row() for a in alignments] + + committed = list(csv.DictReader(COMMITTED_CSV.read_text(encoding="utf-8").splitlines())) + assert len(committed) == len(fresh), ( + f"la table commise porte {len(committed)} lignes, une execution fraiche " + f"en produit {len(fresh)} : regenerer par l'organe (NOTICE)" + ) + + for i, (c_row, f_row) in enumerate(zip(committed, fresh), start=1): + for key, f_val in f_row.items(): + expected = "" if f_val is None else str(f_val) + assert c_row[key] == expected, ( + f"ligne {i}, colonne {key!r} : table commise {c_row[key]!r} != " + f"execution fraiche {expected!r}" + ) + + +def test_rapport_commis_egale_execution_fraiche(): + """Les compteurs de couverture sont epingles, pas seulement affiches.""" + assert COMMITTED_JSON.exists(), f"rapport absent : {COMMITTED_JSON}" + _, report = _fresh_build() + committed = json.loads(COMMITTED_JSON.read_text(encoding="utf-8")) + assert committed == report, ( + "le rapport commis diverge d'une execution fraiche : regenerer par " + "l'organe (commande de la NOTICE), jamais a la main" + ) + + +def test_couverture_par_source_epinglee(): + _, report = _fresh_build() + for source, expected in EXPECTED_COVERAGE.items(): + stats = report["by_source"][source] + for key, value in expected.items(): + assert stats[key] == value, ( + f"{source}.{key} = {stats[key]}, attendu {value} " + f"(couverture en baisse : un mot-cle a ete retire ?)" + ) + + +def test_labels_not_found_documentes(): + """Les 2 NOT_FOUND restants sont ceux documentes comme attendus.""" + _, report = _fresh_build() + assert sorted(report["by_source"][MAFALDA_SOURCE]["labels_not_found"]) == [ + "Appeal to values", + "Fallacy of logic", + ] + assert report["by_source"][LOGIC_SOURCE]["labels_not_found"] == [] + assert report["argumentum_families_never_hit"] == [] + + +# --------------------------------------------------------------------------- +# 5. L'ancre de reproductibilite ne porte aucun chemin machine. +# --------------------------------------------------------------------------- + +def test_ancre_de_reproductibilite_est_un_chemin_relatif(): + _, report = _fresh_build() + anchor = report["argumentum_csv"] + assert "\\" not in anchor, f"chemin Windows dans l'artefact : {anchor!r}" + assert ":" not in anchor, f"chemin absolu (lecteur) dans l'artefact : {anchor!r}" + assert not anchor.startswith("/"), f"chemin absolu dans l'artefact : {anchor!r}" + assert anchor == ( + "MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/data/" + "argumentum_fallacies_taxonomy.csv" + ) + + +def test_ancre_de_reproductibilite_porte_l_empreinte_du_csv(): + """L'empreinte rend l'ancre verifiable meme si le chemin devient illisible.""" + _, report = _fresh_build() + attendu = hashlib.sha256(_DEFAULT_TAXO.read_bytes()).hexdigest() + assert report["argumentum_csv_sha256"] == attendu + assert len(report["argumentum_csv_sha256"]) == 64 + + +# --------------------------------------------------------------------------- +# 6. Le nom porte la provenance, pas un cardinal (reserve 2 de NanoClaw). +# --------------------------------------------------------------------------- + +def test_le_nom_de_source_ne_porte_aucun_cardinal(): + """« Logic-13 » annoncait 13 pour 15 entrees : le nom ne compte plus rien.""" + assert LOGIC_SOURCE == "LOGIC" + assert not any(ch.isdigit() for ch in LOGIC_SOURCE), ( + "le libelle de source ne doit pas porter de cardinal -- le compte fait " + "foi dans le champ `total` du rapport et dans EXPECTED_COVERAGE" + ) + + +def test_le_cardinal_reel_est_celui_du_test_pas_du_nom(): + """Le chiffre qui fait foi : 15 entrees encodees, 23 pour MAFALDA-L2.""" + assert len(LOGIC_LABELS) == EXPECTED_COVERAGE[LOGIC_SOURCE]["total"] == 15 + assert len(MAFALDA_L2) == EXPECTED_COVERAGE[MAFALDA_SOURCE]["total"] == 23 + + +def test_les_mots_cles_commis_sont_relisibles(): + """Garde-fou de forme : la colonne keywords du CSV est une liste litterale.""" + committed = list(csv.DictReader(COMMITTED_CSV.read_text(encoding="utf-8").splitlines())) + for row in committed: + parsed = ast.literal_eval(row["keywords"]) + assert isinstance(parsed, list) and parsed + assert all(isinstance(k, str) and k for k in parsed) diff --git a/scripts/fallacy_detection/tests/test_align_academic_to_argumentum.py b/scripts/fallacy_detection/tests/test_align_academic_to_argumentum.py index 08e7d7f2ed..54b1e55e3c 100644 --- a/scripts/fallacy_detection/tests/test_align_academic_to_argumentum.py +++ b/scripts/fallacy_detection/tests/test_align_academic_to_argumentum.py @@ -22,10 +22,12 @@ from fallacy_detection.align_academic_to_argumentum import ( # noqa: E402 ARGUMENTUM_FAMILIES, + LOGIC_LABELS, + LOGIC_SOURCE, + MAFALDA_L2, + MAFALDA_SOURCE, Alignment, ArgumentumEntry, - LOGIC_13, - MAFALDA_L2, _jaccard, _norm, _tokens, @@ -170,10 +172,10 @@ def test_align_not_found_when_no_overlap(argum: list[ArgumentumEntry]): def test_build_returns_alignments_and_report(taxo_path: Path): alignments, report = build(taxo_path) - # Logic-13 (15 entrees encodees) + MAFALDA-L2 (23) = 38 alignements. - assert len(alignments) == len(LOGIC_13) + len(MAFALDA_L2) - assert "Logic13" in report["by_source"] - assert "MAFALDA-L2" in report["by_source"] + # LOGIC (15 entrees encodees) + MAFALDA-L2 (23) = 38 alignements. + assert len(alignments) == len(LOGIC_LABELS) + len(MAFALDA_L2) + assert LOGIC_SOURCE in report["by_source"] + assert MAFALDA_SOURCE in report["by_source"] assert report["argumentum_total_leaves"] == 3 @@ -213,7 +215,7 @@ def test_main_writes_outputs(tmp_path: Path, taxo_path: Path): # Le CSV a un header + au moins 38 lignes de donnees. text = out_csv.read_text(encoding="utf-8") assert "academic_label" in text.splitlines()[0] - assert len(text.splitlines()) >= 1 + len(LOGIC_13) + len(MAFALDA_L2) + assert len(text.splitlines()) >= 1 + len(LOGIC_LABELS) + len(MAFALDA_L2) def test_main_report_to_stdout(taxo_path: Path, capsys): @@ -221,8 +223,8 @@ def test_main_report_to_stdout(taxo_path: Path, capsys): assert rc == 0 captured = capsys.readouterr() assert "Couverture academique" in captured.out - assert "Logic13" in captured.out - assert "MAFALDA-L2" in captured.out + assert LOGIC_SOURCE in captured.out + assert MAFALDA_SOURCE in captured.out assert "Familles Argumentum touchees" in captured.out @@ -230,11 +232,11 @@ def test_main_report_to_stdout(taxo_path: Path, capsys): # Sanity : les listes academiques encodees sont conformes aux papiers. # --------------------------------------------------------------------------- -def test_logic_13_has_core_types(): - labels = {l for l, _ in LOGIC_13} +def test_logic_labels_has_core_types(): + labels = {l for l, _ in LOGIC_LABELS} # Les types les plus cites du papier Jin 2021 doivent etre presents. for core in ["Ad hominem", "Straw man", "Slippery slope", "Red herring"]: - assert core in labels, f"Logic-13 missing core type: {core}" + assert core in labels, f"inventaire LOGIC : type noyau manquant : {core}" def test_mafalda_l2_has_23_distinct_and_l1_categories():