Chunking (pensé “DataModel”)
Passer d’un découpage “bête” à un découpage structure-aware centré modèles, migrations, contraintes, conventions. Taille cible 400–900 tokens avec overlap 80–150.
Par modèle Par migration Par contrainte/index Par convention projet Contexte adjoint Taille & overlap Métadonnées QA & KPIs
Pourquoi “DataModel-first” ?
🎯 Objectif
- Donner au LLM des unités sémantiques stables : modèles, migrations, contraintes, conventions.
- Maximiser la précision des citations et la pertinence des passages.
- Faciliter l’audit : chaque chunk porte un rôle clair.
🧭 Plan de découpage
[Model] → 1 chunk / modèle
[Migration]→ 1 chunk / migration significative (groupée par feature)
[Constraint]→ 1 chunk / contrainte ou index (justifié)
[Convention]→ 1 chunk / règle projet (naming, on_delete, tz, IDs)Taille 400–900 tokens, overlap 80–150 entre sections voisines.
✅ Gains attendus
- Meilleur hit@k quand questions portent sur un modèle précis.
- Réponses auditées (citations par modèle/migration).
- Maintenance: un changement → un chunk.
Punchline : « On chunk par sens, pas par taille fixe. »
1) 1 chunk par modèle — header + fields + Meta + docstring
🧱 Structure du chunk
### Model: billing.Invoice
Doc: "Factures clients" (fr)
Fields:
- ref: Char(32), db_index ✓
- customer: FK → crm.Customer (CASCADE, related_name="invoices")
- total: Decimal(10,2), default=0
Meta:
- db_table: billing_invoice
- ordering: -created_at
- unique_together: (ref, customer)
Citations: ["models.py#Invoice", "admin.py#InvoiceAdmin"]- Inclure docstring et extraits admin/DRF pertinents.
- Ordonner : contexte → champs → meta → liens.
- Limiter à ~700–900 tokens si modèle riche (sinon 400–600).
🛠️ Heuristiques
- Regrouper champs par catégories (identité, relations, montants, dates).
- Indiquer
on_delete,validators,choicessi présents. - Ajouter un résumé 1–2 lignes (utilité métier).
Anti-patterns : coller du code brut; oublier Meta; mélange de plusieurs modèles.
2) 1 chunk par migration significative — avec “avant/après”
🧭 Contenu attendu
### Migration: 0007_add_idx_ref (Invoice)
Avant:
- pas d'index dédié sur ref
Après:
- index (ref) → idx_invoice_ref
Justification:
- accélère recherche par référence
Groupage "feature":
- "Perf recherche facture"- Lister les opérations (AddField, AlterField, RunSQL…).
- Indiquer l’impact (perf, intégrité, conformité).
- Mettre les citations vers le fichier de migration exact.
🧪 Détection “significative”
- Touches : ajout/suppression de champ, index/unique, FK, contrainte, RunSQL.
- Regrouper par feature (commit message / dossier).
- Limiter chaque chunk à 400–700 tokens; fusionner petites migrations.
Anti-patterns : un chunk par fichier même anodin; pas de “avant/après”.
3) 1 chunk par contrainte / index — avec justification
📌 Exemple
### Contrainte: unique(ref, customer) (Invoice)
But: éviter doublon de références client
Impact: intégrité métier; index unique → perf ↑
Source: Meta.unique_together + migration 0005_unique_ref_customer
Citations: ["models.py#Invoice.Meta","0005_unique_ref_customer.py"]- Présenter but, impact, tables/champs concernés.
- Pour index: type (btree/hash), colonnes, ordre, nom.
- Lier aux requêtes admin/DRF si justif perf.
🧪 Règles
- Un chunk par contrainte/index important.
- Si nombreux index → chunk par groupe logique (ex: “recherche facture”).
- Taille 300–600 tokens.
Anti-patterns : lister tous les index sans contexte; noms d’index non normalisés.
4) 1 chunk par convention projet — naming, on_delete, TZ, IDs
📋 Contenu
### Convention: Relations & on_delete
- Toujours CASCADE pour entités “enfants” (LineItem)
- PROTECT pour référentiels (Currency, Country)
- SET_NULL autorisé si nullable expliqué
Exceptions documentées → lien ADR-012- Naming tables/index (préfixes, snake_case).
- IDs (UUID v4 ? séquences ?), TZ (UTC only ?).
- Références vers ADR/README d’architecture.
🔎 Pourquoi chunker les conventions
- Le LLM explique/justifie des décisions récurrentes.
- Facilite l’alignement équipe et la revue.
- Permet un routing “guidelines” séparé des API.
Anti-patterns : conventions mélangées aux modèles; pas de lien vers ADR.
Contexte adjoint — imports, abstract base, mixins
🧷 Ce qu’on ajoute au chunk
- Imports utiles (abstract/mixins, validators, signals).
- Parents (AbstractModel, TimeStampedMixin).
- Admin/DRF pertinents (list_filter, serializers).
Context:
- from core.models import TimeStampedModel
- class Invoice(TimeStampedModel, models.Model): ...
- Admin: search_fields=("ref","customer__name")🧪 Heuristiques
- Limiter à 120–200 tokens de “contexte adjoint”.
- Éviter le bruit (imports inutiles, utilitaires génériques).
- Placer ce contexte après le corps principal.
Taille & overlap — fenêtres intelligentes
📐 Règles
- 400–900 tokens par chunk (selon densité).
- Overlap 80–150 entre sections voisines (titre parent + 1-2 phrases).
- Jamais couper une liste/tableau au milieu → remonter au titre.
🧮 Pseudo-code
def windowize(blocks, min_t=400, max_t=900, ov_t=120):
cur, out = [], []
for b in blocks:
if tokens(cur)+tokens(b) > max_t:
out.append(join(cur))
cur = tail(cur, ov_t) # overlap par tokens
cur.append(b)
if cur: out.append(join(cur))
return outLe “block” = section logique (titre, paragraphe, table) issue du parseur structurel.
Schéma & métadonnées d’un chunk
{
"chunk_id":"mod:billing.Invoice#v12",
"type":"model|migration|constraint|convention",
"scope":{"app":"billing","model":"Invoice"},
"title":"Model: billing.Invoice",
"body_md":"...markdown...",
"tokens_est":720, "overlap_prev":120,
"citations":[{"href":"models.py#L42-L120","label":"models.py"}],
"context":{"imports":["core.TimeStampedModel"],"admin":["search_fields=..."]},
"meta":{"lang":"fr","conf":"internal","pii":"low","last_updated":"ts","index_ver":"v3"}
}🔎 Détails importants
- chunk_id stable (inclut version du modèle/migration).
- type clair pour le routing (context builder).
- citations cliquables (fichier/ligne/page).
- meta.conf pour guardrails; pii pour redaction.
Anti-patterns : absence de citations; mélange de types; pas de version.
QA & KPIs — règles automatiques
✅ Règles QA
- 400 ≤ tokens_est ≤ 900 (warning sinon).
- citations ≥ 1; title non vide; type ∈ set.
- Meta obligatoires : lang, conf, last_updated.
- Pas de TODO/boilerplate; pas d’overlap > 200.
📏 KPIs chunking
- % chunks dans la plage (400–900).
- Couverture modèles/migrations/contraintes.
- Duplication (Jaccard/SimHash) < 0.15.
🧪 Tests d’entretien
- “Montrez un chunk ‘convention on_delete’ avec citations”.
- “Comment gérez-vous les migrations RunSQL ?”.
- “Comment choisissez-vous overlap 120 vs 80 ?”.
Punchline : « Un bon chunk est autonome, cité, et borné en tokens. »
Exemple complet — “Invoice”
CHUNK#M-INV
Title: Model: billing.Invoice
Body:
- ref: Char(32) db_index; customer: FK→crm.Customer CASCADE; total: Decimal(10,2)
- Meta: db_table=billing_invoice; ordering=-created_at; unique(ref,customer)
- Usage: "Référence facture" utilisée en Admin (search_fields) et API (InvoiceSer)
Citations: ["models.py#L42-L120","admin.py#L10-L35","serializers.py#L5-L28"]
Tokens≈780 OverlapPrev=120CHUNK#G-MIG-0007
Title: Migration 0007 — index ref
Avant: pas d'index dédié; Après: idx_invoice_ref(ref)
Justification: accélère recherche par ref; aligné avec Admin.search_fields
Citations: ["0007_add_idx_ref.py"]
Tokens≈420
CHUNK#C-UNIQ
Title: Contrainte unique(ref,customer)
Justif: éviter doublons; supporte "duplicate check" support
Citations: ["models.py#Meta","0005_unique_ref_customer.py"]
Tokens≈360Outils & pipeline — de l’AST aux chunks
🛠️ Étapes
1) Parse AST/Tree-sitter → models, fields, Meta, docstrings
2) Lire migrations séquentielles → opérations significatives
3) Introspect DB → index/contraintes normalisés
4) Construire chunks (model/migration/constraint/convention)
5) Joindre contexte adjoint (imports, mixins, Admin/DRF)
6) Windowize 400–900 tok + overlap 80–150
7) QA → indexer (vecteur + lexical)🔧 Pseudo-API
build_model_chunk(model_json) → Chunk
build_migration_chunks(mig_list) → [Chunk]
build_constraint_chunks(indexes, constraints) → [Chunk]
build_convention_chunks(conventions) → [Chunk]
attach_context(chunk, ctx) → Chunk
windowize(blocks, 400, 900, 120) → [Chunk]Tip : tagger chaque chunk avec type et scope pour le routing & l’évaluation.
