diff --git a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08e-Argument-Profile-Python.ipynb b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08e-Argument-Profile-Python.ipynb index 85e79d896d..2f5a7b09c5 100644 --- a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08e-Argument-Profile-Python.ipynb +++ b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08e-Argument-Profile-Python.ipynb @@ -964,7 +964,9 @@ "\n", "**Lecon centrale** : les dimensions sont **independantes**. Un argument peut etre formellement valide mais rhétoriquement fallacieux ; clair mais mal fondé ; attaqué mais vrai. Le profil est le seul endroit ou cette indépendance devient visible — c'est pourquoi il est l'unite canonique de l'analyse argumentative, et le point d'ancrage de toute restitution narrative (cf [Restitution 3 Actes](Argumentation-08d-Restitution-3-Actes-Python.ipynb)).\n", "\n", - "**Note d'honnetete (G.2)** : ce notebook injecte les analyses **deterministement** (pas d'appel LLM) pour se concentrer sur la *structure* du profil. En production, les dimensions sont peuplées par les agents (detection de sophismes CamemBERT, scoring LLM, solveurs Tweety) — mais le contrat du profil, lui, est invariant.\n" + "**Note d'honnetete (G.2)** : ce notebook injecte les analyses **deterministement** (pas d'appel LLM) pour se concentrer sur la *structure* du profil. En production, les dimensions sont peuplées par les agents (detection de sophismes CamemBERT, scoring LLM, solveurs Tweety) — mais le contrat du profil, lui, est invariant.\n", + "\n", + "Le carnet suivant est la [Synthèse multidimensionnelle](Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb) : le catalogue light/standard/full qui orchestre l'ensemble de ces dimensions et en restitue la conclusion en trois actes.\n" ] } ], diff --git a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb new file mode 100644 index 0000000000..a129bda2a4 --- /dev/null +++ b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb @@ -0,0 +1,1276 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "titre", + "metadata": { + "papermill": { + "duration": 0.007479, + "end_time": "2026-10-12T04:08:35.279429+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:35.271950+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "# Synthèse multidimensionnelle — le catalogue light / standard / full\n", + "\n", + "Ce carnet est le dernier de la sous-série *exécution* : après les modes\n", + "d'orchestration (`07c`) et la fiche d'identité d'un argument (`08e`), il porte\n", + "le **fil conducteur du catalogue de niveaux** — quelles dimensions d'analyse\n", + "activer à quel niveau — et la **synthèse en 3 actes** qui en restitue le\n", + "résultat.\n", + "\n", + "Le catalogue vient de l'organe `workflow_levels.py` (étape 5 du volet B,\n", + "EPIC #4960) : un port **déterministe** de la recette pédagogique du catalogue\n", + "EPITA `orchestration/workflows.py`. Tout ce que ce carnet exécute est\n", + "rejouable hors ligne, à l'identique — aucun appel LLM, aucune clé API." + ] + }, + { + "cell_type": "markdown", + "id": "objectifs", + "metadata": { + "papermill": { + "duration": 0.005646, + "end_time": "2026-10-12T04:08:35.291297+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:35.285651+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Objectifs\n", + "\n", + "1. **Lire le catalogue** : les 9 dimensions, leur niveau minimal, l'organe\n", + " que chacune invoque (provenance jamais cachée).\n", + "2. **Exécuter les trois niveaux** sur le banc embarqué et observer la\n", + " gradation du fill-rate — la part de l'état d'analyse que chaque niveau\n", + " peuple.\n", + "3. **Croiser avec le sas EPITA** : les mesures du dépôt étudiant\n", + " (`orchestration_modes.py`) donnaient des DAG de 3/15/17 phases LLM ;\n", + " ici 3/6/9 dimensions déterministes. Les deux échelles ne sont pas\n", + " communes — et c'est documenté, pas maquillé." + ] + }, + { + "cell_type": "markdown", + "id": "rappels", + "metadata": { + "papermill": { + "duration": 0.004528, + "end_time": "2026-10-12T04:08:35.300545+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:35.296017+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Deux mondes, un même fil conducteur\n", + "\n", + "| | Sas EPITA (`07c`, mesures embarquées) | Ce carnet (organe `workflow_levels`) |\n", + "|---|---|---|\n", + "| Moteur | DAG d'agents LLM (+ Tweety) | organes déterministes de la série |\n", + "| Niveaux | light 3 / standard 15 / full 17 **phases** | light 3 / standard 6 / full 9 **dimensions** |\n", + "| Coût par run | 0,55 / 0,95 USD (BYOK mesuré) | 0 — rejouable hors ligne |\n", + "| Fill de l'état | 0,179 / 0,462 / 0,487 (LLM) | mesuré ci-dessous (déterministe) |\n", + "\n", + "L'emboîtement est strict dans les deux cas : light ⊂ standard ⊂ full. Les\n", + "constantes 3/15/17 sont portées par `orchestration_modes.DEPTH_PARITY` ; les\n", + "compteurs 3/6/9 sont **dérivés** du catalogue lui-même (aucun littéral durci)." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "setup", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:35.314447Z", + "iopub.status.busy": "2026-10-12T04:08:35.313958Z", + "iopub.status.idle": "2026-10-12T04:08:48.114767Z", + "shell.execute_reply": "2026-10-12T04:08:48.113195Z" + }, + "papermill": { + "duration": 12.809941, + "end_time": "2026-10-12T04:08:48.116175+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:35.306234+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "JVM demarree : True | is_jvm_started : True\n" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "spaCy : 3.8.16\n" + ] + } + ], + "source": [ + "import sys\n", + "from pathlib import Path\n", + "\n", + "# La serie vit dans ce dossier ; argumentation_lib et workflow_levels y resident.\n", + "BASE = Path.cwd()\n", + "if str(BASE) not in sys.path:\n", + " sys.path.insert(0, str(BASE))\n", + "\n", + "from argumentation_lib import initialize_jvm, is_jvm_started\n", + "\n", + "# Fail-loud : la serie refuse le mode degrade (anti-theatre, 00-Setup).\n", + "ok = initialize_jvm(verbose=False)\n", + "print(\"JVM demarree :\", ok, \"| is_jvm_started :\", is_jvm_started())\n", + "assert ok, \"JVM non demarree : verifier Tweety/libs (jar 7a-java8-shade)\"\n", + "\n", + "import spacy\n", + "print(\"spaCy :\", spacy.__version__)" + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "imports", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.135866Z", + "iopub.status.busy": "2026-10-12T04:08:48.135081Z", + "iopub.status.idle": "2026-10-12T04:08:48.153057Z", + "shell.execute_reply": "2026-10-12T04:08:48.151468Z" + }, + "papermill": { + "duration": 0.030653, + "end_time": "2026-10-12T04:08:48.154451+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.123798+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Niveaux : ('light', 'standard', 'full')\n", + "Dimensions au catalogue : 9\n", + "Parity note : Catalogue deterministe : 3/6/9 dimensions par niveau (emboitement strict). DAG LLM de la source : 3/15/17 phases (consta ...\n" + ] + } + ], + "source": [ + "import workflow_levels as wl\n", + "import orchestration_modes as om\n", + "\n", + "print(\"Niveaux :\", wl.LEVELS)\n", + "print(\"Dimensions au catalogue :\", len(wl.DIMENSIONS))\n", + "print(\"Parity note :\", wl.PARITY_NOTE.format(\n", + " n=\"3/6/9\", detail=\"emboitement strict\"\n", + ")[:120], \"...\")" + ] + }, + { + "cell_type": "markdown", + "id": "banc", + "metadata": { + "papermill": { + "duration": 0.007945, + "end_time": "2026-10-12T04:08:48.169721+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.161776+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Le banc : un débat municipal, annoté\n", + "\n", + "Le banc embarque un court débat sur la piétonisation d'un centre-ville, et\n", + "**ses annotations formelles déclarées** : traduction PL, AF de Dung, formule\n", + "de décision. Le passage du texte naturel à ces structures n'est **pas**\n", + "automatique ici — le gap NL→structure est documenté comme non bouché\n", + "déterministement (body #4960) ; les formules sont des artefacts du banc,\n", + "déclarés comme tels." + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "banc-texte", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.187664Z", + "iopub.status.busy": "2026-10-12T04:08:48.187242Z", + "iopub.status.idle": "2026-10-12T04:08:48.193041Z", + "shell.execute_reply": "2026-10-12T04:08:48.191640Z" + }, + "papermill": { + "duration": 0.017076, + "end_time": "2026-10-12T04:08:48.194065+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.176989+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Le conseil municipal doit interdire les echoppes du centre-ville, car elles detruisent le commerce de proximite. Si les echoppes detruisent le commerce, alors les boutiques centrales fermeront. On ne peut pas faire confiance à ce conseiller qui defend les echoppes. Selon une etude de boutique, la mesure est efficace. On a toujours fait comme ça, pourquoi changer la position de la ville ? Le premier pas vers le declin du centre-ville. Toute regulation qui protege le commerce de proximite merite d'etre essayee, et la fermeture des echoppes protege le commerce de proximite. Donc la fermeture des echoppes merite d'etre essayee.\n" + ] + } + ], + "source": [ + "print(wl.BENCH[\"raw_text\"])" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "banc-annotations", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.210217Z", + "iopub.status.busy": "2026-10-12T04:08:48.209681Z", + "iopub.status.idle": "2026-10-12T04:08:48.216656Z", + "shell.execute_reply": "2026-10-12T04:08:48.215337Z" + }, + "papermill": { + "duration": 0.017118, + "end_time": "2026-10-12T04:08:48.217892+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.200774+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "KB PL (3 formules) :\n", + "detruit_proximite\n", + "detruit_proximite => fermeture_boutiques\n", + "fermeture_echoppes => protege_proximite\n", + "\n", + "AF Dung — arguments : ['A1_interdire', 'A2_evidence', 'A3_fermeture', 'A4_ad_hominem', 'A5_regulation']\n", + "AF Dung — attaques : [['A4_ad_hominem', 'A1_interdire'], ['A2_evidence', 'A4_ad_hominem'], ['A5_regulation', 'A3_fermeture']]\n", + "\n", + "Formule de decision : fermeture_boutiques\n" + ] + } + ], + "source": [ + "print(\"KB PL (3 formules) :\")\n", + "print(wl.BENCH[\"pl_belief_set\"])\n", + "print()\n", + "print(\"AF Dung — arguments :\", wl.BENCH[\"dung_arguments\"])\n", + "print(\"AF Dung — attaques :\", wl.BENCH[\"dung_attacks\"])\n", + "print()\n", + "print(\"Formule de decision :\", wl.BENCH[\"decision_formula\"])" + ] + }, + { + "cell_type": "markdown", + "id": "banc-lecture", + "metadata": { + "papermill": { + "duration": 0.007453, + "end_time": "2026-10-12T04:08:48.232366+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.224913+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : ce que le banc embarque\n", + "\n", + "Le débat porte quatre sophismes — et pas n'importe lesquels : ses phrases sont\n", + "les **formes canoniques** mesurées sur l'organe de détection symbolique\n", + "(`fallacy_rules`, #17348). Sur le corpus de référence de cet organe, 4 formes\n", + "sur 9 matchent ; le banc n'embarque que des formes qui matchent, pour que la\n", + "dimension sophismes du niveau standard ait de la matière réelle. L'AF, elle,\n", + "condense le débat en 5 arguments et 3 attaques : l'attaque *ad hominem* y\n", + "est un argument qui en attaque un autre — exactement comme Dung le formalise." + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "catalogue", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.249653Z", + "iopub.status.busy": "2026-10-12T04:08:48.249088Z", + "iopub.status.idle": "2026-10-12T04:08:48.256176Z", + "shell.execute_reply": "2026-10-12T04:08:48.255138Z" + }, + "papermill": { + "duration": 0.017224, + "end_time": "2026-10-12T04:08:48.256993+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.239769+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "dimension niveau min organes\n", + "----------------------------------------------------------------------------------------\n", + "extract_arguments light releve deterministe (marqueurs)\n", + "pl_belief_set light Tweety PlParser + SimplePlReasoner (pattern 00-Setup)\n", + "pl_queries light Tweety SimplePlReasoner (requetes)\n", + "informal_fallacies standard fallacy_rules.py — organe symbolique #17348\n", + "dung_semantics standard Tweety Simple{Grounded,Preferred,Stable}Reasoner (Tweety-05)\n", + "fol_consistency standard DECLARE INDISPONIBLE (divergence 5 : jar sans logics.fol)\n", + "modal_queries full DECLAREE INDISPONIBLE (divergence 5 : jar sans logics.ml)\n", + "sat_decision full Tweety SimplePlReasoner sur la formule de decision\n", + "synthesis_3_actes full format du contrat 3 actes (carnet 08d)\n" + ] + } + ], + "source": [ + "print(f\"{'dimension':<22} {'niveau min':<12} organes\")\n", + "print(\"-\" * 88)\n", + "for d in wl.DIMENSIONS:\n", + " print(f\"{d.name:<22} {d.level_min:<12} {d.organ}\")" + ] + }, + { + "cell_type": "markdown", + "id": "catalogue-lecture", + "metadata": { + "papermill": { + "duration": 0.007446, + "end_time": "2026-10-12T04:08:48.269983+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.262537+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : le catalogue et ses organes\n", + "\n", + "Chaque dimension cite l'organe qu'elle invoque — les classes Tweety réelles\n", + "(`Simple*Reasoner`, noms mesurés dans le jar), l'organe symbolique de la\n", + "série (`fallacy_rules`), ou un état **déclaré** : deux dimensions du\n", + "catalogue (FOL, modale) sont annoncées **indisponibles dans cet arbre**, avec\n", + "leur preuve. C'est la différence entre un catalogue qui prétend et un\n", + "catalogue qui prouve." + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "catalogue-emboitement", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.288549Z", + "iopub.status.busy": "2026-10-12T04:08:48.287890Z", + "iopub.status.idle": "2026-10-12T04:08:48.294965Z", + "shell.execute_reply": "2026-10-12T04:08:48.293570Z" + }, + "papermill": { + "duration": 0.019701, + "end_time": "2026-10-12T04:08:48.296736+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.277035+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Dimensions actives : {'light': 3, 'standard': 6, 'full': 9}\n" + ] + } + ], + "source": [ + "comptages = [len(wl.dimensions_at(niveau)) for niveau in wl.LEVELS]\n", + "print(\"Dimensions actives :\", dict(zip(wl.LEVELS, comptages)))\n", + "assert comptages == [3, 6, 9], \"l'emboitement est structurel, pas un litteral\"" + ] + }, + { + "cell_type": "markdown", + "id": "niveau-light", + "metadata": { + "papermill": { + "duration": 0.007751, + "end_time": "2026-10-12T04:08:48.311896+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.304145+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Niveau light : relever et requêter\n", + "\n", + "Trois dimensions : le relevé des arguments (marqueurs déterministes), la KB\n", + "PL posée, la requête Tweety. C'est le niveau « lecteur pressé » : il répond à\n", + "*que dit ce texte, et que peut-on en dériver formellement ?*" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "run-light", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.329806Z", + "iopub.status.busy": "2026-10-12T04:08:48.329281Z", + "iopub.status.idle": "2026-10-12T04:08:48.399219Z", + "shell.execute_reply": "2026-10-12T04:08:48.397979Z" + }, + "papermill": { + "duration": 0.080353, + "end_time": "2026-10-12T04:08:48.400300+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.319947+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[light] 3 dimensions executees, fill 0.71 (5/7 champs peuples)" + ] + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "\n", + " - extract_arguments: 4 segments releves (marqueurs deterministes)\n", + " - pl_belief_set: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + " - pl_queries: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + "Reponses aux requetes : {'fermeture_boutiques': 'True'}\n" + ] + } + ], + "source": [ + "etat_light, run_light = wl.run_level(wl.BENCH, \"light\")\n", + "print(run_light.verdict_band())\n", + "for note in run_light.notes:\n", + " print(\" -\", note)\n", + "print(\"Reponses aux requetes :\", etat_light.answers)" + ] + }, + { + "cell_type": "markdown", + "id": "run-light-lecture", + "metadata": { + "papermill": { + "duration": 0.005943, + "end_time": "2026-10-12T04:08:48.414390+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.408447+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : light\n", + "\n", + "Le fill-rate monte à 0,71 : cinq champs de l'état sur sept sont peuplés. La\n", + "requête `fermeture_boutiques` est **dérivée** (réponse vraie) dès ce niveau —\n", + "la KB embarque la chaîne complète (fait + règles), et `SimplePlReasoner`\n", + "n'a besoin de rien d'autre. Ce que le light ne fait pas encore : aucun\n", + "sophisme relevé, aucune sémantique d'attaque — les champs restent vides,\n", + "et c'est exactement ce que le fill-report montre." + ] + }, + { + "cell_type": "markdown", + "id": "exo1-enonce", + "metadata": { + "papermill": { + "duration": 0.007007, + "end_time": "2026-10-12T04:08:48.428324+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.421317+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 1 — Prédire le relevé\n", + "\n", + "La dimension `extract_arguments` retient les phrases portant un marqueur\n", + "d'argumentation (`car`, `donc`, `alors`, `si`, `toute`, `tout le monde`,\n", + "`merite`). Ajoutez au banc une phrase **sans** marqueur puis une phrase\n", + "**avec**, et prédisez le compte de segments avant de relancer le niveau\n", + "light." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "exo1-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.445170Z", + "iopub.status.busy": "2026-10-12T04:08:48.444589Z", + "iopub.status.idle": "2026-10-12T04:08:48.450764Z", + "shell.execute_reply": "2026-10-12T04:08:48.449272Z" + }, + "papermill": { + "duration": 0.015138, + "end_time": "2026-10-12T04:08:48.451912+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.436774+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer\n" + ] + } + ], + "source": [ + "# Exercice a completer\n", + "# 1. Construire banc_ex1 = dict(wl.BENCH) avec raw_text modifie :\n", + "# - une phrase sans marqueur (ex. \"Les echoppes durent toute l'annee.\")\n", + "# - une phrase avec marqueur (ex. \"... car la ville doit vivre.\")\n", + "# 2. Predire le nombre de segments, ecrire la prediction ici.\n", + "# 3. Relancer wl.run_level(banc_ex1, \"light\") et comparer.\n", + "prediction = None # TODO etudiant : nombre de segments attendu\n", + "# ... votre code ...\n", + "print(\"Exercice a completer\")" + ] + }, + { + "cell_type": "markdown", + "id": "niveau-standard", + "metadata": { + "papermill": { + "duration": 0.00731, + "end_time": "2026-10-12T04:08:48.466128+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.458818+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Niveau standard : attaquer\n", + "\n", + "Le standard ajoute la détection de sophismes (organe symbolique) et\n", + "l'analyse d'acceptabilité de Dung (trois sémantiques, classes Tweety\n", + "réelles). Le texte cesse d'être une suite d'affirmations : il devient un\n", + "graphe d'attaques dont on calcule ce qui **tient**." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "run-standard", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:48.484219Z", + "iopub.status.busy": "2026-10-12T04:08:48.483712Z", + "iopub.status.idle": "2026-10-12T04:08:55.320871Z", + "shell.execute_reply": "2026-10-12T04:08:55.319593Z" + }, + "papermill": { + "duration": 6.847775, + "end_time": "2026-10-12T04:08:55.322097+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:48.474322+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[standard] 5 dimensions executees, 1 declarees indisponibles, fill 0.86 (6/7 champs peuples)\n", + " - extract_arguments: 4 segments releves (marqueurs deterministes)\n", + " - pl_belief_set: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + " - pl_queries: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + " - informal_fallacies: 4 sophisme(s) releve(s) par motifs symboliques (AD_HOMINEM_DIRECT, APPEL_A_LA_TRADITION, ARGUMENT_AUTORITE, PENTE_GLISSANTE)\n", + " - dung_semantics: AF sous 3 semantiques : {'grounded': 1, 'preferred': 1, 'stable': 1}\n", + " - fol_consistency: INDISPONIBLE — organe FOL absent : 0 classe logics.fol dans le jar 7a-java8-shade (inventaire zipfile) ; handler porte en dependances non vendues (prover9, TweetyInitializer C186f) — note G.9 de argumentation_lib\n" + ] + } + ], + "source": [ + "etat_std, run_std = wl.run_level(wl.BENCH, \"standard\")\n", + "print(run_std.verdict_band())\n", + "for note in run_std.notes:\n", + " print(\" -\", note)" + ] + }, + { + "cell_type": "markdown", + "id": "run-standard-lecture", + "metadata": { + "papermill": { + "duration": 0.006822, + "end_time": "2026-10-12T04:08:55.337218+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.330396+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : standard\n", + "\n", + "Quatre sophismes relevés, sur quatre clés distinctes — les formes canoniques\n", + "du banc ont toutes matché. L'AF sous trois sémantiques rend : une extension\n", + "**grounded** (la plus prudente — ce qui survit à tout doute), et les\n", + "extensions preferred/stable. La note `1 declaree(s) indisponible(s)` est la\n", + "dimension FOL : le catalogue l'annonce, le run la rend avec sa preuve — on\n", + "l'ouvre au niveau full." + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "run-standard-fallacies", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.351517Z", + "iopub.status.busy": "2026-10-12T04:08:55.350998Z", + "iopub.status.idle": "2026-10-12T04:08:55.357774Z", + "shell.execute_reply": "2026-10-12T04:08:55.356689Z" + }, + "papermill": { + "duration": 0.014484, + "end_time": "2026-10-12T04:08:55.358879+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.344395+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "AD_HOMINEM_DIRECT On ne peut pas faire confiance à ce conseiller\n", + "ARGUMENT_AUTORITE Selon une etude de\n", + "APPEL_A_LA_TRADITION On a toujours fait comme ça\n", + "PENTE_GLISSANTE Le premier pas vers le\n" + ] + } + ], + "source": [ + "for f in etat_std.identified_fallacies:\n", + " print(f\"{f['key']:<24} {f['excerpt'][:52]}\")" + ] + }, + { + "cell_type": "markdown", + "id": "run-standard-fallacies-lecture", + "metadata": { + "papermill": { + "duration": 0.005098, + "end_time": "2026-10-12T04:08:55.371435+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.366337+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : les quatre sophismes\n", + "\n", + "Chaque détection porte sa clé, son extrait et sa provenance\n", + "(`fallacy_rules#17348`). La limite de l'organe est mesurée et assumée : ses\n", + "motifs spaCy attrapent les formes canoniques, pas les paraphrases — sur le\n", + "corpus de référence, 4 formes sur 9 matchent. La taxonomie complète (1552\n", + "sophismes, pilotée LLM dans le sas EPITA) n'est pas standalone-importable\n", + "dans cet arbre : sa dépendance SK amont n'est pas vendue. C'est la\n", + "divergence 6 du contrat de fidélité de l'organe." + ] + }, + { + "cell_type": "markdown", + "id": "exo2-enonce", + "metadata": { + "papermill": { + "duration": 0.005777, + "end_time": "2026-10-12T04:08:55.383184+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.377407+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 2 — Faire tomber un argument\n", + "\n", + "L'AF du banc porte 3 attaques. Ajoutez une attaque de `A3_fermeture` vers\n", + "`A1_interdire` et relancez le niveau standard : comment l'extension grounded\n", + "change-t-elle ? Un argument attaqué non défendu n'appartient jamais à\n", + "l'extension grounded — prédisez lesquels survivent avant de exécuter." + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "exo2-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.398361Z", + "iopub.status.busy": "2026-10-12T04:08:55.397908Z", + "iopub.status.idle": "2026-10-12T04:08:55.402843Z", + "shell.execute_reply": "2026-10-12T04:08:55.401770Z" + }, + "papermill": { + "duration": 0.014111, + "end_time": "2026-10-12T04:08:55.403630+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.389519+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer\n" + ] + } + ], + "source": [ + "# Exercice a completer\n", + "# 1. banc_ex2 = dict(wl.BENCH) avec dung_attacks + [\"A3_fermeture\", \"A1_interdire\"]\n", + "# 2. Predire le contenu de l'extension grounded.\n", + "grounded_attendu = None # TODO etudiant : liste de noms d'arguments attendus\n", + "# 3. Relancer wl.run_level(banc_ex2, \"standard\") et lire\n", + "# etat.belief_sets[\"dung_extensions\"].\n", + "print(\"Exercice a completer\")" + ] + }, + { + "cell_type": "markdown", + "id": "niveau-full", + "metadata": { + "papermill": { + "duration": 0.004994, + "end_time": "2026-10-12T04:08:55.414187+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.409193+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Niveau full : décider, synthétiser — et prouver les absences\n", + "\n", + "Le full ajoute la décision (la formule d'engagement interrogée sur la KB\n", + "complète) et la synthèse 3 actes. Il porte aussi les deux dimensions\n", + "**déclarées indisponibles** — FOL et modale — dont l'absence d'organe est\n", + "prouvée ci-dessous par l'inventaire du jar." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "run-full", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.429806Z", + "iopub.status.busy": "2026-10-12T04:08:55.429245Z", + "iopub.status.idle": "2026-10-12T04:08:55.461379Z", + "shell.execute_reply": "2026-10-12T04:08:55.460144Z" + }, + "papermill": { + "duration": 0.04329, + "end_time": "2026-10-12T04:08:55.462493+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.419203+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "[full] 7 dimensions executees, 2 declarees indisponibles, fill 1.00 (7/7 champs peuples)\n", + " - extract_arguments: 4 segments releves (marqueurs deterministes)\n", + " - pl_belief_set: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + " - pl_queries: KB PL coherente ; 1 requete(s) Tweety (fermeture_boutiques)\n", + " - informal_fallacies: 4 sophisme(s) releve(s) par motifs symboliques (AD_HOMINEM_DIRECT, APPEL_A_LA_TRADITION, ARGUMENT_AUTORITE, PENTE_GLISSANTE)\n", + " - dung_semantics: AF sous 3 semantiques : {'grounded': 1, 'preferred': 1, 'stable': 1}\n", + " - fol_consistency: INDISPONIBLE — organe FOL absent : 0 classe logics.fol dans le jar 7a-java8-shade (inventaire zipfile) ; handler porte en dependances non vendues (prover9, TweetyInitializer C186f) — note G.9 de argumentation_lib\n", + " - modal_queries: INDISPONIBLE — organe modal absent : 0 classe logics.ml dans le jar 7a-java8-shade (inventaire zipfile) ; handler porte en siblings non vendus (normalizer, TweetyInitializer C186f) — note G.9 de argumentation_lib\n", + " - sat_decision: Formule de decision : fermeture_boutiques => True\n", + " - synthesis_3_actes: conclusion 3 actes ecrite dans l'etat\n" + ] + } + ], + "source": [ + "etat_full, run_full = wl.run_level(wl.BENCH, \"full\")\n", + "print(run_full.verdict_band())\n", + "for note in run_full.notes:\n", + " print(\" -\", note)" + ] + }, + { + "cell_type": "markdown", + "id": "run-full-lecture", + "metadata": { + "papermill": { + "duration": 0.007438, + "end_time": "2026-10-12T04:08:55.477524+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.470086+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : full\n", + "\n", + "Le fill atteint 1,00 : les sept champs utiles de l'état sont peuplés, la\n", + "décision `fermeture_boutiques` est **dérivée** de la KB complète (les trois\n", + "formules, pas le seul fait initial — c'est toute la chaîne qui porte la\n", + "dérivation), et la conclusion 3 actes est écrite. Les deux dimensions\n", + "indisponibles ne comptent ni comme exécutées ni comme échecs : elles sont\n", + "rendues **avec leur preuve**. Vérifions-la nous-mêmes." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "preuve-jar", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.494290Z", + "iopub.status.busy": "2026-10-12T04:08:55.493777Z", + "iopub.status.idle": "2026-10-12T04:08:55.507533Z", + "shell.execute_reply": "2026-10-12T04:08:55.506301Z" + }, + "papermill": { + "duration": 0.023401, + "end_time": "2026-10-12T04:08:55.508637+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.485236+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Jar inspecte : org.tweetyproject.tweety-7a-java8-shade.jar\n", + "Sous-packages logics du jar :\n", + " - org.tweetyproject.logics.commons\n", + " - org.tweetyproject.logics.commons.analysis\n", + " - org.tweetyproject.logics.commons.analysis.streams\n", + " - org.tweetyproject.logics.commons.error\n", + " - org.tweetyproject.logics.commons.syntax\n", + " - org.tweetyproject.logics.commons.syntax.interfaces\n", + " - org.tweetyproject.logics.pl.analysis\n", + " - org.tweetyproject.logics.pl.error\n", + " - org.tweetyproject.logics.pl.parser\n", + " - org.tweetyproject.logics.pl.postulates\n", + " - org.tweetyproject.logics.pl.reasoner\n", + " - org.tweetyproject.logics.pl.sat\n", + " - org.tweetyproject.logics.pl.semantics\n", + " - org.tweetyproject.logics.pl.syntax\n", + " - org.tweetyproject.logics.pl.util\n", + " - org.tweetyproject.logics.pl.writer\n", + "Classes logics.fol : 0 | Classes logics.ml : 0\n" + ] + } + ], + "source": [ + "import zipfile\n", + "\n", + "JAR = BASE.parent / \"Tweety\" / \"libs\" / \"org.tweetyproject.tweety-7a-java8-shade.jar\"\n", + "print(\"Jar inspecte :\", JAR.name)\n", + "with zipfile.ZipFile(JAR) as jar:\n", + " packages = sorted({\n", + " n.rsplit(\"/\", 1)[0]\n", + " for n in jar.namelist()\n", + " if n.endswith(\".class\") and \"/logics/\" in n\n", + " })\n", + "print(\"Sous-packages logics du jar :\")\n", + "for p in packages:\n", + " print(\" -\", p.replace(\"/\", \".\"))\n", + "fol = sum(\"logics/fol\" in p for p in packages)\n", + "ml = sum(\"logics/ml\" in p for p in packages)\n", + "print(\"Classes logics.fol :\", fol, \"| Classes logics.ml :\", ml)" + ] + }, + { + "cell_type": "markdown", + "id": "preuve-jar-lecture", + "metadata": { + "papermill": { + "duration": 0.006792, + "end_time": "2026-10-12T04:08:55.523429+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.516637+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : la preuve d'indisponibilité\n", + "\n", + "Le jar que charge la série ne porte que `logics.commons` et `logics.pl` :\n", + "aucune classe FOL, aucune classe modale. Les handlers portés dans\n", + "`argumentation_lib` le documentent eux-mêmes (notes G.9 du `__init__`) :\n", + "leurs dépendances amont — prover9, `TweetyInitializer`, le normalizer\n", + "modal — ne sont pas vendues dans cet arbre. Le sas EPITA exécutait FOL et\n", + "modale sur **ses** jars 1.28, dans un dossier étudiant que le volet B\n", + "s'interdit d'importer. La remontée attend les sous-tranches C186f/C186g :\n", + "jusque-là, le catalogue annonce, le run prouve, le carnet ne prétend pas." + ] + }, + { + "cell_type": "markdown", + "id": "exo3-enonce", + "metadata": { + "papermill": { + "duration": 0.007605, + "end_time": "2026-10-12T04:08:55.536661+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.529056+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 3 — Construire la non-dérivation\n", + "\n", + "La décision du banc est dérivée parce que la KB porte les trois formules.\n", + "Construisez un banc où la formule de décision **n'est plus dérivée** :\n", + "retirez la règle `detruit_proximite => fermeture_boutiques` de la KB et\n", + "relancez le niveau full. Que répond la dimension décision ? Quel champ du\n", + "fill-report change, le cas échéant ?" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "exo3-stub", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.554251Z", + "iopub.status.busy": "2026-10-12T04:08:55.553992Z", + "iopub.status.idle": "2026-10-12T04:08:55.559012Z", + "shell.execute_reply": "2026-10-12T04:08:55.557905Z" + }, + "papermill": { + "duration": 0.015167, + "end_time": "2026-10-12T04:08:55.560095+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.544928+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice a completer\n" + ] + } + ], + "source": [ + "# Exercice a completer\n", + "# 1. banc_ex3 = dict(wl.BENCH) avec pl_belief_set sans la 2e regle.\n", + "# 2. Predire la reponse de la dimension decision.\n", + "decision_attendue = None # TODO etudiant : True / False attendu\n", + "# 3. Relancer wl.run_level(banc_ex3, \"full\") et lire la note sat_decision.\n", + "print(\"Exercice a completer\")" + ] + }, + { + "cell_type": "markdown", + "id": "croisement", + "metadata": { + "papermill": { + "duration": 0.005053, + "end_time": "2026-10-12T04:08:55.571957+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.566904+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Croisement avec le sas EPITA\n", + "\n", + "L'organe `orchestration_modes.py` (carnet `07c`) porte les mesures du sas :\n", + "les fill-rates LLM par niveau et la parité de profondeur. Croisons-les avec\n", + "ce que le catalogue déterministe vient de produire." + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "croisement-fill", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.585109Z", + "iopub.status.busy": "2026-10-12T04:08:55.584712Z", + "iopub.status.idle": "2026-10-12T04:08:55.592071Z", + "shell.execute_reply": "2026-10-12T04:08:55.590761Z" + }, + "papermill": { + "duration": 0.015013, + "end_time": "2026-10-12T04:08:55.593203+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.578190+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "mode phases fill UB fill cal\n", + "pipeline_standard 6/15 0.196 0.462\n", + "pipeline_light 3/3 0.179 0.179\n", + "pipeline_full 7/17 0.196 0.487\n", + "conversational 1/3 0.096 0.308\n", + "conversation_deterministic 3/3 n/a n/a\n", + "hierarchical_bridge 2/4 0.118 n/a\n", + "hierarchical_delegation 1/5 n/a n/a\n", + "DEPTH_PARITY : (DepthParityRow(mode='pipeline_light', depth_dimension='workflow phases (DAG)', depth_count=3, nature='breadth', measured_range=None), DepthParityRow(mode='pipeline_standard', depth_dimension='workflow phases (DAG)', depth_count=15, nature='breadth', measured_range=None), DepthParityRow(mode='pipeline_full', depth_dimension='workflow phases (DAG)', depth_count=17, nature='breadth', measured_range=None), DepthParityRow(mode='hierarchical_bridge', depth_dimension='strategic objectives (default axes)', depth_count=4, nature='delegation', measured_range=None), DepthParityRow(mode='hierarchical_delegation', depth_dimension='strategic objectives (LLM-derived, measured)', depth_count=4, nature='delegation (3-tier depth)', measured_range='4-4 objectifs -> 5-5 taches (n=3, corpus_A/B/C, firsthand R711, po-2023 projet-is)'), DepthParityRow(mode='conversational', depth_dimension='dialogue macro-phases (multi-turn)', depth_count=3, nature='dialogue-depth', measured_range=None), DepthParityRow(mode='conversation_deterministic', depth_dimension='dialogue macro-phases (deterministic)', depth_count=3, nature='dialogue-depth (no LLM)', measured_range=None))\n" + ] + } + ], + "source": [ + "ub_liste, cal_liste = om.RUNS[\"under_budget\"], om.RUNS[\"calibrated\"]\n", + "\n", + "def fmt(v):\n", + " # Un run non termine n'a pas de fill : le dire, jamais le fabriquer.\n", + " return f\"{v:.3f}\" if v is not None else \" n/a\"\n", + "\n", + "print(f\"{'mode':<24} {'phases':>6} {'fill UB':>8} {'fill cal':>9}\")\n", + "for ub, cal in zip(ub_liste, cal_liste):\n", + " print(f\"{ub.mode:<24} {ub.phases_completed:>2}/{ub.phases_total:<3}\"\n", + " f\" {fmt(ub.state_fill_rate):>8} {fmt(cal.state_fill_rate):>9}\")\n", + "print(\"DEPTH_PARITY :\", om.DEPTH_PARITY)" + ] + }, + { + "cell_type": "markdown", + "id": "croisement-lecture", + "metadata": { + "papermill": { + "duration": 0.009071, + "end_time": "2026-10-12T04:08:55.612971+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.603900+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : deux échelles, une asymétrie documentée\n", + "\n", + "Les trois modes *pipeline* du sas montrent la gradation LLM : 0,179\n", + "(pipeline_light, sous budget), 0,462 (pipeline_standard calibré), 0,487\n", + "(pipeline_full calibré) — quand le catalogue déterministe produit\n", + "0,71 / 0,86 / 1,00. Les deux ne mesurent **pas** la même chose : le premier\n", + "compte des champs peuplés par des agents qui peuvent échouer, dévier ou\n", + "refuser (et certains runs du tableau rendent `n/a` — non terminé, jamais\n", + "fabriqué), le second des champs peuplés par des organes qui répondent\n", + "toujours. Les DAG de la source (3/15/17 phases) n'ont pas non plus de\n", + "contrepartie numérique ici (3/6/9 dimensions). L'asymétrie est documentée\n", + "dans `orchestration_modes.DEPTH_PARITY` et dans le contrat de fidélité de\n", + "`workflow_levels` : **portée, pas réparée** — maquiller une parité de\n", + "nombres serait exactement le contre-sens que le volet B refuse." + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "conclusion-3-actes", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-12T04:08:55.632138Z", + "iopub.status.busy": "2026-10-12T04:08:55.631611Z", + "iopub.status.idle": "2026-10-12T04:08:55.636959Z", + "shell.execute_reply": "2026-10-12T04:08:55.635715Z" + }, + "papermill": { + "duration": 0.016794, + "end_time": "2026-10-12T04:08:55.638105+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.621311+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Acte I — 4 arguments releves, KB posees : dung_extensions, pl. Acte II — 4 sophisme(s) releve(s), AF attaquee. Acte III — 4 requetes formelles executees, verdict par semantique Dung. (format compact du contrat 3 actes ; renderer integral : carnet 08d)\n" + ] + } + ], + "source": [ + "print(etat_full.final_conclusion)" + ] + }, + { + "cell_type": "markdown", + "id": "conclusion-3-actes-lecture", + "metadata": { + "papermill": { + "duration": 0.008385, + "end_time": "2026-10-12T04:08:55.655385+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.647000+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : les trois actes\n", + "\n", + "Acte I ce qui est posé (arguments relevés, KB posées) ; Acte II ce qui est\n", + "attaqué (sophismes, AF) ; Acte III ce qui tient (requêtes exécutées, verdict\n", + "par sémantique). C'est le **format compact** du contrat 3 actes — le\n", + "renderer intégral (narration, rendu, mise en forme complète) vit dans le\n", + "carnet `08d`, où il est embarqué et démontré ; l'extraire vers la lib est le\n", + "refactoring à demander à la série source (copie pédagogique déclarée,\n", + "organ-first)." + ] + }, + { + "cell_type": "markdown", + "id": "conclusion", + "metadata": { + "papermill": { + "duration": 0.008184, + "end_time": "2026-10-12T04:08:55.672489+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.664305+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Ce que ce carnet a établi\n", + "\n", + "- **Un catalogue exécutable** : 9 dimensions dont 7 réellement exécutées\n", + " ici — relevé, PL, sophismes symboliques, Dung (3 sémantiques), décision,\n", + " synthèse — chaque dimension citant son organe.\n", + "- **Des absences prouvées** : FOL et modale déclarées indisponibles avec\n", + " preuve d'inventaire du jar — pas de silence, pas de faux résultat.\n", + "- **Une gradation mesurée** : fill 0,71 / 0,86 / 1,00, lisible champ par\n", + " champ dans le fill-report, et croisée avec les mesures LLM du sas sans\n", + " prétendre à une parité d'échelle qui n'existe pas." + ] + }, + { + "cell_type": "markdown", + "id": "pour-aller-plus-loin", + "metadata": { + "papermill": { + "duration": 0.007858, + "end_time": "2026-10-12T04:08:55.689157+00:00", + "exception": false, + "start_time": "2026-10-12T04:08:55.681299+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Pour aller plus loin\n", + "\n", + "- `Argumentation-08d` — le renderer 3 actes intégral (narration *gated*,\n", + " mise en forme complète) dont ce carnet n'utilise que le format compact.\n", + "- `orchestration_modes` (carnet `07c`) — les 7 modes du sas, leurs mesures\n", + " de coût et la parité de profondeur.\n", + "- La remontée FOL/modale : sous-tranches C186f (`TweetyInitializer`) et\n", + " C186g (shim JPype) — le jour où un jar complet rejoint la série, les deux\n", + " dimensions déclarées indisponibles deviennent exécutables sans changer\n", + " le catalogue." + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.13.7" + }, + "papermill": { + "default_parameters": {}, + "duration": 26.567304, + "end_time": "2026-10-12T04:08:58.981768+00:00", + "environment_variables": {}, + "exception": null, + "input_path": "Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb", + "output_path": "Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb", + "parameters": {}, + "start_time": "2026-10-12T04:08:32.414464+00:00", + "version": "2.7.0" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/README.md b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/README.md index e399c353ad..d3de59ee2b 100644 --- a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/README.md +++ b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/README.md @@ -86,6 +86,7 @@ L'analyse argumentative outillée s'inscrit dans plusieurs cas concrets où la d | 08c | [Argumentation-08c-UI-Configuration-Python](Argumentation-08c-UI-Configuration-Python.ipynb) | Interface utilisateur widgets | Interaction | | 08d | [Argumentation-08d-Restitution-3-Actes-Python](Argumentation-08d-Restitution-3-Actes-Python.ipynb) | Restitution honnête en 3 actes : scaffold déterministe pur stdlib (evidence réel-en-état, bande de verdict *gated*, gate de lisibilité §4, renderer *fail-loud*) + narration LLM **réelle** (SDK OpenAI, clé via `GenAI/.env`) *gated* — prompts conduits, callable injectable, fail-loud sans clé | Restitution / honnêteté | | 08e | [Argumentation-08e-Argument-Profile-Python](Argumentation-08e-Argument-Profile-Python.ipynb) | Vue agrégée par argument (`ArgumentProfile`) : réunit les 5 dimensions (sophismes, qualité, contre-arguments, JTMS, formel) en une fiche exploitable, et trie un débat par force (arguments faibles / fallacieux). Démontre l'**indépendance des dimensions** (valide formellement ≠ non fallacieux). Auto-contenu, déterministe, sans LLM | Vue agrégée / multidimensionnelle | +| 08f | [Argumentation-08f-Synthese-Multidimensionnelle-Python](Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb) | Synthèse par niveaux via le catalogue light/standard/full (organe `workflow_levels`, étape 5 volet B #4960) : 9 dimensions dont 7 exécutées réellement (PL `SimplePlReasoner`, sophismes `fallacy_rules` #17348, Dung `Simple*Reasoner`, décision dérivée, synthèse 3 actes) et 2 **déclarées indisponibles avec preuve** (FOL/modale : inventaire du jar 7a, dépendances non vendues) ; gradation fill 0,71/0,86/1,00 croisée avec les fill LLM du sas (`07c`) sans prétendre à une parité d'échelle | Synthèse / catalogue de niveaux | | 0* | [Agentic-0-init_agent](_archive/Argument_Analysis_Agentic-0-init_agent.ipynb) | *(legacy, archivé)* Configuration LLM/OpenAI (semantic_kernel) | Setup | | 1* | [Agentic-1-informal_agent](_archive/Argument_Analysis_Agentic-1-informal_agent.ipynb) | *(legacy, archivé)* Agent analyse informelle | Détection d'arguments | | 2* | [Agentic-2-pl_agent](_archive/Argument_Analysis_Agentic-2-pl_agent.ipynb) | *(legacy, archivé)* Agent logique propositionnelle | Formalisation | @@ -434,7 +435,7 @@ Le titre annonce l'analyse d'arguments. Mais le geste que cette série enseigne - [Semantic Kernel Docs](https://learn.microsoft.com/en-us/semantic-kernel/) - [TweetyProject](https://tweetyproject.org/) -## Ordre partiel et prérequis — mapping exhaustif (34/34, version consolidée) +## Ordre partiel et prérequis — mapping exhaustif (version consolidée ; le périmètre à jour vit dans le catalogue régénéré) Cette section pose un **ordre partiel** sur l'ensemble des notebooks du répertoire, fondé sur les déclarations de prérequis et les chaînes de @@ -460,6 +461,9 @@ sous `groupe-I2-contre-arguments-aspic/`) : `Obs-02-Initiation` `Communication_Channels` — mesure re-vérifiée au 2026-09-23 (`git ls-tree` : 27 → 32 à la racine, aucune disparition). Chaque notebook est balisé dans un des trois arcs ou déclaré hors-arc avec sa raison. +Puis `Argumentation-08f-Synthese-Multidimensionnelle` (étape 5 volet B #4960, +organe `workflow_levels`) rejoint la série comme transverse de synthèse — le +compte à jour du répertoire vit dans le catalogue régénéré. La présente section consolide la version c.1030 (livrée par session antérieure) et le mapping exhaustif 28/28 (PR #15371) — les deux contributions sont fusionnées sans doublon, le verdict renum (#15283) reste préservé ci-dessous. @@ -537,7 +541,7 @@ Dung_AF_Semantics ──→ Value_Based_AF Toulmin_Model (indépendant, racine propre) ``` -### Hors-arc (14/34) — chaque notebook restant, avec sa raison +### Hors-arc — chaque notebook restant, avec sa raison | Notebook | Raison du hors-arc | |----------|--------------------| @@ -549,6 +553,7 @@ Toulmin_Model (indépendant, racine propre) | `Argumentation-Obs-03-Recollement-Lectures-Python.ipynb` | **Transverse (consolidation)** : lectures croisées de la série | | `Argumentation-Obs-04-Recollement-Strate6-Python.ipynb` | **Transverse (consolidation)** : strate 6 du récollement | | `Argumentation-08e-Argument-Profile-Python.ipynb` | **Transverse (vue agrégée)** : fiche réunissant les 5 dimensions (sophismes, qualité, contre-arguments, JTMS, formel) — consomme les arcs 1, 2 et 3 | +| `Argumentation-08f-Synthese-Multidimensionnelle-Python.ipynb` | **Transverse (synthèse)** : catalogue de niveaux light/standard/full (organe `workflow_levels`, #4960 étape 5) — exécute 7 dimensions réelles (PL, sophismes `fallacy_rules`, Dung, décision) et prouve les 2 indisponibles (FOL/modale) ; après `07c` et `08e` | | `Argumentation-08c-UI-Configuration-Python.ipynb` | **Transverse (interaction)** : interface utilisateur widgets, parcours alternatif « 0 → 1 → UI_configuration » | | `Argumentation-02b-Argumentum-Cards-Python.ipynb` | **Hors-arc (production)** : deck imprimable du jeu Argumentum (176 cartes depuis la taxonomie) — consomme le socle ontologique, n'est pas un jalon de progression | | `Argumentation-Onto-01-AIF-OWL2-Python.ipynb` | **Fondation (socle ontologique)** : lecture de l'OWL upstream (1 513 classes / 1 509 concepts, 6,8 MB) — outillage, pas un jalon d'apprentissage | @@ -571,7 +576,8 @@ sont des **ordres partiels** : est explicitement cité par `05 §6`, et `06` cite Dung 1995 dans son introduction. Les **transverses** (`Executor`, `Multi_Backend_Routing`, `Communication_Channels`, `Formal_Richness_Matrix`, `Restitution_3_Actes`, - `Recollement_*`, `ArgumentProfile`, `UI_configuration`) se croisent avec les arcs sans chaîne + `Recollement_*`, `ArgumentProfile`, `UI_configuration`, + `Synthese_Multidimensionnelle`) se croisent avec les arcs sans chaîne de prérequis stricte — voir « Limites » ci-dessous. ### Arithmétique (résolution du désaccord 14 vs 15 de #15283) diff --git a/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/workflow_levels.py b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/workflow_levels.py new file mode 100644 index 0000000000..524ac6c7c6 --- /dev/null +++ b/MyIA.AI.Notebooks/SymbolicAI/Argument_Analysis/workflow_levels.py @@ -0,0 +1,517 @@ +# -*- coding: utf-8 -*- +"""Catalogue de niveaux d'analyse light / standard / full — etape 5 du volet B (EPIC #4960). + +Port deterministe de la RECETTE pedagogique du catalogue EPITA +``argumentation_analysis/orchestration/workflows.py`` (lignes 152-610) : +quelles dimensions activer a quel niveau. La source orchestrait des agents +LLM (+ Tweety) en DAG de 3 / 15 / 17 phases ; ce port orchestre les +ORGANES DETERMINISTES de la serie (TweetyBridge, TaxonomySophismDetector) +en catalogues de 3 / 6 / 9 dimensions. La parite est STRUCTURELLE, pas +numerique : les niveaux s'emboitent strictement (light ⊂ standard ⊂ full), +comme les DAG de la source -- les constantes 3/15/17 sont mesurees et +portees par ``orchestration_modes.DEPTH_PARITY`` ; les compteurs 3/6/9 sont +DERIVES du present catalogue (anti-#1019 : aucun litteral durci). + +>>> [len(dimensions_at(l)) for l in LEVELS] +[3, 6, 9] + +Le banc de texte (``BENCH``) embarque un court debat ET ses annotations +formelles (traduction PL/FOL/modale declarees). Le gap NL->structure est +documente en source comme non bouché deterministement (translation-gap, +body #4960) : la traduction LLM vit dans ``_runner.py`` ; ici les formules +sont des artefacts du banc, declares comme tels -- jamais presentees comme +une traduction automatique. + +Divergences mesurees sur la source (contrat de fidelite) : + +1. **Les DAG LLM ne sont pas portes.** Le catalogue source activait des + agents LLM par phase (cout reel mesure 0,55-0,95 USD par run, porte + par ``orchestration_modes.PROVENANCE``). Ce port n'appelle AUCUN LLM : + chaque dimension invoque un organe deterministe de la serie, rejouable + a l'identique hors ligne. +2. **La restitution 3 actes est COMPACTE et DECLAREE.** Le renderer + complet vit dans le carnet 08d (embarque, non extractible sans toucher + au carnet) ; le niveau full produit la bande de verdict et la + conclusion au FORMAT du contrat 3 actes, en citant 08d comme porteur + du renderer integral. Copie pedagogique declaree (organ-first, Q3 : + l'extraction du renderer de 08d vers la lib est le refactoring a + demander a la serie source). +3. **JTMS n'est pas une dimension.** La brique JTMS vit dans le carnet 06 + (embarquee), pas dans ``argumentation_lib`` : l'invoquer exigerait le + meme refactoring d'extraction que le renderer. Les dimensions full + restent sur les organes reellement consommables (decision, synthese). +4. **ASPIC+/ABA/SETAF/weighted/bipolar ne sont pas des dimensions non plus** + (interdit explicite du body #4960 : gap NL->structure non bouche -- + les porter serait consacrer un workaround #3801). +5. **FOL et modale sont des dimensions DECLAREES INDISPONIBLES dans cet + arbre** — l'organe n'existe pas ici, preuve mesuree : le jar charge par + la serie (``Tweety/libs/org.tweetyproject.tweety-7a-java8-shade.jar``) + ne porte AUCUNE classe ``logics.fol`` ni ``logics.ml`` (inventaire + zipfile : sous-packages ``logics/`` du jar = commons + pl seulement). + Les handlers portes (``argumentation_lib._fol_handler``, + ``_modal_handler``) le documentent eux-memes : dependances amont non + vendues (prover9, TweetyInitializer C186f, normalizer modal) — + import-module en echec, note G.9 du ``__init__``. L'EPITA executait + FOL/modal sur SES jars 1.28 (dossier etudiant, interdit d'import). + Ces dimensions restent au catalogue (le niveau full les annonce) mais + ``run_level`` les rend ``unavailable`` avec cette preuve — jamais un + faux resultat, jamais un silence. +6. **La detection de sophismes est symbolique, pas taxonomique.** La source + pilotait la taxonomie 1552 par ``TaxonomySophismDetector`` ; ce module + n'est pas standalone-importable dans cet arbre (notice DEPENDENCY du + fichier : le plugin SK amont n'est pas vendu). L'organe reellement + livre par la serie est ``fallacy_rules.py`` (#17348 : 13 motifs / 5 + cles, extraction de donnees declaree) — c'est lui qu'on invoque, avec + sa limite mesuree : seules les formes canoniques matchent (4 formes + sur le corpus de reference du carnet des regles symboliques). +""" + +from __future__ import annotations + +from dataclasses import dataclass, field +from typing import Any, Callable, Dict, List, Optional, Tuple + +__all__ = [ + "LEVELS", + "Dimension", + "BENCH", + "dimensions_at", + "run_level", + "LevelRun", + "OrganUnavailable", + "fill_report", + "fill_rate", + "verdict_band", + "PARITY_NOTE", +] + +#: Les trois niveaux du catalogue source, dans l'ordre d'emboitement. +LEVELS: Tuple[str, ...] = ("light", "standard", "full") + +#: Parite documentee avec les mesures du sas (orchestration_modes, divergence 2). +PARITY_NOTE = ( + "Catalogue deterministe : {n} dimensions par niveau ({detail}). " + "DAG LLM de la source : 3/15/17 phases (constantes mesurees, " + "orchestration_modes.DEPTH_PARITY). Emboitement strict light ⊂ " + "standard ⊂ full dans les deux cas ; les echelles ne sont pas " + "communes (asymetrie documentee, pas reparee)." +) + + +# --------------------------------------------------------------------------- +# Le banc : un court debat + ses annotations formelles declarees +# --------------------------------------------------------------------------- + +#: Texte de reference — debat municipal dont les sophismes sont les FORMES +#: CANONIQUES mesurees sur l'organe fallacy_rules (corpus du carnet des +#: regles symboliques : 4 formes matchent sur 9 essayees — divergence 6). +BENCH: Dict[str, Any] = { + "raw_text": ( + "Le conseil municipal doit interdire les echoppes du centre-ville, " + "car elles detruisent le commerce de proximite. " + "Si les echoppes detruisent le commerce, alors les boutiques " + "centrales fermeront. " + "On ne peut pas faire confiance à ce conseiller qui defend les " + "echoppes. " + "Selon une etude de boutique, la mesure est efficace. " + "On a toujours fait comme ça, pourquoi changer la position de la " + "ville ? " + "Le premier pas vers le declin du centre-ville. " + "Toute regulation qui protege le commerce de proximite merite " + "d'etre essayee, et la fermeture des echoppes protege le " + "commerce de proximite. " + "Donc la fermeture des echoppes merite d'etre essayee." + ), + # Annotations formelles DECLAREES (artefacts du banc — pas une traduction + # automatique ; le gap NL->structure est documente en entete de module). + "pl_belief_set": ( + "detruit_proximite\n" + "detruit_proximite => fermeture_boutiques\n" + "fermeture_echoppes => protege_proximite" + ), + "pl_queries": ["fermeture_boutiques"], + "fol_belief_set": ( + "forall X: (echoppe(X) => nuisance(X))\n" + "forall X: (nuisance(X) => fermeture_boutiques(X))" + ), + # AF Dung declaree : les arguments du debat + leurs attaques. + "dung_arguments": ["A1_interdire", "A2_evidence", "A3_fermeture", "A4_ad_hominem", "A5_regulation"], + "dung_attacks": [ + ["A4_ad_hominem", "A1_interdire"], # l'attaque ad hominem vise A1 + ["A2_evidence", "A4_ad_hominem"], # l'appel a l'evidence ne prouve rien -> contre-attaque + ["A5_regulation", "A3_fermeture"], # la regulation soutient, l'attaque vient du sens + ], + # KB modale declaree : croyance du conseil sur la fermeture. + "modal_belief_set": "[] detruit_proximite\n[] (detruit_proximite => fermeture_boutiques)", + "modal_query": "[] fermeture_boutiques", + # Decision : la consequence que le conseil veut etablir (requete PL). + "decision_formula": "fermeture_boutiques", +} + + +# --------------------------------------------------------------------------- +# Le catalogue : dimension = niveau minimal + runner + champs peuples +# --------------------------------------------------------------------------- + +@dataclass(frozen=True) +class Dimension: + """Une dimension d'analyse du catalogue. + + ``level_min`` — le plus petit niveau qui active cette dimension + (emboitement strict : une dimension ``standard`` tourne aussi en + ``full``). ``fills`` nomme les champs de ``RhetoricalAnalysisState`` + que la dimension peuple — c'est la matiere du fill-rate. ``organ`` + cite l'organe invoque (provenance, jamais cachee). + """ + + name: str + level_min: str + fills: Tuple[str, ...] + organ: str + runner: Callable[[Dict[str, Any], Any], str] + + +def _dimension_extract(bench: Dict[str, Any], state: Any) -> str: + """Decoupage declaratif du texte en segments d'argument. + + Regles simples et audibles : un segment est une phrase portant un + marqueur d'argumentation (connecteur conclusif ou causal). Ce n'est + PAS l'extraction LLM du catalogue source — c'est un releve + deterministe declare. + """ + import re + + markers = re.compile( + r"\b(car|donc|alors|si\b|toute|tout le monde|merite)", re.IGNORECASE + ) + segments = [ + s.strip() + for s in re.split(r"(?<=[.!?])\s+", bench["raw_text"]) + if markers.search(s) + ] + state.identified_arguments = [ + {"arg_id": f"A{i+1}", "description": seg} for i, seg in enumerate(segments) + ] + return f"{len(segments)} segments releves (marqueurs deterministes)" + + +def _dimension_pl(bench: Dict[str, Any], state: Any) -> str: + """PL : KB declaree passee au vrai SimplePlReasoner (pattern 00-Setup).""" + import jpype + + JClass = jpype.JClass + PlParser = JClass("org.tweetyproject.logics.pl.parser.PlParser") + SimplePlReasoner = JClass("org.tweetyproject.logics.pl.reasoner.SimplePlReasoner") + PlBeliefSet = JClass("org.tweetyproject.logics.pl.syntax.PlBeliefSet") + + parser, reasoner = PlParser(), SimplePlReasoner() + bs = PlBeliefSet() + for line in bench["pl_belief_set"].splitlines(): + bs.add(parser.parseFormula(line)) + answers = [] + for q in bench["pl_queries"]: + answers.append((q, bool(reasoner.query(bs, parser.parseFormula(q))))) + state.belief_sets["pl"] = bench["pl_belief_set"] + state.query_log.extend( + {"dimension": "pl", "query": q, "answer": str(a)} for q, a in answers + ) + for q, a in answers: + state.answers[q] = str(a) + # Consistance lue sur la requete duale : la KB est incoherente si elle + # implique une contradiction (pattern SimplePlReasoner, source 00-Setup). + contradiction = bool(reasoner.query(bs, parser.parseFormula("a && !a"))) + return ( + f"KB PL {'incoherente' if contradiction else 'coherente'} ; " + f"{len(answers)} requete(s) Tweety ({', '.join(q for q, _ in answers)})" + ) + + +#: Pipeline spaCy partage (chargement paresseux unique — le modele +#: fr_core_news_sm coute ~2 s a charger, inutile de le refaire par requete). +_NLP_CACHE: List[Any] = [] + + +def _dimension_informal(bench: Dict[str, Any], state: Any) -> str: + """Sophismes : l'organe symbolique de la serie (fallacy_rules, #17348). + + Divergence 6 : la taxonomie 1552 de la source n'est pas + standalone-importable (plugin SK amont non vendu, notice DEPENDENCY + du fichier) ; fallacy_rules est l'organe reellement livre. Ses motifs + spaCy attrapent les formes canoniques — le banc n'en embarque que + ces formes (mesure sur le corpus de reference : 4/9). + """ + import spacy + + from fallacy_rules import detect_fallacies + + if not _NLP_CACHE: + _NLP_CACHE.append(spacy.load("fr_core_news_sm")) + hits = detect_fallacies(bench["raw_text"], nlp=_NLP_CACHE[0]) + state.identified_fallacies = [ + { + "key": h["key"], + "label": h["label"], + "excerpt": h["excerpt"], + "source": "fallacy_rules#17348", + } + for h in hits + ] + familles = sorted({h["key"] for h in hits}) + return ( + f"{len(hits)} sophisme(s) releve(s) par motifs symboliques " + f"({', '.join(familles) if familles else 'aucun motif canonique'})" + ) + + +def _dimension_dung(bench: Dict[str, Any], state: Any) -> str: + """AF de Dung : extensions par semantique — classes Tweety reelles. + + Pattern canonique de la serie Tweety voisine (Tweety-05, cellule 8) : + ``jpype.imports`` + ``Simple*Reasoner`` — l'inventaire du jar charge + par la serie (7a-java8-shade) ne porte QUE les reasoners prefixes + ``Simple*`` (mesure zipfile : pas de classe ``GroundedReasoner``). + ``SimpleGroundedReasoner.getModel`` rend UNE extension ; + ``getModels`` rend une collection. + """ + from org.tweetyproject.arg.dung.syntax import DungTheory, Argument, Attack + from org.tweetyproject.arg.dung.reasoner import ( + SimpleGroundedReasoner, + SimplePreferredReasoner, + SimpleStableReasoner, + ) + + theory = DungTheory() + args = {name: Argument(name) for name in bench["dung_arguments"]} + for name, arg in args.items(): + theory.add(arg) + for attacker, attacked in bench["dung_attacks"]: + theory.add(Attack(args[attacker], args[attacked])) + + result: Dict[str, List[List[str]]] = {} + grounded = SimpleGroundedReasoner().getModel(theory) + result["grounded"] = [sorted(str(a.getName()) for a in grounded)] + for label, reasoner_cls in ( + ("preferred", SimplePreferredReasoner), + ("stable", SimpleStableReasoner), + ): + exts = list(reasoner_cls().getModels(theory)) + result[label] = sorted( + sorted(str(a.getName()) for a in ext) for ext in exts + ) + state.belief_sets["dung_extensions"] = repr(result)[:300] + state.query_log.append( + {"dimension": "dung", "query": "extensions", "answer": str(result)[:400]} + ) + n_ext = {k: len(v) for k, v in result.items()} + return f"AF sous 3 semantiques : {n_ext}" + + +class OrganUnavailable(RuntimeError): + """Dimension declaree indisponible : l'organe n'existe pas dans cet arbre. + + Portee par une PREUVE mesuree (divergence 5 du contrat de fidelite) — + a distinguer d'un echec d'execution, qui lui reste fail-loud. + """ + + +def _dimension_fol(bench: Dict[str, Any], state: Any) -> str: + """FOL : DECLARE INDISPONIBLE dans cet arbre (divergence 5). + + Le jar de la serie (7a-java8-shade) ne porte aucune classe + ``logics.fol`` ; le handler porte (``_fol_handler``) documente ses + dependances amont non vendues (note G.9 du ``__init__``). La KB FOL + du banc reste AFFICHEE par le carnet comme annotation declaree. + """ + raise OrganUnavailable( + "organe FOL absent : 0 classe logics.fol dans le jar 7a-java8-shade " + "(inventaire zipfile) ; handler porte en dependances non vendues " + "(prover9, TweetyInitializer C186f) — note G.9 de argumentation_lib" + ) + + +def _dimension_modal(bench: Dict[str, Any], state: Any) -> str: + """Modale : DECLAREE INDISPONIBLE dans cet arbre (divergence 5). + + Idem FOL : 0 classe ``logics.ml`` dans le jar ; ``_modal_handler`` + leve ModuleNotFoundError a l'import (sibling non vendu, note G.9). + """ + raise OrganUnavailable( + "organe modal absent : 0 classe logics.ml dans le jar 7a-java8-shade " + "(inventaire zipfile) ; handler porte en siblings non vendus " + "(normalizer, TweetyInitializer C186f) — note G.9 de argumentation_lib" + ) + + +def _dimension_sat(bench: Dict[str, Any], state: Any) -> str: + """Decision : la formule d'engagement passee au SimplePlReasoner. + + Divergence declaree : le catalogue source resolvait la decision par un + solveur SAT ; ce port interroge le moteur de requete PL (organe + reellement consomme par la serie, pattern 00-Setup) sur la formule de + decision. Aucun solveur natif n'est pretendu. + """ + import jpype + + JClass = jpype.JClass + PlParser = JClass("org.tweetyproject.logics.pl.parser.PlParser") + SimplePlReasoner = JClass("org.tweetyproject.logics.pl.reasoner.SimplePlReasoner") + PlBeliefSet = JClass("org.tweetyproject.logics.pl.syntax.PlBeliefSet") + + parser, reasoner = PlParser(), SimplePlReasoner() + bs = PlBeliefSet() + # KB COMPLETE : la decision se derive par la chaine (fait + regle x2), + # pas sur le seul fait initial — charger les trois formules. + for line in bench["pl_belief_set"].splitlines(): + bs.add(parser.parseFormula(line)) + answer = bool(reasoner.query(bs, parser.parseFormula(bench["decision_formula"]))) + state.query_log.append( + {"dimension": "decision", "query": bench["decision_formula"], "answer": str(answer)} + ) + return f"Formule de decision : {bench['decision_formula']} => {answer}" + + +def _dimension_synthesis(bench: Dict[str, Any], state: Any) -> str: + """Synthese au FORMAT du contrat 3 actes (copie pedagogique declaree). + + Acte I ce qui est pose ; Acte II ce qui est attaque ; Acte III ce qui + tient. Le renderer integral vit dans le carnet 08d. + """ + n_args = len(state.identified_arguments or []) + n_fallacies = len(state.identified_fallacies or []) + n_queries = len(state.query_log or []) + kb_posees = ", ".join(sorted(state.belief_sets or {})) or "aucune" + act1 = f"{n_args} arguments releves, KB posees : {kb_posees}" + act2 = f"{n_fallacies} sophisme(s) releve(s), AF attaquee" + act3 = f"{n_queries} requetes formelles executees, verdict par semantique Dung" + state.final_conclusion = ( + f"Acte I — {act1}. Acte II — {act2}. Acte III — {act3}. " + "(format compact du contrat 3 actes ; renderer integral : carnet 08d)" + ) + return "conclusion 3 actes ecrite dans l'etat" + + +#: Le catalogue — l'ORDRE est le fil conducteur, l'emboitement est structurel. +#: Les dimensions fol/modal restent annoncees au catalogue (le niveau full +#: les nome) mais leurs runners rendent OrganUnavailable (divergence 5). +DIMENSIONS: Tuple[Dimension, ...] = ( + Dimension("extract_arguments", "light", ("identified_arguments",), + "releve deterministe (marqueurs)", _dimension_extract), + Dimension("pl_belief_set", "light", ("belief_sets", "query_log"), + "Tweety PlParser + SimplePlReasoner (pattern 00-Setup)", _dimension_pl), + Dimension("pl_queries", "light", ("query_log", "answers"), + "Tweety SimplePlReasoner (requetes)", _dimension_pl), + Dimension("informal_fallacies", "standard", ("identified_fallacies",), + "fallacy_rules.py — organe symbolique #17348", _dimension_informal), + Dimension("dung_semantics", "standard", ("query_log",), + "Tweety Simple{Grounded,Preferred,Stable}Reasoner (Tweety-05)", _dimension_dung), + Dimension("fol_consistency", "standard", ("belief_sets", "query_log"), + "DECLARE INDISPONIBLE (divergence 5 : jar sans logics.fol)", _dimension_fol), + Dimension("modal_queries", "full", ("belief_sets", "query_log"), + "DECLAREE INDISPONIBLE (divergence 5 : jar sans logics.ml)", _dimension_modal), + Dimension("sat_decision", "full", ("query_log",), + "Tweety SimplePlReasoner sur la formule de decision", _dimension_sat), + Dimension("synthesis_3_actes", "full", ("final_conclusion",), + "format du contrat 3 actes (carnet 08d)", _dimension_synthesis), +) + +_LEVEL_ORDER = {name: i for i, name in enumerate(LEVELS)} + + +def dimensions_at(level: str) -> List[Dimension]: + """Les dimensions actives a un niveau, dans l'ordre du catalogue. + + >>> dimensions_at("light")[0].name + 'extract_arguments' + >>> len(dimensions_at("full")) + 9 + """ + if level not in _LEVEL_ORDER: + raise ValueError(f"niveau inconnu : {level!r} (attendus : {LEVELS})") + return [d for d in DIMENSIONS if _LEVEL_ORDER[d.level_min] <= _LEVEL_ORDER[level]] + + +# --------------------------------------------------------------------------- +# Execution d'un niveau + fill-rate +# --------------------------------------------------------------------------- + +#: Champs de RhetoricalAnalysisState comptes dans le fill-rate (7 utiles). +STATE_FIELDS: Tuple[str, ...] = ( + "raw_text", "identified_arguments", "identified_fallacies", + "belief_sets", "query_log", "answers", "final_conclusion", +) + + +def _fresh_state(bench: Dict[str, Any]): + """Un etat partage neuf, raw_text deja pose (comme la source).""" + from argumentation_lib._shared_state import RhetoricalAnalysisState + + return RhetoricalAnalysisState(bench["raw_text"]) + + +def fill_report(state: Any) -> Dict[str, bool]: + """Champ par champ : peuplee ou non (le fill-rate de la source, en deterministe).""" + report: Dict[str, bool] = {} + for f in STATE_FIELDS: + value = getattr(state, f, None) + if isinstance(value, (list, dict, str)): + report[f] = len(value) > 0 + else: + report[f] = value is not None + return report + + +def fill_rate(state: Any) -> float: + """Fraction des champs utiles peuples — comparable au state_fill_rate du sas.""" + report = fill_report(state) + return sum(report.values()) / len(report) if report else 0.0 + + +@dataclass +class LevelRun: + """Le resultat d'un niveau sur le banc : dimensions, fill, verdicts. + + ``unavailable`` porte les dimensions DECLAREES indisponibles (divergence + 5) avec leur preuve — elles ne comptent ni comme executees ni comme + echecs : le niveau full annonce 9 dimensions dont 2 rendues avec preuve. + """ + + level: str + dimensions: List[str] = field(default_factory=list) + unavailable: Dict[str, str] = field(default_factory=dict) + notes: List[str] = field(default_factory=list) + fill_rate: float = 0.0 + fill_report: Dict[str, bool] = field(default_factory=dict) + + def verdict_band(self) -> str: + """La bande de verdict : niveau, dimensions, fill — comptes DERIVES.""" + return ( + f"[{self.level}] {len(self.dimensions)} dimensions executees" + + (f", {len(self.unavailable)} declarees indisponibles" if self.unavailable else "") + + f", fill {self.fill_rate:.2f} ({sum(self.fill_report.values())}/" + f"{len(self.fill_report)} champs peuples)" + ) + + +def run_level(bench: Dict[str, Any], level: str, state: Optional[Any] = None) -> Tuple[Any, LevelRun]: + """Execute les dimensions actives d'un niveau sur le banc. + + Retourne (etat, LevelRun). Fail-loud pour toute erreur d'execution + (la serie refuse le mode degrade, 00-Setup) — SAUF OrganUnavailable, + qui est une absence declaree avec preuve, rendue a part. + """ + active = dimensions_at(level) + if state is None: + state = _fresh_state(bench) + run = LevelRun(level=level) + for dim in active: + try: + note = dim.runner(bench, state) + except OrganUnavailable as unavailable: + run.unavailable[dim.name] = str(unavailable) + run.notes.append(f"{dim.name}: INDISPONIBLE — {unavailable}") + continue + run.dimensions.append(dim.name) + run.notes.append(f"{dim.name}: {note}") + run.fill_report = fill_report(state) + run.fill_rate = fill_rate(state) + return state, run