From 7920925fa21df70c3b77e89f6abc8f6436225632 Mon Sep 17 00:00:00 2001 From: jpramil Date: Fri, 19 Jun 2026 09:19:32 +0000 Subject: [PATCH 1/4] publish website --- .github/workflows/publish-website.yml | 51 +++++++++++++++++++++ docs/00-generalites.qmd | 45 +++++++++++++++++++ docs/09-rag-annotation.qmd | 37 ++++++++++++++++ docs/_quarto.yml | 64 +++++++++++++++------------ docs/brand.scss | 7 +++ docs/donnees.qmd | 40 +++++++++++++++++ docs/ressources.qmd | 31 +++++++++++++ 7 files changed, 247 insertions(+), 28 deletions(-) create mode 100644 .github/workflows/publish-website.yml create mode 100644 docs/00-generalites.qmd create mode 100644 docs/09-rag-annotation.qmd create mode 100644 docs/brand.scss create mode 100644 docs/donnees.qmd create mode 100644 docs/ressources.qmd diff --git a/.github/workflows/publish-website.yml b/.github/workflows/publish-website.yml new file mode 100644 index 0000000..5ca2c5a --- /dev/null +++ b/.github/workflows/publish-website.yml @@ -0,0 +1,51 @@ +name: Publish website + +# Déploie le site Quarto (docs/) sur GitHub Pages à chaque commit sur website ou main, +# via la méthode artifact officielle (pas de branche gh-pages). +on: + push: + branches: + - website + - main + workflow_dispatch: + +# Permissions requises par la méthode artifact (OIDC + déploiement Pages). +permissions: + contents: read + pages: write + id-token: write + +# Un seul déploiement à la fois ; on ne coupe pas un déploiement en cours. +concurrency: + group: pages + cancel-in-progress: false + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up Quarto + uses: quarto-dev/quarto-actions/setup@v2 + + # Pas de setup Python/R : les pages docs/ ne contiennent aucun chunk exécutable. + - name: Render site + run: quarto render docs + + - name: Upload Pages artifact + uses: actions/upload-pages-artifact@v3 + with: + path: docs/_site + + deploy: + needs: build + runs-on: ubuntu-latest + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@v4 diff --git a/docs/00-generalites.qmd b/docs/00-generalites.qmd new file mode 100644 index 0000000..1754de2 --- /dev/null +++ b/docs/00-generalites.qmd @@ -0,0 +1,45 @@ +--- +title: "Le processus — Généralités" +subtitle: "Vue d'ensemble du pipeline de codification COICOP" +--- + +> 🚧 **Page en construction (squelette).** Compléter les sections ci-dessous. + +## Principe + +Le pipeline `codif-coicop-bdf` rattache chaque libellé de produit à un poste de la +nomenclature COICOP en combinant plusieurs classifieurs complémentaires, puis en les +arbitrant via un LLM (*LLM-as-judge*). Il est orchestré par **Argo Workflows** +(`argo/codif-pipeline.yaml`). + +## Le DAG du pipeline + +```text + ┌──→ create-vector-db ──────────────┐ (skippable) + preprocessing ──┐ ┌──→ prune ────┤ ├──→ run-rag(-annotations) ─┐ + └──→┤ └──→ create-vector-db-annotations ───┘ │ + └──→ codif-regex ─┬──→ codif-lcs ──────────────────────────────────────────────┼──→ decide-coicop ──→ final-output ──→ report + (→ prune) └──→ run-ttc ───────────────────────────────────────────────┘ +``` + +## Les classifieurs + +Quatre classifieurs tournent en parallèle, puis sont conciliés par `decide-coicop` : + +| Classifieur | Approche | Page | +|---|---|---| +| Regex | Règles | [La codification par regex](02-codif-regex.qmd) | +| LCS | Similarité de chaînes | [LCS](03-codif-lcs.qmd) | +| TTC | Classifieur neuronal | [TTC](05-run-ttc.qmd) | +| RAG COICOP | RAG sur notices | [RAG sur COICOP](04-coicop-rag.qmd) | +| RAG annotations | RAG sur exemples codifiés | [RAG sur annotation](09-rag-annotation.qmd) | + +## Modes prod / éval + +*TODO* — `input_file` non vide = **production** (code un fichier externe) ; vide = +**évaluation** (code le split test des annotations). Décrire le pilotage de bout en bout. + +## Échantillonnage + +*TODO* — sampling centralisé à `codif-regex` (`sample-observations` en prod, +`sample-annotations` en éval) hérité par tous les classifieurs. diff --git a/docs/09-rag-annotation.qmd b/docs/09-rag-annotation.qmd new file mode 100644 index 0000000..3769942 --- /dev/null +++ b/docs/09-rag-annotation.qmd @@ -0,0 +1,37 @@ +--- +title: "RAG sur annotation" +subtitle: "Codification par RAG sur exemples déjà codifiés (few-shot)" +--- + +> 🚧 **Page en construction (squelette).** Compléter les sections ci-dessous. + +## Principe + +Le module `coicop-rag-annotations/` code chaque produit par **RAG sur exemples annotés** : +on récupère dans une base vectorielle les produits déjà codifiés les plus similaires, puis on +construit un prompt *few-shot* pour demander le code COICOP à un LLM. + +## Étapes + +1. **Construction de la vector DB** (`0_build_annotation_vector_db.py`) — indexe la KB + d'annotations prunée (+ suggester) dans Qdrant. +2. **Codification** (`1_run_rag.py`) — embeddings de l'input, recherche des k plus proches + voisins, prompt few-shot, génération, parsing JSON, export des prédictions. + + + +## KB indexée et sources exclues + +*TODO* — `exclude_sources` (éval) vs `exclude_sources_prod` (prod), inclusion du suggester, +sources exclues (ex. `copain`). + +## Collection Qdrant de test + +*TODO* — quand `sample-annotations` est non vide, une collection `_test` (suffixe sur +`collection_name`) est utilisée pour ne pas écraser la vector DB de production. + +## Sortie et intégration + +*TODO* — colonnes `ragann_*` consommées par [l'étape de réconciliation finale](06-decide-coicop.qmd) +(`decide-coicop`). diff --git a/docs/_quarto.yml b/docs/_quarto.yml index 1d46af9..3163681 100644 --- a/docs/_quarto.yml +++ b/docs/_quarto.yml @@ -4,52 +4,60 @@ project: website: title: "Codification COICOP — BDF" - description: "Documentation pas à pas du pipeline de codification automatique des produits BDF selon la nomenclature COICOP." + description: "Documentation du pipeline de codification automatique des produits BDF selon la nomenclature COICOP." navbar: left: - href: index.qmd - text: Accueil - - text: Étapes + text: Introduction + - href: donnees.qmd + text: Les données + - text: Le processus menu: + - href: 00-generalites.qmd + text: Généralités - href: 01-preprocessing.qmd - text: 1. Preprocessing + text: Le preprocessing - href: 02-codif-regex.qmd - text: 2. Codif regex + text: La codification par regex - href: 03-codif-lcs.qmd - text: 3. Codif LCS - - href: 04-coicop-rag.qmd - text: 4. RAG (prune, vector-db, run-rag) + text: LCS - href: 05-run-ttc.qmd - text: 5. TTC + text: TTC + - href: 04-coicop-rag.qmd + text: RAG sur COICOP + - href: 09-rag-annotation.qmd + text: RAG sur annotation - href: 06-decide-coicop.qmd - text: 6. Decide (LLM-as-judge) + text: Étape de réconciliation finale - href: 07-final-output.qmd - text: 7. Final output + text: Final output - href: 08-report.qmd - text: 8. Report - sidebar: - style: docked - search: true - contents: - - index.qmd - - section: "Étapes du pipeline" - contents: - - 01-preprocessing.qmd - - 02-codif-regex.qmd - - 03-codif-lcs.qmd - - 04-coicop-rag.qmd - - 05-run-ttc.qmd - - 06-decide-coicop.qmd - - 07-final-output.qmd - - 08-report.qmd + text: Report + - href: ressources.qmd + text: Les ressources + tools: + - icon: github + href: https://github.com/InseeFrLab/codif-coicop-bdf + page-navigation: true + back-to-top-navigation: true + reader-mode: true + page-footer: + left: "INSEE — Codification COICOP BDF" + right: "Construit avec [Quarto](https://quarto.org)" format: html: - theme: cosmo + theme: + light: [flatly, brand.scss] + dark: [darkly, brand.scss] toc: true toc-depth: 3 toc-title: "Sur cette page" code-copy: true code-overflow: wrap number-sections: false + anchor-sections: true + smooth-scroll: true + link-external-icon: true + link-external-newwindow: true lang: fr diff --git a/docs/brand.scss b/docs/brand.scss new file mode 100644 index 0000000..5944b16 --- /dev/null +++ b/docs/brand.scss @@ -0,0 +1,7 @@ +/*-- scss:defaults --*/ +$primary: #2c6e9b; +$font-family-sans-serif: system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; +$h1-font-size: 2.1rem; +$h2-font-size: 1.5rem; +$toc-font-size: 0.85rem; +$border-radius: 0.5rem; diff --git a/docs/donnees.qmd b/docs/donnees.qmd new file mode 100644 index 0000000..feb7062 --- /dev/null +++ b/docs/donnees.qmd @@ -0,0 +1,40 @@ +--- +title: "Les données" +subtitle: "Sources mobilisées par le pipeline de codification COICOP" +--- + +> 🚧 **Page en construction (squelette).** Compléter chaque section ci-dessous. + +## Vue d'ensemble + +Le pipeline mobilise plusieurs sources de données, en entrée comme en référence. + +| Source | Rôle | Format | Localisation | +|---|---|---|---| +| Annotations BdF 2024 | Vérité terrain / KB | parquet | S3 | +| Annotations historiques BdF 2017 | KB (historique) | csv | S3 | +| Suggester (liste produits) | Exemples additionnels | csv | S3 | +| Nomenclature COICOP | Référentiel des codes | csv | S3 | +| Observations à coder | Entrée production | parquet/csv | fourni par l'utilisateur | + + + +## Les annotations + +*TODO* — décrire les sources d'annotations (`receipts_from_app`, `manual_from_app`, +`manual_from_book`, `bdf_2017`), la colonne `source`, et les sources exclues du pipeline +(ex. `copain`). + +## Le suggester + +*TODO* — décrire la liste produits (`liste_produits_fr_copain.csv`) et son usage comme +exemples additionnels dans la KB des RAG. + +## La nomenclature COICOP + +*TODO* — structure des codes (niveaux 1 à 5), pruning niveau 4, table de mapping. + +## Conventions de stockage (S3) + +*TODO* — convention de chemins `s3://////` et échanges +inter-modules en parquet. diff --git a/docs/ressources.qmd b/docs/ressources.qmd new file mode 100644 index 0000000..7228bd9 --- /dev/null +++ b/docs/ressources.qmd @@ -0,0 +1,31 @@ +--- +title: "Les ressources" +subtitle: "Liens utiles, outils et contacts du projet" +--- + +> 🚧 **Page en construction (squelette).** Compléter les liens ci-dessous. + +## Code et documentation + +- **Dépôt GitHub** : +- **Documentation des modules** : voir les `README.md` / `CLAUDE.md` de chaque module. + + + +## Outils du pipeline + +| Outil | Usage | Lien | +|---|---|---| +| Argo Workflows | Orchestration du pipeline | *TODO* | +| MLflow | Suivi des expériences / métriques | *TODO* | +| Qdrant | Base vectorielle (RAG) | *TODO* | +| Langfuse | Traçage des prompts LLM | *TODO* | +| LLMLab | Endpoints embeddings + génération | *TODO* | + +## Nomenclature COICOP (références externes) + +*TODO* — liens vers la nomenclature officielle COICOP et la documentation BdF. + +## Contacts + +*TODO* — équipe projet, référents. From 45d16ed2d9dbaf19b38d7b24d7313f7c0c719db0 Mon Sep 17 00:00:00 2001 From: jpramil Date: Fri, 19 Jun 2026 11:43:35 +0000 Subject: [PATCH 2/4] up site --- docs/00-generalites.qmd | 38 +++++++++++++++++ docs/index.qmd | 90 ++++++++++++----------------------------- docs/ressources.qmd | 18 +-------- 3 files changed, 66 insertions(+), 80 deletions(-) diff --git a/docs/00-generalites.qmd b/docs/00-generalites.qmd index 1754de2..f873829 100644 --- a/docs/00-generalites.qmd +++ b/docs/00-generalites.qmd @@ -36,6 +36,18 @@ Quatre classifieurs tournent en parallèle, puis sont conciliés par `decide-coi ## Modes prod / éval + + + *TODO* — `input_file` non vide = **production** (code un fichier externe) ; vide = **évaluation** (code le split test des annotations). Décrire le pilotage de bout en bout. @@ -43,3 +55,29 @@ Quatre classifieurs tournent en parallèle, puis sont conciliés par `decide-coi *TODO* — sampling centralisé à `codif-regex` (`sample-observations` en prod, `sample-annotations` en éval) hérité par tous les classifieurs. + + + diff --git a/docs/index.qmd b/docs/index.qmd index 484273f..1eed43e 100644 --- a/docs/index.qmd +++ b/docs/index.qmd @@ -1,18 +1,16 @@ --- title: "Codification COICOP des produits BDF" -subtitle: "Documentation pas à pas du pipeline de codification automatique" +subtitle: "Documentation de la démarche et du pipeline de codification automatique" --- ## À quoi sert ce pipeline ? -L'enquête **Budget de Famille (BDF)** collecte les tickets de caisse des ménages. Chaque -ligne de ticket décrit un produit acheté par un libellé court et bruité -(`"Bagu. Tradition U Blé Bretagne"`, `"Nescafé DLC Gust ESP 30 cap 165g"`, `"Illisible"`…). +L'enquête **Budget de Famille (BDF)** vise à comprendre la structure et l'évolution de la consommation des ménages et collecte pour cela les dépenses réalisées par les ménages enquêtés. Ces dépenses remontent sous forme de libellés textuels relativement courts, souvent bruités et sémantiquement différents selon leur source (descripitions manuelles, lignes de tickets de caisse, etc.). Pour exploiter ces données, chaque libellé doit être rattaché à un poste de la nomenclature **COICOP** (*Classification of Individual Consumption According to Purpose*). Le faire à la main est coûteux : le pipeline `codif-coicop-bdf` **automatise cette codification** en -combinant plusieurs approches (règles, similarité de chaînes, classifieur neuronal, RAG) puis +combinant plusieurs approches (règles, similarité de chaînes, machine learning supervisé, RAG) puis en les arbitrant via un LLM. @@ -39,6 +37,17 @@ produit consommable : | `98.5` | Remises / réductions | | `99.x` | Divers non codables | + +## Davantage de contexte et échéances + +La codification porte sur le millésime BDF 2026 (dont les questionnaires remontent progressivement au cours de l'année 2026). + +Les résultats consolidés doivent être transmis à Eurostat en 2028. + +Les travaux de mise mise en place d'un pipeline de codification automatique ont débuté à l'autonne 2025, impliquant la MoA de l'enquête, la division IPC et le SSPlab. + +À noter que la prochaine enquête BDF aura lieu en 2030 et il convient ainsi de rendre le processus de codification pérenne et reproductible pour pouvoir l'utiliser lors de ce prochain millésime. + ## Le pipeline étape par étape Le pipeline est un **DAG Argo** (`argo/codif-pipeline.yaml`). Chaque étape lit/écrit sur S3 @@ -48,62 +57,29 @@ dans un dossier propre au run : s3://projet-budget-famille/data/workflow_runs/{run_date}/{run_id}/<étape>/ ``` -``` - ┌─→ create-vector-db ──────────────┐ (skippable) - preprocessing ──┐ ┌──→ prune ────┤ ├──→ run-rag(-annotations) ─┐ - (input_file) └──→┤ └─→ create-vector-db-annotations ──┘ │ - └──→ codif-regex ─┬─→ codif-lcs ─────────────────────────────────────────────┼─→ decide-coicop ─→ final-output ─→ report - (→ prune) └─→ run-ttc ──────────────────────────────────────────────┘ -``` | # | Étape | Rôle | Page | |---|---|---|---| | 1 | `preprocessing` | Construit toujours les annotations (complet + train/test) ; en prédiction, prépare aussi les observations à coder | [→](01-preprocessing.qmd) | | 2 | `codif-regex` | Code les libellés évidents par règles regex | [→](02-codif-regex.qmd) | | 3 | `codif-lcs` | Code par similarité de chaîne (LCS) sur un pool de référence | [→](03-codif-lcs.qmd) | -| 4 | `prune` / `create-vector-db` / `run-rag` | Codification sémantique par RAG (pruning unifié en amont) | [→](04-coicop-rag.qmd) | -| 5 | `run-ttc` | Classifieur neuronal basique (caisses + fine-tuning BDF 2024) | [→](05-run-ttc.qmd) | -| 6 | `decide-coicop` | Arbitrage final LLM-as-judge entre LCS / RAG / TTC | [→](06-decide-coicop.qmd) | -| 7 | `final-output` | Assemble le résultat utilisateur (regex + décision LLM) | [→](07-final-output.qmd) | -| 8 | `report` | Rapport Quarto (qualité ou suivi de prédiction) | [→](08-report.qmd) | +| 4 | `prune` / `create-vector-db` / `run-rag` | Codification sémantique par RAG sur les notices COICOP (pruning unifié en amont) | [→](04-coicop-rag.qmd) | +| 5 | `create-vector-db-annotations` / `run-rag-annotations` | Codification par RAG sur exemples déjà codifiés (few-shot) | [→](09-rag-annotation.qmd) | +| 6 | `run-ttc` | Classifieur neuronal basique (caisses + fine-tuning BDF 2024) | [→](05-run-ttc.qmd) | +| 7 | `decide-coicop` | Arbitrage final LLM-as-judge entre LCS / RAG notices / RAG annotations / TTC | [→](06-decide-coicop.qmd) | +| 8 | `final-output` | Assemble le résultat utilisateur (regex + décision LLM) | [→](07-final-output.qmd) | +| 9 | `report` | Rapport Quarto (qualité ou suivi de prédiction) | [→](08-report.qmd) | -## Le fichier d'entrée et son mappage -L'exemple utilisé dans toute cette documentation est -`BDF_data_tickets_appli_20260320_a_codif.csv` (séparateur `;`, valeurs entre guillemets) : -| `ID_TICK` | `NAT_DEP` (produit) | `MONT_DEP` | `MAG_DEP` (enseigne) | `COM_DEP` | -|---|---|---|---|---| -| `10203_91_01` | Gasoil | 30.25 | Station Netto | Saint-Vite | -| `10203_91_06` | Ticket CB | 40.24 | Station Netto | Saint-Vite | -| `11622_91_08` | Bagu. Tradition U Blé Bretagne | 1.00 | U | Liffré | -| `10521_91_03` | Moyen frite Sce Pomme Frite | 4.10 | Mc Donald's | Civrieux-d'Azergues | -| `11165_91_01` | Illisible | 0.00 | — | — | +## Lancer le pipeline -Le pipeline n'impose pas de noms de colonnes : l'étape `preprocessing` reçoit un -**mappage configurable** (paramètres Argo). Valeurs par défaut pour ce fichier : +Un script d'aide est présent dans le repo : **codif-coicop-bdf/argo/argo_helper.md**. + +Il donne les commandes d'installation et de lancement. Par ailleurs, le script de paramétrages du workflow se trouve ici **codif-coicop-bdf/argo/params.yaml** (il permet notamment de choisir sur quel fichier s'applique la codification, choisir entre une approche de production courante ou d'évaluation, coder sur un échantillon de produits, etc.). -| Paramètre Argo | Valeur | Colonne pipeline produite | -|---|---|---| -| `input_file` | `s3://…/workflow_inputs/BDF_data_tickets_appli_20260320_a_codif.csv` | — | -| `text_column` | `NAT_DEP` | `raw_product` (libellé à coder) | -| `shop_column` | `MAG_DEP` | `shop` (enseigne) | -| `budget_column` | `MONT_DEP` | `budget` (montant €) | -| `annee_column` | *(vide)* | `annee` | -| `source_column` | *(vide)* | `source` | - -::: {.callout-important} -## Mode prédiction -Ce fichier contient des produits **à coder**, sans vérité terrain. Le pipeline tourne donc en -**mode prédiction** : il n'y a pas de colonne `code` de référence. Les observations préparées -sont écrites dans `observations.parquet` (l'étape `preprocessing` produit par ailleurs toujours -les annotations train/test). Cela change la dernière -étape (`report`), qui bascule sur `prediction_report.qmd` (suivi des prédictions) au lieu du -rapport d'évaluation d'exactitude. Le livrable final est le fichier produit par -[`final-output`](07-final-output.qmd). -::: -## Fil rouge + -## Lancer le pipeline - -```bash -# Pipeline complet (défaut : skip-vector-db=true, skip-report=false) -argo submit argo/codif-pipeline.yaml -# Sur un autre fichier d'entrée et un autre mappage de colonnes -argo submit argo/codif-pipeline.yaml \ - -p input_file=s3://projet-budget-famille/data/workflow_inputs/mon_fichier.csv \ - -p text_column=NAT_DEP -p shop_column=MAG_DEP -p budget_column=MONT_DEP - -# Limiter à N observations à coder pour un test (sampling centralisé à codif-regex, -# hérité par tous les classifieurs). En prod, -p sample-observations=100. -argo submit argo/codif-pipeline.yaml -p sample-annotations=100 -``` diff --git a/docs/ressources.qmd b/docs/ressources.qmd index 7228bd9..d635490 100644 --- a/docs/ressources.qmd +++ b/docs/ressources.qmd @@ -3,29 +3,15 @@ title: "Les ressources" subtitle: "Liens utiles, outils et contacts du projet" --- -> 🚧 **Page en construction (squelette).** Compléter les liens ci-dessous. ## Code et documentation - **Dépôt GitHub** : -- **Documentation des modules** : voir les `README.md` / `CLAUDE.md` de chaque module. - - -## Outils du pipeline - -| Outil | Usage | Lien | -|---|---|---| -| Argo Workflows | Orchestration du pipeline | *TODO* | -| MLflow | Suivi des expériences / métriques | *TODO* | -| Qdrant | Base vectorielle (RAG) | *TODO* | -| Langfuse | Traçage des prompts LLM | *TODO* | -| LLMLab | Endpoints embeddings + génération | *TODO* | +- **Présentation du projet à ISI 2026** : ## Nomenclature COICOP (références externes) -*TODO* — liens vers la nomenclature officielle COICOP et la documentation BdF. +https://www.insee.fr/fr/metadonnees/coicop2018/division/01?champRecherche=true -## Contacts -*TODO* — équipe projet, référents. From 5be98baf89564524d9a0aa5cc766f98425c480f9 Mon Sep 17 00:00:00 2001 From: jpramil Date: Fri, 19 Jun 2026 15:58:45 +0000 Subject: [PATCH 3/4] up website --- docs/00-generalites.qmd | 2 ++ docs/donnees.qmd | 58 +++++++++++++++++++---------------------- docs/ressources.qmd | 3 +++ 3 files changed, 32 insertions(+), 31 deletions(-) diff --git a/docs/00-generalites.qmd b/docs/00-generalites.qmd index f873829..8e5ffd7 100644 --- a/docs/00-generalites.qmd +++ b/docs/00-generalites.qmd @@ -14,6 +14,8 @@ arbitrant via un LLM (*LLM-as-judge*). Il est orchestré par **Argo Workflows** ## Le DAG du pipeline +Schéma Excalidraw du [pipeline](https://link.excalidraw.com/l/9vLymO4fSxN/3JjfZov3UgX). + ```text ┌──→ create-vector-db ──────────────┐ (skippable) preprocessing ──┐ ┌──→ prune ────┤ ├──→ run-rag(-annotations) ─┐ diff --git a/docs/donnees.qmd b/docs/donnees.qmd index feb7062..e9746ed 100644 --- a/docs/donnees.qmd +++ b/docs/donnees.qmd @@ -3,38 +3,34 @@ title: "Les données" subtitle: "Sources mobilisées par le pipeline de codification COICOP" --- -> 🚧 **Page en construction (squelette).** Compléter chaque section ci-dessous. +> 🚧 **Page en construction. -## Vue d'ensemble - -Le pipeline mobilise plusieurs sources de données, en entrée comme en référence. - -| Source | Rôle | Format | Localisation | -|---|---|---|---| -| Annotations BdF 2024 | Vérité terrain / KB | parquet | S3 | -| Annotations historiques BdF 2017 | KB (historique) | csv | S3 | -| Suggester (liste produits) | Exemples additionnels | csv | S3 | -| Nomenclature COICOP | Référentiel des codes | csv | S3 | -| Observations à coder | Entrée production | parquet/csv | fourni par l'utilisateur | - - - -## Les annotations -*TODO* — décrire les sources d'annotations (`receipts_from_app`, `manual_from_app`, -`manual_from_book`, `bdf_2017`), la colonne `source`, et les sources exclues du pipeline -(ex. `copain`). - -## Le suggester - -*TODO* — décrire la liste produits (`liste_produits_fr_copain.csv`) et son usage comme -exemples additionnels dans la KB des RAG. - -## La nomenclature COICOP - -*TODO* — structure des codes (niveaux 1 à 5), pruning niveau 4, table de mapping. +## Vue d'ensemble -## Conventions de stockage (S3) +Le pipeline mobilise plusieurs fichiers **en entrée** (sources externes, non produites par +le pipeline lui-même). Tous résident dans le bucket S3 `projet-budget-famille`. + +A noter que ces fichiers ne sont pas utilisées de la même manière selon qu'on utilise le pipeline en mode production (il faut coder un fichier) ou en mode évaluation (on utilise des observations déjà labellisées pour évaluer le processus). + +| Fichier | Définition | Format | Path S3 | Lu par | Commentaire +|---|--------|---|---|---|--------| +| `annotations_test_2024.csv` | Annotations manuelles BdF 2024 (vérité terrain / KB ; sources `receipts_from_app`, `manual_from_app`, `manual_from_book`) | CSV `;` | `s3://projet-budget-famille/data/codification-manuelle-anterieure/annotations_test_2024.csv` | `preprocessing` | Base produite à partir de trois fichiers fournis par l'équipe BDF. Voir ce repo : https://git.lab.sspcloud.fr/ssplab/experimentation-bdf/construction-dataset| +| `annotations_BDF_2017.csv` | Annotations historiques de l'enquête BdF 2017 (source `bdf_2017`, KB uniquement) | CSV `;` | `s3://projet-budget-famille/data/codification-manuelle-anterieure/annotations_BDF_2017.csv` | `preprocessing` | Utilise l'ancien millésime de la Coicop (difficilement exploitable) | +| `liste_produits_fr_copain.csv` | Liste de produits codifiés issue du *suggester* de l'application de l'enquête — exemples additionnels de la KB des RAG | CSV | `s3://projet-budget-famille/data/input-annotation/liste_produits_fr_copain.csv` | `preprocessing`, `prune` | | +| `liste_magasins.csv` | Table de correspondance enseigne → type de magasin | CSV | `s3://projet-budget-famille/data/input-annotation/liste_magasins.csv` | `preprocessing` | | +| `produit_non_annotable.csv` | Liste des libellés non codables (retirés en amont) | CSV | `s3://projet-budget-famille/data/codification-manuelle-anterieure/ produit_non_annotable.csv` | `preprocessing` | | +| `coicop-2018_envoi_rmes_20251022.csv` | Nomenclature COICOP 2018 brute (référentiel des codes + libellés) | CSV | `s3://projet-budget-famille/data/coicop-2018_envoi_rmes_20251022.csv` | `prune`, `coicop-rag` | | +| *fichier d'observations* | Entrée de **production** : libellés à coder, sans vérité terrain (paramètre Argo `input_file`) | CSV / parquet | `s3://projet-budget-famille/data/workflow_inputs/…` (ex. `BDF_data_tickets_appli_20260320_a_codif.csv`) | `preprocessing` (mode prod) | | + +::: {.callout-note} +## Deux entrées particulières +- **Modèle TTC** : le classifieur neuronal n'est pas un fichier S3 mais un artefact MLflow, + référencé par le paramètre Argo `ttc-model-uri` + (ex. `mlflow-artifacts:/10/…/artifacts/model`), lu par `run-ttc`. +- **Les données de caisse (ddc)** : ce sont des données récoltées dans le cadre de la production de l'IPC. Il s'agit de dépenses issues de tickets de caisse avec le code Coicop correspondant (labellisation faite via un référentiel fourni par un prestataire). Ces données sont utilisées dans le cadre de l'entraînement du modèle TTC (en dehors de ce pipeline). +- **Source `copain`** (`s3://projet-budget-famille/data/output-annotation/`) : historiquement + une entrée du `preprocessing`, elle est **exclue du pipeline** — son chargement est désactivé. +- Les listes de règles pour la codification déterministe par Regex est contenue dans le code, et non dans un fichier sur S3 (https://github.com/InseeFrLab/codif-coicop-bdf/blob/main/regex-codif/config/rules.yaml). +::: -*TODO* — convention de chemins `s3://////` et échanges -inter-modules en parquet. diff --git a/docs/ressources.qmd b/docs/ressources.qmd index d635490..cf06e22 100644 --- a/docs/ressources.qmd +++ b/docs/ressources.qmd @@ -14,4 +14,7 @@ subtitle: "Liens utiles, outils et contacts du projet" https://www.insee.fr/fr/metadonnees/coicop2018/division/01?champRecherche=true +## Application Copain (solution OJS pour faire de l'annotation) +- App : https://ssplab.pages.lab.sspcloud.fr/experimentation-bdf/copain/ +- Code : https://git.lab.sspcloud.fr/ssplab/experimentation-bdf/copain/pages#overview From 59b9b378e57aed8e522fa4ba56392e326ab25792 Mon Sep 17 00:00:00 2001 From: jpramil Date: Tue, 23 Jun 2026 09:28:25 +0000 Subject: [PATCH 4/4] up doc decide-coicop --- docs/06-decide-coicop.qmd | 107 +++++++++++++++++++++++++++++++------- 1 file changed, 87 insertions(+), 20 deletions(-) diff --git a/docs/06-decide-coicop.qmd b/docs/06-decide-coicop.qmd index 4e80093..cabf235 100644 --- a/docs/06-decide-coicop.qmd +++ b/docs/06-decide-coicop.qmd @@ -5,21 +5,32 @@ title: "6. Decide — arbitrage LLM-as-judge" ## Rôle de l'étape `decide-coicop` est l'**arbitre final**. Pour chaque produit non codé par regex, elle réunit -les trois prédictions candidates — [LCS](03-codif-lcs.qmd), [RAG](04-coicop-rag.qmd), -[TTC](05-run-ttc.qmd) — et choisit le code COICOP final, soit automatiquement (consensus), -soit en interrogeant un LLM qui pèse les indices. +les prédictions candidates — [LCS](03-codif-lcs.qmd), [RAG](04-coicop-rag.qmd), +[TTC](05-run-ttc.qmd) et, optionnellement, [RAG-Annotations](09-rag-annotation.qmd) — puis +choisit le code COICOP final, soit automatiquement (consensus), soit en interrogeant un LLM +qui pèse les indices. - Code : [`coicop-bdf-classifier/src/decide_coicop.py`](../coicop-bdf-classifier/src/decide_coicop.py) - Commande : sous-commande `decide-coicop` de `main.py` ## Entrées -| Source | Chemin S3 | -|---|---| -| LCS | `…/{run}/codif-lcs/raw_test_LCS.parquet` | -| RAG | `…/{run}/run-rag/predictions.parquet` | -| TTC | `…/{run}/run-ttc/predictions.parquet` | -| Nomenclature | fichier des codes COICOP valides | +| Source | Chemin S3 | Argument | +|---|---|---| +| LCS | `…/{run}/codif-lcs/raw_test_LCS.parquet` | `--lcs-file` | +| RAG | `…/{run}/run-rag/predictions.parquet` | `--rag-file` | +| TTC | `…/{run}/run-ttc/predictions.parquet` | `--ttc-file` | +| RAG-Annotations *(optionnel)* | `…/{run}/run-rag-annotations/predictions.parquet` | `--rag-annotations-file` | +| Nomenclature | fichier CSV des codes COICOP valides (`Code`, `Libelle`) | `--nomenclature` | + +Les prédictions sont fusionnées sur la colonne `id` (`load_all_observations()`). Le bloc +RAG-Annotations (colonnes `ragann_code`, `ragann_confidence`, `ragann_codable`) n'est joint que +si `--rag-annotations-file` est fourni : le 4ᵉ classifieur est donc **optionnel au niveau du +module/CLI** (`default=None`), et tout le traitement aval s'adapte au nombre de modèles +réellement disponibles. En revanche, **le pipeline Argo le passe systématiquement** (la step +`decide-coicop` dépend de `run-rag-annotations` et fournit toujours `--rag-annotations-file`) : +les 4 classifieurs sont donc la norme en production, l'optionalité servant surtout aux lancements +manuels ou au débogage. ## Traitement @@ -29,38 +40,48 @@ Pour économiser temps et tokens sur les cas faciles, `try_consensus_decision()` **sans appeler le LLM** quand : - la confiance TTC top-1 est suffisante (`ttc_conf_1 ≥ 0,90`), **et** -- tous les codes disponibles (`lcs_code`, `rag_code`) sont **égaux** à `ttc_code_1`. +- tous les autres codes **disponibles** (`lcs_code`, `rag_code`, `ragann_code`) sont **égaux** + à `ttc_code_1`. ```python if float(ttc_conf) < threshold: # threshold = 0.90 return None ... +# codes non nuls parmi lcs_code, rag_code, ragann_code if not all(c == ttc_code for c in other_codes): return None # → décision par consensus, confiance = 5, llm_model = "consensus" ``` -La décision porte alors la mention *« Consensus automatique : les N modèles s'accordent… »*. +Seuls les codes réellement présents (non `NA`) participent au vote : si RAG-Annotations n'a pas +été fourni, le consensus se joue sur les modèles restants. La décision porte alors la mention +*« Consensus automatique : les N modèles s'accordent… »* (où `N` est le nombre de codes +concordants). ### 2. Arbitrage par LLM Sinon, le LLM est sollicité : -- **Filtrage de la nomenclature** (`filter_nomenclature()`) : seules les sections COICOP - pertinentes (préfixes L2 des prédictions) sont envoyées dans le prompt — réduction ×4 à ×10 - du nombre de tokens. +- **Filtrage de la nomenclature** (`filter_nomenclature()`) : au lieu d'envoyer les ~700 codes + COICOP, on ne soumet au LLM que les codes « autour » des prédictions des modèles (voir le + détail [ci-dessous](#comment-est-construite-la-liste-de-codes-soumise-au-llm)) — réduction + typique de ~700 à ~50-150 lignes (gain ×4 à ×10 sur les tokens). L'option + `--full-nomenclature` désactive ce filtrage et envoie la nomenclature complète. - **Construction du prompt** (`build_prompt()`) : un prompt structuré présentant le produit, - les trois prédictions, la nomenclature filtrée et les consignes : + les prédictions des modèles, la nomenclature filtrée et les consignes : ```text ═══ PRODUIT ═══ Libellé brut : Max garden flowers balle surprise Libellé normalisé : max garden flowers balle surprise Enseigne : Action + Type de magasin : … Montant (€) : 3.99 + Code de référence : … ═══ PRÉDICTIONS DES MODÈLES ═══ - [LCS] Code prédit : … (proportion …, distance …) - [RAG] Code prédit : … (confiance …) + [LCS] Code prédit : … (sous-chaîne …, proportion …, distance …) + [RAG] Code prédit : … (confiance …, codable …) + [RAG-Annotations] Code prédit : … (confiance …, codable …) ← seulement si fourni [TTC] Top 1 : … (confiance …) / Top 2 : … / Top 3 : … ═══ NOMENCLATURE COICOP (codes valides) ═══ … @@ -69,6 +90,46 @@ Sinon, le LLM est sollicité : 4. Attribue un score de confiance de 1 (très faible) à 5 (très élevé). ``` + Le bloc `[RAG-Annotations]` n'apparaît dans le prompt que si une prédiction `ragann_code` + non nulle existe pour l'observation. + +#### Comment est construite la liste de codes soumise au LLM ? + +`filter_nomenclature()` part de **tous** les codes prédits pour l'observation (`lcs_code`, +`rag_code`, `ragann_code`, `ttc_code_1`, `ttc_code_2`, `ttc_code_3`, après suppression des +`NA`) et en extrait deux ensembles de préfixes : + +- **L1** = la *section*, soit le segment avant le premier point — `"01.1.2.3"` → `"01"` ; +- **L2** = la *sous-section*, soit les deux premiers segments — `"01.1.2.3"` → `"01.1"`. + +Puis, pour **chaque code de la nomenclature complète**, la décision de le garder dépend de +*son propre* niveau de profondeur : + +| Code de la nomenclature | Exemple | Conservé si… | +|---|---|---| +| 1 segment — en-tête de section | `01` | sa section appartient à **L1** | +| 2 segments — sous-section | `01.1`, `01.2` | sa section appartient à **L1** (filtré sur L1, pas sur L2) | +| 3 segments et plus | `01.1.2`, `01.1.2.3` | il **commence par** l'un des préfixes **L2** | + +L'asymétrie est volontaire : + +- **Aux niveaux 1 et 2**, le filtre est *large* : on garde l'en-tête de section **et toutes ses + sous-sections** (`01.1`, `01.2`, `01.3`…), même celles dont aucun code fin ne sera listé. Cela + donne au LLM une **vue d'ensemble de la section** pour réorienter si les modèles se sont + trompés de sous-section. +- **À partir du niveau 3**, le filtre est *étroit* : on ne garde que la descendance des L2 + réellement prédits, pour ne pas noyer le prompt sous les feuilles non pertinentes. + +**Exemple** — si les modèles prédisent `09.3.1` et `05.1.1`, alors `L1 = {09, 05}` et +`L2 = {09.3, 05.1}`. La liste soumise contient : les en-têtes `05` et `09` ; **toutes** les +sous-sections de `05` et `09` (`05.1`, `05.2`, …, `09.1`, …, `09.3`, …) ; mais, au niveau fin, +**uniquement** les codes sous `05.1.*` et `09.3.*`. Le LLM reste donc libre de choisir un code +précis dans ces deux sous-sections, de basculer vers une autre sous-section des sections 05/09, +ou de remonter à un niveau plus agrégé. + +Cas limites : si **aucune** prédiction n'est disponible (tous les codes `NA`), ou si +`full=True` (`--full-nomenclature`), la nomenclature **complète** est renvoyée. + - **Sortie structurée** : le LLM répond en JSON validé par le modèle Pydantic `DecisionCoicop` : @@ -104,7 +165,7 @@ Le message *user* est produit par `build_prompt()` (valeurs des modèles illustr ```text Tu es un expert en comptabilité nationale et en statistiques de consommation. Ta mission est de déterminer le code COICOP le plus approprié pour un produit acheté, -en t'appuyant sur le contexte d'achat et les prédictions de trois modèles automatiques. +en t'appuyant sur le contexte d'achat et les prédictions de plusieurs modèles automatiques. ═══════════════════════════════════════ PRODUIT @@ -130,6 +191,11 @@ PRÉDICTIONS DES MODÈLES Confiance : 60% Codable : True +[RAG-Annotations — RAG sur exemples déjà codifiés] ← présent seulement si --rag-annotations-file fourni + Code prédit : 09.3.1 + Confiance : 72% + Codable : True + [TTC — Classificateur par apprentissage profond] Top 1 : 09.3.1 (confiance 41%) Top 2 : 05.1.1 (confiance 22%) @@ -149,7 +215,7 @@ INSTRUCTIONS ═══════════════════════════════════════ 1. Choisis UN code COICOP parmi ceux listés dans la nomenclature ci-dessus. 2. Le code doit correspondre au niveau le plus précis qui te semble justifié. -3. Explique en 1 phrase maximum comment tu as pesé les prédictions des trois modèles +3. Explique en 1 phrase maximum comment tu as pesé les prédictions des différents modèles et tout autre indice (enseigne, type de magasin, montant). 4. Attribue un score de confiance de 1 (très faible) à 5 (très élevé). ``` @@ -185,7 +251,7 @@ On peut reproduire ce prompt pour n'importe quelle ligne avec le mode mono-obser | Produit | Consensus ? | Décision | |---|---|---| -| `Bagu. Tradition U Blé Bretagne` | si LCS = RAG = TTC et `ttc_conf_1 ≥ 0,90` → **oui** | code pain repris sans LLM, `confiance = 5`, `llm_model = "consensus"` | +| `Bagu. Tradition U Blé Bretagne` | si tous les codes disponibles = `ttc_code_1` et `ttc_conf_1 ≥ 0,90` → **oui** | code pain repris sans LLM, `confiance = 5`, `llm_model = "consensus"` | | `Max garden flowers balle surprise` | les modèles divergent (jouet vs jardin) → **non** | le LLM arbitre à partir de l'enseigne (*Action*), du prix et des candidats | ## Que peut renvoyer la décision ? @@ -208,6 +274,7 @@ possibles : | Connexion impossible | `APIConnectionError: …` | | Réponse coupée (limite de tokens) | `Response truncated before JSON was complete…` | | JSON illisible | `JSON parsing failed: …` | + | Toute autre erreur (auth, etc.) | `: …` (catch-all) | ::: {.callout-warning} ## Le code n'est pas validé contre la nomenclature