Qu'est-ce que la skeleton compression pour les lectures de fichiers ?
La skeleton compression est une façon de lire un fichier source qui transmet à l'agent la structure du fichier — ses imports, ses définitions de types, les signatures de classes et de fonctions, les symboles exportés — tout en laissant tomber les corps de fonctions dont l'agent n'a pas encore besoin. Vous obtenez la carte du fichier pour une fraction des tokens, et l'agent ne déplie le corps d'une fonction précise que lorsqu'il doit réellement l'éditer. Je construis des outils de réduction de tokens pour gagner ma vie, et de tous les leviers que j'ai mesurés, c'est celui que les gens sous-estiment le plus. L'intuition est fausse d'une manière instructive : on suppose qu'un modèle a besoin de « voir le code » pour comprendre un fichier. En réalité, il a surtout besoin d'en voir la forme. Un module de service de 600 lignes représente peut-être 8 000 tokens lu en entier ; son squelette — les signatures, les types, les exports, avec les corps élidés — fait souvent moins de 1 500. Et surtout, ce squelette de 1 500 tokens répond à la question que l'agent se pose généralement, à savoir « qu'est-ce qui vit dans ce fichier et comment l'appeler », pas « qu'y a-t-il à la ligne 312 ». C'est le pendant, au niveau du fichier, de l'argument plus large que je développe dans Comment réduire la consommation de tokens d'un agent de coding IA. Ici, je veux creuser un seul mécanisme, car c'est celui qu'il est le plus facile de rater et celui qui offre le meilleur rapport gain/effort.Pourquoi les lectures de fichiers entiers gaspillent-elles autant de tokens ?
Les lectures de fichiers entiers gaspillent des tokens parce que l'agent re-paie chaque ligne à chaque tour suivant, alors qu'il n'a jamais eu besoin que d'une infime portion du fichier. Les boucles d'agentic coding relisent tout l'historique de la conversation à chaque étape — donc un fichier de 8 000 tokens lu au tour 3 est encore facturé comme input au tour 20. Lisez une douzaine de fichiers de cette manière et vous avez discrètement constitué un context window qui coûte plus cher à transporter que le code ne coûte à écrire. Deux choses aggravent le gaspillage : La majeure partie d'un fichier est du corps, et la majorité des corps sont hors sujet. Quand un agent litsrc/billing/invoice.ts pour ajouter un champ à une seule fonction, les quinze autres fonctions de ce fichier sont du pur lest. Leurs implémentations n'éclairent pas le changement ; leurs signatures, en revanche, le peuvent, pour que l'agent sache ce qu'il peut appeler. La skeleton compression garde exactement cela — noms, paramètres, types de retour — et élide le reste.
Le modèle traite moins bien un contexte gonflé. Ce n'est pas qu'une question de coût. Les modèles raisonnent de façon plus fiable sur un contexte resserré et à fort signal, et se dégradent sur des fenêtres longues et bruitées — l'effet « lost in the middle » documenté par Liu et al. pour le long-contexte en retrieval (https://arxiv.org/abs/2307.03172). Rembourrer la fenêtre avec des corps de fonctions que l'agent ne référencera jamais ne fait pas que coûter de l'argent ; cela peut rendre la réponse moins bonne. Réduire au squelette élève le rapport signal/bruit. Voir compression de contexte pour le principe général.
Comment fonctionne concrètement la skeleton compression ?
Elle fonctionne en parsant le fichier en son arbre syntaxique et en n'émettant que les nœuds structurels, chaque corps de fonction ou de méthode étant remplacé par un court placeholder. En pratique, la sortie pour un fichier TypeScript ressemble au vrai fichier mais avec l'intérieur évidé :import { Stripe } from "stripe";
import { db } from "@/lib/db";
export interface InvoiceDraft {
customerId: string;
lineItems: LineItem[];
currency: "usd" | "eur";
}
export async function createDraft(input: InvoiceDraft): Promise<Invoice> { / … / }
export async function finalizeInvoice(id: string): Promise<Invoice> { / … / }
class InvoiceRepository {
async findById(id: string): Promise<Invoice | null> { / … / }
async save(invoice: Invoice): Promise<void> { / … / }
}
import { db } from "@/lib/db";
export interface InvoiceDraft {
customerId: string;
lineItems: LineItem[];
currency: "usd" | "eur";
}
export async function createDraft(input: InvoiceDraft): Promise<Invoice> { / … / }
export async function finalizeInvoice(id: string): Promise<Invoice> { / … / }
class InvoiceRepository {
async findById(id: string): Promise<Invoice | null> { / … / }
async save(invoice: Invoice): Promise<void> { / … / }
}
/* … */. Quand l'agent décide qu'il a besoin de la logique réelle de finalizeInvoice, il déplie cette seule fonction — et paie pour un corps au lieu du fichier entier.
Quelques détails comptent pour que ce soit sûr plutôt que destructeur :
- Les signatures sont sacrées. Noms de paramètres, types, types de retour, generics, décorateurs, modificateurs de visibilité — tout est conservé verbatim. Le squelette n'est utile que s'il constitue une interface fidèle.
- Les types et les constantes restent complets. Un
type, unenum, un objet de configconstexporté — ceux-là sont la structure. On ne les élide pas. - C'est conscient du langage, pas du regex. On parse l'arbre (tree-sitter ou le parser propre au langage), de sorte à distinguer correctement un corps de fonction d'un littéral de chaîne qui contiendrait
function. Une heuristique naïve ligne par ligne va massacrer du vrai code ; c'est pourquoi les lectures structure-first ont besoin d'un vrai parser. C'est la même famille d'analyse statique qui sous-tend la semantic code search.
Quand l'agent doit-il lire le corps complet à la place ?
L'agent doit lire le corps complet dès qu'il a besoin de modifier cette fonction précise, de raisonner sur sa logique interne, ou de déboguer un comportement qui y réside — et la skeleton compression doit rendre ce dépliage bon marché et ciblé, pas tout-ou-rien. Tout l'intérêt est une lecture en deux phases : le squelette d'abord pour s'orienter, puis un dépliage précis du ou des deux corps qui comptent réellement pour la tâche. Bons déclencheurs de dépliage :- « Pourquoi
finalizeInvoicefacture-t-elle deux fois lors d'un retry ? » → déplierfinalizeInvoice, laisser le reste squelettique. - « Ajoute un paramètre
metadataàcreateDraft» → dépliercreateDraftplus tout ce qui l'appelle. - Un test qui échoue pointe vers une assertion précise au sein d'une méthode → déplier cette méthode.
- « Lis le fichier pour comprendre le module. » Vous n'avez pas besoin des corps pour comprendre la surface d'un module ; vous avez besoin du squelette. Allez chercher les corps complets sur une intuition et vous voilà de retour à payer pour tout.
Combien cela économise-t-il réellement ?
Sur les fichiers à forte structure, l'économie est importante — fréquemment 70 à 90 % des tokens du fichier — et elle se compose, car cette économie est multipliée à chaque tour où le fichier reste en contexte. Le chiffre exact dépend entièrement du ratio corps/signature. Un fichier essentiellement composé de définitions de types et de fonctions courtes se compresse modestement, car sa structure est l'essentiel de son contenu. Un fichier avec une poignée de longs corps de fonctions touffus se compresse énormément, car ces corps sont précisément ce que le squelette laisse tomber. Mettons des chiffres approximatifs dessus avec la tarification actuelle. Supposons qu'un agent lise dix fichiers d'une moyenne de 6 000 tokens chacun au début d'une session et les transporte pendant quinze tours. Cela fait 60 000 input tokens refacturés quinze fois — 900 000 lectures de tokens. Sur Claude Sonnet 4.6 à $3 par million d'input tokens, cela représente environ $2,70 de pur transport de fichiers, avant même que l'agent n'écrive une ligne. Compressez ces lectures en squelettes d'une moyenne de 1 500 tokens et vous transportez 15 000 tokens par tour au lieu de 60 000 — soit environ $0,68 pour les mêmes quinze tours. Même compréhension, un quart de la facture. (Et si le préfixe est stable, le prompt caching fait tomber la portion transportée à ≈10 % d'une lecture fraîche, en plus de cela.) Je m'abstiens délibérément de citer un pourcentage phare unique, car quiconque vous donne un chiffre unique pour « combien la skeleton compression économise » vous vend le chiffre, pas la technique. Mesurez votre propre mix de fichiers. Le détail des coûts en tokens d'un agent de coding IA contient la tarification par modèle si vous voulez faire le calcul sur votre propre palier tarifaire.Comment l'appliquer dès aujourd'hui
- Arrêtez de demander à l'agent de « lire le fichier ». Demandez-lui d'abord la structure : « montre-moi les signatures et les types dans
invoice.ts», puis « maintenant dépliefinalizeInvoice». Deux lectures bon marché valent mieux qu'une lecture coûteuse. - Commencez par le symbole, pas par le fichier. « Trouve la fonction qui finalise les factures et montre sa signature » vous donne gratuitement une réponse de type squelette ; « lis
src/billing/invoice.ts» vous donne tout. - Dépliez chirurgicalement. Quand vous avez besoin d'un corps, nommez la fonction. Ne relisez pas le fichier pour l'obtenir.
- Gardez les types et les exports verbatim dans tout résumé manuel que vous écrivez pour l'agent — ce sont les parties porteuses.
- Automatisez-le pour ne rien avoir à retenir de tout ce qui précède au milieu d'une tâche.
Ce qui tourne mal (anti-patterns)
Les « skeletonizers » à base de regex. Tenter de retirer les corps avec du pattern matching ligne par ligne au lieu d'un vrai parser finira par avaler une accolade à l'intérieur d'une chaîne, lâcher un token de fermeture, et remettre à l'agent un code qui ne parse pas. Utilisez le parser du langage. Un squelette cassé est pire qu'une lecture complète, car il a tort avec assurance. Élider les types en même temps que les corps. Certaines implémentations réduisent tout ce qui n'est pas une fonction de premier niveau. Cela jette les interfaces et les enums qui sont la structure — et désormais l'agent ne connaît plus les formes de données contre lesquelles il appelle. Gardez les types complets ; n'élidez que les corps. Déplier trop avidement. Si l'agent déplie réflexivement chaque corps « au cas où », vous avez reconstruit la lecture de fichier entier avec des étapes en plus. La skeleton compression ne paie que si le dépliage est l'exception, déclenché par un besoin concret. La traiter comme une réduction d'output. La skeleton compression est un levier d'input — elle rogne ce que vous donnez au modèle. Dire au modèle d'« être bref » rogne sa sortie, qui est la direction bon marché : sur Claude Opus 4.8, l'output revient à $25 par million de tokens contre $5 en input, mais le volume d'output est faible. Les tokens qui dominent la facture sont ceux que vous relisez en boucle dans la fenêtre, et c'est exactement ce que cette technique attaque.Foire aux questions
La skeleton compression rend-elle les réponses de l'agent moins bonnes ?
Non — quand c'est fait fidèlement, elle tend à les rendre meilleures, pour la même raison que rogner n'importe quel bruit le fait : le modèle traite plus fiablement une fenêtre resserrée et à fort signal qu'une fenêtre gonflée. Vous retirez des corps de fonctions qui étaient hors sujet pour la tâche courante, pas les signatures et les types sur lesquels l'agent raisonne. La seule façon de nuire à la qualité est d'élider quelque chose de porteur — c'est pourquoi une implémentation correcte garde chaque signature, type et export verbatim et déplie un corps à l'instant où la tâche en a besoin.Est-ce la même chose que la semantic code search ?
Elles sont complémentaires, pas identiques. La semantic code search répond à « quel code est pertinent pour cette tâche » en classant les symboles de tout le repo par sens. La skeleton compression répond à « comment lire ce fichier à moindre coût une fois que je l'ai trouvé ». Dans un bon setup, la recherche pointe l'agent vers le bon fichier, et la skeleton compression régit la façon dont ce fichier entre dans le contexte — la structure d'abord, les corps à la demande.Quels agents peuvent l'utiliser ?
Tout agent de coding facturé au token qui lit des fichiers en bénéficie, car le mécanisme est agnostique de l'agent — il s'agit de la façon dont un fichier est représenté avant d'entrer dans le context window, pas des entrailles d'un agent en particulier. La vraie question est l'outillage : le faire à la main fonctionne dans Claude Code ou Cursor mais vous devez le piloter à chaque lecture. L'automatiser à travers Claude Code, Cursor, Codex, Copilot et Windsurf, c'est ce que fait Tokenade, pour que la lecture structure-first se produise que vous pensiez ou non à la demander.Le prompt caching ne résout-il pas déjà le problème de refacturation ?
Le caching aide beaucoup mais c'est un autre axe. Le prompt caching rend bon marché la relecture d'un préfixe stable — environ 10 % d'un token frais — mais il ne rend pas le préfixe plus petit, et il n'aide que lorsque le préfixe est identique octet pour octet d'un tour à l'autre. La skeleton compression rétrécit la chose transportée dès le départ, si bien qu'un squelette mis en cache coûte 10 % d'un petit nombre plutôt que 10 % d'un grand. Utilisez les deux : compressez la lecture, puis laissez le cache transporter la version compressée à moindre coût.À voir aussi :
- Comment réduire la consommation de tokens d'un agent de coding IA — le pilier cross-agent dans lequel s'inscrit cette technique.
- Comment réduire la consommation de tokens de Claude Code — les mêmes leviers appliqués à Claude Code.
- Context engineering pour les agents de coding IA — la discipline derrière la lecture de la représentation suffisante la moins chère.
- Coûts en tokens d'un agent de coding IA — la tarification par modèle derrière le calcul ci-dessus.
Classé n°1 au Token-Harness Optimizer Leaderboard.
Tokenade est classé n°1 au Token-Harness Optimizer Leaderboard — un benchmark de bout en bout des optimiseurs de tokens, mesuré sur de vraies sessions de code. Installez-le une fois, il agit sur chaque prompt. Compatible avec Claude Code, Cursor, Codex, Copilot et plus.