feat(experiments): ajoute une entrée de volumétrie par locale basée sur TEST_RECIPE

TEST_SENTENCES gagne 2 entrées supplémentaires (fr-recette-complete,
en-recette-complete) qui concatènent les 7 étapes de TEST_RECIPE ("Tarte
aux pommes rustique") en un seul step par locale — même principe que
fr-concat-volumetrie/en-concat-volumetrie, mais sur du texte de recette
réel plutôt qu'une concaténation de phrases-pièges synthétiques.

TEST_RECIPE (déjà ajoutée localement) déplacée avant TEST_SENTENCES
(nécessaire pour être référencée dans sa construction) et reformatée à la
convention du fichier (clés non citées, virgules finales) ; contenu
inchangé. Retire le stub dummyRecipeFr/dummyRecipeEn de
benchmark-harness.ts : le harness générique n'a besoin d'aucun
cas particulier, TEST_SENTENCES suffit.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Nicolas 2026-08-21 22:48:39 +02:00
parent 9f274f9461
commit b8e599106f

View file

@ -1,12 +1,12 @@
/** /**
* Les 9 phrases de test partagées par les trois moteurs de ce PoC (7 phrases * Les 11 phrases de test partagées par les trois moteurs de ce PoC (7
* de complexité variable + 2 concaténations pour tester l'effet du volume, * phrases de complexité variable + 2 concaténations synthétiques + 2
* voir {@link TEST_SENTENCES}) un seul jeu de phrases, importé par * concaténations d'une vraie recette, voir {@link TEST_SENTENCES}) un seul
* `llm-tech-step-poc.ts`, `nlp-tech-step-poc.ts` et `hybrid-tech-step-poc.ts`, * jeu de phrases, importé par `llm-tech-step-poc.ts`, `nlp-tech-step-poc.ts`
* pour que leurs runs soient directement comparables phrase par phrase sans * et `hybrid-tech-step-poc.ts`, pour que leurs runs soient directement
* risque de désynchronisation (un défaut de la toute première version de ce * comparables phrase par phrase sans risque de désynchronisation (un défaut
* PoC, le pendant `node-nlp` vivait dans `apps/api` et recopiait ces * de la toute première version de ce PoC, le pendant `node-nlp` vivait
* phrases à la main). * dans `apps/api` et recopiait ces phrases à la main).
*/ */
/** Une phrase de test, avec sa langue et ce qui la rend "complexe" (documentation, non exploité par le code). */ /** Une phrase de test, avec sa langue et ce qui la rend "complexe" (documentation, non exploité par le code). */
@ -66,6 +66,13 @@ export interface BenchmarkSentence {
* normale de ce jeu. * normale de ce jeu.
* 9. EN, la concaténation des 3 phrases EN ci-dessus (2, 5, 7) en un seul * 9. EN, la concaténation des 3 phrases EN ci-dessus (2, 5, 7) en un seul
* step même principe côté EN. * step même principe côté EN.
* 10. FR, les 7 étapes de {@link TEST_RECIPE} ("Tarte aux pommes rustique")
* concaténées en un seul step même principe volumétrique que les
* items 8/9, mais sur du texte de recette RÉEL (rédigé normalement,
* sans les tournures adversariales des phrases 1-7) plutôt qu'une
* concaténation de phrases-pièges synthétiques.
* 11. EN, les 7 étapes de {@link TEST_RECIPE} ("Rustic Apple Tart")
* concaténées en un seul step même principe que l'item 10, côté EN.
*/ */
const BASE_SENTENCES: readonly BenchmarkSentence[] = [ const BASE_SENTENCES: readonly BenchmarkSentence[] = [
{ {
@ -112,6 +119,111 @@ const BASE_SENTENCES: readonly BenchmarkSentence[] = [
}, },
]; ];
/**
* Une vraie recette (titre, ingrédients, 7 étapes rédigées normalement en
* FR/EN) matière première d'une volumétrie plus RÉALISTE que la
* concaténation de phrases-pièges synthétiques ci-dessus (items 8/9) : du
* texte de recette tel qu'un utilisateur l'écrirait vraiment, sans
* tournures adversariales délibérées. Seuls `steps[].text.fr`/`.en` sont
* utilisés par ce fichier ({@link concatenateRecipeByLocale}) le reste
* (`ingredients`, `servings`, temps de préparation...) est conservé tel
* quel pour une éventuelle recette de test plus complète plus tard, pas
* exploité aujourd'hui.
*/
export const TEST_RECIPE = {
recipe_id: "apple_tart_001",
title: {
fr: "Tarte aux pommes rustique",
en: "Rustic Apple Tart",
},
servings: 6,
prep_time_minutes: 20,
cook_time_minutes: 35,
ingredients: [
{
id: "ing_1",
name: { fr: "pâte brisée", en: "shortcrust pastry" },
quantity: 1,
unit: "piece",
},
{
id: "ing_2",
name: { fr: "pommes Golden", en: "Golden Delicious apples" },
quantity: 4,
unit: "pieces",
},
{
id: "ing_3",
name: { fr: "beurre", en: "butter" },
quantity: 30,
unit: "g",
},
{
id: "ing_4",
name: { fr: "sucre vanillé", en: "vanilla sugar" },
quantity: 1,
unit: "packet",
},
{
id: "ing_5",
name: { fr: "compote de pommes", en: "applesauce" },
quantity: 150,
unit: "g",
},
],
steps: [
{
step_number: 1,
text: {
fr: "Préchauffez votre four à 180°C pendant 10 minutes.",
en: "Preheat your oven to 180°C (350°F) for 10 minutes.",
},
},
{
step_number: 2,
text: {
fr: "Épluchez et évidez les pommes, puis coupez-les en fines lamelles régulières sur votre planche à découper.",
en: "Peel and core the apples, then slice them into thin, even slices on your cutting board.",
},
},
{
step_number: 3,
text: {
fr: "Déroulez la pâte brisée dans un moule à tarte et piquez le fond avec une fourchette.",
en: "Unroll the shortcrust pastry into a tart pan and prick the bottom with a fork.",
},
},
{
step_number: 4,
text: {
fr: "Étalez la compote de pommes de manière égale sur le fond de pâte avec une spatule.",
en: "Spread the applesauce evenly over the pastry base using a spatula.",
},
},
{
step_number: 5,
text: {
fr: "Disposez les lamelles de pommes en rosette par-dessus la compote, puis parsemez de noisettes de beurre et de sucre vanillé.",
en: "Arrange the apple slices in a rosette pattern on top of the sauce, then dot with small knobs of butter and sprinkle with vanilla sugar.",
},
},
{
step_number: 6,
text: {
fr: "Enfournez à 180°C et laissez cuire pendant 35 minutes jusqu'à ce que les bordures soient bien dorées.",
en: "Bake at 180°C (350°F) for 35 minutes until the edges are golden brown.",
},
},
{
step_number: 7,
text: {
fr: "Sortez la tarte du four et laissez-la reposer au frais pendant 15 minutes avant de démouler et de servir.",
en: "Remove the tart from the oven and let it rest at room temperature for 15 minutes before unmolding and serving.",
},
},
],
};
/** /**
* Concatène les textes de {@link BASE_SENTENCES} d'une locale donnée, * Concatène les textes de {@link BASE_SENTENCES} d'une locale donnée,
* séparés par un espace, dans leur ordre d'apparition calculé plutôt que * séparés par un espace, dans leur ordre d'apparition calculé plutôt que
@ -125,16 +237,28 @@ function concatenateByLocale(locale: BenchmarkSentence["locale"]): string {
.join(" "); .join(" ");
} }
/**
* Concatène les 7 étapes de {@link TEST_RECIPE} pour une locale donnée,
* dans leur ordre (`step_number`), séparées par un espace même principe
* que {@link concatenateByLocale} mais sur la recette réelle plutôt que sur
* les phrases de test synthétiques.
*/
function concatenateRecipeByLocale(locale: BenchmarkSentence["locale"]): string {
return TEST_RECIPE.steps.map((step) => step.text[locale]).join(" ");
}
const FR_SENTENCE_COUNT = BASE_SENTENCES.filter((sentence) => sentence.locale === "fr").length; const FR_SENTENCE_COUNT = BASE_SENTENCES.filter((sentence) => sentence.locale === "fr").length;
const EN_SENTENCE_COUNT = BASE_SENTENCES.filter((sentence) => sentence.locale === "en").length; const EN_SENTENCE_COUNT = BASE_SENTENCES.filter((sentence) => sentence.locale === "en").length;
/** /**
* {@link BASE_SENTENCES} (7 phrases, complexité variable à taille à peu * {@link BASE_SENTENCES} (7 phrases, complexité variable à taille à peu
* près constante) plus deux entrées dérivées par locale (items 8 et 9 du * près constante) plus quatre entrées dérivées par locale (items 8-11 du
* doc-comment ci-dessus) qui concatènent toutes les phrases de cette locale * doc-comment ci-dessus) qui concatènent, respectivement, toutes les
* en un seul step pour isoler l'effet du VOLUME de texte sur la durée de * phrases de test d'une locale et toutes les étapes de {@link TEST_RECIPE}
* traitement de chaque moteur, indépendamment de la complexité sémantique * de cette même locale en un seul step chacune pour isoler l'effet du
* déjà couverte par les 7 premières. * VOLUME de texte sur la durée de traitement de chaque moteur,
* indépendamment de la complexité sémantique déjà couverte par les 7
* premières, sur du texte synthétique ET sur du texte de recette réel.
*/ */
export const TEST_SENTENCES: readonly BenchmarkSentence[] = [ export const TEST_SENTENCES: readonly BenchmarkSentence[] = [
...BASE_SENTENCES, ...BASE_SENTENCES,
@ -150,4 +274,16 @@ export const TEST_SENTENCES: readonly BenchmarkSentence[] = [
text: concatenateByLocale("en"), text: concatenateByLocale("en"),
note: `Concaténation des ${EN_SENTENCE_COUNT} étapes EN ci-dessus en un seul step — même test de volumétrie côté EN.`, note: `Concaténation des ${EN_SENTENCE_COUNT} étapes EN ci-dessus en un seul step — même test de volumétrie côté EN.`,
}, },
{
id: "fr-recette-complete",
locale: "fr",
text: concatenateRecipeByLocale("fr"),
note: `Les ${TEST_RECIPE.steps.length} étapes de "${TEST_RECIPE.title.fr}" concaténées en un seul step — même test de volumétrie que ci-dessus, mais sur du texte de recette réel plutôt qu'une concaténation de phrases-pièges synthétiques.`,
},
{
id: "en-recette-complete",
locale: "en",
text: concatenateRecipeByLocale("en"),
note: `Les ${TEST_RECIPE.steps.length} étapes de "${TEST_RECIPE.title.en}" concaténées en un seul step — même principe côté EN.`,
},
]; ];