diff --git a/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb b/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb index ba749f89b5..a44ca4eae0 100644 --- a/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb +++ b/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb @@ -6,16 +6,16 @@ "id": "3dbb1aca", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:18.523287Z", - "iopub.status.busy": "2026-08-17T03:46:18.522932Z", - "iopub.status.idle": "2026-08-17T03:46:18.528156Z", - "shell.execute_reply": "2026-08-17T03:46:18.527752Z" + "iopub.execute_input": "2026-10-16T06:00:00.415221Z", + "iopub.status.busy": "2026-10-16T06:00:00.414861Z", + "iopub.status.idle": "2026-10-16T06:00:00.425210Z", + "shell.execute_reply": "2026-10-16T06:00:00.424329Z" }, "papermill": { - "duration": 0.008977, - "end_time": "2026-08-17T03:46:18.528863", + "duration": 0.017045, + "end_time": "2026-10-16T06:00:00.426479+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.519886", + "start_time": "2026-10-16T06:00:00.409434+00:00", "status": "completed" }, "tags": [ @@ -31,7 +31,16 @@ { "cell_type": "markdown", "id": "12da34db", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003506, + "end_time": "2026-10-16T06:00:00.435698+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:00.432192+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Vue d'ensemble : un duel d'agents Père Fouras vs Laurent Jalabert\n", "\n", @@ -42,7 +51,16 @@ { "cell_type": "markdown", "id": "d9a88198", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.004915, + "end_time": "2026-10-16T06:00:00.444054+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:00.439139+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "> **Adapté d'une production étudiante EPF** ([#890](https://github.com/jsboige/CoursIA/pull/890)) — groupe d'origine non préservé par le refactor.\n" ] @@ -52,10 +70,10 @@ "id": "7a5a944e", "metadata": { "papermill": { - "duration": 0.002433, - "end_time": "2026-08-17T03:46:18.533900", + "duration": 0.004925, + "end_time": "2026-10-16T06:00:00.454227+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.531467", + "start_time": "2026-10-16T06:00:00.449302+00:00", "status": "completed" }, "tags": [] @@ -74,16 +92,16 @@ "id": "34b19aac", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:18.538916Z", - "iopub.status.busy": "2026-08-17T03:46:18.538506Z", - "iopub.status.idle": "2026-08-17T03:46:23.551467Z", - "shell.execute_reply": "2026-08-17T03:46:23.550740Z" + "iopub.execute_input": "2026-10-16T06:00:00.466805Z", + "iopub.status.busy": "2026-10-16T06:00:00.466530Z", + "iopub.status.idle": "2026-10-16T06:00:08.842251Z", + "shell.execute_reply": "2026-10-16T06:00:08.841748Z" }, "papermill": { - "duration": 5.01681, - "end_time": "2026-08-17T03:46:23.552521", + "duration": 8.385081, + "end_time": "2026-10-16T06:00:08.843469+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.535711", + "start_time": "2026-10-16T06:00:00.458388+00:00", "status": "completed" }, "tags": [] @@ -139,10 +157,10 @@ "id": "48b0b793", "metadata": { "papermill": { - "duration": 0.001724, - "end_time": "2026-08-17T03:46:23.556364", + "duration": 0.003495, + "end_time": "2026-10-16T06:00:08.850889+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.554640", + "start_time": "2026-10-16T06:00:08.847394+00:00", "status": "completed" }, "tags": [] @@ -161,16 +179,16 @@ "id": "85411416", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.560968Z", - "iopub.status.busy": "2026-08-17T03:46:23.560508Z", - "iopub.status.idle": "2026-08-17T03:46:23.564310Z", - "shell.execute_reply": "2026-08-17T03:46:23.563657Z" + "iopub.execute_input": "2026-10-16T06:00:08.859618Z", + "iopub.status.busy": "2026-10-16T06:00:08.859184Z", + "iopub.status.idle": "2026-10-16T06:00:08.863752Z", + "shell.execute_reply": "2026-10-16T06:00:08.863082Z" }, "papermill": { - "duration": 0.006996, - "end_time": "2026-08-17T03:46:23.565081", + "duration": 0.010024, + "end_time": "2026-10-16T06:00:08.864714+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.558085", + "start_time": "2026-10-16T06:00:08.854690+00:00", "status": "completed" }, "tags": [] @@ -194,17 +212,35 @@ { "cell_type": "markdown", "id": "2adf4585", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003608, + "end_time": "2026-10-16T06:00:08.873808+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.870200+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : pourquoi ce choix de mot est pédagogique\n", "\n", - "Le mot `\"anticonstitutionnellement\"` (25 lettres, Grand Robert) cumule plusieurs difficultés : longueur (force des indices progressifs), composition morphologique (`anti-+constitution+nelle+ment` — l'agent peut deviner par décomposition), et rareté (vocabulaire juridique peu courant). Pour un **jeu d'énigmes LLM**, c'est un excellent stress-test : si l'agent devine ce mot en moins de 10 essais, le prompt Père Fouras est bien calibré. L'étudiant peut **mesurer le taux de réussite** sur 100 parties avec `evaluer_partie()` (à compléter, exercice 3) puis comparer avec un mot plus simple (ex. `\"kayak\"`, 5 lettres) pour isoler l'effet longueur du mot.\n" + "Le mot `\"anticonstitutionnellement\"` (25 lettres, Grand Robert) cumule plusieurs difficultés : longueur (force des indices progressifs), composition morphologique (`anti-+constitution+nelle+ment` — l'agent peut deviner par décomposition), et rareté (vocabulaire juridique peu courant). Pour un **jeu d'énigmes LLM**, c'est un excellent stress-test : si l'agent devine ce mot en moins de 10 essais, le prompt Père Fouras est bien calibré. L'étudiant peut **mesurer le taux de réussite** sur 100 parties avec `evaluer_partie()` (implémentée dans l'exemple guidé 3) puis comparer avec un mot plus simple (ex. `\"kayak\"`, 5 lettres) pour isoler l'effet longueur du mot.\n" ] }, { "cell_type": "markdown", "id": "fdc6262b", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003254, + "end_time": "2026-10-16T06:00:08.880684+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.877430+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : kernel Semantic Kernel instancié\n", "\n", @@ -216,10 +252,10 @@ "id": "f0496c5b", "metadata": { "papermill": { - "duration": 0.001988, - "end_time": "2026-08-17T03:46:23.569263", + "duration": 0.003477, + "end_time": "2026-10-16T06:00:08.887499+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.567275", + "start_time": "2026-10-16T06:00:08.884022+00:00", "status": "completed" }, "tags": [] @@ -238,16 +274,16 @@ "id": "9bedfe8b", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.574048Z", - "iopub.status.busy": "2026-08-17T03:46:23.573632Z", - "iopub.status.idle": "2026-08-17T03:46:23.576972Z", - "shell.execute_reply": "2026-08-17T03:46:23.576249Z" + "iopub.execute_input": "2026-10-16T06:00:08.895466Z", + "iopub.status.busy": "2026-10-16T06:00:08.895208Z", + "iopub.status.idle": "2026-10-16T06:00:08.899788Z", + "shell.execute_reply": "2026-10-16T06:00:08.898990Z" }, "papermill": { - "duration": 0.006842, - "end_time": "2026-08-17T03:46:23.577966", + "duration": 0.010014, + "end_time": "2026-10-16T06:00:08.900867+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.571124", + "start_time": "2026-10-16T06:00:08.890853+00:00", "status": "completed" }, "tags": [] @@ -272,11 +308,20 @@ { "cell_type": "markdown", "id": "3e19b11c", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003776, + "end_time": "2026-10-16T06:00:08.908280+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.904504+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : prompts Père Fouras et cycliste configurés\n", "\n", - "La cellule 10 définit `PERE_FOURAS_PROMPT` et `LAURENT_JALABERT_PROMPT` structurés via `f\"\"\"...\"\"\"` — seule la f-string du Père Fouras interpole `MOT_A_DEVINER`. Le prompt du Père Fouras encode la **persona + la mécanique** : pose énigme, attend réponse, valide, propose indice suivant. Le prompt du cycliste encode la **stratégie de devinette** : extraire lettres, longueur, contexte, demander indice. Cette séparation persona/mécanique est cruciale — sans persona explicite, le LLM répondrait en mode Q&A générique sans le **cadre narratif Fort Boyard**. L'exercice 1 invite l'étudiant à remplacer le duo par deux nouveaux personnages — un **Détective** (questions déductives) et un **Témoin** (réponses évasives) — extension classique du **multi-agent cast**.\n", + "La cellule 10 définit `PERE_FOURAS_PROMPT` et `LAURENT_JALABERT_PROMPT` structurés via `f\"\"\"...\"\"\"` — seule la f-string du Père Fouras interpole `MOT_A_DEVINER`. Le prompt du Père Fouras encode la **persona + la mécanique** : pose énigme, attend réponse, valide, propose indice suivant. Le prompt du cycliste encode la **stratégie de devinette** : extraire lettres, longueur, contexte, demander indice. Cette séparation persona/mécanique est cruciale — sans persona explicite, le LLM répondrait en mode Q&A générique sans le **cadre narratif Fort Boyard**. L'exemple guidé 1 (contribution de la PR #21009) remplace le duo par deux nouveaux personnages — un **Détective** (questions déductives) et un **Témoin** (réponses évasives) — extension classique du **multi-agent cast**.\n", " Le format `f\"\"\"...\"\"\"` permet l'interpolation de variables runtime (mot à deviner, score actuel).\n" ] }, @@ -284,26 +329,22 @@ "cell_type": "markdown", "id": "649ac34e", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.001725, - "end_time": "2026-08-17T03:46:23.581576", + "duration": 0.004027, + "end_time": "2026-10-16T06:00:08.915690+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.579851", + "start_time": "2026-10-16T06:00:08.911663+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercice 1 : Personnaliser les personnages du duel\n", + "### Exemple guidé 1 : Personnaliser les personnages du duel\n", "\n", - "Les prompts actuels definissent le Pere Fouras comme enigmatique et Laurent Jalabert comme perspicace. L'objectif est de créer une variante du jeu avec deux personnages différents : un **Detective** (qui pose des questions deductives) et un **Temoin** (qui repond de maniere evasive sans reveler le mot).\n", + "*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.*\n", "\n", - "**Objectif** : ecrire les instructions système pour ces deux nouveaux personnages et configurer les agents correspondants.\n", - "\n", - "**Indices** :\n", - "- `# Étape 1` : Rediger le prompt du Temoin (rôle, règles, interdictions)\n", - "- `# Étape 2` : Rediger le prompt du Detective (stratégie de questionnement)\n", - "- `# Indice` : le Temoin ne doit jamais reveler le mot directement, mais peut donner des indices indirects\n" + "Les prompts initiaux définissent le Père Fouras comme énigmatique et Laurent Jalabert comme perspicace. Le groupe de la PR #21009 livre la variante demandée : un **Detective** (questions déductives, fermées de préférence, qui ne renonce pas) et un **Témoin** (réponses courtes et évasives, indices indirects, jamais le mot en clair) — puis configure les deux agents `ChatCompletionAgent` sur le même motif que le duo original.\n" ] }, { @@ -312,16 +353,16 @@ "id": "ac94f4f0", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.586230Z", - "iopub.status.busy": "2026-08-17T03:46:23.585867Z", - "iopub.status.idle": "2026-08-17T03:46:23.591262Z", - "shell.execute_reply": "2026-08-17T03:46:23.590498Z" + "iopub.execute_input": "2026-10-16T06:00:08.924877Z", + "iopub.status.busy": "2026-10-16T06:00:08.924532Z", + "iopub.status.idle": "2026-10-16T06:00:10.107373Z", + "shell.execute_reply": "2026-10-16T06:00:10.106555Z" }, "papermill": { - "duration": 0.008725, - "end_time": "2026-08-17T03:46:23.592095", + "duration": 1.188654, + "end_time": "2026-10-16T06:00:10.108687+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.583370", + "start_time": "2026-10-16T06:00:08.920033+00:00", "status": "completed" }, "tags": [] @@ -331,22 +372,69 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : personnages Detective et Temoin\n" + "Les agents Detective et Temoin ont ete crees avec succes !\n" ] } ], "source": [ - "# Exercice 1 : Personnaliser les personnages du duel\n", - "# TODO etudiant : definir les prompts et creer les agents\n", + "# EXEMPLE GUIDE 1 : personnaliser les personnages du duel (groupe de la PR #21009)\n", + "TEMoin_PROMPT = f\"\"\"\n", + "Tu es un témoin interrogé par un détective.\n", + "Tu connais le mot secret : '{MOT_A_DEVINER}'.\n", "\n", - "TEMoin_PROMPT = \"\" # Etape 1 : instructions du Temoin (evasif, indices indirects)\n", - "DETECTIVE_PROMPT = \"\" # Etape 2 : instructions du Detective (questions deductives)\n", + "Ton rôle est de répondre aux questions du détective.\n", + "Tu dois rester mystérieux et parfois évasif.\n", + "Ne révèle jamais directement le mot secret.\n", + "Tu peux donner des indices indirects pour aider le détective.\n", + "Réponds de manière courte et précise.\n", + "\"\"\"\n", "\n", - "# Indice : creer les agents avec ChatCompletionAgent(kernel=..., name=..., instructions=...)\n", - "temoin = None # TODO etudiant : creer l'agent Temoin\n", - "detective = None # TODO etudiant : creer l'agent Detective\n", + "# Etape 2 : Instructions du Detective\n", + "DETECTIVE_PROMPT = \"\"\"\n", + "Tu es un détective intelligent et perspicace.\n", + "Ton objectif est de découvrir un mot secret.\n", "\n", - "print(\"Exercice a completer : personnages Detective et Temoin\")" + "Pose des questions logiques et déductives au témoin.\n", + "Privilégie les questions fermées (Oui/Non).\n", + "Analyse les réponses pour progresser dans ton enquête.\n", + "Lorsque tu penses avoir trouvé, propose le mot.\n", + "Ne renonce pas trop rapidement.\n", + "\"\"\"\n", + "\n", + "# Etape 3 : Creation de l'agent Temoin\n", + "temoin = ChatCompletionAgent(\n", + " kernel=create_kernel(),\n", + " name=\"Temoin\",\n", + " instructions=TEMoin_PROMPT,\n", + ")\n", + "\n", + "# Etape 4 : Creation de l'agent Detective\n", + "detective = ChatCompletionAgent(\n", + " kernel=create_kernel(),\n", + " name=\"Detective\",\n", + " instructions=DETECTIVE_PROMPT,\n", + ")\n", + "\n", + "print(\"Les agents Detective et Temoin ont ete crees avec succes !\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0672c52a", + "metadata": { + "papermill": { + "duration": 0.003717, + "end_time": "2026-10-16T06:00:10.116639+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.112922+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : deux nouveaux personnages configurés\n", + "\n", + "Les deux agents du groupe sont créés sans erreur. Deux choix méritent l'œil : le prompt du **Témoin** est une f-string qui interpole `MOT_A_DEVINER` — le témoin « connaît » le mot parce qu'il est injecté dans ses instructions, mais l'interdiction « Ne révèle jamais directement le mot secret » maintient l'écart entre savoir et dire. Le prompt du **Detective** impose des questions fermées (oui/non) : ce format contraint la conversation à converger, comme la sélection round-robin du groupe contraint l'ordre des tours (section suivante). Chaque agent reçoit son propre `create_kernel()` — le même motif que le duo Père Fouras / Laurent Jalabert de la cellule précédente.\n" ] }, { @@ -354,10 +442,10 @@ "id": "6a8dbe2b", "metadata": { "papermill": { - "duration": 0.001813, - "end_time": "2026-08-17T03:46:23.595862", + "duration": 0.0036, + "end_time": "2026-10-16T06:00:10.123964+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.594049", + "start_time": "2026-10-16T06:00:10.120364+00:00", "status": "completed" }, "tags": [] @@ -376,16 +464,16 @@ "id": "83e35526", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.600588Z", - "iopub.status.busy": "2026-08-17T03:46:23.600215Z", - "iopub.status.idle": "2026-08-17T03:46:24.269659Z", - "shell.execute_reply": "2026-08-17T03:46:24.268772Z" + "iopub.execute_input": "2026-10-16T06:00:10.132863Z", + "iopub.status.busy": "2026-10-16T06:00:10.132598Z", + "iopub.status.idle": "2026-10-16T06:00:10.770159Z", + "shell.execute_reply": "2026-10-16T06:00:10.769633Z" }, "papermill": { - "duration": 0.672891, - "end_time": "2026-08-17T03:46:24.270614", + "duration": 0.643563, + "end_time": "2026-10-16T06:00:10.771156+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.597723", + "start_time": "2026-10-16T06:00:10.127593+00:00", "status": "completed" }, "tags": [] @@ -409,7 +497,16 @@ { "cell_type": "markdown", "id": "2bbc1e0f", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003971, + "end_time": "2026-10-16T06:00:10.779127+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.775156+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : agents conversationnels créés\n", "\n", @@ -421,10 +518,10 @@ "id": "4a00c813", "metadata": { "papermill": { - "duration": 0.001948, - "end_time": "2026-08-17T03:46:24.274675", + "duration": 0.003258, + "end_time": "2026-10-16T06:00:10.785856+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.272727", + "start_time": "2026-10-16T06:00:10.782598+00:00", "status": "completed" }, "tags": [] @@ -443,16 +540,16 @@ "id": "a86ac80f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.279543Z", - "iopub.status.busy": "2026-08-17T03:46:24.279286Z", - "iopub.status.idle": "2026-08-17T03:46:24.284899Z", - "shell.execute_reply": "2026-08-17T03:46:24.284400Z" + "iopub.execute_input": "2026-10-16T06:00:10.794438Z", + "iopub.status.busy": "2026-10-16T06:00:10.793912Z", + "iopub.status.idle": "2026-10-16T06:00:10.799763Z", + "shell.execute_reply": "2026-10-16T06:00:10.799132Z" }, "papermill": { - "duration": 0.009132, - "end_time": "2026-08-17T03:46:24.285729", + "duration": 0.011676, + "end_time": "2026-10-16T06:00:10.801032+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.276597", + "start_time": "2026-10-16T06:00:10.789356+00:00", "status": "completed" }, "tags": [] @@ -483,26 +580,22 @@ "cell_type": "markdown", "id": "81de6677", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.001777, - "end_time": "2026-08-17T03:46:24.289620", + "duration": 0.004574, + "end_time": "2026-10-16T06:00:10.809100+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.287843", + "start_time": "2026-10-16T06:00:10.804526+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercice 2 : Terminaison avec detection de renoncement\n", - "\n", - "La stratégie actuelle arrete la partie uniquement si le mot est devine. Mais Laurent Jalabert pourrait aussi abandonner. L'objectif est de créer une stratégie hybride qui arrete la partie si le mot est devine **ou** si Laurent exprime un renoncement (\"je donne ma langue au chat\", \"j'abandonne\").\n", + "### Exemple guidé 2 : Terminaison avec détection de renoncement\n", "\n", - "**Objectif** : implementer `RenoncementTerminationStrategy` qui combine la detection du mot secret avec la detection de phrases d'abandon.\n", + "*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.*\n", "\n", - "**Indices** :\n", - "- `# Étape 1` : Définir une liste de phrases d'abandon typiques\n", - "- `# Étape 2` : Verifier si le dernier message contient une de ces phrases (en minuscules)\n", - "- `# Indice` : combiner les deux conditions avec `or` dans `should_agent_terminate`\n" + "La stratégie du notebook arrête la partie uniquement si le mot est deviné. Le groupe de la PR #21009 implémente la variante hybride `RenoncementTerminationStrategy` : la partie s'arrête si le mot est deviné **ou** si Laurent exprime un renoncement (« je donne ma langue au chat », « j'abandonne », …) — puis vérifie sa stratégie sur trois situations témoin.\n" ] }, { @@ -511,16 +604,16 @@ "id": "0e96e11d", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.294492Z", - "iopub.status.busy": "2026-08-17T03:46:24.294104Z", - "iopub.status.idle": "2026-08-17T03:46:24.299208Z", - "shell.execute_reply": "2026-08-17T03:46:24.298604Z" + "iopub.execute_input": "2026-10-16T06:00:10.818549Z", + "iopub.status.busy": "2026-10-16T06:00:10.818139Z", + "iopub.status.idle": "2026-10-16T06:00:10.824370Z", + "shell.execute_reply": "2026-10-16T06:00:10.823791Z" }, "papermill": { - "duration": 0.008303, - "end_time": "2026-08-17T03:46:24.299926", + "duration": 0.011456, + "end_time": "2026-10-16T06:00:10.825251+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.291623", + "start_time": "2026-10-16T06:00:10.813795+00:00", "status": "completed" }, "tags": [] @@ -530,24 +623,134 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : RenoncementTerminationStrategy\n" + "Strategie de renoncement configuree !\n" ] } ], "source": [ + "# EXEMPLE GUIDE 2 : terminaison avec detection de renoncement (groupe de la PR #21009)\n", "from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy\n", "from semantic_kernel.contents.chat_message_content import ChatMessageContent\n", "\n", "class RenoncementTerminationStrategy(TerminationStrategy):\n", - " # TODO etudiant : implementer la strategie de terminaison hybride\n", - " PHRASES_ABANDON: list[str] = [] # Etape 1 : phrases de renoncement\n", - " \n", - " async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool:\n", - " # Etape 2 : verifier mot devine OU abandon detecte\n", - " result = False # TODO etudiant : remplacer par la logique\n", - " return result\n", "\n", - "print(\"Exercice a completer : RenoncementTerminationStrategy\")" + " # Etape 1 : Definir les phrases d'abandon\n", + " PHRASES_ABANDON: list[str] = [\n", + " \"j'abandonne\",\n", + " \"je donne ma langue au chat\",\n", + " \"je renonce\",\n", + " \"je laisse tomber\"\n", + " ]\n", + "\n", + " async def should_agent_terminate(\n", + " self, agent, history: list[ChatMessageContent],\n", + " cancellation_token=None\n", + " ) -> bool:\n", + "\n", + " # Verifier que l'historique n'est pas vide\n", + " if not history:\n", + " return False\n", + "\n", + " # Recuperer le dernier message en minuscules\n", + " dernier_message = str(history[-1].content or \"\").lower()\n", + "\n", + " # Verifier si le mot secret est trouve\n", + " mot_trouve = MOT_A_DEVINER.lower() in dernier_message\n", + "\n", + " # Verifier si une phrase d'abandon est presente\n", + " abandon = any(\n", + " phrase in dernier_message\n", + " for phrase in self.PHRASES_ABANDON\n", + " )\n", + "\n", + " # Arreter si le mot est trouve OU si le joueur abandonne\n", + " return mot_trouve or abandon\n", + "\n", + "print(\"Strategie de renoncement configuree !\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6b1b975c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:00:10.833293Z", + "iopub.status.busy": "2026-10-16T06:00:10.832840Z", + "iopub.status.idle": "2026-10-16T06:00:10.844943Z", + "shell.execute_reply": "2026-10-16T06:00:10.844360Z" + }, + "papermill": { + "duration": 0.017086, + "end_time": "2026-10-16T06:00:10.845860+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.828774+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Abandon : True (attendu : True)\n", + "Mot trouve : True (attendu : True)\n", + "Mauvaise reponse : False (attendu : False)\n" + ] + } + ], + "source": [ + "\n", + "# Tests de l'exemple guide 2 : trois situations (groupe de la PR #21009)\n", + "\n", + "from semantic_kernel.contents import AuthorRole, ChatMessageContent\n", + "\n", + "strategie = RenoncementTerminationStrategy(\n", + " agents=[laurent_jalabert],\n", + " maximum_iterations=20\n", + ")\n", + "\n", + "# Trois situations a tester\n", + "tests = [\n", + " (\"Abandon\", \"J'abandonne\", True),\n", + " (\"Mot trouve\", f\"Ma reponse est {MOT_A_DEVINER}\", True),\n", + " (\"Mauvaise reponse\", \"Je pense que c'est un objet\", False)\n", + "]\n", + "\n", + "for nom, reponse, attendu in tests:\n", + "\n", + " message = ChatMessageContent(\n", + " role=AuthorRole.ASSISTANT,\n", + " content=reponse,\n", + " name=\"Laurent_Jalabert\"\n", + " )\n", + "\n", + " resultat = await strategie.should_agent_terminate(\n", + " agent=laurent_jalabert,\n", + " history=[message]\n", + " )\n", + "\n", + " print(f\"{nom} : {resultat} (attendu : {attendu})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0ed41dbe", + "metadata": { + "papermill": { + "duration": 0.003641, + "end_time": "2026-10-16T06:00:10.853349+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.849708+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Lecture du résultat : la stratégie hybride passe ses trois tests\n", + "\n", + "Les trois situations renvoient le verdict attendu : un « J'abandonne » arrête la partie, un message qui contient `MOT_A_DEVINER` aussi, et une réponse neutre laisse le duel continuer. La mécanique tient en deux gestes : tout est comparé en minuscules (le modèle peut écrire « J'ABANDONNE »), et la condition finale est un `or` — il suffit que le mot soit trouvé **ou** qu'une phrase d'abandon apparaisse dans le **dernier** message de l'historique. La stratégie est attachée à `laurent_jalabert` : c'est bien au tour du devineur que la question « continuer ? » se pose.\n" ] }, { @@ -555,10 +758,10 @@ "id": "c61e7f51", "metadata": { "papermill": { - "duration": 0.001853, - "end_time": "2026-08-17T03:46:24.303711", + "duration": 0.003397, + "end_time": "2026-10-16T06:00:10.860124+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.301858", + "start_time": "2026-10-16T06:00:10.856727+00:00", "status": "completed" }, "tags": [] @@ -573,20 +776,20 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "fc868e1b", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.308636Z", - "iopub.status.busy": "2026-08-17T03:46:24.308282Z", - "iopub.status.idle": "2026-08-17T03:46:24.312015Z", - "shell.execute_reply": "2026-08-17T03:46:24.311548Z" + "iopub.execute_input": "2026-10-16T06:00:10.868540Z", + "iopub.status.busy": "2026-10-16T06:00:10.868263Z", + "iopub.status.idle": "2026-10-16T06:00:10.872943Z", + "shell.execute_reply": "2026-10-16T06:00:10.872057Z" }, "papermill": { - "duration": 0.007189, - "end_time": "2026-08-17T03:46:24.312786", + "duration": 0.010201, + "end_time": "2026-10-16T06:00:10.873865+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.305597", + "start_time": "2026-10-16T06:00:10.863664+00:00", "status": "completed" }, "tags": [] @@ -606,11 +809,20 @@ { "cell_type": "markdown", "id": "cb4bd6c9", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.00381, + "end_time": "2026-10-16T06:00:10.881607+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.877797+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : groupe de chat configuré\n", "\n", - "La cellule 22 construit `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — sans `selection_strategy` explicite. C'est l'**objet central** qui orchestre le duel : à chaque tour, l'agent suivant parle dans l'ordre de la liste (round-robin — la sélection par défaut, visible dans les lignes `Selected agent at index 0` / `at index 1` alternées de la sortie), et le `termination_strategy` détecte la fin — ici, le mot deviné : la sortie montre `should terminate: True` dès que Laurent_Jalabert nomme « anticonstitutionnellement ». Sans ce wrapper, les deux agents parleraient en parallèle. La **spécificité Fort Boyard** est le renoncement : l'exercice 2 (cellules 19-20) demande une stratégie hybride qui laisse aussi l'agent abandonner (« je donne ma langue au chat ») — émulation du stress télévisé.\n" + "La cellule 22 construit `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — sans `selection_strategy` explicite. C'est l'**objet central** qui orchestre le duel : à chaque tour, l'agent suivant parle dans l'ordre de la liste (round-robin — la sélection par défaut, visible dans les lignes `Selected agent at index 0` / `at index 1` alternées de la sortie), et le `termination_strategy` détecte la fin — ici, le mot deviné : la sortie évalue `should terminate` après chaque tour de Laurent_Jalabert, et la partie s'arrête sur le premier `True` — ou à l'épuisement des itérations si le mot n'est jamais nommé. Sans ce wrapper, les deux agents parleraient en parallèle. La **spécificité Fort Boyard** est le renoncement : l'exemple guidé 2 montre la stratégie hybride qui laisse aussi l'agent abandonner (« je donne ma langue au chat ») — émulation du stress télévisé.\n" ] }, { @@ -618,10 +830,10 @@ "id": "ada98689", "metadata": { "papermill": { - "duration": 0.001946, - "end_time": "2026-08-17T03:46:24.316751", + "duration": 0.003713, + "end_time": "2026-10-16T06:00:10.889273+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.314805", + "start_time": "2026-10-16T06:00:10.885560+00:00", "status": "completed" }, "tags": [] @@ -636,20 +848,20 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "bad1ce48", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.321638Z", - "iopub.status.busy": "2026-08-17T03:46:24.321222Z", - "iopub.status.idle": "2026-08-17T03:46:46.092563Z", - "shell.execute_reply": "2026-08-17T03:46:46.091973Z" + "iopub.execute_input": "2026-10-16T06:00:10.897135Z", + "iopub.status.busy": "2026-10-16T06:00:10.896838Z", + "iopub.status.idle": "2026-10-16T06:01:05.617069Z", + "shell.execute_reply": "2026-10-16T06:01:05.616301Z" }, "papermill": { - "duration": 21.774859, - "end_time": "2026-08-17T03:46:46.093496", + "duration": 54.725636, + "end_time": "2026-10-16T06:01:05.618408+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.318637", + "start_time": "2026-10-16T06:00:10.892772+00:00", "status": "completed" }, "tags": [] @@ -659,479 +871,339 @@ "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,323 [INFO] Depart du duel Pere Fouras vs Laurent Jalabert !\n" + "2026-10-16 08:00:10,899 [INFO] Depart du duel Pere Fouras vs Laurent Jalabert !\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,323 [INFO] Mot a deviner : ANTICONSTITUTIONNELLEMENT\n" + "2026-10-16 08:00:10,899 [INFO] Mot a deviner : ANTICONSTITUTIONNELLEMENT\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,325 [INFO] Adding `1` agent chat messages\n" + "2026-10-16 08:00:10,900 [INFO] Adding `1` agent chat messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,326 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:10,901 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,327 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:10,901 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,809 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:22,780 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,818 [INFO] OpenAI usage: CompletionUsage(completion_tokens=167, prompt_tokens=72, total_tokens=239, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:22,795 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1188, prompt_tokens=72, total_tokens=1260, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1024, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,819 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:22,796 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,820 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:22,797 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,820 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… écoute l’indice du vieux sage.\n", - "\n", - "Je suis un mot si long qu’il essouffle même les plus bavards, \n", - "on me cite quand une règle, une loi ou un décret ne respecte pas la Loi suprême.\n", + "2026-10-16 08:00:22,797 [INFO] [AuthorRole.ASSISTANT] : Ah ! Tu veux un indice, jeune aventurier ? Écoute bien la charade du vieux Père Fouras.\n", "\n", - "Charade :\n", - "- Mon 1er est l’opposé de « pour ».\n", - "- Mon 2e est ce qui fait tenir debout une nation, écrit noir sur blanc.\n", - "- Mon 3e est un petit suffixe qui transforme une idée en adjectif.\n", - "- Mon 4e est un autre suffixe qui transforme l’adjectif… en manière d’agir.\n", + "- Mon premier est un préfixe qui signifie \"contre\" — il s'oppose à \"pro\".\n", + "- Mon deuxième désigne le texte fondamental qui organise un État, souvent utilisé pour parler des lois et droits.\n", + "- Mon troisième est le petit suffixe qui transforme un adjectif en manière : on le trouve à la fin de nombreux adverbes.\n", "\n", - "Assemblés, je dis : « d’une manière contraire à ce qui est au sommet des lois ».\n", + "Indice supplémentaire, énigmatique : c'est un adverbe très long, souvent évoqué comme l'un des plus longs en français, et il qualifie une action faite en opposition à la Constitution.\n", "\n", - "À toi de jouer… quel est ce mot interminable ?\n" + "Quand tu penses avoir trouvé, réponds — le Fort écoute.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,820 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:00:22,798 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:28,821 [INFO] Invoking agent Laurent_Jalabert\n" + "2026-10-16 08:00:22,799 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,774 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:30,294 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,776 [INFO] OpenAI usage: CompletionUsage(completion_tokens=84, prompt_tokens=415, total_tokens=499, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:30,297 [INFO] OpenAI usage: CompletionUsage(completion_tokens=740, prompt_tokens=397, total_tokens=1137, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=704, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,777 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:00:30,298 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,777 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" + "2026-10-16 08:00:30,299 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: False\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,777 [INFO] [AuthorRole.ASSISTANT] : Je n’peux pas te donner le mot tout cru, moussaillon… mais je peux te guider.\n", - "\n", - "Indice : \n", - "On prononce ce mot quand une loi, un décret ou une décision **n’est pas conforme à la Loi suprême** d’un pays.\n", - "\n", - "Question fermée (réponds Oui/Non) : \n", - "Ton mot commence-t-il par **« anti- »** ?\n" + "2026-10-16 08:00:30,299 [INFO] [AuthorRole.ASSISTANT] : Parfait — je commence mon enquête. Première question : est-ce que le mot est un adverbe ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,778 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:30,300 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,778 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:30,301 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,472 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:40,908 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,475 [INFO] OpenAI usage: CompletionUsage(completion_tokens=146, prompt_tokens=610, total_tokens=756, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:40,910 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1154, prompt_tokens=484, total_tokens=1638, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=960, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,475 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:40,911 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,476 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:40,912 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,476 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… voici un nouvel indice, sans jamais trahir le secret.\n", - "\n", - "Je suis un adverbe gigantesque, qu’on invoque quand un texte de loi est jugé **contraire à la Constitution**.\n", - "\n", - "Charade :\n", - "- Mon 1er : le contraire de « pour ».\n", - "- Mon 2e : la loi fondamentale d’un pays.\n", - "- Mon 3e : ce qui transforme un nom en adjectif (comme dans *nation → national*).\n", - "- Mon 4e : ce qui transforme l’adjectif en adverbe (la “manière de”).\n", + "2026-10-16 08:00:40,913 [INFO] [AuthorRole.ASSISTANT] : Ha ha ! Tu veux un nouvel indice, petit malin ? Le vieux Père Fouras t'en offre encore un, mais gare aux oublis...\n", "\n", - "Avec tout cela, je signifie : **« d’une manière contraire à la Constitution »**.\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:33,476 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:33,477 [INFO] Invoking agent Laurent_Jalabert\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,399 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,404 [INFO] OpenAI usage: CompletionUsage(completion_tokens=77, prompt_tokens=911, total_tokens=988, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,405 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,406 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,407 [INFO] [AuthorRole.ASSISTANT] : Je peux pas te donner le mot directement, mais voilà un indice clair.\n", + "- Mon premier s'oppose, il dit \"contre\".\n", + "- Mon deuxième est le texte fondamental d'un État, celui qui fixe les lois.\n", + "- Ma fin est le petit ajout qui transforme un adjectif en manière — on le colle à la fin des adverbes.\n", "\n", - "On emploie ce mot quand une loi, un décret ou une décision est jugé **contraire à la Constitution** (la loi suprême).\n", + "Autres soupçons du Fort : ce mot compte 25 lettres ; c'est un adverbe qui qualifie une action faite en opposition à la Constitution. \n", + "Si tu retires mon premier, tu tombes sur le texte suprême lui-même. Si tu ajoutes simplement le suffixe de manière, tu obtiens l'ensemble.\n", "\n", - "Question Oui/Non (pour avancer) : \n", - "Le mot que tu cherches **se termine-t-il par « -ment »** ?\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:35,408 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "Veux-tu un énigme chiffrée, une charade sonore, ou... une autre devinette pour t'approcher encore ?\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,409 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:40,913 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,419 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:40,914 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,421 [INFO] OpenAI usage: CompletionUsage(completion_tokens=141, prompt_tokens=1092, total_tokens=1233, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:52,886 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,423 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:52,887 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1305, prompt_tokens=869, total_tokens=2174, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1280, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,424 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:52,888 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,424 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… voici un indice de plus, sans trahir le secret.\n", - "\n", - "Je suis un **adverbe interminable** qu’on sort quand on affirme qu’une décision a été prise **d’une manière contraire à la Constitution**.\n", - "\n", - "Énigme en morceaux :\n", - "- Je commence par le préfixe qui signifie **« contre »**.\n", - "- Au milieu, j’abrite la **loi fondamentale** d’un pays.\n", - "- Je finis par le petit bout qui veut dire **« de façon… »** (comme dans beaucoup d’adverbes).\n", - "\n", - "Donne-moi ta proposition… et je te dirai si tu brûles ou si tu gèles.\n" + "2026-10-16 08:00:52,889 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: False\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,425 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:00:52,889 [INFO] [AuthorRole.ASSISTANT] : Est-ce que le mot commence par \"anti\" ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,425 [INFO] Invoking agent Laurent_Jalabert\n" + "2026-10-16 08:00:52,890 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,122 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:52,891 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,123 [INFO] OpenAI usage: CompletionUsage(completion_tokens=57, prompt_tokens=1383, total_tokens=1440, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:57,155 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:00:57,157 [INFO] OpenAI usage: CompletionUsage(completion_tokens=360, prompt_tokens=934, total_tokens=1294, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=320, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" + "2026-10-16 08:00:57,159 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] [AuthorRole.ASSISTANT] : Bien tenté, moussaillon… mais ici, c’est **moi** qui dois deviner **ton** mot, uniquement avec des questions **Oui/Non**.\n", - "\n", - "Première question : \n", - "Ton mot est-il **un objet** ? (Oui/Non)\n" + "2026-10-16 08:00:57,159 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,125 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:57,160 [INFO] [AuthorRole.ASSISTANT] : Oui — mon premier hurle \"contre\" : c'est bien \"anti\". Le Fort avance un pas, mais la chasse n'est pas finie...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,125 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:57,161 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,701 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:57,161 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,708 [INFO] OpenAI usage: CompletionUsage(completion_tokens=181, prompt_tokens=1524, total_tokens=1705, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:01:05,600 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,710 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:01:05,609 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1052, prompt_tokens=1011, total_tokens=2063, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1024, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,711 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:01:05,610 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,712 [INFO] [AuthorRole.ASSISTANT] : Approche, aventurier… l’indice du Père Fouras t’attend.\n", - "\n", - "Je suis un **adverbe-monstre**, si long qu’il fait trembler les dictionnaires. On me prononce lorsqu’on juge qu’un texte, une loi ou une décision agit **contrairement à la loi suprême** d’un pays.\n", - "\n", - "Charade :\n", - "- Mon 1er signifie **« contre »**. \n", - "- Mon 2e est la **loi fondamentale** d’un État. \n", - "- Mon 3e est un morceau qui sert à dire **« qui concerne »** (il transforme souvent un nom en adjectif). \n", - "- Mon 4e termine beaucoup d’adverbes et veut dire **« d’une manière »**.\n", - "\n", - "Assemble tout cela… et tu obtiendras le mot que même les champions d’orthographe redoutent.\n" + "2026-10-16 08:01:05,611 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: True\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,713 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:01:05,611 [INFO] [AuthorRole.ASSISTANT] : Est-ce que le mot est \"anticonstitutionnellement\" ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,715 [INFO] Invoking agent Laurent_Jalabert\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,082 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,085 [INFO] OpenAI usage: CompletionUsage(completion_tokens=48, prompt_tokens=1895, total_tokens=1943, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=1152))\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,086 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,087 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: True\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,088 [INFO] [AuthorRole.ASSISTANT] : Ton indice pointe très fort vers **« anticonstitutionnellement »**.\n", - "\n", - "Pour respecter le jeu, je pose une question Oui/Non : \n", - "Le mot à deviner est-il **« anticonstitutionnellement »** ?\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:46,088 [INFO] Partie terminee !\n" + "2026-10-16 08:01:05,612 [INFO] Partie terminee !\n" ] } ], @@ -1160,65 +1232,79 @@ { "cell_type": "markdown", "id": "3aa490d9", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.007379, + "end_time": "2026-10-16T06:01:05.634443+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.627064+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : déroulé du duel Père Fouras vs Jalabert\n", "\n", - "Le log `\"Depart du duel Pere Fouras vs Laurent Jalabert\"` marque l'entrée dans la boucle asynchrone. À partir de cette cellule, **l'étudiant observe** : (1) l'alternance Père Fouras (énigme) ↔ Jalabert (réponse), (2) la convergence vers le mot deviné — `should terminate: True` dès que Laurent_Jalabert le nomme, puis `Partie terminee !` ; le renoncement (exercice 2) et l'évaluation par `evaluer_partie()` (exercice 3) restent à compléter par l'étudiant. Le **logging structuré** (timestamp + niveau INFO + préfixe événement) est essentiel dans un chat asynchrone multi-agent : sans lui, l'étudiant ne peut pas distinguer **qui parle** dans la sortie, ni **quand une étape se termine**. C'est ce logging qui rend le notebook pédagogique — rejouable.\n" + "Le log `\"Depart du duel Pere Fouras vs Laurent Jalabert\"` marque l'entrée dans la boucle asynchrone. À partir de cette cellule, **l'étudiant observe** : (1) l'alternance Père Fouras (énigme) ↔ Jalabert (réponse), (2) la boucle d'arrêt — `should terminate` évalué après chaque tour de Laurent_Jalabert : la partie s'arrête sur le premier `True`, ou à l'épuisement des itérations (ce run : le mot est trouvé en 3 tentatives, la cellule de score affiche 70/100), puis `Partie terminee !` ; la variante renoncement (exemple guidé 2) et l'évaluation par `evaluer_partie()` (exemple guidé 3) prolongent ce socle. Le **logging structuré** (timestamp + niveau INFO + préfixe événement) est essentiel dans un chat asynchrone multi-agent : sans lui, l'étudiant ne peut pas distinguer **qui parle** dans la sortie, ni **quand une étape se termine**. C'est ce logging qui rend le notebook pédagogique — rejouable.\n" ] }, { "cell_type": "markdown", "id": "5306a243", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.008379, + "end_time": "2026-10-16T06:01:05.650764+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.642385+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : qui parle, et quand ?\n", "\n", - "Dans le run committé, aucun paramètre `description` ni `selection_strategy` n'est passé au groupe : la sélection est celle par défaut, et la sortie le montre — les lignes `Selected agent at index 0` (Pere_Fouras) et `Selected agent at index 1` (Laurent_Jalabert) s'alternent strictement tour après tour. Ce round-robin suffit ici : le duel est à deux voix, et l'ordre des tours est l'essence du jeu. Le raffinement, pour des casts plus larges : donner à chaque agent une `description` et au groupe une `selection_strategy` sémantique, qui choisit le prochain locuteur en comparant les descriptions au contexte du dernier message au lieu d'alterner mécaniquement — le pattern **role-based selection**, utile dès que le cast dépasse deux agents (l'exercice 1, avec son Détective et son Témoin, en est l'occasion).\n" + "Dans le run committé, aucun paramètre `description` ni `selection_strategy` n'est passé au groupe : la sélection est celle par défaut, et la sortie le montre — les lignes `Selected agent at index 0` (Pere_Fouras) et `Selected agent at index 1` (Laurent_Jalabert) s'alternent strictement tour après tour. Ce round-robin suffit ici : le duel est à deux voix, et l'ordre des tours est l'essence du jeu. Le raffinement, pour des casts plus larges : donner à chaque agent une `description` et au groupe une `selection_strategy` sémantique, qui choisit le prochain locuteur en comparant les descriptions au contexte du dernier message au lieu d'alterner mécaniquement — le pattern **role-based selection**, utile dès que le cast dépasse deux agents (l'exemple guidé 1, avec son Détective et son Témoin, en est l'occasion).\n" ] }, { "cell_type": "markdown", "id": "9c2a66c0", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.004782, - "end_time": "2026-08-17T03:46:46.102468", + "duration": 0.005374, + "end_time": "2026-10-16T06:01:05.663101+00:00", "exception": false, - "start_time": "2026-08-17T03:46:46.097686", + "start_time": "2026-10-16T06:01:05.657727+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercice 3 : Système de score pour la partie\n", + "### Exemple guidé 3 : Système de score pour la partie\n", "\n", - "Actuellement, la partie s'arrete simplement quand le mot est devine ou après 20 itérations. L'objectif est d'ajouter un **système de score** qui compte le nombre de questions posees par Laurent Jalabert et affiche un score final.\n", + "*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.*\n", "\n", - "**Objectif** : ecrire une fonction `evaluer_partie` qui prend l'historique des messages et retourne un dictionnaire contenant le nombre de questions posees, si le mot a ete trouve, et un score (moins de questions = meilleur score).\n", - "\n", - "**Indices** :\n", - "- `# Étape 1` : Compter les messages ou le rôle est celui de Laurent Jalabert\n", - "- `# Étape 2` : Verifier si le mot secret apparait dans le dernier message\n", - "- `# Indice` : le score peut etre `max(0, 100 - nb_questions * 10)`\n" + "La partie s'arrête quand le mot est deviné ou après 20 itérations. Le groupe de la PR #21009 ajoute la mesure : la fonction `evaluer_partie` prend l'historique des messages et retourne le nombre de questions posées par Laurent Jalabert, si le mot a été trouvé, et un score qui décroît avec le nombre d'essais (`max(0, 100 - nb_questions * 10)`).\n" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "df0d3126", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:46.115062Z", - "iopub.status.busy": "2026-08-17T03:46:46.114806Z", - "iopub.status.idle": "2026-08-17T03:46:46.118549Z", - "shell.execute_reply": "2026-08-17T03:46:46.117975Z" + "iopub.execute_input": "2026-10-16T06:01:05.679403Z", + "iopub.status.busy": "2026-10-16T06:01:05.678991Z", + "iopub.status.idle": "2026-10-16T06:01:05.685167Z", + "shell.execute_reply": "2026-10-16T06:01:05.684108Z" }, "papermill": { - "duration": 0.011605, - "end_time": "2026-08-17T03:46:46.119457", + "duration": 0.017456, + "end_time": "2026-10-16T06:01:05.686481+00:00", "exception": false, - "start_time": "2026-08-17T03:46:46.107852", + "start_time": "2026-10-16T06:01:05.669025+00:00", "status": "completed" }, "tags": [] @@ -1228,31 +1314,331 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : systeme de score\n" + "Systeme de score configure avec succes !\n" ] } ], "source": [ + "\n", + "# EXEMPLE GUIDE 3 : systeme de score (groupe de la PR #21009)\n", + "\n", "def evaluer_partie(history_messages: list) -> dict:\n", - " # TODO etudiant : implementer le systeme de score\n", - " nb_questions = 0 # Etape 1 : compter les questions de Laurent\n", - " mot_trouve = False # Etape 2 : verifier la presence du mot\n", - " \n", - " # Indice : parcourir history_messages et compter les messages de Laurent\n", - " score = None # TODO etudiant : calculer le score\n", - " return {\"nb_questions\": nb_questions, \"mot_trouve\": mot_trouve, \"score\": score}\n", "\n", - "print(\"Exercice a completer : systeme de score\")" + " # Etape 1 : Recuperer les messages de Laurent Jalabert\n", + " messages_laurent = [\n", + " message for message in history_messages\n", + " if getattr(message, \"name\", None) == \"Laurent_Jalabert\"\n", + " ]\n", + "\n", + " # Compter le nombre de tentatives\n", + " nb_questions = len(messages_laurent)\n", + "\n", + " # Etape 2 : Verifier si le mot a ete trouve\n", + " mot_trouve = False\n", + "\n", + " if messages_laurent:\n", + " dernier_message = str(\n", + " messages_laurent[-1].content or \"\"\n", + " ).lower()\n", + "\n", + " mot_trouve = MOT_A_DEVINER.lower() in dernier_message\n", + "\n", + " # Etape 3 : Calculer le score\n", + " if mot_trouve:\n", + " score = max(0, 100 - nb_questions * 10)\n", + " else:\n", + " score = 0\n", + "\n", + " # Retourner les resultats\n", + " return {\n", + " \"nb_questions\": nb_questions,\n", + " \"mot_trouve\": mot_trouve,\n", + " \"score\": score\n", + " }\n", + "\n", + "print(\"Systeme de score configure avec succes !\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2a2f46fe", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.702897Z", + "iopub.status.busy": "2026-10-16T06:01:05.702480Z", + "iopub.status.idle": "2026-10-16T06:01:05.709156Z", + "shell.execute_reply": "2026-10-16T06:01:05.708129Z" + }, + "papermill": { + "duration": 0.016594, + "end_time": "2026-10-16T06:01:05.710494+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.693900+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===== RESULTATS DE LA PARTIE =====\n", + "Nombre de tentatives : 3\n", + "Mot trouve : True\n", + "Score final : 70 / 100\n" + ] + } + ], + "source": [ + "\n", + "# Recuperer l'historique de la partie\n", + "historique = chat.history.messages\n", + "\n", + "# Calculer le score\n", + "resultat = evaluer_partie(historique)\n", + "\n", + "# Afficher les resultats\n", + "print(\"===== RESULTATS DE LA PARTIE =====\")\n", + "print(\"Nombre de tentatives :\", resultat[\"nb_questions\"])\n", + "print(\"Mot trouve :\", resultat[\"mot_trouve\"])\n", + "print(\"Score final :\", resultat[\"score\"], \"/ 100\")\n" ] }, { "cell_type": "markdown", "id": "d2a4bdca", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.006116, + "end_time": "2026-10-16T06:01:05.722221+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.716105+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Lecture du résultat : système de score en fin de partie\n", "\n", - "La cellule 29 expose `evaluer_partie(history_messages: list) -> dict` — l'étudiant doit implémenter cette fonction (exercice 3, le dernier du carnet). Le contrat, donné par l'énoncé : compter les questions posées par Laurent Jalabert, vérifier si le mot secret apparaît, et retourner un score qui décroît avec le nombre d'essais (l'indice propose `max(0, 100 - nb_questions * 10)`). Ce **scoring** est ce qui transforme le duel en **jeu avec issue mesurable** — sans score, le notebook serait une simple conversation sans fin. C'est aussi ce qui ouvre la porte à des **expériences reproductibles** : l'étudiant peut comparer deux configurations de prompts en mesurant le score moyen sur 100 parties.\n" + "La fonction du groupe compte les messages signés `Laurent_Jalabert` dans l'historique réel du groupe de chat, vérifie si le mot secret apparaît dans son **dernier** message, et applique la règle `max(0, 100 - 10 × questions)` : une victoire à la première question vaut 90, chaque question supplémentaire coûte 10 points, et un échec vaut 0 — quelle que soit la longueur de la partie. Si le mot n'est pas trouvé, le score rendu est 0 et le nombre de questions reste affiché pour le diagnostic. Ce scoring transforme le duel en **jeu à issue mesurable** : en rejouant la partie avec deux configurations de prompts, on peut comparer des scores moyens — la porte d'entrée des expériences reproductibles.\n" + ] + }, + { + "cell_type": "markdown", + "id": "ae952339", + "metadata": { + "papermill": { + "duration": 0.005599, + "end_time": "2026-10-16T06:01:05.737250+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.731651+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "## Exercices\n", + "\n", + "Trois nouveaux défis, un par compétence travaillée dans les exemples guidés ci-dessus : rédiger des prompts qui contraignent un comportement, composer une condition de terminaison, mesurer une partie jouée. Les stubs sont incomplets par conception — le carnet s'exécute de bout en bout sans eux.\n" + ] + }, + { + "cell_type": "markdown", + "id": "5b19df38", + "metadata": { + "papermill": { + "duration": 0.008275, + "end_time": "2026-10-16T06:01:05.753416+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.745141+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 1 : Un duel au protocole binaire\n", + "\n", + "Les personnages de l'exemple guidé 1 laissent le Témoin répondre par des phrases entières. Variante plus stricte : un **Indicateur** qui ne répond **que** par « oui », « non » ou « peut-être » — jamais une phrase — et un **Enquêteur méthodique** qui annonce son hypothèse d'élimination (« Je cherche un mot de plus de 15 lettres ») avant chaque question.\n", + "\n", + "**Objectif** : rédiger les deux prompts et configurer les deux agents correspondants.\n", + "\n", + "**Indices** :\n", + "- `# Étape 1` : rédiger le prompt de l'Indicateur (rôle + liste fermée des trois réponses autorisées)\n", + "- `# Étape 2` : rédiger le prompt de l'Enquêteur (stratégie d'élimination annoncée avant chaque question)\n", + "- `# Indice` : un prompt peut interdire (« jamais de phrase ») autant qu'il peut prescrire (« réponds uniquement par »)\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "ec943b8d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.768419Z", + "iopub.status.busy": "2026-10-16T06:01:05.767930Z", + "iopub.status.idle": "2026-10-16T06:01:05.773125Z", + "shell.execute_reply": "2026-10-16T06:01:05.772032Z" + }, + "papermill": { + "duration": 0.015741, + "end_time": "2026-10-16T06:01:05.774940+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.759199+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 1 a completer\n" + ] + } + ], + "source": [ + "# Exercice 1 : un duel au protocole binaire\n", + "# TODO etudiant : definir les prompts et creer les agents\n", + "INDICATEUR_PROMPT = \"\" # Etape 1 : instructions de l'Indicateur (oui / non / peut-etre, jamais une phrase)\n", + "ENQUETEUR_PROMPT = \"\" # Etape 2 : instructions de l'Enqueteur (hypothese annoncee avant chaque question)\n", + "indicateur = None # TODO etudiant : ChatCompletionAgent(kernel=create_kernel(), ...)\n", + "enqueteur = None # TODO etudiant\n", + "print(\"Exercice 1 a completer\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "08f0cfb2", + "metadata": { + "papermill": { + "duration": 0.009634, + "end_time": "2026-10-16T06:01:05.794075+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.784441+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 2 : Terminaison sur question répétée\n", + "\n", + "Un devineur qui répète la même question trois fois de suite ne progresse plus : la partie est perdue. L'objectif est d'implémenter `RepetitionTerminationStrategy`, une variante qui arrête la partie si le mot est deviné **ou** si les trois derniers messages de Laurent Jalabert sont identiques (comparaison en minuscules).\n", + "\n", + "**Indices** :\n", + "- `# Étape 1` : extraire les messages de Laurent de l'historique (s'il y en a moins de trois, la partie continue)\n", + "- `# Étape 2` : comparer les trois derniers en minuscules, et combiner avec la détection du mot par un `or`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3610348b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.812063Z", + "iopub.status.busy": "2026-10-16T06:01:05.811809Z", + "iopub.status.idle": "2026-10-16T06:01:05.818968Z", + "shell.execute_reply": "2026-10-16T06:01:05.817824Z" + }, + "papermill": { + "duration": 0.017264, + "end_time": "2026-10-16T06:01:05.820719+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.803455+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 2 a completer\n" + ] + } + ], + "source": [ + "# Exercice 2 : terminaison sur question repetee\n", + "# TODO etudiant\n", + "from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy\n", + "from semantic_kernel.contents.chat_message_content import ChatMessageContent\n", + "\n", + "class RepetitionTerminationStrategy(TerminationStrategy):\n", + " # TODO etudiant : implementer la strategie de detection de repetition\n", + " SEUIL_REPETITION: int = 3 # Etape 1 : nombre de questions identiques consecutives\n", + "\n", + " async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool:\n", + " result = False # TODO etudiant : remplacer par la logique (mot trouve OU repetition)\n", + " return result\n", + "\n", + "print(\"Exercice 2 a completer\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "edb7a025", + "metadata": { + "papermill": { + "duration": 0.009017, + "end_time": "2026-10-16T06:01:05.839606+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.830589+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercice 3 : Mesurer la concision des indices du Père Fouras\n", + "\n", + "`evaluer_partie` (exemple guidé 3) mesure le devineur. L'autre moitié du duel mérite sa mesure : des indices trop longs facilitent la tâche. **Objectif** : écrire `mesurer_indices` qui prend l'historique des messages et retourne la longueur moyenne des messages du Père Fouras, et un booléen `indices_concis` vrai si cette moyenne reste sous 200 caractères.\n", + "\n", + "**Indices** :\n", + "- `# Étape 1` : filtrer l'historique sur les messages signés `Pere_Fouras` (même filtre que l'exemple guidé 3)\n", + "- `# Étape 2` : moyenner les longueurs (`len(str(message.content))`) et comparer au seuil\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "a85748ad", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.860111Z", + "iopub.status.busy": "2026-10-16T06:01:05.859557Z", + "iopub.status.idle": "2026-10-16T06:01:05.865257Z", + "shell.execute_reply": "2026-10-16T06:01:05.864533Z" + }, + "papermill": { + "duration": 0.018852, + "end_time": "2026-10-16T06:01:05.866485+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.847633+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 3 a completer\n" + ] + } + ], + "source": [ + "# Exercice 3 : mesurer la concision des indices du Pere Fouras\n", + "# TODO etudiant\n", + "def mesurer_indices(history_messages: list) -> dict:\n", + " longueur_moyenne = 0.0 # TODO etudiant : moyenne des longueurs des messages du Pere Fouras\n", + " indices_concis = False # TODO etudiant : True si longueur_moyenne < 200\n", + " return {\"longueur_moyenne\": longueur_moyenne, \"indices_concis\": indices_concis}\n", + "\n", + "print(\"Exercice 3 a completer\")\n" ] } ], @@ -1293,19 +1679,17 @@ }, "papermill": { "default_parameters": {}, - "duration": 29.875179, - "end_time": "2026-08-17T03:46:46.680034", + "duration": 68.269231, + "end_time": "2026-10-16T06:01:06.440156+00:00", "environment_variables": {}, "exception": null, - "input_path": "fort-boyard-python.ipynb", - "output_path": "fort-boyard-python.ipynb", - "parameters": { - "BATCH_MODE": true - }, - "start_time": "2026-08-17T03:46:16.804855", - "version": "2.6.0" + "input_path": "fb-fr.ipynb", + "output_path": "fb-fr-out.ipynb", + "parameters": {}, + "start_time": "2026-10-16T05:59:58.170925+00:00", + "version": "2.7.0" } }, "nbformat": 4, "nbformat_minor": 5 -} +} \ No newline at end of file diff --git a/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python_en.ipynb b/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python_en.ipynb index af35f41f14..210c3593d7 100644 --- a/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python_en.ipynb +++ b/MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python_en.ipynb @@ -6,16 +6,16 @@ "id": "3dbb1aca", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:18.523287Z", - "iopub.status.busy": "2026-08-17T03:46:18.522932Z", - "iopub.status.idle": "2026-08-17T03:46:18.528156Z", - "shell.execute_reply": "2026-08-17T03:46:18.527752Z" + "iopub.execute_input": "2026-10-16T06:00:00.415221Z", + "iopub.status.busy": "2026-10-16T06:00:00.414861Z", + "iopub.status.idle": "2026-10-16T06:00:00.425210Z", + "shell.execute_reply": "2026-10-16T06:00:00.424329Z" }, "papermill": { - "duration": 0.008977, - "end_time": "2026-08-17T03:46:18.528863", + "duration": 0.017045, + "end_time": "2026-10-16T06:00:00.426479+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.519886", + "start_time": "2026-10-16T06:00:00.409434+00:00", "status": "completed" }, "tags": [ @@ -31,7 +31,16 @@ { "cell_type": "markdown", "id": "12da34db", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003506, + "end_time": "2026-10-16T06:00:00.435698+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:00.432192+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Overview: an agent duel between Père Fouras and Laurent Jalabert\n\nThis notebook implements a multi-agent simulation of the TV game show **Fort Boyard**: Père Fouras poses riddles to a celebrity (here, the cyclist **Laurent Jalabert**), and the agent must solve the riddle by working with a **secret word to guess** (by default `\"anticonstitutionnellement\"`, one of the longest French words). The orchestration relies on a Semantic Kernel `AgentGroupChat` with a **give-up strategy** (the agent quits after N attempts) and a **scoring system** at the end of the game. The notebook covers the patterns: shared kernel, `ChatCompletionAgent`, persona engineering via structured prompts, and custom termination strategy. This is a typical use case for **game-theoretic agent simulations** where two LLM agents face off in a structured dialogue.\n The notebook architecture — shared kernel, two agents with asymmetric prompts, chat group with custom strategies — is deliberately minimal to stay **readable**: a student can follow the dynamics of the duel without getting lost in a forest of plugins. It is the opposite of industrial cases with 10+ agents where the orchestration becomes unreadable.\n Reproducible pattern.\n" ] @@ -39,7 +48,16 @@ { "cell_type": "markdown", "id": "d9a88198", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.004915, + "end_time": "2026-10-16T06:00:00.444054+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:00.439139+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "> **Adapted from a student production at EPF** ([#890](https://github.com/jsboige/CoursIA/pull/890)) — the original group is not preserved by the refactor.\n" ] @@ -49,10 +67,10 @@ "id": "7a5a944e", "metadata": { "papermill": { - "duration": 0.002433, - "end_time": "2026-08-17T03:46:18.533900", + "duration": 0.004925, + "end_time": "2026-10-16T06:00:00.454227+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.531467", + "start_time": "2026-10-16T06:00:00.449302+00:00", "status": "completed" }, "tags": [] @@ -67,16 +85,16 @@ "id": "34b19aac", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:18.538916Z", - "iopub.status.busy": "2026-08-17T03:46:18.538506Z", - "iopub.status.idle": "2026-08-17T03:46:23.551467Z", - "shell.execute_reply": "2026-08-17T03:46:23.550740Z" + "iopub.execute_input": "2026-10-16T06:00:00.466805Z", + "iopub.status.busy": "2026-10-16T06:00:00.466530Z", + "iopub.status.idle": "2026-10-16T06:00:08.842251Z", + "shell.execute_reply": "2026-10-16T06:00:08.841748Z" }, "papermill": { - "duration": 5.01681, - "end_time": "2026-08-17T03:46:23.552521", + "duration": 8.385081, + "end_time": "2026-10-16T06:00:08.843469+00:00", "exception": false, - "start_time": "2026-08-17T03:46:18.535711", + "start_time": "2026-10-16T06:00:00.458388+00:00", "status": "completed" }, "tags": [] @@ -132,10 +150,10 @@ "id": "48b0b793", "metadata": { "papermill": { - "duration": 0.001724, - "end_time": "2026-08-17T03:46:23.556364", + "duration": 0.003495, + "end_time": "2026-10-16T06:00:08.850889+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.554640", + "start_time": "2026-10-16T06:00:08.847394+00:00", "status": "completed" }, "tags": [] @@ -150,16 +168,16 @@ "id": "85411416", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.560968Z", - "iopub.status.busy": "2026-08-17T03:46:23.560508Z", - "iopub.status.idle": "2026-08-17T03:46:23.564310Z", - "shell.execute_reply": "2026-08-17T03:46:23.563657Z" + "iopub.execute_input": "2026-10-16T06:00:08.859618Z", + "iopub.status.busy": "2026-10-16T06:00:08.859184Z", + "iopub.status.idle": "2026-10-16T06:00:08.863752Z", + "shell.execute_reply": "2026-10-16T06:00:08.863082Z" }, "papermill": { - "duration": 0.006996, - "end_time": "2026-08-17T03:46:23.565081", + "duration": 0.010024, + "end_time": "2026-10-16T06:00:08.864714+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.558085", + "start_time": "2026-10-16T06:00:08.854690+00:00", "status": "completed" }, "tags": [] @@ -183,7 +201,16 @@ { "cell_type": "markdown", "id": "2adf4585", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003608, + "end_time": "2026-10-16T06:00:08.873808+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.870200+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Reading the result: why this word choice is pedagogical\n\nThe word `\"anticonstitutionnellement\"` (25 letters, Grand Robert) combines several difficulties: length (strength of the progressive hints), morphological composition (`anti-+constitution+nelle+ment` — the agent can guess by decomposition), and rarity (an uncommon legal vocabulary). For an **LLM riddle game**, it is an excellent stress test: if the agent guesses this word in fewer than 10 attempts, the Père Fouras prompt is well calibrated. The student can **measure the success rate** over 100 games with `evaluer_partie()` and then compare with a simpler word (e.g. `\"kayak\"`, 5 letters) to isolate the effect of the word's length.\n The contract must be tested.\n Grand Robert reference.\n Reference.\n" ] @@ -191,7 +218,16 @@ { "cell_type": "markdown", "id": "fdc6262b", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003254, + "end_time": "2026-10-16T06:00:08.880684+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.877430+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Reading the result: Semantic Kernel kernel instantiated\n\nCell 4 creates the kernel and stores `MOT_A_DEVINER = \"anticonstitutionnellement\"` (25 letters, one of the longest standard French words). This constant is what **the agent will have to guess** during the duel through a riddle strategy set by Père Fouras. The choice of word is pedagogical: its length forces the agent to mobilize **indirect hint strategies** (rhyme, context, length) rather than a direct description. The student can substitute a shorter word for faster games (`\"kayak\"` for a palindrome).\n Forces progression.\n More.\n Progression.\n" ] @@ -201,10 +237,10 @@ "id": "f0496c5b", "metadata": { "papermill": { - "duration": 0.001988, - "end_time": "2026-08-17T03:46:23.569263", + "duration": 0.003477, + "end_time": "2026-10-16T06:00:08.887499+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.567275", + "start_time": "2026-10-16T06:00:08.884022+00:00", "status": "completed" }, "tags": [] @@ -219,16 +255,16 @@ "id": "9bedfe8b", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.574048Z", - "iopub.status.busy": "2026-08-17T03:46:23.573632Z", - "iopub.status.idle": "2026-08-17T03:46:23.576972Z", - "shell.execute_reply": "2026-08-17T03:46:23.576249Z" + "iopub.execute_input": "2026-10-16T06:00:08.895466Z", + "iopub.status.busy": "2026-10-16T06:00:08.895208Z", + "iopub.status.idle": "2026-10-16T06:00:08.899788Z", + "shell.execute_reply": "2026-10-16T06:00:08.898990Z" }, "papermill": { - "duration": 0.006842, - "end_time": "2026-08-17T03:46:23.577966", + "duration": 0.010014, + "end_time": "2026-10-16T06:00:08.900867+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.571124", + "start_time": "2026-10-16T06:00:08.890853+00:00", "status": "completed" }, "tags": [] @@ -253,26 +289,36 @@ { "cell_type": "markdown", "id": "3e19b11c", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003776, + "end_time": "2026-10-16T06:00:08.908280+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:08.904504+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ - "### Reading the result: Père Fouras and cyclist prompts configured\n\nCell 6 defines two structured `PERE_FOURAS_PROMPT` and `CYCLISTE_PROMPT` via `f\"\"\"...\"\"\"`. The Père Fouras prompt encodes the **persona + the mechanics**: pose a riddle, wait for an answer, validate, offer the next hint. The cyclist prompt encodes the **guessing strategy**: extract letters, length, context, ask for a hint. This persona/mechanics separation is crucial — without an explicit persona, the LLM would answer in generic Q&A mode without the **Fort Boyard narrative frame**. Exercise 1 invites the student to add a 3rd character (Detective, Witness) — a classic extension of the **multi-agent cast**.\n The `f\"\"\"...\"\"\"` format allows the interpolation of runtime variables (word to guess, current score).\n" + "### Reading the result: Père Fouras and cyclist prompts configured\n\nCell 10 defines `PERE_FOURAS_PROMPT` and `LAURENT_JALABERT_PROMPT` structured via `f\"\"\"...\"\"\"` — only the Père Fouras f-string interpolates `MOT_A_DEVINER`. The Père Fouras prompt encodes the **persona + the mechanics**: pose a riddle, wait for an answer, validate, offer the next hint. The cyclist prompt encodes the **guessing strategy**: extract letters, length, context, ask for a hint. This persona/mechanics separation is crucial — without an explicit persona, the LLM would answer in generic Q&A mode without the **Fort Boyard narrative frame**. Guided example 1 (contribution from PR #21009) replaces the duo with two new characters — a **Detective** (deductive questions) and a **Witness** (evasive answers) — a classic extension of the **multi-agent cast**.\n The `f\"\"\"...\"\"\"` format allows the interpolation of runtime variables\n" ] }, { "cell_type": "markdown", "id": "649ac34e", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.001725, - "end_time": "2026-08-17T03:46:23.581576", + "duration": 0.004027, + "end_time": "2026-10-16T06:00:08.915690+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.579851", + "start_time": "2026-10-16T06:00:08.911663+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercise 1: Customize the duel characters\n\nThe current prompts define Père Fouras as enigmatic and Laurent Jalabert as perceptive. The goal is to create a variant of the game with two different characters: a **Detective** (who asks deductive questions) and a **Witness** (who answers evasively without revealing the word).\n\n**Objective**: write the system instructions for these two new characters and configure the corresponding agents.\n\n**Hints**:\n- `# Step 1`: Write the Witness prompt (role, rules, prohibitions)\n- `# Step 2`: Write the Detective prompt (questioning strategy)\n- `# Hint`: the Witness must never reveal the word directly, but may give indirect clues\n" + "### Guided example 1: Customize the duel characters\n\n*Contribution from the group of PR #21009, integrated as a guided example.*\n\nThe initial prompts define Père Fouras as enigmatic and Laurent Jalabert as sharp-minded. The group of PR #21009 delivers the requested variant: a **Detective** (deductive questions, closed-ended preferred, never giving up) and a **Witness** (short evasive answers, indirect hints, never the word in clear) — then configures both `ChatCompletionAgent` agents on the same pattern as the original duo.\n" ] }, { @@ -281,16 +327,16 @@ "id": "ac94f4f0", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.586230Z", - "iopub.status.busy": "2026-08-17T03:46:23.585867Z", - "iopub.status.idle": "2026-08-17T03:46:23.591262Z", - "shell.execute_reply": "2026-08-17T03:46:23.590498Z" + "iopub.execute_input": "2026-10-16T06:00:08.924877Z", + "iopub.status.busy": "2026-10-16T06:00:08.924532Z", + "iopub.status.idle": "2026-10-16T06:00:10.107373Z", + "shell.execute_reply": "2026-10-16T06:00:10.106555Z" }, "papermill": { - "duration": 0.008725, - "end_time": "2026-08-17T03:46:23.592095", + "duration": 1.188654, + "end_time": "2026-10-16T06:00:10.108687+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.583370", + "start_time": "2026-10-16T06:00:08.920033+00:00", "status": "completed" }, "tags": [] @@ -300,22 +346,67 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : personnages Detective et Temoin\n" + "Les agents Detective et Temoin ont ete crees avec succes !\n" ] } ], "source": [ - "# Exercice 1 : Personnaliser les personnages du duel\n", - "# TODO etudiant : definir les prompts et creer les agents\n", + "# EXEMPLE GUIDE 1 : personnaliser les personnages du duel (groupe de la PR #21009)\n", + "TEMoin_PROMPT = f\"\"\"\n", + "Tu es un témoin interrogé par un détective.\n", + "Tu connais le mot secret : '{MOT_A_DEVINER}'.\n", + "\n", + "Ton rôle est de répondre aux questions du détective.\n", + "Tu dois rester mystérieux et parfois évasif.\n", + "Ne révèle jamais directement le mot secret.\n", + "Tu peux donner des indices indirects pour aider le détective.\n", + "Réponds de manière courte et précise.\n", + "\"\"\"\n", + "\n", + "# Etape 2 : Instructions du Detective\n", + "DETECTIVE_PROMPT = \"\"\"\n", + "Tu es un détective intelligent et perspicace.\n", + "Ton objectif est de découvrir un mot secret.\n", "\n", - "TEMoin_PROMPT = \"\" # Etape 1 : instructions du Temoin (evasif, indices indirects)\n", - "DETECTIVE_PROMPT = \"\" # Etape 2 : instructions du Detective (questions deductives)\n", + "Pose des questions logiques et déductives au témoin.\n", + "Privilégie les questions fermées (Oui/Non).\n", + "Analyse les réponses pour progresser dans ton enquête.\n", + "Lorsque tu penses avoir trouvé, propose le mot.\n", + "Ne renonce pas trop rapidement.\n", + "\"\"\"\n", "\n", - "# Indice : creer les agents avec ChatCompletionAgent(kernel=..., name=..., instructions=...)\n", - "temoin = None # TODO etudiant : creer l'agent Temoin\n", - "detective = None # TODO etudiant : creer l'agent Detective\n", + "# Etape 3 : Creation de l'agent Temoin\n", + "temoin = ChatCompletionAgent(\n", + " kernel=create_kernel(),\n", + " name=\"Temoin\",\n", + " instructions=TEMoin_PROMPT,\n", + ")\n", "\n", - "print(\"Exercice a completer : personnages Detective et Temoin\")" + "# Etape 4 : Creation de l'agent Detective\n", + "detective = ChatCompletionAgent(\n", + " kernel=create_kernel(),\n", + " name=\"Detective\",\n", + " instructions=DETECTIVE_PROMPT,\n", + ")\n", + "\n", + "print(\"Les agents Detective et Temoin ont ete crees avec succes !\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0672c52a", + "metadata": { + "papermill": { + "duration": 0.003717, + "end_time": "2026-10-16T06:00:10.116639+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.112922+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Reading the result: two new characters configured\n\nThe group's two agents are created without error. Two choices deserve attention: the **Witness** prompt is an f-string that interpolates `MOT_A_DEVINER` — the witness \"knows\" the word because it is injected into its instructions, but the rule \"Ne révèle jamais directement le mot secret\" (never reveal the secret word directly) maintains the gap between knowing and telling. The **Detective** prompt imposes closed-ended (yes/no) questions: this format forces the conversation to converge, just as the group's round-robin selection forces the order of turns (next section). Each agent receives its own `create_kernel()` — the same pattern as the Père Fouras / Laurent Jalabert duo of the previous cell.\n" ] }, { @@ -323,10 +414,10 @@ "id": "6a8dbe2b", "metadata": { "papermill": { - "duration": 0.001813, - "end_time": "2026-08-17T03:46:23.595862", + "duration": 0.0036, + "end_time": "2026-10-16T06:00:10.123964+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.594049", + "start_time": "2026-10-16T06:00:10.120364+00:00", "status": "completed" }, "tags": [] @@ -341,16 +432,16 @@ "id": "83e35526", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:23.600588Z", - "iopub.status.busy": "2026-08-17T03:46:23.600215Z", - "iopub.status.idle": "2026-08-17T03:46:24.269659Z", - "shell.execute_reply": "2026-08-17T03:46:24.268772Z" + "iopub.execute_input": "2026-10-16T06:00:10.132863Z", + "iopub.status.busy": "2026-10-16T06:00:10.132598Z", + "iopub.status.idle": "2026-10-16T06:00:10.770159Z", + "shell.execute_reply": "2026-10-16T06:00:10.769633Z" }, "papermill": { - "duration": 0.672891, - "end_time": "2026-08-17T03:46:24.270614", + "duration": 0.643563, + "end_time": "2026-10-16T06:00:10.771156+00:00", "exception": false, - "start_time": "2026-08-17T03:46:23.597723", + "start_time": "2026-10-16T06:00:10.127593+00:00", "status": "completed" }, "tags": [] @@ -374,7 +465,16 @@ { "cell_type": "markdown", "id": "2bbc1e0f", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.003971, + "end_time": "2026-10-16T06:00:10.779127+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.775156+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ "### Reading the result: conversational agents created\n\nCell 10 instantiates `pere_fouras` and `jalabert` via `ChatCompletionAgent(kernel=..., name=..., instructions=..., description=...)`. The `description` parameter is often underused: it serves the **selection_strategy** to choose the active agent when the chat group must decide who speaks. Without an explicit description, the orchestrator would risk alternating arbitrarily. Cell 12 (after agents) imports `TerminationStrategy` — the base class for custom termination strategies. The student must observe the **difference between agents and the kernel**: agents use the kernel but do not own it, and each agent can have a different prompt.\n It is a parameter.\n" ] @@ -384,10 +484,10 @@ "id": "4a00c813", "metadata": { "papermill": { - "duration": 0.001948, - "end_time": "2026-08-17T03:46:24.274675", + "duration": 0.003258, + "end_time": "2026-10-16T06:00:10.785856+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.272727", + "start_time": "2026-10-16T06:00:10.782598+00:00", "status": "completed" }, "tags": [] @@ -402,16 +502,16 @@ "id": "a86ac80f", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.279543Z", - "iopub.status.busy": "2026-08-17T03:46:24.279286Z", - "iopub.status.idle": "2026-08-17T03:46:24.284899Z", - "shell.execute_reply": "2026-08-17T03:46:24.284400Z" + "iopub.execute_input": "2026-10-16T06:00:10.794438Z", + "iopub.status.busy": "2026-10-16T06:00:10.793912Z", + "iopub.status.idle": "2026-10-16T06:00:10.799763Z", + "shell.execute_reply": "2026-10-16T06:00:10.799132Z" }, "papermill": { - "duration": 0.009132, - "end_time": "2026-08-17T03:46:24.285729", + "duration": 0.011676, + "end_time": "2026-10-16T06:00:10.801032+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.276597", + "start_time": "2026-10-16T06:00:10.789356+00:00", "status": "completed" }, "tags": [] @@ -442,17 +542,18 @@ "cell_type": "markdown", "id": "81de6677", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.001777, - "end_time": "2026-08-17T03:46:24.289620", + "duration": 0.004574, + "end_time": "2026-10-16T06:00:10.809100+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.287843", + "start_time": "2026-10-16T06:00:10.804526+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercise 2: Termination with giving-up detection\n\nThe current strategy stops the game only if the word is guessed. But Laurent Jalabert could also give up. The goal is to create a hybrid strategy that stops the game if the word is guessed **or** if Laurent expresses giving up (\"je donne ma langue au chat\", \"j'abandonne\").\n\n**Objective**: implement `RenoncementTerminationStrategy` which combines detection of the secret word with detection of giving-up phrases.\n\n**Hints**:\n- `# Step 1`: Define a list of typical giving-up phrases\n- `# Step 2`: Check whether the last message contains one of these phrases (in lowercase)\n- `# Hint`: combine the two conditions with `or` in `should_agent_terminate`\n" + "### Guided example 2: Termination with give-up detection\n\n*Contribution from the group of PR #21009, integrated as a guided example.*\n\nThe notebook's strategy stops the game only if the word is guessed. The group of PR #21009 implements the hybrid variant `RenoncementTerminationStrategy`: the game stops if the word is guessed **or** if Laurent expresses a give-up (\"je donne ma langue au chat\", \"j'abandonne\", …) — then checks the strategy on three witness situations.\n" ] }, { @@ -461,16 +562,16 @@ "id": "0e96e11d", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.294492Z", - "iopub.status.busy": "2026-08-17T03:46:24.294104Z", - "iopub.status.idle": "2026-08-17T03:46:24.299208Z", - "shell.execute_reply": "2026-08-17T03:46:24.298604Z" + "iopub.execute_input": "2026-10-16T06:00:10.818549Z", + "iopub.status.busy": "2026-10-16T06:00:10.818139Z", + "iopub.status.idle": "2026-10-16T06:00:10.824370Z", + "shell.execute_reply": "2026-10-16T06:00:10.823791Z" }, "papermill": { - "duration": 0.008303, - "end_time": "2026-08-17T03:46:24.299926", + "duration": 0.011456, + "end_time": "2026-10-16T06:00:10.825251+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.291623", + "start_time": "2026-10-16T06:00:10.813795+00:00", "status": "completed" }, "tags": [] @@ -480,24 +581,132 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : RenoncementTerminationStrategy\n" + "Strategie de renoncement configuree !\n" ] } ], "source": [ + "# EXEMPLE GUIDE 2 : terminaison avec detection de renoncement (groupe de la PR #21009)\n", "from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy\n", "from semantic_kernel.contents.chat_message_content import ChatMessageContent\n", "\n", "class RenoncementTerminationStrategy(TerminationStrategy):\n", - " # TODO etudiant : implementer la strategie de terminaison hybride\n", - " PHRASES_ABANDON: list[str] = [] # Etape 1 : phrases de renoncement\n", - " \n", - " async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool:\n", - " # Etape 2 : verifier mot devine OU abandon detecte\n", - " result = False # TODO etudiant : remplacer par la logique\n", - " return result\n", "\n", - "print(\"Exercice a completer : RenoncementTerminationStrategy\")" + " # Etape 1 : Definir les phrases d'abandon\n", + " PHRASES_ABANDON: list[str] = [\n", + " \"j'abandonne\",\n", + " \"je donne ma langue au chat\",\n", + " \"je renonce\",\n", + " \"je laisse tomber\"\n", + " ]\n", + "\n", + " async def should_agent_terminate(\n", + " self, agent, history: list[ChatMessageContent],\n", + " cancellation_token=None\n", + " ) -> bool:\n", + "\n", + " # Verifier que l'historique n'est pas vide\n", + " if not history:\n", + " return False\n", + "\n", + " # Recuperer le dernier message en minuscules\n", + " dernier_message = str(history[-1].content or \"\").lower()\n", + "\n", + " # Verifier si le mot secret est trouve\n", + " mot_trouve = MOT_A_DEVINER.lower() in dernier_message\n", + "\n", + " # Verifier si une phrase d'abandon est presente\n", + " abandon = any(\n", + " phrase in dernier_message\n", + " for phrase in self.PHRASES_ABANDON\n", + " )\n", + "\n", + " # Arreter si le mot est trouve OU si le joueur abandonne\n", + " return mot_trouve or abandon\n", + "\n", + "print(\"Strategie de renoncement configuree !\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "6b1b975c", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:00:10.833293Z", + "iopub.status.busy": "2026-10-16T06:00:10.832840Z", + "iopub.status.idle": "2026-10-16T06:00:10.844943Z", + "shell.execute_reply": "2026-10-16T06:00:10.844360Z" + }, + "papermill": { + "duration": 0.017086, + "end_time": "2026-10-16T06:00:10.845860+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.828774+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Abandon : True (attendu : True)\n", + "Mot trouve : True (attendu : True)\n", + "Mauvaise reponse : False (attendu : False)\n" + ] + } + ], + "source": [ + "\n", + "# Tests de l'exemple guide 2 : trois situations (groupe de la PR #21009)\n", + "\n", + "from semantic_kernel.contents import AuthorRole, ChatMessageContent\n", + "\n", + "strategie = RenoncementTerminationStrategy(\n", + " agents=[laurent_jalabert],\n", + " maximum_iterations=20\n", + ")\n", + "\n", + "# Trois situations a tester\n", + "tests = [\n", + " (\"Abandon\", \"J'abandonne\", True),\n", + " (\"Mot trouve\", f\"Ma reponse est {MOT_A_DEVINER}\", True),\n", + " (\"Mauvaise reponse\", \"Je pense que c'est un objet\", False)\n", + "]\n", + "\n", + "for nom, reponse, attendu in tests:\n", + "\n", + " message = ChatMessageContent(\n", + " role=AuthorRole.ASSISTANT,\n", + " content=reponse,\n", + " name=\"Laurent_Jalabert\"\n", + " )\n", + "\n", + " resultat = await strategie.should_agent_terminate(\n", + " agent=laurent_jalabert,\n", + " history=[message]\n", + " )\n", + "\n", + " print(f\"{nom} : {resultat} (attendu : {attendu})\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "0ed41dbe", + "metadata": { + "papermill": { + "duration": 0.003641, + "end_time": "2026-10-16T06:00:10.853349+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.849708+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Reading the result: the hybrid strategy passes its three tests\n\nThe three situations return the expected verdict: a \"J'abandonne\" (I give up) stops the game, a message containing `MOT_A_DEVINER` does too, and a neutral answer lets the duel continue. The mechanics boil down to two gestures: everything is compared in lowercase (the model may write \"J'ABANDONNE\"), and the final condition is an `or` — it is enough that the word is found **or** that a give-up phrase appears in the **last** message of the history. The strategy is attached to `laurent_jalabert`: it is indeed on the guesser's turn that the question \"continue?\" arises.\n" ] }, { @@ -505,10 +714,10 @@ "id": "c61e7f51", "metadata": { "papermill": { - "duration": 0.001853, - "end_time": "2026-08-17T03:46:24.303711", + "duration": 0.003397, + "end_time": "2026-10-16T06:00:10.860124+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.301858", + "start_time": "2026-10-16T06:00:10.856727+00:00", "status": "completed" }, "tags": [] @@ -519,20 +728,20 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "id": "fc868e1b", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.308636Z", - "iopub.status.busy": "2026-08-17T03:46:24.308282Z", - "iopub.status.idle": "2026-08-17T03:46:24.312015Z", - "shell.execute_reply": "2026-08-17T03:46:24.311548Z" + "iopub.execute_input": "2026-10-16T06:00:10.868540Z", + "iopub.status.busy": "2026-10-16T06:00:10.868263Z", + "iopub.status.idle": "2026-10-16T06:00:10.872943Z", + "shell.execute_reply": "2026-10-16T06:00:10.872057Z" }, "papermill": { - "duration": 0.007189, - "end_time": "2026-08-17T03:46:24.312786", + "duration": 0.010201, + "end_time": "2026-10-16T06:00:10.873865+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.305597", + "start_time": "2026-10-16T06:00:10.863664+00:00", "status": "completed" }, "tags": [] @@ -552,9 +761,18 @@ { "cell_type": "markdown", "id": "cb4bd6c9", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.00381, + "end_time": "2026-10-16T06:00:10.881607+00:00", + "exception": false, + "start_time": "2026-10-16T06:00:10.877797+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ - "### Reading the result: chat group configured\n\nCell 16 builds `AgentGroupChat(agents=[pere_fouras, jalabert], termination_strategy=..., selection_strategy=...)`. This is the **central object** that orchestrates the duel: at each turn, the `selection_strategy` chooses who speaks (round-robin by default), the `termination_strategy` detects the end (give-up or score). Without this wrapper, both agents would speak in parallel. The **Fort Boyard specificity** is that the give-up strategy (cell 14, exercise) must let the agent abandon after N unsuccessful attempts — an emulation of the TV stress.\n" + "### Reading the result: chat group configured\n\nThe cell builds `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — without an explicit `selection_strategy`. This is the **central object** that orchestrates the duel: at each turn, the next agent speaks in list order (round-robin — the default selection, visible in the alternating `Selected agent at index 0` / `at index 1` lines of the output), and the `termination_strategy` detects the end — here, the guessed word: the output evaluates `should terminate` after each Laurent_Jalabert turn, and the game stops on the first `True` — or when the iterations run out if the word is never named. Without this wrapper, both agents would speak in parallel. The **Fort Boyard specificity** is the give-up: guided example 2 shows the hybrid strategy that also lets the agent abandon (\"je donne ma langue au chat\") — an emulation of the TV stress.\n" ] }, { @@ -562,10 +780,10 @@ "id": "ada98689", "metadata": { "papermill": { - "duration": 0.001946, - "end_time": "2026-08-17T03:46:24.316751", + "duration": 0.003713, + "end_time": "2026-10-16T06:00:10.889273+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.314805", + "start_time": "2026-10-16T06:00:10.885560+00:00", "status": "completed" }, "tags": [] @@ -576,20 +794,20 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "id": "bad1ce48", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:24.321638Z", - "iopub.status.busy": "2026-08-17T03:46:24.321222Z", - "iopub.status.idle": "2026-08-17T03:46:46.092563Z", - "shell.execute_reply": "2026-08-17T03:46:46.091973Z" + "iopub.execute_input": "2026-10-16T06:00:10.897135Z", + "iopub.status.busy": "2026-10-16T06:00:10.896838Z", + "iopub.status.idle": "2026-10-16T06:01:05.617069Z", + "shell.execute_reply": "2026-10-16T06:01:05.616301Z" }, "papermill": { - "duration": 21.774859, - "end_time": "2026-08-17T03:46:46.093496", + "duration": 54.725636, + "end_time": "2026-10-16T06:01:05.618408+00:00", "exception": false, - "start_time": "2026-08-17T03:46:24.318637", + "start_time": "2026-10-16T06:00:10.892772+00:00", "status": "completed" }, "tags": [] @@ -599,479 +817,339 @@ "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:24,323 [INFO] Depart du duel Pere Fouras vs Laurent Jalabert !\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:24,323 [INFO] Mot a deviner : ANTICONSTITUTIONNELLEMENT\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:24,325 [INFO] Adding `1` agent chat messages\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:24,326 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:24,327 [INFO] Invoking agent Pere_Fouras\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,809 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,818 [INFO] OpenAI usage: CompletionUsage(completion_tokens=167, prompt_tokens=72, total_tokens=239, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,819 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,820 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,820 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… écoute l’indice du vieux sage.\n", - "\n", - "Je suis un mot si long qu’il essouffle même les plus bavards, \n", - "on me cite quand une règle, une loi ou un décret ne respecte pas la Loi suprême.\n", - "\n", - "Charade :\n", - "- Mon 1er est l’opposé de « pour ».\n", - "- Mon 2e est ce qui fait tenir debout une nation, écrit noir sur blanc.\n", - "- Mon 3e est un petit suffixe qui transforme une idée en adjectif.\n", - "- Mon 4e est un autre suffixe qui transforme l’adjectif… en manière d’agir.\n", - "\n", - "Assemblés, je dis : « d’une manière contraire à ce qui est au sommet des lois ».\n", - "\n", - "À toi de jouer… quel est ce mot interminable ?\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,820 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:28,821 [INFO] Invoking agent Laurent_Jalabert\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:30,774 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:30,776 [INFO] OpenAI usage: CompletionUsage(completion_tokens=84, prompt_tokens=415, total_tokens=499, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" - ] - }, - { - "name": "stderr", - "output_type": "stream", - "text": [ - "2026-08-17 05:46:30,777 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:00:10,899 [INFO] Depart du duel Pere Fouras vs Laurent Jalabert !\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,777 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" + "2026-10-16 08:00:10,899 [INFO] Mot a deviner : ANTICONSTITUTIONNELLEMENT\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,777 [INFO] [AuthorRole.ASSISTANT] : Je n’peux pas te donner le mot tout cru, moussaillon… mais je peux te guider.\n", - "\n", - "Indice : \n", - "On prononce ce mot quand une loi, un décret ou une décision **n’est pas conforme à la Loi suprême** d’un pays.\n", - "\n", - "Question fermée (réponds Oui/Non) : \n", - "Ton mot commence-t-il par **« anti- »** ?\n" + "2026-10-16 08:00:10,900 [INFO] Adding `1` agent chat messages\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,778 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:10,901 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:30,778 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:10,901 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,472 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:22,780 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,475 [INFO] OpenAI usage: CompletionUsage(completion_tokens=146, prompt_tokens=610, total_tokens=756, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:22,795 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1188, prompt_tokens=72, total_tokens=1260, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1024, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,475 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:22,796 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,476 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:22,797 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,476 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… voici un nouvel indice, sans jamais trahir le secret.\n", + "2026-10-16 08:00:22,797 [INFO] [AuthorRole.ASSISTANT] : Ah ! Tu veux un indice, jeune aventurier ? Écoute bien la charade du vieux Père Fouras.\n", "\n", - "Je suis un adverbe gigantesque, qu’on invoque quand un texte de loi est jugé **contraire à la Constitution**.\n", + "- Mon premier est un préfixe qui signifie \"contre\" — il s'oppose à \"pro\".\n", + "- Mon deuxième désigne le texte fondamental qui organise un État, souvent utilisé pour parler des lois et droits.\n", + "- Mon troisième est le petit suffixe qui transforme un adjectif en manière : on le trouve à la fin de nombreux adverbes.\n", "\n", - "Charade :\n", - "- Mon 1er : le contraire de « pour ».\n", - "- Mon 2e : la loi fondamentale d’un pays.\n", - "- Mon 3e : ce qui transforme un nom en adjectif (comme dans *nation → national*).\n", - "- Mon 4e : ce qui transforme l’adjectif en adverbe (la “manière de”).\n", + "Indice supplémentaire, énigmatique : c'est un adverbe très long, souvent évoqué comme l'un des plus longs en français, et il qualifie une action faite en opposition à la Constitution.\n", "\n", - "Avec tout cela, je signifie : **« d’une manière contraire à la Constitution »**.\n" + "Quand tu penses avoir trouvé, réponds — le Fort écoute.\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,476 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:00:22,798 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:33,477 [INFO] Invoking agent Laurent_Jalabert\n" + "2026-10-16 08:00:22,799 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,399 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:30,294 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,404 [INFO] OpenAI usage: CompletionUsage(completion_tokens=77, prompt_tokens=911, total_tokens=988, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:30,297 [INFO] OpenAI usage: CompletionUsage(completion_tokens=740, prompt_tokens=397, total_tokens=1137, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=704, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,405 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:00:30,298 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,406 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" + "2026-10-16 08:00:30,299 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: False\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,407 [INFO] [AuthorRole.ASSISTANT] : Je peux pas te donner le mot directement, mais voilà un indice clair.\n", - "\n", - "On emploie ce mot quand une loi, un décret ou une décision est jugé **contraire à la Constitution** (la loi suprême).\n", - "\n", - "Question Oui/Non (pour avancer) : \n", - "Le mot que tu cherches **se termine-t-il par « -ment »** ?\n" + "2026-10-16 08:00:30,299 [INFO] [AuthorRole.ASSISTANT] : Parfait — je commence mon enquête. Première question : est-ce que le mot est un adverbe ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,408 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:30,300 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:35,409 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:30,301 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,419 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:40,908 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,421 [INFO] OpenAI usage: CompletionUsage(completion_tokens=141, prompt_tokens=1092, total_tokens=1233, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:40,910 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1154, prompt_tokens=484, total_tokens=1638, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=960, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,423 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:40,911 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,424 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:40,912 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,424 [INFO] [AuthorRole.ASSISTANT] : Approche, moussaillon… voici un indice de plus, sans trahir le secret.\n", + "2026-10-16 08:00:40,913 [INFO] [AuthorRole.ASSISTANT] : Ha ha ! Tu veux un nouvel indice, petit malin ? Le vieux Père Fouras t'en offre encore un, mais gare aux oublis...\n", "\n", - "Je suis un **adverbe interminable** qu’on sort quand on affirme qu’une décision a été prise **d’une manière contraire à la Constitution**.\n", + "- Mon premier s'oppose, il dit \"contre\".\n", + "- Mon deuxième est le texte fondamental d'un État, celui qui fixe les lois.\n", + "- Ma fin est le petit ajout qui transforme un adjectif en manière — on le colle à la fin des adverbes.\n", "\n", - "Énigme en morceaux :\n", - "- Je commence par le préfixe qui signifie **« contre »**.\n", - "- Au milieu, j’abrite la **loi fondamentale** d’un pays.\n", - "- Je finis par le petit bout qui veut dire **« de façon… »** (comme dans beaucoup d’adverbes).\n", + "Autres soupçons du Fort : ce mot compte 25 lettres ; c'est un adverbe qui qualifie une action faite en opposition à la Constitution. \n", + "Si tu retires mon premier, tu tombes sur le texte suprême lui-même. Si tu ajoutes simplement le suffixe de manière, tu obtiens l'ensemble.\n", "\n", - "Donne-moi ta proposition… et je te dirai si tu brûles ou si tu gèles.\n" + "Veux-tu un énigme chiffrée, une charade sonore, ou... une autre devinette pour t'approcher encore ?\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,425 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:00:40,913 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:39,425 [INFO] Invoking agent Laurent_Jalabert\n" + "2026-10-16 08:00:40,914 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,122 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:52,886 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,123 [INFO] OpenAI usage: CompletionUsage(completion_tokens=57, prompt_tokens=1383, total_tokens=1440, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:52,887 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1305, prompt_tokens=869, total_tokens=2174, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1280, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:00:52,888 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: False\n" + "2026-10-16 08:00:52,889 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: False\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,124 [INFO] [AuthorRole.ASSISTANT] : Bien tenté, moussaillon… mais ici, c’est **moi** qui dois deviner **ton** mot, uniquement avec des questions **Oui/Non**.\n", - "\n", - "Première question : \n", - "Ton mot est-il **un objet** ? (Oui/Non)\n" + "2026-10-16 08:00:52,889 [INFO] [AuthorRole.ASSISTANT] : Est-ce que le mot commence par \"anti\" ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,125 [INFO] Selected agent at index 0 (ID: a204ff22-4ed4-4518-b01f-7ea4afa61589, name: Pere_Fouras)\n" + "2026-10-16 08:00:52,890 [INFO] Selected agent at index 0 (ID: ccef3d6f-0553-4bf2-8550-6d742ed501dd, name: Pere_Fouras)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:41,125 [INFO] Invoking agent Pere_Fouras\n" + "2026-10-16 08:00:52,891 [INFO] Invoking agent Pere_Fouras\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,701 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:00:57,155 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,708 [INFO] OpenAI usage: CompletionUsage(completion_tokens=181, prompt_tokens=1524, total_tokens=1705, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" + "2026-10-16 08:00:57,157 [INFO] OpenAI usage: CompletionUsage(completion_tokens=360, prompt_tokens=934, total_tokens=1294, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=320, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,710 [INFO] Evaluating termination criteria for a204ff22-4ed4-4518-b01f-7ea4afa61589\n" + "2026-10-16 08:00:57,159 [INFO] Evaluating termination criteria for ccef3d6f-0553-4bf2-8550-6d742ed501dd\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,711 [INFO] Agent a204ff22-4ed4-4518-b01f-7ea4afa61589 is out of scope\n" + "2026-10-16 08:00:57,159 [INFO] Agent ccef3d6f-0553-4bf2-8550-6d742ed501dd is out of scope\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,712 [INFO] [AuthorRole.ASSISTANT] : Approche, aventurier… l’indice du Père Fouras t’attend.\n", - "\n", - "Je suis un **adverbe-monstre**, si long qu’il fait trembler les dictionnaires. On me prononce lorsqu’on juge qu’un texte, une loi ou une décision agit **contrairement à la loi suprême** d’un pays.\n", - "\n", - "Charade :\n", - "- Mon 1er signifie **« contre »**. \n", - "- Mon 2e est la **loi fondamentale** d’un État. \n", - "- Mon 3e est un morceau qui sert à dire **« qui concerne »** (il transforme souvent un nom en adjectif). \n", - "- Mon 4e termine beaucoup d’adverbes et veut dire **« d’une manière »**.\n", - "\n", - "Assemble tout cela… et tu obtiendras le mot que même les champions d’orthographe redoutent.\n" + "2026-10-16 08:00:57,160 [INFO] [AuthorRole.ASSISTANT] : Oui — mon premier hurle \"contre\" : c'est bien \"anti\". Le Fort avance un pas, mais la chasse n'est pas finie...\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,713 [INFO] Selected agent at index 1 (ID: 56c2c734-70ef-4c89-a754-abb2d7bc60b3, name: Laurent_Jalabert)\n" + "2026-10-16 08:00:57,161 [INFO] Selected agent at index 1 (ID: 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, name: Laurent_Jalabert)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:44,715 [INFO] Invoking agent Laurent_Jalabert\n" + "2026-10-16 08:00:57,161 [INFO] Invoking agent Laurent_Jalabert\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,082 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" + "2026-10-16 08:01:05,600 [INFO] HTTP Request: POST https://api.openai.com/v1/chat/completions \"HTTP/1.1 200 OK\"\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,085 [INFO] OpenAI usage: CompletionUsage(completion_tokens=48, prompt_tokens=1895, total_tokens=1943, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=0, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=1152))\n" + "2026-10-16 08:01:05,609 [INFO] OpenAI usage: CompletionUsage(completion_tokens=1052, prompt_tokens=1011, total_tokens=2063, completion_tokens_details=CompletionTokensDetails(accepted_prediction_tokens=0, audio_tokens=0, reasoning_tokens=1024, rejected_prediction_tokens=0), prompt_tokens_details=PromptTokensDetails(audio_tokens=0, cached_tokens=0))\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,086 [INFO] Evaluating termination criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3\n" + "2026-10-16 08:01:05,610 [INFO] Evaluating termination criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,087 [INFO] Evaluated criteria for 56c2c734-70ef-4c89-a754-abb2d7bc60b3, should terminate: True\n" + "2026-10-16 08:01:05,611 [INFO] Evaluated criteria for 720ff1ae-7ee6-4c62-8574-b36b682a0b5c, should terminate: True\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,088 [INFO] [AuthorRole.ASSISTANT] : Ton indice pointe très fort vers **« anticonstitutionnellement »**.\n", - "\n", - "Pour respecter le jeu, je pose une question Oui/Non : \n", - "Le mot à deviner est-il **« anticonstitutionnellement »** ?\n" + "2026-10-16 08:01:05,611 [INFO] [AuthorRole.ASSISTANT] : Est-ce que le mot est \"anticonstitutionnellement\" ? (Oui / Non)\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ - "2026-08-17 05:46:46,088 [INFO] Partie terminee !\n" + "2026-10-16 08:01:05,612 [INFO] Partie terminee !\n" ] } ], @@ -1100,52 +1178,71 @@ { "cell_type": "markdown", "id": "3aa490d9", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.007379, + "end_time": "2026-10-16T06:01:05.634443+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.627064+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ - "### Reading the result: start of the duel Père Fouras vs Jalabert\n\nThe log `\"Depart du duel Pere Fouras vs Laurent Jalabert\"` marks the entry into the asynchronous loop. From this cell on, **the student observes**: (1) the alternation Père Fouras (riddle) ↔ Jalabert (answer), (2) the convergence toward a guessed word or a give-up, (3) the final evaluation by `evaluer_partie()`. **Structured logging** (timestamp + INFO level + event prefix) is essential in an asynchronous multi-agent chat: without it, the student cannot distinguish **who speaks** in the output, nor **when a step ends**. It is this logging that makes the notebook pedagogical — replayable.\n" + "### Reading the result: start of the duel Père Fouras vs Jalabert\n\nThe log `\"Depart du duel Pere Fouras vs Laurent Jalabert\"` marks the entry into the asynchronous loop. From this cell on, **the student observes**: (1) the alternation Père Fouras (riddle) ↔ Jalabert (answer), (2) the stopping loop — `should terminate` evaluated after each Laurent_Jalabert turn: the game stops on the first `True`, or when the iterations run out (this run: the word is found in 3 attempts, the score cell shows 70/100), then `Partie terminee !` (game over); the give-up variant (guided example 2) and the evaluation by `evaluer_partie()` (guided example 3) extend this foundation. **Structured logging** (timestamp + INFO level + event prefix) is essential in an asynchronous multi-agent chat: without it, the student cannot distinguish **who speaks** in the output, nor **when a step ends**. It is this logging that makes the notebook pedagogical — replayable.\n" ] }, { "cell_type": "markdown", "id": "5306a243", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.008379, + "end_time": "2026-10-16T06:01:05.650764+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.642385+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ - "### Reading the result: agent selection by description\n\nWhen the `selection_strategy` of `AgentGroupChat` must choose the next speaker, it reads the **description** of each agent (`description=\"Le Père Fouras pose des énigmes...\"` cell 10) and compares it with the context of the last message. This mechanism decides whether Jalabert asks a question (cyclist turn) or whether Père Fouras validates (game master turn). Without an explicit description, the orchestrator risks falling into a rigid **round-robin pattern** that does not adapt to the conversational flow. The **role-based selection** pattern is an important refinement for credible multi-agent simulations.\n" + "### Reading the result: who speaks, and when?\n\nIn the committed run, no `description` parameter nor `selection_strategy` is passed to the group: the selection is the default one, and the output shows it — the `Selected agent at index 0` (Pere_Fouras) and `at index 1` (Laurent_Jalabert) lines alternate strictly turn after turn. This round-robin is enough here: the duel has two voices, and the order of turns is the essence of the game. The refinement, for larger casts: give each agent a `description` and the group a semantic `selection_strategy`, which chooses the next speaker by comparing descriptions to the context of the last message instead of alternating mechanically — the **role-based selection** pattern, useful as soon as the cast exceeds two agents (guided example 1, with its Detective and its Witness, is the occasion).\n" ] }, { "cell_type": "markdown", "id": "9c2a66c0", "metadata": { + "credited_from": "#21009", "papermill": { - "duration": 0.004782, - "end_time": "2026-08-17T03:46:46.102468", + "duration": 0.005374, + "end_time": "2026-10-16T06:01:05.663101+00:00", "exception": false, - "start_time": "2026-08-17T03:46:46.097686", + "start_time": "2026-10-16T06:01:05.657727+00:00", "status": "completed" }, "tags": [] }, "source": [ - "### Exercise 3: Scoring system for the game\n\nCurrently, the game simply stops when the word is guessed or after 20 iterations. The objective is to add a **scoring system** that counts the number of questions asked by Laurent Jalabert and displays a final score.\n\n**Objective**: write a function `evaluer_partie` that takes the message history and returns a dictionary containing the number of questions asked, whether the word was found, and a score (fewer questions = better score).\n\n**Hints**:\n- `# Step 1`: Count the messages where the role is that of Laurent Jalabert\n- `# Step 2`: Check whether the secret word appears in the last message\n- `# Hint`: the score can be `max(0, 100 - nb_questions * 10)`\n" + "### Guided example 3: Scoring system for the game\n\n*Contribution from the group of PR #21009, integrated as a guided example.*\n\nThe game stops when the word is guessed or after 20 iterations. The group of PR #21009 adds the measurement: the `evaluer_partie` function takes the message history and returns the number of questions asked by Laurent Jalabert, whether the word was found, and a score that decreases with the number of attempts (`max(0, 100 - nb_questions * 10)`).\n" ] }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "id": "df0d3126", "metadata": { "execution": { - "iopub.execute_input": "2026-08-17T03:46:46.115062Z", - "iopub.status.busy": "2026-08-17T03:46:46.114806Z", - "iopub.status.idle": "2026-08-17T03:46:46.118549Z", - "shell.execute_reply": "2026-08-17T03:46:46.117975Z" + "iopub.execute_input": "2026-10-16T06:01:05.679403Z", + "iopub.status.busy": "2026-10-16T06:01:05.678991Z", + "iopub.status.idle": "2026-10-16T06:01:05.685167Z", + "shell.execute_reply": "2026-10-16T06:01:05.684108Z" }, "papermill": { - "duration": 0.011605, - "end_time": "2026-08-17T03:46:46.119457", + "duration": 0.017456, + "end_time": "2026-10-16T06:01:05.686481+00:00", "exception": false, - "start_time": "2026-08-17T03:46:46.107852", + "start_time": "2026-10-16T06:01:05.669025+00:00", "status": "completed" }, "tags": [] @@ -1155,29 +1252,306 @@ "name": "stdout", "output_type": "stream", "text": [ - "Exercice a completer : systeme de score\n" + "Systeme de score configure avec succes !\n" ] } ], "source": [ + "\n", + "# EXEMPLE GUIDE 3 : systeme de score (groupe de la PR #21009)\n", + "\n", "def evaluer_partie(history_messages: list) -> dict:\n", - " # TODO etudiant : implementer le systeme de score\n", - " nb_questions = 0 # Etape 1 : compter les questions de Laurent\n", - " mot_trouve = False # Etape 2 : verifier la presence du mot\n", - " \n", - " # Indice : parcourir history_messages et compter les messages de Laurent\n", - " score = None # TODO etudiant : calculer le score\n", - " return {\"nb_questions\": nb_questions, \"mot_trouve\": mot_trouve, \"score\": score}\n", "\n", - "print(\"Exercice a completer : systeme de score\")" + " # Etape 1 : Recuperer les messages de Laurent Jalabert\n", + " messages_laurent = [\n", + " message for message in history_messages\n", + " if getattr(message, \"name\", None) == \"Laurent_Jalabert\"\n", + " ]\n", + "\n", + " # Compter le nombre de tentatives\n", + " nb_questions = len(messages_laurent)\n", + "\n", + " # Etape 2 : Verifier si le mot a ete trouve\n", + " mot_trouve = False\n", + "\n", + " if messages_laurent:\n", + " dernier_message = str(\n", + " messages_laurent[-1].content or \"\"\n", + " ).lower()\n", + "\n", + " mot_trouve = MOT_A_DEVINER.lower() in dernier_message\n", + "\n", + " # Etape 3 : Calculer le score\n", + " if mot_trouve:\n", + " score = max(0, 100 - nb_questions * 10)\n", + " else:\n", + " score = 0\n", + "\n", + " # Retourner les resultats\n", + " return {\n", + " \"nb_questions\": nb_questions,\n", + " \"mot_trouve\": mot_trouve,\n", + " \"score\": score\n", + " }\n", + "\n", + "print(\"Systeme de score configure avec succes !\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "2a2f46fe", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.702897Z", + "iopub.status.busy": "2026-10-16T06:01:05.702480Z", + "iopub.status.idle": "2026-10-16T06:01:05.709156Z", + "shell.execute_reply": "2026-10-16T06:01:05.708129Z" + }, + "papermill": { + "duration": 0.016594, + "end_time": "2026-10-16T06:01:05.710494+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.693900+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "===== RESULTATS DE LA PARTIE =====\n", + "Nombre de tentatives : 3\n", + "Mot trouve : True\n", + "Score final : 70 / 100\n" + ] + } + ], + "source": [ + "\n", + "# Recuperer l'historique de la partie\n", + "historique = chat.history.messages\n", + "\n", + "# Calculer le score\n", + "resultat = evaluer_partie(historique)\n", + "\n", + "# Afficher les resultats\n", + "print(\"===== RESULTATS DE LA PARTIE =====\")\n", + "print(\"Nombre de tentatives :\", resultat[\"nb_questions\"])\n", + "print(\"Mot trouve :\", resultat[\"mot_trouve\"])\n", + "print(\"Score final :\", resultat[\"score\"], \"/ 100\")\n" ] }, { "cell_type": "markdown", "id": "d2a4bdca", - "metadata": {}, + "metadata": { + "papermill": { + "duration": 0.006116, + "end_time": "2026-10-16T06:01:05.722221+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.716105+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Reading the result: scoring system at the end of the game\n\nThe group's function counts the messages signed `Laurent_Jalabert` in the chat group's real history, checks whether the secret word appears in his **last** message, and applies the rule `max(0, 100 - 10 × questions)`: a first-question win is worth 90, each additional question costs 10 points, and a failure is worth 0 — regardless of the length of the game. If the word is not found, the returned score is 0 and the number of questions remains displayed for diagnosis. This scoring turns the duel into a **game with a measurable outcome**: by replaying the game with two prompt configurations, one can compare average scores — the gateway to reproducible experiments.\n" + ] + }, + { + "cell_type": "markdown", + "id": "ae952339", + "metadata": { + "papermill": { + "duration": 0.005599, + "end_time": "2026-10-16T06:01:05.737250+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.731651+00:00", + "status": "completed" + }, + "tags": [] + }, "source": [ - "### Reading the result: scoring system at the end of the game\n\nCell 20 exposes `evaluer_partie(history_messages: list) -> dict` — the student must implement this function (exercise 4). The typical contract: count the attempts, assign a score based on the length of the guessed word vs the number of attempts, and return a serializable dict. This **scoring** is what turns the duel into a **game with a measurable outcome** — without a score, the notebook would be a simple endless conversation. It is also what opens the door to **reproducible experiments**: the student can compare two prompt configurations by measuring the average score over 100 games.\n" + "## Exercises\n\nThree new challenges, one per skill practiced in the guided examples above: writing prompts that constrain a behavior, composing a termination condition, measuring a played game. The stubs are incomplete by design — the notebook runs end to end without them.\n" + ] + }, + { + "cell_type": "markdown", + "id": "5b19df38", + "metadata": { + "papermill": { + "duration": 0.008275, + "end_time": "2026-10-16T06:01:05.753416+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.745141+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercise 1: A binary-protocol duel\n\nThe characters of guided example 1 let the Witness answer with full sentences. A stricter variant: an **Informer** who answers **only** with \"yes\", \"no\" or \"maybe\" — never a sentence — and a **Methodical Investigator** who announces the elimination hypothesis (\"I am looking for a word longer than 15 letters\") before each question.\n\n**Objective**: write both prompts and configure the two corresponding agents.\n\n**Hints**:\n- `# Step 1`: write the Informer's prompt (role + closed list of the three allowed answers)\n- `# Step 2`: write the Investigator's prompt (elimination strategy announced before each question)\n- `# Hint`: a prompt can forbid (\"never a sentence\") as much as it can prescribe (\"answer only with\")\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "ec943b8d", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.768419Z", + "iopub.status.busy": "2026-10-16T06:01:05.767930Z", + "iopub.status.idle": "2026-10-16T06:01:05.773125Z", + "shell.execute_reply": "2026-10-16T06:01:05.772032Z" + }, + "papermill": { + "duration": 0.015741, + "end_time": "2026-10-16T06:01:05.774940+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.759199+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 1 a completer\n" + ] + } + ], + "source": [ + "# Exercice 1 : un duel au protocole binaire\n", + "# TODO etudiant : definir les prompts et creer les agents\n", + "INDICATEUR_PROMPT = \"\" # Etape 1 : instructions de l'Indicateur (oui / non / peut-etre, jamais une phrase)\n", + "ENQUETEUR_PROMPT = \"\" # Etape 2 : instructions de l'Enqueteur (hypothese annoncee avant chaque question)\n", + "indicateur = None # TODO etudiant : ChatCompletionAgent(kernel=create_kernel(), ...)\n", + "enqueteur = None # TODO etudiant\n", + "print(\"Exercice 1 a completer\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "08f0cfb2", + "metadata": { + "papermill": { + "duration": 0.009634, + "end_time": "2026-10-16T06:01:05.794075+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.784441+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercise 2: Termination on a repeated question\n\nA guesser who repeats the same question three times in a row is no longer progressing: the game is lost. The objective is to implement `RepetitionTerminationStrategy`, a variant that stops the game if the word is guessed **or** if Laurent Jalabert's last three messages are identical (lowercase comparison).\n\n**Hints**:\n- `# Step 1`: extract Laurent's messages from the history (if there are fewer than three, the game continues)\n- `# Step 2`: compare the last three in lowercase, and combine with word detection using an `or`\n" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "3610348b", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.812063Z", + "iopub.status.busy": "2026-10-16T06:01:05.811809Z", + "iopub.status.idle": "2026-10-16T06:01:05.818968Z", + "shell.execute_reply": "2026-10-16T06:01:05.817824Z" + }, + "papermill": { + "duration": 0.017264, + "end_time": "2026-10-16T06:01:05.820719+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.803455+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 2 a completer\n" + ] + } + ], + "source": [ + "# Exercice 2 : terminaison sur question repetee\n", + "# TODO etudiant\n", + "from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy\n", + "from semantic_kernel.contents.chat_message_content import ChatMessageContent\n", + "\n", + "class RepetitionTerminationStrategy(TerminationStrategy):\n", + " # TODO etudiant : implementer la strategie de detection de repetition\n", + " SEUIL_REPETITION: int = 3 # Etape 1 : nombre de questions identiques consecutives\n", + "\n", + " async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool:\n", + " result = False # TODO etudiant : remplacer par la logique (mot trouve OU repetition)\n", + " return result\n", + "\n", + "print(\"Exercice 2 a completer\")\n" + ] + }, + { + "cell_type": "markdown", + "id": "edb7a025", + "metadata": { + "papermill": { + "duration": 0.009017, + "end_time": "2026-10-16T06:01:05.839606+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.830589+00:00", + "status": "completed" + }, + "tags": [] + }, + "source": [ + "### Exercise 3: Measuring the concision of Père Fouras's hints\n\n`evaluer_partie` (guided example 3) measures the guesser. The other half of the duel deserves its own measurement: overly long hints make the task easier. **Objective**: write `mesurer_indices` which takes the message history and returns the average length of Père Fouras's messages, and a boolean `indices_concis` true if this average stays under 200 characters.\n\n**Hints**:\n- `# Step 1`: filter the history on messages signed `Pere_Fouras` (same filter as guided example 3)\n- `# Step 2`: average the lengths (`len(str(message.content))`) and compare to the threshold\n" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "a85748ad", + "metadata": { + "execution": { + "iopub.execute_input": "2026-10-16T06:01:05.860111Z", + "iopub.status.busy": "2026-10-16T06:01:05.859557Z", + "iopub.status.idle": "2026-10-16T06:01:05.865257Z", + "shell.execute_reply": "2026-10-16T06:01:05.864533Z" + }, + "papermill": { + "duration": 0.018852, + "end_time": "2026-10-16T06:01:05.866485+00:00", + "exception": false, + "start_time": "2026-10-16T06:01:05.847633+00:00", + "status": "completed" + }, + "tags": [] + }, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Exercice 3 a completer\n" + ] + } + ], + "source": [ + "# Exercice 3 : mesurer la concision des indices du Pere Fouras\n", + "# TODO etudiant\n", + "def mesurer_indices(history_messages: list) -> dict:\n", + " longueur_moyenne = 0.0 # TODO etudiant : moyenne des longueurs des messages du Pere Fouras\n", + " indices_concis = False # TODO etudiant : True si longueur_moyenne < 200\n", + " return {\"longueur_moyenne\": longueur_moyenne, \"indices_concis\": indices_concis}\n", + "\n", + "print(\"Exercice 3 a completer\")\n" ] } ], @@ -1218,17 +1592,15 @@ }, "papermill": { "default_parameters": {}, - "duration": 29.875179, - "end_time": "2026-08-17T03:46:46.680034", + "duration": 68.269231, + "end_time": "2026-10-16T06:01:06.440156+00:00", "environment_variables": {}, "exception": null, - "input_path": "fort-boyard-python.ipynb", - "output_path": "fort-boyard-python.ipynb", - "parameters": { - "BATCH_MODE": true - }, - "start_time": "2026-08-17T03:46:16.804855", - "version": "2.6.0" + "input_path": "fb-fr.ipynb", + "output_path": "fb-fr-out.ipynb", + "parameters": {}, + "start_time": "2026-10-16T05:59:58.170925+00:00", + "version": "2.7.0" } }, "nbformat": 4, diff --git a/translations/genai/casestudies.csv b/translations/genai/casestudies.csv index 71eb3e6492..c836e85ca2 100644 --- a/translations/genai/casestudies.csv +++ b/translations/genai/casestudies.csv @@ -527,16 +527,16 @@ def create_kernel(): )) return kernel ",,,,,,,,f831efcb9bbeae84,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,2adf4585,markdown,fr,ee4e0400a35e856c,"### Lecture du résultat : pourquoi ce choix de mot est pédagogique +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,2adf4585,markdown,fr,c6aca173601faf40,"### Lecture du résultat : pourquoi ce choix de mot est pédagogique -Le mot `""anticonstitutionnellement""` (25 lettres, Grand Robert) cumule plusieurs difficultés : longueur (force des indices progressifs), composition morphologique (`anti-+constitution+nelle+ment` — l'agent peut deviner par décomposition), et rareté (vocabulaire juridique peu courant). Pour un **jeu d'énigmes LLM**, c'est un excellent stress-test : si l'agent devine ce mot en moins de 10 essais, le prompt Père Fouras est bien calibré. L'étudiant peut **mesurer le taux de réussite** sur 100 parties avec `evaluer_partie()` (à compléter, exercice 3) puis comparer avec un mot plus simple (ex. `""kayak""`, 5 lettres) pour isoler l'effet longueur du mot. +Le mot `""anticonstitutionnellement""` (25 lettres, Grand Robert) cumule plusieurs difficultés : longueur (force des indices progressifs), composition morphologique (`anti-+constitution+nelle+ment` — l'agent peut deviner par décomposition), et rareté (vocabulaire juridique peu courant). Pour un **jeu d'énigmes LLM**, c'est un excellent stress-test : si l'agent devine ce mot en moins de 10 essais, le prompt Père Fouras est bien calibré. L'étudiant peut **mesurer le taux de réussite** sur 100 parties avec `evaluer_partie()` (implémentée dans l'exemple guidé 3) puis comparer avec un mot plus simple (ex. `""kayak""`, 5 lettres) pour isoler l'effet longueur du mot. ","### Reading the result: why this word choice is pedagogical The word `""anticonstitutionnellement""` (25 letters, Grand Robert) combines several difficulties: length (strength of the progressive hints), morphological composition (`anti-+constitution+nelle+ment` — the agent can guess by decomposition), and rarity (an uncommon legal vocabulary). For an **LLM riddle game**, it is an excellent stress test: if the agent guesses this word in fewer than 10 attempts, the Père Fouras prompt is well calibrated. The student can **measure the success rate** over 100 games with `evaluer_partie()` and then compare with a simpler word (e.g. `""kayak""`, 5 letters) to isolate the effect of the word's length. The contract must be tested. Grand Robert reference. Reference. -",,,,,,,ee4e0400a35e856c,8f5af9edc3274d33,,,,,,, +",,,,,,,c6aca173601faf40,8f5af9edc3274d33,,,,,,, MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,fdc6262b,markdown,fr,e811182b5c647dff,"### Lecture du résultat : kernel Semantic Kernel instancié La cellule 6 crée le kernel et stocke `MOT_A_DEVINER = ""anticonstitutionnellement""` (25 lettres, l'un des plus longs mots français standards). Cette constante est ce que **l'agent va devoir deviner** au cours du duel via une stratégie d'énigmes posée par le Père Fouras. Le choix du mot est pédagogique : sa longueur force l'agent à mobiliser des **stratégies d'indice indirect** (rime, contexte, longueur) plutôt qu'une description directe. L'étudiant peut substituer un mot plus court pour des parties plus rapides (`""kayak""` pour un palindrome). @@ -571,35 +571,25 @@ Tu es Laurent Jalabert. Tu dois deviner le mot en posant des questions fermées (Oui/Non). Sois perspicace et stratégique dans tes questions. """"""",,,,,,,,7f877f1f31e39469,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,3e19b11c,markdown,fr,6959e07349adb018,"### Lecture du résultat : prompts Père Fouras et cycliste configurés +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,3e19b11c,markdown,fr,e147f1f8a80c8b9c,"### Lecture du résultat : prompts Père Fouras et cycliste configurés -La cellule 10 définit `PERE_FOURAS_PROMPT` et `LAURENT_JALABERT_PROMPT` structurés via `f""""""...""""""` — seule la f-string du Père Fouras interpole `MOT_A_DEVINER`. Le prompt du Père Fouras encode la **persona + la mécanique** : pose énigme, attend réponse, valide, propose indice suivant. Le prompt du cycliste encode la **stratégie de devinette** : extraire lettres, longueur, contexte, demander indice. Cette séparation persona/mécanique est cruciale — sans persona explicite, le LLM répondrait en mode Q&A générique sans le **cadre narratif Fort Boyard**. L'exercice 1 invite l'étudiant à remplacer le duo par deux nouveaux personnages — un **Détective** (questions déductives) et un **Témoin** (réponses évasives) — extension classique du **multi-agent cast**. +La cellule 10 définit `PERE_FOURAS_PROMPT` et `LAURENT_JALABERT_PROMPT` structurés via `f""""""...""""""` — seule la f-string du Père Fouras interpole `MOT_A_DEVINER`. Le prompt du Père Fouras encode la **persona + la mécanique** : pose énigme, attend réponse, valide, propose indice suivant. Le prompt du cycliste encode la **stratégie de devinette** : extraire lettres, longueur, contexte, demander indice. Cette séparation persona/mécanique est cruciale — sans persona explicite, le LLM répondrait en mode Q&A générique sans le **cadre narratif Fort Boyard**. L'exemple guidé 1 (contribution de la PR #21009) remplace le duo par deux nouveaux personnages — un **Détective** (questions déductives) et un **Témoin** (réponses évasives) — extension classique du **multi-agent cast**. Le format `f""""""...""""""` permet l'interpolation de variables runtime (mot à deviner, score actuel). ","### Reading the result: Père Fouras and cyclist prompts configured -Cell 6 defines two structured `PERE_FOURAS_PROMPT` and `CYCLISTE_PROMPT` via `f""""""...""""""`. The Père Fouras prompt encodes the **persona + the mechanics**: pose a riddle, wait for an answer, validate, offer the next hint. The cyclist prompt encodes the **guessing strategy**: extract letters, length, context, ask for a hint. This persona/mechanics separation is crucial — without an explicit persona, the LLM would answer in generic Q&A mode without the **Fort Boyard narrative frame**. Exercise 1 invites the student to add a 3rd character (Detective, Witness) — a classic extension of the **multi-agent cast**. - The `f""""""...""""""` format allows the interpolation of runtime variables (word to guess, current score). -",,,,,,,6959e07349adb018,396c67a0eed4eb12,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,649ac34e,markdown,fr,f85a9ba7239d19b5,"### Exercice 1 : Personnaliser les personnages du duel +Cell 10 defines `PERE_FOURAS_PROMPT` and `LAURENT_JALABERT_PROMPT` structured via `f""""""...""""""` — only the Père Fouras f-string interpolates `MOT_A_DEVINER`. The Père Fouras prompt encodes the **persona + the mechanics**: pose a riddle, wait for an answer, validate, offer the next hint. The cyclist prompt encodes the **guessing strategy**: extract letters, length, context, ask for a hint. This persona/mechanics separation is crucial — without an explicit persona, the LLM would answer in generic Q&A mode without the **Fort Boyard narrative frame**. Guided example 1 (contribution from PR #21009) replaces the duo with two new characters — a **Detective** (deductive questions) and a **Witness** (evasive answers) — a classic extension of the **multi-agent cast**. + The `f""""""...""""""` format allows the interpolation of runtime variables +",,,,,,,e147f1f8a80c8b9c,db144478a918d275,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,649ac34e,markdown,fr,7562414dd8da085d,"### Exemple guidé 1 : Personnaliser les personnages du duel -Les prompts actuels definissent le Pere Fouras comme enigmatique et Laurent Jalabert comme perspicace. L'objectif est de créer une variante du jeu avec deux personnages différents : un **Detective** (qui pose des questions deductives) et un **Temoin** (qui repond de maniere evasive sans reveler le mot). +*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.* -**Objectif** : ecrire les instructions système pour ces deux nouveaux personnages et configurer les agents correspondants. +Les prompts initiaux définissent le Père Fouras comme énigmatique et Laurent Jalabert comme perspicace. Le groupe de la PR #21009 livre la variante demandée : un **Detective** (questions déductives, fermées de préférence, qui ne renonce pas) et un **Témoin** (réponses courtes et évasives, indices indirects, jamais le mot en clair) — puis configure les deux agents `ChatCompletionAgent` sur le même motif que le duo original. +","### Guided example 1: Customize the duel characters -**Indices** : -- `# Étape 1` : Rediger le prompt du Temoin (rôle, règles, interdictions) -- `# Étape 2` : Rediger le prompt du Detective (stratégie de questionnement) -- `# Indice` : le Temoin ne doit jamais reveler le mot directement, mais peut donner des indices indirects -","### Exercise 1: Customize the duel characters - -The current prompts define Père Fouras as enigmatic and Laurent Jalabert as perceptive. The goal is to create a variant of the game with two different characters: a **Detective** (who asks deductive questions) and a **Witness** (who answers evasively without revealing the word). - -**Objective**: write the system instructions for these two new characters and configure the corresponding agents. +*Contribution from the group of PR #21009, integrated as a guided example.* -**Hints**: -- `# Step 1`: Write the Witness prompt (role, rules, prohibitions) -- `# Step 2`: Write the Detective prompt (questioning strategy) -- `# Hint`: the Witness must never reveal the word directly, but may give indirect clues +The initial prompts define Père Fouras as enigmatic and Laurent Jalabert as sharp-minded. The group of PR #21009 delivers the requested variant: a **Detective** (deductive questions, closed-ended preferred, never giving up) and a **Witness** (short evasive answers, indirect hints, never the word in clear) — then configures both `ChatCompletionAgent` agents on the same pattern as the original duo. ",,,,,"### Упражнение 1: Настроить персонажей дуэли Текущие промпты определяют Отца Фура как загадочного, а Лорана Жалабера — как проницательного. Цель — создать вариант игры с двумя другими персонажами: **Детективом** (который задаёт дедуктивные вопросы) и **Свидетелем** (который отвечает уклончиво, не раскрывая слово). @@ -609,18 +599,47 @@ The current prompts define Père Fouras as enigmatic and Laurent Jalabert as per **Подсказки**: - # Шаг 1: Написать промпт Свидетеля (роль, правила, запреты) - # Шаг 2: Написать промпт Детектива (стратегия задавания вопросов) -- # Подсказка: Свидетель никогда не должен раскрывать слово напрямую, но может давать косвенные намёки",,f85a9ba7239d19b5,862ca468490efd24,,,,,3295a1f8a4a7068b,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,ac94f4f0,code,fr,fb5c1ca4effe98b3,"# Exercice 1 : Personnaliser les personnages du duel -# TODO etudiant : definir les prompts et creer les agents +- # Подсказка: Свидетель никогда не должен раскрывать слово напрямую, но может давать косвенные намёки",,7562414dd8da085d,af92340e54ce1218,,,,,3295a1f8a4a7068b,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,ac94f4f0,code,fr,f1637b412a0513d1,"# EXEMPLE GUIDE 1 : personnaliser les personnages du duel (groupe de la PR #21009) +TEMoin_PROMPT = f"""""" +Tu es un témoin interrogé par un détective. +Tu connais le mot secret : '{MOT_A_DEVINER}'. + +Ton rôle est de répondre aux questions du détective. +Tu dois rester mystérieux et parfois évasif. +Ne révèle jamais directement le mot secret. +Tu peux donner des indices indirects pour aider le détective. +Réponds de manière courte et précise. +"""""" -TEMoin_PROMPT = """" # Etape 1 : instructions du Temoin (evasif, indices indirects) -DETECTIVE_PROMPT = """" # Etape 2 : instructions du Detective (questions deductives) +# Etape 2 : Instructions du Detective +DETECTIVE_PROMPT = """""" +Tu es un détective intelligent et perspicace. +Ton objectif est de découvrir un mot secret. -# Indice : creer les agents avec ChatCompletionAgent(kernel=..., name=..., instructions=...) -temoin = None # TODO etudiant : creer l'agent Temoin -detective = None # TODO etudiant : creer l'agent Detective +Pose des questions logiques et déductives au témoin. +Privilégie les questions fermées (Oui/Non). +Analyse les réponses pour progresser dans ton enquête. +Lorsque tu penses avoir trouvé, propose le mot. +Ne renonce pas trop rapidement. +"""""" + +# Etape 3 : Creation de l'agent Temoin +temoin = ChatCompletionAgent( + kernel=create_kernel(), + name=""Temoin"", + instructions=TEMoin_PROMPT, +) + +# Etape 4 : Creation de l'agent Detective +detective = ChatCompletionAgent( + kernel=create_kernel(), + name=""Detective"", + instructions=DETECTIVE_PROMPT, +) -print(""Exercice a completer : personnages Detective et Temoin"")",,,,,,,,fb5c1ca4effe98b3,,,,,,,, +print(""Les agents Detective et Temoin ont ete crees avec succes !"") +",,,,,,,,f1637b412a0513d1,,,,,,,, MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,6a8dbe2b,markdown,fr,8dce0df794c9d197,"## Création des agents avec stratégies personnalisées Un `ChatCompletionAgent` concrétise l'association entre un Kernel (le moteur), un `name` (son identité dans le dialogue) et des `instructions` (son prompt système). Une fois instanciés, les deux agents peuvent produire des messages, mais ils ne savent pas encore *quand* parler ni *quand* s'arrêter — c'est le groupe de discussion qui le décidera (voir plus bas). @@ -681,26 +700,16 @@ class FortBoyardTerminationStrategy(TerminationStrategy): last_message = str(history[-1].content).lower() return MOT_A_DEVINER in last_message",,,,,,,,c41a211f227072d5,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,81de6677,markdown,fr,5636696f2d83887e,"### Exercice 2 : Terminaison avec detection de renoncement - -La stratégie actuelle arrete la partie uniquement si le mot est devine. Mais Laurent Jalabert pourrait aussi abandonner. L'objectif est de créer une stratégie hybride qui arrete la partie si le mot est devine **ou** si Laurent exprime un renoncement (""je donne ma langue au chat"", ""j'abandonne""). - -**Objectif** : implementer `RenoncementTerminationStrategy` qui combine la detection du mot secret avec la detection de phrases d'abandon. +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,81de6677,markdown,fr,1f53a1cd6b269484,"### Exemple guidé 2 : Terminaison avec détection de renoncement -**Indices** : -- `# Étape 1` : Définir une liste de phrases d'abandon typiques -- `# Étape 2` : Verifier si le dernier message contient une de ces phrases (en minuscules) -- `# Indice` : combiner les deux conditions avec `or` dans `should_agent_terminate` -","### Exercise 2: Termination with giving-up detection +*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.* -The current strategy stops the game only if the word is guessed. But Laurent Jalabert could also give up. The goal is to create a hybrid strategy that stops the game if the word is guessed **or** if Laurent expresses giving up (""je donne ma langue au chat"", ""j'abandonne""). +La stratégie du notebook arrête la partie uniquement si le mot est deviné. Le groupe de la PR #21009 implémente la variante hybride `RenoncementTerminationStrategy` : la partie s'arrête si le mot est deviné **ou** si Laurent exprime un renoncement (« je donne ma langue au chat », « j'abandonne », …) — puis vérifie sa stratégie sur trois situations témoin. +","### Guided example 2: Termination with give-up detection -**Objective**: implement `RenoncementTerminationStrategy` which combines detection of the secret word with detection of giving-up phrases. +*Contribution from the group of PR #21009, integrated as a guided example.* -**Hints**: -- `# Step 1`: Define a list of typical giving-up phrases -- `# Step 2`: Check whether the last message contains one of these phrases (in lowercase) -- `# Hint`: combine the two conditions with `or` in `should_agent_terminate` +The notebook's strategy stops the game only if the word is guessed. The group of PR #21009 implements the hybrid variant `RenoncementTerminationStrategy`: the game stops if the word is guessed **or** if Laurent expresses a give-up (""je donne ma langue au chat"", ""j'abandonne"", …) — then checks the strategy on three witness situations. ",,,,,"### Упражнение 2 : Завершение с обнаружением отказа Текущая стратегия останавливает игру только если слово угадано. Но Лоран Жалабер также может сдаться. Цель — создать гибридную стратегию, которая останавливает игру, если слово угадано **или** если Лоран выражает отказ (""je donne ma langue au chat"", ""j'abandonne""). @@ -710,20 +719,47 @@ The current strategy stops the game only if the word is guessed. But Laurent Jal **Подсказки** : - # Шаг 1 : Определить список типичных фраз отказа - # Шаг 2 : Проверить, содержит ли последнее сообщение одну из этих фраз (в нижнем регистре) -- # Подсказка : объединить два условия с помощью `or` в `should_agent_terminate`",,5636696f2d83887e,157448c151147789,,,,,95bd36831fc14824,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,0e96e11d,code,fr,9a91cbf579858a4b,"from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy +- # Подсказка : объединить два условия с помощью `or` в `should_agent_terminate`",,1f53a1cd6b269484,0efec10e398ef93b,,,,,95bd36831fc14824,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,0e96e11d,code,fr,eff1056d8e1a114b,"# EXEMPLE GUIDE 2 : terminaison avec detection de renoncement (groupe de la PR #21009) +from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy from semantic_kernel.contents.chat_message_content import ChatMessageContent class RenoncementTerminationStrategy(TerminationStrategy): - # TODO etudiant : implementer la strategie de terminaison hybride - PHRASES_ABANDON: list[str] = [] # Etape 1 : phrases de renoncement - - async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool: - # Etape 2 : verifier mot devine OU abandon detecte - result = False # TODO etudiant : remplacer par la logique - return result -print(""Exercice a completer : RenoncementTerminationStrategy"")",,,,,,,,9a91cbf579858a4b,,,,,,,, + # Etape 1 : Definir les phrases d'abandon + PHRASES_ABANDON: list[str] = [ + ""j'abandonne"", + ""je donne ma langue au chat"", + ""je renonce"", + ""je laisse tomber"" + ] + + async def should_agent_terminate( + self, agent, history: list[ChatMessageContent], + cancellation_token=None + ) -> bool: + + # Verifier que l'historique n'est pas vide + if not history: + return False + + # Recuperer le dernier message en minuscules + dernier_message = str(history[-1].content or """").lower() + + # Verifier si le mot secret est trouve + mot_trouve = MOT_A_DEVINER.lower() in dernier_message + + # Verifier si une phrase d'abandon est presente + abandon = any( + phrase in dernier_message + for phrase in self.PHRASES_ABANDON + ) + + # Arreter si le mot est trouve OU si le joueur abandonne + return mot_trouve or abandon + +print(""Strategie de renoncement configuree !"") +",,,,,,,,eff1056d8e1a114b,,,,,,,, MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,c61e7f51,markdown,fr,9270af3e7f1c1541,"## Configuration du groupe de discussion `AgentGroupChat` est l'orchestrateur multi-agent : il fait tourner les agents à tour de rôle selon une **stratégie de sélection** (quel agent parle ensuite) et s'arrête selon une **stratégie de terminaison** (quand arrêter). Par défaut, la sélection alterne simplement les agents dans l'ordre de la liste. @@ -744,13 +780,13 @@ chat = AgentGroupChat( ) ) ",,,,,,,,e3c965a025256976,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,cb4bd6c9,markdown,fr,5f91ae23ebeeca27,"### Lecture du résultat : groupe de chat configuré +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,cb4bd6c9,markdown,fr,5cc5f90b5ed5518b,"### Lecture du résultat : groupe de chat configuré -La cellule 22 construit `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — sans `selection_strategy` explicite. C'est l'**objet central** qui orchestre le duel : à chaque tour, l'agent suivant parle dans l'ordre de la liste (round-robin — la sélection par défaut, visible dans les lignes `Selected agent at index 0` / `at index 1` alternées de la sortie), et le `termination_strategy` détecte la fin — ici, le mot deviné : la sortie montre `should terminate: True` dès que Laurent_Jalabert nomme « anticonstitutionnellement ». Sans ce wrapper, les deux agents parleraient en parallèle. La **spécificité Fort Boyard** est le renoncement : l'exercice 2 (cellules 19-20) demande une stratégie hybride qui laisse aussi l'agent abandonner (« je donne ma langue au chat ») — émulation du stress télévisé. +La cellule 22 construit `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — sans `selection_strategy` explicite. C'est l'**objet central** qui orchestre le duel : à chaque tour, l'agent suivant parle dans l'ordre de la liste (round-robin — la sélection par défaut, visible dans les lignes `Selected agent at index 0` / `at index 1` alternées de la sortie), et le `termination_strategy` détecte la fin — ici, le mot deviné : la sortie évalue `should terminate` après chaque tour de Laurent_Jalabert, et la partie s'arrête sur le premier `True` — ou à l'épuisement des itérations si le mot n'est jamais nommé. Sans ce wrapper, les deux agents parleraient en parallèle. La **spécificité Fort Boyard** est le renoncement : l'exemple guidé 2 montre la stratégie hybride qui laisse aussi l'agent abandonner (« je donne ma langue au chat ») — émulation du stress télévisé. ","### Reading the result: chat group configured -Cell 16 builds `AgentGroupChat(agents=[pere_fouras, jalabert], termination_strategy=..., selection_strategy=...)`. This is the **central object** that orchestrates the duel: at each turn, the `selection_strategy` chooses who speaks (round-robin by default), the `termination_strategy` detects the end (give-up or score). Without this wrapper, both agents would speak in parallel. The **Fort Boyard specificity** is that the give-up strategy (cell 14, exercise) must let the agent abandon after N unsuccessful attempts — an emulation of the TV stress. -",,,,,,,5f91ae23ebeeca27,4c11018e55bbbd8b,,,,,,, +The cell builds `AgentGroupChat(agents=[pere_fouras, laurent_jalabert], termination_strategy=...)` — without an explicit `selection_strategy`. This is the **central object** that orchestrates the duel: at each turn, the next agent speaks in list order (round-robin — the default selection, visible in the alternating `Selected agent at index 0` / `at index 1` lines of the output), and the `termination_strategy` detects the end — here, the guessed word: the output evaluates `should terminate` after each Laurent_Jalabert turn, and the game stops on the first `True` — or when the iterations run out if the word is never named. Without this wrapper, both agents would speak in parallel. The **Fort Boyard specificity** is the give-up: guided example 2 shows the hybrid strategy that also lets the agent abandon (""je donne ma langue au chat"") — an emulation of the TV stress. +",,,,,,,5cc5f90b5ed5518b,093c7850c9ab3f9e,,,,,,, MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,ada98689,markdown,fr,9894be02bbc84b15,"## Lancement de la partie ! Le groupe est configuré, on peut lancer la partie. On sème le dialogue avec un **message utilisateur initial** (`add_chat_message`) — sinon les agents n'ont rien à quoi réagir — puis on itère asynchronement sur `chat.invoke()`, qui produit chaque message tour à tour. @@ -781,40 +817,30 @@ async def jouer_partie(): logger.info(""Partie terminee !"") await jouer_partie()",,,,,,,,62bb9d8c5d80b76b,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,3aa490d9,markdown,fr,fcc68661a0b4ab5b,"### Lecture du résultat : déroulé du duel Père Fouras vs Jalabert +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,3aa490d9,markdown,fr,1de2992d517e2cc7,"### Lecture du résultat : déroulé du duel Père Fouras vs Jalabert -Le log `""Depart du duel Pere Fouras vs Laurent Jalabert""` marque l'entrée dans la boucle asynchrone. À partir de cette cellule, **l'étudiant observe** : (1) l'alternance Père Fouras (énigme) ↔ Jalabert (réponse), (2) la convergence vers le mot deviné — `should terminate: True` dès que Laurent_Jalabert le nomme, puis `Partie terminee !` ; le renoncement (exercice 2) et l'évaluation par `evaluer_partie()` (exercice 3) restent à compléter par l'étudiant. Le **logging structuré** (timestamp + niveau INFO + préfixe événement) est essentiel dans un chat asynchrone multi-agent : sans lui, l'étudiant ne peut pas distinguer **qui parle** dans la sortie, ni **quand une étape se termine**. C'est ce logging qui rend le notebook pédagogique — rejouable. +Le log `""Depart du duel Pere Fouras vs Laurent Jalabert""` marque l'entrée dans la boucle asynchrone. À partir de cette cellule, **l'étudiant observe** : (1) l'alternance Père Fouras (énigme) ↔ Jalabert (réponse), (2) la boucle d'arrêt — `should terminate` évalué après chaque tour de Laurent_Jalabert : la partie s'arrête sur le premier `True`, ou à l'épuisement des itérations (ce run : le mot est trouvé en 3 tentatives, la cellule de score affiche 70/100), puis `Partie terminee !` ; la variante renoncement (exemple guidé 2) et l'évaluation par `evaluer_partie()` (exemple guidé 3) prolongent ce socle. Le **logging structuré** (timestamp + niveau INFO + préfixe événement) est essentiel dans un chat asynchrone multi-agent : sans lui, l'étudiant ne peut pas distinguer **qui parle** dans la sortie, ni **quand une étape se termine**. C'est ce logging qui rend le notebook pédagogique — rejouable. ","### Reading the result: start of the duel Père Fouras vs Jalabert -The log `""Depart du duel Pere Fouras vs Laurent Jalabert""` marks the entry into the asynchronous loop. From this cell on, **the student observes**: (1) the alternation Père Fouras (riddle) ↔ Jalabert (answer), (2) the convergence toward a guessed word or a give-up, (3) the final evaluation by `evaluer_partie()`. **Structured logging** (timestamp + INFO level + event prefix) is essential in an asynchronous multi-agent chat: without it, the student cannot distinguish **who speaks** in the output, nor **when a step ends**. It is this logging that makes the notebook pedagogical — replayable. -",,,,,,,fcc68661a0b4ab5b,bc3eb9742b305e6c,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,5306a243,markdown,fr,09666e85a85ccb93,"### Lecture du résultat : qui parle, et quand ? - -Dans le run committé, aucun paramètre `description` ni `selection_strategy` n'est passé au groupe : la sélection est celle par défaut, et la sortie le montre — les lignes `Selected agent at index 0` (Pere_Fouras) et `Selected agent at index 1` (Laurent_Jalabert) s'alternent strictement tour après tour. Ce round-robin suffit ici : le duel est à deux voix, et l'ordre des tours est l'essence du jeu. Le raffinement, pour des casts plus larges : donner à chaque agent une `description` et au groupe une `selection_strategy` sémantique, qui choisit le prochain locuteur en comparant les descriptions au contexte du dernier message au lieu d'alterner mécaniquement — le pattern **role-based selection**, utile dès que le cast dépasse deux agents (l'exercice 1, avec son Détective et son Témoin, en est l'occasion). -","### Reading the result: agent selection by description - -When the `selection_strategy` of `AgentGroupChat` must choose the next speaker, it reads the **description** of each agent (`description=""Le Père Fouras pose des énigmes...""` cell 10) and compares it with the context of the last message. This mechanism decides whether Jalabert asks a question (cyclist turn) or whether Père Fouras validates (game master turn). Without an explicit description, the orchestrator risks falling into a rigid **round-robin pattern** that does not adapt to the conversational flow. The **role-based selection** pattern is an important refinement for credible multi-agent simulations. -",,,,,,,09666e85a85ccb93,f50748791d4079a7,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,9c2a66c0,markdown,fr,c31399d55a9d3ca0,"### Exercice 3 : Système de score pour la partie +The log `""Depart du duel Pere Fouras vs Laurent Jalabert""` marks the entry into the asynchronous loop. From this cell on, **the student observes**: (1) the alternation Père Fouras (riddle) ↔ Jalabert (answer), (2) the stopping loop — `should terminate` evaluated after each Laurent_Jalabert turn: the game stops on the first `True`, or when the iterations run out (this run: the word is found in 3 attempts, the score cell shows 70/100), then `Partie terminee !` (game over); the give-up variant (guided example 2) and the evaluation by `evaluer_partie()` (guided example 3) extend this foundation. **Structured logging** (timestamp + INFO level + event prefix) is essential in an asynchronous multi-agent chat: without it, the student cannot distinguish **who speaks** in the output, nor **when a step ends**. It is this logging that makes the notebook pedagogical — replayable. +",,,,,,,1de2992d517e2cc7,2e9cb0ee85198483,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,5306a243,markdown,fr,bb70b6d3b0c9fe08,"### Lecture du résultat : qui parle, et quand ? -Actuellement, la partie s'arrete simplement quand le mot est devine ou après 20 itérations. L'objectif est d'ajouter un **système de score** qui compte le nombre de questions posees par Laurent Jalabert et affiche un score final. +Dans le run committé, aucun paramètre `description` ni `selection_strategy` n'est passé au groupe : la sélection est celle par défaut, et la sortie le montre — les lignes `Selected agent at index 0` (Pere_Fouras) et `Selected agent at index 1` (Laurent_Jalabert) s'alternent strictement tour après tour. Ce round-robin suffit ici : le duel est à deux voix, et l'ordre des tours est l'essence du jeu. Le raffinement, pour des casts plus larges : donner à chaque agent une `description` et au groupe une `selection_strategy` sémantique, qui choisit le prochain locuteur en comparant les descriptions au contexte du dernier message au lieu d'alterner mécaniquement — le pattern **role-based selection**, utile dès que le cast dépasse deux agents (l'exemple guidé 1, avec son Détective et son Témoin, en est l'occasion). +","### Reading the result: who speaks, and when? -**Objectif** : ecrire une fonction `evaluer_partie` qui prend l'historique des messages et retourne un dictionnaire contenant le nombre de questions posees, si le mot a ete trouve, et un score (moins de questions = meilleur score). +In the committed run, no `description` parameter nor `selection_strategy` is passed to the group: the selection is the default one, and the output shows it — the `Selected agent at index 0` (Pere_Fouras) and `at index 1` (Laurent_Jalabert) lines alternate strictly turn after turn. This round-robin is enough here: the duel has two voices, and the order of turns is the essence of the game. The refinement, for larger casts: give each agent a `description` and the group a semantic `selection_strategy`, which chooses the next speaker by comparing descriptions to the context of the last message instead of alternating mechanically — the **role-based selection** pattern, useful as soon as the cast exceeds two agents (guided example 1, with its Detective and its Witness, is the occasion). +",,,,,,,bb70b6d3b0c9fe08,6d22a0ed648506d7,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,9c2a66c0,markdown,fr,6c11a06299c4c744,"### Exemple guidé 3 : Système de score pour la partie -**Indices** : -- `# Étape 1` : Compter les messages ou le rôle est celui de Laurent Jalabert -- `# Étape 2` : Verifier si le mot secret apparait dans le dernier message -- `# Indice` : le score peut etre `max(0, 100 - nb_questions * 10)` -","### Exercise 3: Scoring system for the game +*Contribution du groupe de la PR #21009, intégrée comme exemple guidé.* -Currently, the game simply stops when the word is guessed or after 20 iterations. The objective is to add a **scoring system** that counts the number of questions asked by Laurent Jalabert and displays a final score. +La partie s'arrête quand le mot est deviné ou après 20 itérations. Le groupe de la PR #21009 ajoute la mesure : la fonction `evaluer_partie` prend l'historique des messages et retourne le nombre de questions posées par Laurent Jalabert, si le mot a été trouvé, et un score qui décroît avec le nombre d'essais (`max(0, 100 - nb_questions * 10)`). +","### Guided example 3: Scoring system for the game -**Objective**: write a function `evaluer_partie` that takes the message history and returns a dictionary containing the number of questions asked, whether the word was found, and a score (fewer questions = better score). +*Contribution from the group of PR #21009, integrated as a guided example.* -**Hints**: -- `# Step 1`: Count the messages where the role is that of Laurent Jalabert -- `# Step 2`: Check whether the secret word appears in the last message -- `# Hint`: the score can be `max(0, 100 - nb_questions * 10)` +The game stops when the word is guessed or after 20 iterations. The group of PR #21009 adds the measurement: the `evaluer_partie` function takes the message history and returns the number of questions asked by Laurent Jalabert, whether the word was found, and a score that decreases with the number of attempts (`max(0, 100 - nb_questions * 10)`). ",,,,,"### Упражнение 3 : Система подсчета очков для партии В настоящее время партия просто останавливается, когда слово угадано или после 20 итераций. Цель — добавить **систему подсчета очков**, которая считает количество вопросов, заданных Laurent Jalabert, и отображает итоговый счет. @@ -824,24 +850,53 @@ Currently, the game simply stops when the word is guessed or after 20 iterations **Подсказки** : - # Шаг 1 : Посчитать сообщения, где роль принадлежит Laurent Jalabert - # Шаг 2 : Проверить, появляется ли секретное слово в последнем сообщении -- # Подсказка : счет может быть `max(0, 100 - nb_questions * 10)`",,c31399d55a9d3ca0,1ef25963c540f75d,,,,,f3277f163627f847,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,df0d3126,code,fr,c67bed915c276918,"def evaluer_partie(history_messages: list) -> dict: - # TODO etudiant : implementer le systeme de score - nb_questions = 0 # Etape 1 : compter les questions de Laurent - mot_trouve = False # Etape 2 : verifier la presence du mot - - # Indice : parcourir history_messages et compter les messages de Laurent - score = None # TODO etudiant : calculer le score - return {""nb_questions"": nb_questions, ""mot_trouve"": mot_trouve, ""score"": score} +- # Подсказка : счет может быть `max(0, 100 - nb_questions * 10)`",,6c11a06299c4c744,10b4f420dc95f3e7,,,,,f3277f163627f847,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,df0d3126,code,fr,883f3dfb294eb8fe," +# EXEMPLE GUIDE 3 : systeme de score (groupe de la PR #21009) + +def evaluer_partie(history_messages: list) -> dict: + + # Etape 1 : Recuperer les messages de Laurent Jalabert + messages_laurent = [ + message for message in history_messages + if getattr(message, ""name"", None) == ""Laurent_Jalabert"" + ] + + # Compter le nombre de tentatives + nb_questions = len(messages_laurent) + + # Etape 2 : Verifier si le mot a ete trouve + mot_trouve = False + + if messages_laurent: + dernier_message = str( + messages_laurent[-1].content or """" + ).lower() + + mot_trouve = MOT_A_DEVINER.lower() in dernier_message + + # Etape 3 : Calculer le score + if mot_trouve: + score = max(0, 100 - nb_questions * 10) + else: + score = 0 + + # Retourner les resultats + return { + ""nb_questions"": nb_questions, + ""mot_trouve"": mot_trouve, + ""score"": score + } -print(""Exercice a completer : systeme de score"")",,,,,,,,c67bed915c276918,,,,,,,, -MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,d2a4bdca,markdown,fr,7a0916a5ece4496c,"### Lecture du résultat : système de score en fin de partie +print(""Systeme de score configure avec succes !"") +",,,,,,,,883f3dfb294eb8fe,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,d2a4bdca,markdown,fr,dad3f8c0b183d4ea,"### Lecture du résultat : système de score en fin de partie -La cellule 29 expose `evaluer_partie(history_messages: list) -> dict` — l'étudiant doit implémenter cette fonction (exercice 3, le dernier du carnet). Le contrat, donné par l'énoncé : compter les questions posées par Laurent Jalabert, vérifier si le mot secret apparaît, et retourner un score qui décroît avec le nombre d'essais (l'indice propose `max(0, 100 - nb_questions * 10)`). Ce **scoring** est ce qui transforme le duel en **jeu avec issue mesurable** — sans score, le notebook serait une simple conversation sans fin. C'est aussi ce qui ouvre la porte à des **expériences reproductibles** : l'étudiant peut comparer deux configurations de prompts en mesurant le score moyen sur 100 parties. +La fonction du groupe compte les messages signés `Laurent_Jalabert` dans l'historique réel du groupe de chat, vérifie si le mot secret apparaît dans son **dernier** message, et applique la règle `max(0, 100 - 10 × questions)` : une victoire à la première question vaut 90, chaque question supplémentaire coûte 10 points, et un échec vaut 0 — quelle que soit la longueur de la partie. Si le mot n'est pas trouvé, le score rendu est 0 et le nombre de questions reste affiché pour le diagnostic. Ce scoring transforme le duel en **jeu à issue mesurable** : en rejouant la partie avec deux configurations de prompts, on peut comparer des scores moyens — la porte d'entrée des expériences reproductibles. ","### Reading the result: scoring system at the end of the game -Cell 20 exposes `evaluer_partie(history_messages: list) -> dict` — the student must implement this function (exercise 4). The typical contract: count the attempts, assign a score based on the length of the guessed word vs the number of attempts, and return a serializable dict. This **scoring** is what turns the duel into a **game with a measurable outcome** — without a score, the notebook would be a simple endless conversation. It is also what opens the door to **reproducible experiments**: the student can compare two prompt configurations by measuring the average score over 100 games. -",,,,,,,7a0916a5ece4496c,4c726fbc1bb22b78,,,,,,, +The group's function counts the messages signed `Laurent_Jalabert` in the chat group's real history, checks whether the secret word appears in his **last** message, and applies the rule `max(0, 100 - 10 × questions)`: a first-question win is worth 90, each additional question costs 10 points, and a failure is worth 0 — regardless of the length of the game. If the word is not found, the returned score is 0 and the number of questions remains displayed for diagnosis. This scoring turns the duel into a **game with a measurable outcome**: by replaying the game with two prompt configurations, one can compare average scores — the gateway to reproducible experiments. +",,,,,,,dad3f8c0b183d4ea,7b2008242d4c0452,,,,,,, MyIA.AI.Notebooks/GenAI/CaseStudies/Medical-Chatbot/medical_chatbot.ipynb,068a6d38,code,fr,d7a1ea74a47bcb51,"# Parameters BATCH_MODE = True ",,,,,,,,d7a1ea74a47bcb51,,,,,,,, @@ -2035,3 +2090,152 @@ BATCH_MODE = True MyIA.AI.Notebooks/GenAI/CaseStudies/Recipe-Maker/receipe_maker.ipynb,ceeeb248,code,fr,d7a1ea74a47bcb51,"# Parameters BATCH_MODE = True ",,,,,,,,d7a1ea74a47bcb51,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,0672c52a,markdown,fr,67d1cdcf62db5170,"### Lecture du résultat : deux nouveaux personnages configurés + +Les deux agents du groupe sont créés sans erreur. Deux choix méritent l'œil : le prompt du **Témoin** est une f-string qui interpole `MOT_A_DEVINER` — le témoin « connaît » le mot parce qu'il est injecté dans ses instructions, mais l'interdiction « Ne révèle jamais directement le mot secret » maintient l'écart entre savoir et dire. Le prompt du **Detective** impose des questions fermées (oui/non) : ce format contraint la conversation à converger, comme la sélection round-robin du groupe contraint l'ordre des tours (section suivante). Chaque agent reçoit son propre `create_kernel()` — le même motif que le duo Père Fouras / Laurent Jalabert de la cellule précédente. +","### Reading the result: two new characters configured + +The group's two agents are created without error. Two choices deserve attention: the **Witness** prompt is an f-string that interpolates `MOT_A_DEVINER` — the witness ""knows"" the word because it is injected into its instructions, but the rule ""Ne révèle jamais directement le mot secret"" (never reveal the secret word directly) maintains the gap between knowing and telling. The **Detective** prompt imposes closed-ended (yes/no) questions: this format forces the conversation to converge, just as the group's round-robin selection forces the order of turns (next section). Each agent receives its own `create_kernel()` — the same pattern as the Père Fouras / Laurent Jalabert duo of the previous cell. +",,,,,,,67d1cdcf62db5170,3ca16b33fe6b815d,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,6b1b975c,code,fr,a2d6d3387925ba62," +# Tests de l'exemple guide 2 : trois situations (groupe de la PR #21009) + +from semantic_kernel.contents import AuthorRole, ChatMessageContent + +strategie = RenoncementTerminationStrategy( + agents=[laurent_jalabert], + maximum_iterations=20 +) + +# Trois situations a tester +tests = [ + (""Abandon"", ""J'abandonne"", True), + (""Mot trouve"", f""Ma reponse est {MOT_A_DEVINER}"", True), + (""Mauvaise reponse"", ""Je pense que c'est un objet"", False) +] + +for nom, reponse, attendu in tests: + + message = ChatMessageContent( + role=AuthorRole.ASSISTANT, + content=reponse, + name=""Laurent_Jalabert"" + ) + + resultat = await strategie.should_agent_terminate( + agent=laurent_jalabert, + history=[message] + ) + + print(f""{nom} : {resultat} (attendu : {attendu})"") +",,,,,,,,a2d6d3387925ba62,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,0ed41dbe,markdown,fr,3abac3ed7dbb0382,"### Lecture du résultat : la stratégie hybride passe ses trois tests + +Les trois situations renvoient le verdict attendu : un « J'abandonne » arrête la partie, un message qui contient `MOT_A_DEVINER` aussi, et une réponse neutre laisse le duel continuer. La mécanique tient en deux gestes : tout est comparé en minuscules (le modèle peut écrire « J'ABANDONNE »), et la condition finale est un `or` — il suffit que le mot soit trouvé **ou** qu'une phrase d'abandon apparaisse dans le **dernier** message de l'historique. La stratégie est attachée à `laurent_jalabert` : c'est bien au tour du devineur que la question « continuer ? » se pose. +","### Reading the result: the hybrid strategy passes its three tests + +The three situations return the expected verdict: a ""J'abandonne"" (I give up) stops the game, a message containing `MOT_A_DEVINER` does too, and a neutral answer lets the duel continue. The mechanics boil down to two gestures: everything is compared in lowercase (the model may write ""J'ABANDONNE""), and the final condition is an `or` — it is enough that the word is found **or** that a give-up phrase appears in the **last** message of the history. The strategy is attached to `laurent_jalabert`: it is indeed on the guesser's turn that the question ""continue?"" arises. +",,,,,,,3abac3ed7dbb0382,8a9e95e2287fcb28,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,2a2f46fe,code,fr,24c9e0be41d45852," +# Recuperer l'historique de la partie +historique = chat.history.messages + +# Calculer le score +resultat = evaluer_partie(historique) + +# Afficher les resultats +print(""===== RESULTATS DE LA PARTIE ====="") +print(""Nombre de tentatives :"", resultat[""nb_questions""]) +print(""Mot trouve :"", resultat[""mot_trouve""]) +print(""Score final :"", resultat[""score""], ""/ 100"") +",,,,,,,,24c9e0be41d45852,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,ae952339,markdown,fr,ec51388ae7d115a1,"## Exercices + +Trois nouveaux défis, un par compétence travaillée dans les exemples guidés ci-dessus : rédiger des prompts qui contraignent un comportement, composer une condition de terminaison, mesurer une partie jouée. Les stubs sont incomplets par conception — le carnet s'exécute de bout en bout sans eux. +","## Exercises + +Three new challenges, one per skill practiced in the guided examples above: writing prompts that constrain a behavior, composing a termination condition, measuring a played game. The stubs are incomplete by design — the notebook runs end to end without them. +",,,,,,,ec51388ae7d115a1,85897e2c7a0023b6,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,5b19df38,markdown,fr,7a379d68d77f6379,"### Exercice 1 : Un duel au protocole binaire + +Les personnages de l'exemple guidé 1 laissent le Témoin répondre par des phrases entières. Variante plus stricte : un **Indicateur** qui ne répond **que** par « oui », « non » ou « peut-être » — jamais une phrase — et un **Enquêteur méthodique** qui annonce son hypothèse d'élimination (« Je cherche un mot de plus de 15 lettres ») avant chaque question. + +**Objectif** : rédiger les deux prompts et configurer les deux agents correspondants. + +**Indices** : +- `# Étape 1` : rédiger le prompt de l'Indicateur (rôle + liste fermée des trois réponses autorisées) +- `# Étape 2` : rédiger le prompt de l'Enquêteur (stratégie d'élimination annoncée avant chaque question) +- `# Indice` : un prompt peut interdire (« jamais de phrase ») autant qu'il peut prescrire (« réponds uniquement par ») +","### Exercise 1: A binary-protocol duel + +The characters of guided example 1 let the Witness answer with full sentences. A stricter variant: an **Informer** who answers **only** with ""yes"", ""no"" or ""maybe"" — never a sentence — and a **Methodical Investigator** who announces the elimination hypothesis (""I am looking for a word longer than 15 letters"") before each question. + +**Objective**: write both prompts and configure the two corresponding agents. + +**Hints**: +- `# Step 1`: write the Informer's prompt (role + closed list of the three allowed answers) +- `# Step 2`: write the Investigator's prompt (elimination strategy announced before each question) +- `# Hint`: a prompt can forbid (""never a sentence"") as much as it can prescribe (""answer only with"") +",,,,,,,7a379d68d77f6379,c05e1b2f9f633ae1,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,ec943b8d,code,fr,f45b7e07fede6ace,"# Exercice 1 : un duel au protocole binaire +# TODO etudiant : definir les prompts et creer les agents +INDICATEUR_PROMPT = """" # Etape 1 : instructions de l'Indicateur (oui / non / peut-etre, jamais une phrase) +ENQUETEUR_PROMPT = """" # Etape 2 : instructions de l'Enqueteur (hypothese annoncee avant chaque question) +indicateur = None # TODO etudiant : ChatCompletionAgent(kernel=create_kernel(), ...) +enqueteur = None # TODO etudiant +print(""Exercice 1 a completer"") +",,,,,,,,f45b7e07fede6ace,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,08f0cfb2,markdown,fr,c54287f8d4241448,"### Exercice 2 : Terminaison sur question répétée + +Un devineur qui répète la même question trois fois de suite ne progresse plus : la partie est perdue. L'objectif est d'implémenter `RepetitionTerminationStrategy`, une variante qui arrête la partie si le mot est deviné **ou** si les trois derniers messages de Laurent Jalabert sont identiques (comparaison en minuscules). + +**Indices** : +- `# Étape 1` : extraire les messages de Laurent de l'historique (s'il y en a moins de trois, la partie continue) +- `# Étape 2` : comparer les trois derniers en minuscules, et combiner avec la détection du mot par un `or` +","### Exercise 2: Termination on a repeated question + +A guesser who repeats the same question three times in a row is no longer progressing: the game is lost. The objective is to implement `RepetitionTerminationStrategy`, a variant that stops the game if the word is guessed **or** if Laurent Jalabert's last three messages are identical (lowercase comparison). + +**Hints**: +- `# Step 1`: extract Laurent's messages from the history (if there are fewer than three, the game continues) +- `# Step 2`: compare the last three in lowercase, and combine with word detection using an `or` +",,,,,,,c54287f8d4241448,d8b4b17808ea4616,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,3610348b,code,fr,cb9ca5e26624dc68,"# Exercice 2 : terminaison sur question repetee +# TODO etudiant +from semantic_kernel.agents.strategies.termination.termination_strategy import TerminationStrategy +from semantic_kernel.contents.chat_message_content import ChatMessageContent + +class RepetitionTerminationStrategy(TerminationStrategy): + # TODO etudiant : implementer la strategie de detection de repetition + SEUIL_REPETITION: int = 3 # Etape 1 : nombre de questions identiques consecutives + + async def should_agent_terminate(self, agent, history: list[ChatMessageContent], cancellation_token=None) -> bool: + result = False # TODO etudiant : remplacer par la logique (mot trouve OU repetition) + return result + +print(""Exercice 2 a completer"") +",,,,,,,,cb9ca5e26624dc68,,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,edb7a025,markdown,fr,f4fd10e340a1f368,"### Exercice 3 : Mesurer la concision des indices du Père Fouras + +`evaluer_partie` (exemple guidé 3) mesure le devineur. L'autre moitié du duel mérite sa mesure : des indices trop longs facilitent la tâche. **Objectif** : écrire `mesurer_indices` qui prend l'historique des messages et retourne la longueur moyenne des messages du Père Fouras, et un booléen `indices_concis` vrai si cette moyenne reste sous 200 caractères. + +**Indices** : +- `# Étape 1` : filtrer l'historique sur les messages signés `Pere_Fouras` (même filtre que l'exemple guidé 3) +- `# Étape 2` : moyenner les longueurs (`len(str(message.content))`) et comparer au seuil +","### Exercise 3: Measuring the concision of Père Fouras's hints + +`evaluer_partie` (guided example 3) measures the guesser. The other half of the duel deserves its own measurement: overly long hints make the task easier. **Objective**: write `mesurer_indices` which takes the message history and returns the average length of Père Fouras's messages, and a boolean `indices_concis` true if this average stays under 200 characters. + +**Hints**: +- `# Step 1`: filter the history on messages signed `Pere_Fouras` (same filter as guided example 3) +- `# Step 2`: average the lengths (`len(str(message.content))`) and compare to the threshold +",,,,,,,f4fd10e340a1f368,4f277e5e0b156580,,,,,,, +MyIA.AI.Notebooks/GenAI/CaseStudies/Fort-Boyard/fort-boyard-python.ipynb,a85748ad,code,fr,d474392d7bce72b4,"# Exercice 3 : mesurer la concision des indices du Pere Fouras +# TODO etudiant +def mesurer_indices(history_messages: list) -> dict: + longueur_moyenne = 0.0 # TODO etudiant : moyenne des longueurs des messages du Pere Fouras + indices_concis = False # TODO etudiant : True si longueur_moyenne < 200 + return {""longueur_moyenne"": longueur_moyenne, ""indices_concis"": indices_concis} + +print(""Exercice 3 a completer"") +",,,,,,,,d474392d7bce72b4,,,,,,,,