đ€ Nolife Tokens - rĂ©duire le contexte LLM sans compromettre la capacitĂ© de rĂ©cupĂ©ration
le 16 août 2026
Les agents de codage IA ont inversé un problÚme de performance bien connu.
Pendant des annĂ©es, nous avons luttĂ© contre les limitations liĂ©es au dĂ©bit de donnĂ©es, aux allers-retours HTTP, Ă la latence et Ă la mĂ©moire des processus. Ces contraintes restent importantes. Ce qui a changĂ©, c'est qu'une part importante du coĂ»t et des dĂ©faillances d'une session d'agent rĂ©side dĂ©sormais dans l'invite de commande : rĂ©sultats des outils, journaux, diffĂ©rences, arborescences du dĂ©pĂŽt, bruit du compilateur, objets JSON et les mĂȘmes lignes d'information rĂ©pĂ©tĂ©es des centaines de fois.
Le processus naïf est simple :
Tool â giant stdout â LLM context
Cela fonctionne jusqu'Ă ce que le contexte soit saturĂ© d'informations sans grande valeur et que le modĂšle doive encore trouver la seule ligne d'erreur pertinente. Des fenĂȘtres de contexte plus larges permettent d'envoyer toutes les donnĂ©es, mais ne rendent pas cette pratique judicieuse.
Cet article décrit nolife-tokens, une expérience volontairement modeste avec Symfony et Flow. La thÚse est ciblée et vérifiable :
Supprimer des informations du contexte LLM actif ne signifie pas nĂ©cessairement les perdre. Le contexte peut ĂȘtre fortement compressĂ©, tandis que les donnĂ©es omises restent rĂ©cupĂ©rables de maniĂšre dĂ©terministe grĂące Ă des rĂ©fĂ©rences lĂ©gĂšres.
Nous avons mesuré le volume, la rétention du signal et la récupération au bit prÚs sur les équipements de test. Nous n'avons pas encore mesuré les jetons facturés par le fournisseur, la latence de bout en bout des agents ni le coût en dollars. Ces distinctions sont importantes.
Inspiration sans clonage de produits
Plusieurs projets ouverts explorent des idĂ©es connexes : RTK (compression des commandes et des sorties avant leur lecture par lâagent), context-mode (conserver les donnĂ©es brutes et les rĂ©cupĂ©rer Ă la demande), Headroom (compression adaptĂ©e au contenu avec stockage rĂ©versible), Claude-mem (divulgation progressive de la mĂ©moire).
Vous nâavez pas besoin de connaĂźtre ces bases de code. LâidĂ©e commune utile nâest pas « un autre framework dâagents », mais plutĂŽt :
Effectuez un travail plus déterministe avant d'envoyer les données au LLM.
La dĂ©duplication des journaux, la suppression des clĂ©s de tĂ©lĂ©mĂ©trie, la classification des diffĂ©rences Git, le hachage d'une section omise : aucune de ces opĂ©rations ne nĂ©cessite de modĂšle. Le budget de contexte du modĂšle doit ĂȘtre consacrĂ© Ă la gestion des ambiguĂŻtĂ©s et Ă la prise de dĂ©cision, et non Ă cinq cents copies de « [INFO] requĂȘte dĂ©marrĂ©e ».
nolife-tokens est un laboratoire pour cette idĂ©e au sein de la pile Darkwood : PHP 8.5, Symfony Console et darkwood/flow. Pas dâembeddings, pas de base de donnĂ©es vectorielle, pas de MCP, pas de Redis, pas de LangChain.
Point de départ : un squelette et un pipeline observable
Le projet a débuté avec une structure Symfony 8.1 minimale. Le code de l'application se résumait essentiellement à un noyau. Flow était déjà une dépendance de Composer, mais inutilisée.
La premiÚre étape, TOKEN_PIPELINE_POC, a permis de connecter un pipeline Flow minimal via un ContextPacket mutable :
Load
â Classify
â MeasureRaw
â Optimize
â MeasureOpt
â EvaluateSignals
Interface de ligne de commande :
bin/console tokens:analyze fixtures/sample.log
bin/console tokens:benchmark
Le flux est utile ici non pas parce que nous avons besoin d'une orchestration simultanée, mais parce que chaque étape est observable. Le paquet accumule une trace : octets et jetons estimés aprÚs le chargement, type de contenu aprÚs la classification, taille aprÚs l'optimisation, réussite/échec aprÚs les vérifications de signaux. Le lecteur (et l'ingénieur) voit :
raw â classified â reduced â evaluated
au lieu d'une fonction optimizeContext() opaque qui masque l'endroit oĂč le volume a disparu.
Voici à quoi ressemble, dans l'esprit, une trace typique de tokens:analyze :
LOAD | fixtures/sample.log | 19,354 bytes
CLASSIFY | log
MEASURE_RAW | 19,354 bytes | ~4,839 tokens
OPTIMIZE | log refs=1 | 4,813 bytes
MEASURE_OPT | markers~4 referenced~3671 | ~1,204 tokens
SIGNAL | PASS
RECOVERY | PASS
Cette liste d'étapes constitue le produit. En cas de problÚme (économies insuffisantes, signal défaillant, nombre excessif de références), vous pouvez identifier l'étape responsable sans avoir recours à une plateforme de métriques supplémentaire.
Pourquoi limiter la taille du projet ? Parce que les systĂšmes contextuels ont tendance Ă se transformer en frameworks avant mĂȘme que lâon ait pu vĂ©rifier la viabilitĂ© de lâidĂ©e de base. nolife-tokens se rapproche volontairement davantage dâun laboratoire en console que dâune API de bibliothĂšque. Pas de ports, dâadaptateurs ni dâinterfaces spĂ©culatives. Uniquement des classes PHP, des services Symfony via lâinjection automatique de dĂ©pendances, et des fichiers dans le rĂ©pertoire var/.
Jetons estimés, et non jetons facturés
La mesure utilise une approximation documentĂ©e, la mĂȘme heuristique que RTK documente publiquement :
// TokenEstimator.php (excerpt)
estimatedTokens: (int) ceil($bytes / 4),
Les pourcentages entre les valeurs brutes et optimisées sont utiles à des fins de comparaison. Les valeurs absolues ne correspondent pas aux jetons facturés par le fournisseur. Les modÚles de tokenisation diffÚrent. Dans cet article, le terme « jetons » désigne cette estimation, sauf indication contraire.
Classification déterministe en premier
La stratégie de compression dépend du type de contenu. La classification est heuristique et sans LLM.
| Type | Croquis de détection |
|---|---|
json |
json_decode réussit sur { / [ racine |
git_diff |
diff --git ou @@ en-tĂȘtes de bloc |
log |
suffisamment de lignes correspondant aux modĂšles de niveau/horodatage |
file_list |
lignes contenant principalement des chemins d'accĂšs |
texte |
repli |
Cette distinction n'est pas purement théorique. Différents types de bruit se compressent différemment.
- Journaux : les lignes répétitives et les messages de faible intensité doivent rester affichées ; les lignes
ERROR/WARNINGimportantes doivent rester affichĂ©es. - JSON : tĂ©lĂ©mĂ©trie, remplissage, identifiants de requĂȘte ; les champs de dĂ©cision (
id,status,error) doivent rester - Différences Git : de nombreuses lignes
+/-contiennent des informations utiles ; un élagage agressif est dangereux - Listes de fichiers : peuvent se décomposer en arbres avec des décomptes
- Texte : espaces blancs légers uniquement dans le POC actuel
Premier test de performance : volume vs signal
Les fixtures insÚrent intentionnellement des chaßnes critiques. Une optimisation se contentant de tronquer la fin de la chaßne permettrait souvent de « gagner des jetons » tout en supprimant l'erreur insérée. Le test de performance suit donc SIGNAL : chaque sous-chaßne attendue doit toujours apparaßtre dans le texte visible optimisé.
RĂ©sultats de la premiĂšre Ă©tape (optimisation avec perte â les donnĂ©es omises n'Ă©taient pas encore enregistrĂ©es) :
| CAS | Octets bruts | Jetons bruts* | Octets optimisés | Jetons optimisés* | ENREGISTRà | SIGNAL |
|---|---|---|---|---|---|---|
| sample.log | 19Â 354 | 4Â 839 | 4Â 759 | 1Â 190 | 75,4Â % | RĂUSSI |
| sample.diff | 12Â 742 | 3Â 186 | 9Â 684 | 2Â 421 | 24,0Â % | RĂUSSI |
| sample.json | 27Â 118 | 6Â 780 | 265 | 67 | 99,0Â % | RĂUSSI |
* estimé en octets / 4
Les signaux implantés comprenaient :
ERREUR PaymentService ligne 421dans le journal des modificationsauthentication check removedetsrc/Security/AuthGuard.phpdans le diff- l'identifiant du paiement, le statut « échec » et le message d'erreur au format JSON.
L'indicateur clé de performance qui compte n'est pas « combien avons-nous supprimé ? », mais plutÎt : les informations nécessaires à la prise de décision ont-elles été préservées ?
JSON : le bruit structurĂ© sâeffondre bien
Le format JSON s'est avéré le plus simple. Les clés structurées ont permis à un filtre déterministe de supprimer ou de référencer ultérieurement les données de télémétrie tout en conservant les champs de décision. Une charge utile composée principalement de trames de remplissage et de débogage est passée d'environ 6 800 jetons estimés à quelques dizaines de jetons JSON visibles lors de la premiÚre étape, avec la validation du protocole SIGNAL PASS.
Câest lĂ lâavantage structurel du bruit typé : on peut nommer ce qui est jetable.
Journal : la rĂ©pĂ©tition, c'est de l'argent facile â jusqu'Ă ce que les signaux se rompent
Les journaux sont compressĂ©s car les agents voient la mĂȘme ligne de maniĂšre rĂ©pĂ©tĂ©e :
[INFO] request started
[INFO] request started
[INFO] request started
[INFO] request started
devient :
[INFO] request started Ă4
Les lignes Ă signal Ă©levĂ© restent inchangĂ©es. Cela paraĂźt anodin jusqu'Ă ce qu'on Ă©crive un test de rĂ©tention dĂ©terministe. Une version prĂ©liminaire utilisait [ERROR] PaymentService ligne 421 alors que le signal attendu Ă©tait ERROR PaymentService ligne 421. La sous-chaĂźne Ă©chouait Ă cause du ] entre ERROR et PaymentService. La leçon est simple mais importante : les tests de signal sont sensibles au formatage, et les lignes Ă signal Ă©levĂ© doivent rester suffisamment stables pour ĂȘtre interprĂ©tĂ©es par les humains et pour les vĂ©rifications.
Différences Git : un résultat utilement faible
La comparaison des différences n'a permis de réduire le temps de traitement que d'environ 24 %. Il ne s'agit pas d'un échec de l'expérience. Si la plupart des données d'entrée contiennent des modifications significatives, le bruit sûr est négligeable. Viser aveuglément une « réduction de 90 % » risquerait de fausser la surface de décision (par exemple, la suppression d'une vérification d'authentification dans AuthGuard.php).
Les différences Git révÚlent une limite de compression naturelle. C'est le type de résultat que l'on attend d'un laboratoire : ne pas forcer les choses.
Ce que la premiĂšre Ă©tape a permis de comprendre, en une phrase : la rĂ©duction du nombre de jetons sans test de signal est vaine. Un test de signal permet de distinguer « nous avons Ă©liminĂ© le bruit » et « nous avons corrigĂ© lâerreur ».
PropriĂ©tĂ© manquante : supprimer â dĂ©truire
Le premier prototype présentait une faiblesse structurelle. Une fois le contenu supprimé, il disparaissait du contexte actif et devenait inaccessible. Si un agent décidait ultérieurement que les données de télémétrie supprimées étaient pertinentes, il n'y avait rien à récupérer.
Cela nous amÚne à la deuxiÚme étape importante : REVERSIBLE_CONTEXT_POC.
Principe:
REMOVE FROM CONTEXT
â
DESTROY INFORMATION
Les optimiseurs renvoient désormais un OptimizeResult :
// OptimizeResult.php
final class OptimizeResult
{
/**
* @param list<array{reason: string, content: string, marker_placeholder?: string}> $omissions
*/
public function __construct(
public readonly string $visible,
public readonly array $omissions = [],
) {}
}
ContextOptimizer conserve chaque omission sous var/context/
// ContextOptimizer.php (excerpt)
$id = $this->store->makeId($packet->sourcePath, $omission['reason'], $omission['content']);
$ref = new ContextReference(
id: $id,
source: $packet->sourcePath,
type: $packet->type->value,
reason: $omission['reason'],
content: $omission['content'],
);
$this->store->put($ref);
// ...
$visible = str_replace($placeholder, $ref->marker(), $visible);
Les identifiants sont déterministes et courts : ctx_ plus les premiers chiffres hexadécimaux de sha256(source|reason|content).
Forme réelle stockée (abrégée) :
{
"id": "ctx_31154f",
"source": "fixtures/sample.log",
"type": "log",
"reason": "deduplicated repeated lines",
"content": "[INFO] request started\n..."
}
Le contexte actif ne nécessite que :
#ref:ctx_31154f
Lors de l'exécution grossiÚre du journal, ce marqueur coûte environ quatre jetons estimés tandis que ~3,6k jetons estimés restent hors contexte sur le disque.
La récupération est une commande de console, pas un moteur de recherche :
bin/console tokens:show-ref ctx_31154f
// TokensShowRefCommand.php (excerpt)
$ref = $this->store->get($id);
$io->writeln(sprintf('Source: %s', $ref->source));
$io->writeln(sprintf('Type: %s', $ref->type));
$io->writeln(sprintf('Reason: %s', $ref->reason));
$io->writeln('--- RAW CONTENT ---');
$io->writeln($ref->content);
La fonction ContextStore::get normalise les identifiants #ref:ctx_⊠ou les identifiants bruts et lit le fichier JSON. La rĂ©cupĂ©ration dans le pipeline est exacte au bit prĂšs : chaque rĂ©fĂ©rence est rechargĂ©e et son contenu est comparĂ© Ă celui Ă©crit lors de lâoptimisation.
Pipeline de flux mis Ă jour
Load
â Classify
â MeasureRaw
â Optimize (+ store refs)
â MeasureOpt
â EvaluateSignals
â EvaluateRecovery
La construction reste un générateur FlowFactory de fermetures sur ContextPacket :
// TokenPipelineFactory.php (excerpt)
return $this->flowFactory->create(static function () use (...) {
yield static function (ContextPacket $packet): ContextPacket {
$packet->type = $classifier->classify($packet->raw);
$packet->addTrace('CLASSIFY', $packet->type->value);
return $packet;
};
yield static function (ContextPacket $packet) use ($optimizer): ContextPacket {
$optimizer->optimize($packet);
$packet->addTrace('OPTIMIZE', sprintf('%s refs=%d', $packet->type->value, count($packet->references)));
return $packet;
};
// ⊠MEASURE_OPT, SIGNAL âŠ
yield static function (ContextPacket $packet) use ($store): ContextPacket {
foreach ($packet->references as $ref) {
$loaded = $store->get($ref->id);
if ($loaded === null || $loaded->content !== $ref->content) {
$missing[] = $ref->id;
}
}
$packet->recoveryPass = $missing === [];
$packet->addTrace('RECOVERY', $packet->recoveryPass ? 'PASS' : 'FAIL');
return $packet;
};
});
Deux invariants :
- SIGNAL â Les chaĂźnes de caractĂšres critiques insĂ©rĂ©es restent dans le texte optimisĂ© visible
- RĂCUPĂRATION â chaque
#refeffectue un aller-retour octet par octet depuisvar/context/
Référence réversible
Résultats mesurés actuels :
| FICHIERS | BRUT | VISIBLES | MARQUEURS | RĂFĂRENCE | ENREGISTRĂ | RĂF. | SIGNAL | RĂCUPĂRATION |
|---|---|---|---|---|---|---|---|---|
| sample.log@coarse | 4Â 839 | 1Â 204 | 4 | 3Â 671 | 75,1Â % | 1 | RĂUSSI | RĂUSSI |
| sample.log@fine | 4Â 839 | 1Â 202 | 12 | 3Â 670 | 75,2Â % | 3 | RĂUSSI | RĂUSSI |
| sample.json | 6Â 780 | 146 | 36 | 6Â 191 | 97,8Â % | 9 | RĂUSSI | RĂUSSI |
| sample.diff | 3Â 186 | 2Â 434 | 4 | 749 | 23,6Â % | 1 | RĂUSSI | RĂUSSI |
Comment lire les colonnes :
- BRUT â nombre estimĂ© de jetons de l'entrĂ©e originale
- VISIBLE â jetons estimĂ©s encore dans le contexte actif (y compris les marqueurs
#ref) - MARQUEURS â coĂ»t estimĂ© des marqueurs seuls
- RĂFĂRĂ â jetons estimĂ©s stockĂ©s hors contexte
- ENREGISTRĂ â rĂ©duction du visible par rapport au brut
- SIGNAL / RĂCUPĂRATION â les deux invariants rĂ©ussite/Ă©chec
Le format JSON se compresse toujours fortement (réduction visible d'environ 98 %) tout en conservant les champs de décision et en stockant environ 6 200 jetons estimés derriÚre des références structurelles. Les différences restent le cas le plus résistant (environ 24 %), avec désormais une seule référence grossiÚre pour le contexte inchangé compressé au lieu de dizaines de micro-références.
Granularité grossiÚre vs granularité fine : la granularité de référence a un coût
Pour les journaux, le test de performance s'exécute avec les deux niveaux de granularité.
| Mode | RĂF. | MARQUEURS | VISIBLES |
|---|---|---|---|
| grossier | 1 | 4 | 1 204 |
| amende | 3 | 12 | 1 202 |
Le mode fin a triplé le nombre de références et la surcharge des marqueurs pour économiser environ deux jetons visibles. Pour cette charge de travail, le mode grossier s'est avéré le plus performant : récupération simplifiée, moins d'identifiants à suivre pour l'agent et contexte actif quasi identique.
Ce n'est pas une rĂšgle absolue. La granularitĂ© doit suivre le modĂšle de rĂ©cupĂ©ration attendu. Les journaux nĂ©cessitent souvent un seul bloc contenant toutes les donnĂ©es agrĂ©gĂ©es. Le JSON structurĂ© exige souvent des rĂ©fĂ©rences aux positions des champs, afin que le modĂšle puisse toujours voir oĂč se trouvaient les donnĂ©es de tĂ©lĂ©mĂ©trie.
Ămission grossiĂšre logarithmique (extrait)Â :
// LogOptimizer.php (coarse branch excerpt)
$out[] = sprintf('%s Ă%d', $prev, $count);
$coarseParts[] = $expanded;
// âŠ
$visible .= "\n\nRepeated informational logs omitted.\n" . $placeholder;
$omissions[] = [
'reason' => 'deduplicated repeated lines',
'content' => implode("\n\n", $coarseParts),
'marker_placeholder' => $placeholder,
];
Références JSON structurelles
Conceptuellement :
{
"id": 42,
"status": "failed",
"error": "PaymentService timeout",
"telemetry": { "... huge payload ..." }
}
devient :
{
"id": 42,
"status": "failed",
"error": "PaymentService timeout",
"telemetry": "#ref:ctx_xxxxxx"
}
Le modĂšle conserve la position sĂ©mantique, les identifiants, l'Ă©tat et les erreurs. Environ six mille jetons de tĂ©lĂ©mĂ©trie restent disponibles hors de la fenĂȘtre. La configuration actuelle fournit neuf rĂ©fĂ©rences (clĂ©s de bruit + remplissage groupĂ©), environ 36 jetons de marqueur, et les instructions SIGNAL PASS et RECOVERY PASS.
C'est plus efficace que la suppression de clĂ©s : la structure subsiste mĂȘme lorsque les valeurs quittent l'invite.
Dans le code, les clĂ©s de bruit ne sont pas supprimĂ©es ; leurs valeurs deviennent des espaces rĂ©servĂ©s que ContextOptimizer réécrit ensuite en #ref:âŠ. Les clĂ©s de remplissage correspondant Ă padding_* / noise_* sont regroupĂ©es en une seule omission lorsque cela est possible, afin qu'un gros bloc ne se transforme pas en quatre-vingts petites rĂ©fĂ©rences :
// JsonOptimizer.php (excerpt)
if ($this->isPaddingKey($key)) {
$paddingBucket[$key] = $item;
continue;
}
if ($this->isNoiseKey($key)) {
$placeholder = '{' . '{REF_' . count($omissions) . '}' . '}';
$omissions[] = [
'reason' => 'omitted json field: ' . $key,
'content' => json_encode($item, JSON_UNESCAPED_SLASHES | JSON_UNESCAPED_UNICODE | JSON_PRETTY_PRINT) ?: '',
'marker_placeholder' => $placeholder,
];
$result[$key] = $placeholder;
continue;
}
Le JSON visible ressemble donc toujours à la réponse de l'API. C'est important pour les agents qui raisonnent autant sur les noms de champs que sur le texte.
Ceci n'est pas un chiffon
nolife-tokens n'utilise pas d'embeddings, de recherche vectorielle, de récupération sémantique, de bases de données externes, de LangChain, de MCP ou de Redis.
La récupération aujourd'hui est :
reference id â local JSON file â exact bytes
Cette simplicité est intentionnelle. Nous voulons démontrer qu'une réduction structurelle peu coûteuse élimine déjà la majeure partie du gaspillage évident. La recherche sémantique peut attendre que des mesures indiquent sa nécessité.
Implications pour les agents de codage
Un processus plus rigoureux ressemble à ceci :
Tool
â deterministic reducer
â high-signal context (+ #ref markers)
â LLM
â (only if needed)
tokens:show-ref / expand
â
raw source
Cela ne nĂ©cessite ni de remplacer Cursor ni d'inventer un autre EDI. Une future intĂ©gration pourrait ĂȘtre aussi simple que :
bin/console tokens:context âŠ
et en intégrant le résultat optimisé à un agent existant. L'objectif est d'offrir un meilleur contexte aux outils que nous utilisons déjà , et non de créer une nouvelle interface pour les agents.
Coût et latence (affirmations prudentes)
RĂ©duire le nombre de jetons d'entrĂ©e peut diminuer les coĂ»ts pour le fournisseur, la charge de prĂ©traitement, la latence et le bruit. Nous n'avons pas comparĂ© ces effets avec une API de fournisseur en production dans le cadre de ce projet. Jusqu'Ă prĂ©sent, nous avons mesurĂ© le volume de contexte, la rĂ©tention du signal et la rĂ©cupĂ©ration. ConsidĂ©rer la rĂ©duction du volume comme une rĂ©duction de facture avĂ©rĂ©e serait malhonnĂȘte.
Il existe Ă©galement un effet de second ordre qui importe aux agents : la distraction. MĂȘme lorsquâun modĂšle « dĂ©tecte » lâerreur dans un journal de 20 Ko, son attention peut ĂȘtre dĂ©tournĂ©e par les rĂ©pĂ©titions environnantes. La rĂ©duction dĂ©terministe ne se rĂ©sume pas Ă une simple question dâĂ©conomie ; elle vise aussi Ă offrir au modĂšle une surface de dĂ©cision plus claire. Cet effet est plus difficile Ă quantifier que la simple rĂ©duction de la taille des fichiers, et nous nâavons pas prĂ©tendu le faire ici.
L'utilisation pratique Ă court terme au sein des flux de travail de type Darkwood reste pour l'instant hors ligne : exĂ©cuter tokens:analyze sur un fixture ou un dump d'outil capturĂ©, examiner la trace de la scĂšne et dĂ©terminer si le contexte visible est suffisant pour ĂȘtre collĂ© dans Cursor. L'intĂ©gration qui intercepte chaque commande shell (Ă la maniĂšre de RTK) est explicitement exclue du pĂ©rimĂštre de ces Ă©tapes.
Philosophie de l'ingénierie
Avant d'appeler un mannequin, demandez :
PHP peut-il résoudre cette partie de maniÚre déterministe ?
Ce code source inclut : la classification du contenu, la dĂ©duplication des journaux, la suppression ou la rĂ©fĂ©rence du bruit JSON, la rĂ©duction des arborescences de fichiers, la suppression des mĂ©tadonnĂ©es git et du contexte inchangĂ© long, les omissions de hachage, la persistance des rĂ©fĂ©rences, la mesure des tailles, lâassertion des signaux, lâassertion de rĂ©cupĂ©ration.
Composants (responsabilités, pas un simple fichier) :
| PiĂšce | RĂŽle |
|---|---|
ContextPacket |
Charge utile du pipeline : texte brut/optimisé, métriques, références, trace |
ContentClassifier |
Détection déterministe du type |
TokenEstimator / ContextMetrics |
octets, lignes, jetons estimés |
OptimizeResult |
Texte visible + omissions |
ContextReference / ContextStore |
Marqueur + var/context/<id> .json |
ContextOptimizer |
Distribution par type, persistance des références, injection de marqueurs |
LogOptimizer / JsonOptimizer / GitDiffOptimizer / ⊠|
Réduction spécifique au contenu |
SignalEvaluator |
Vérifications de sous-chaßnes insérées |
TokenPipelineFactory |
Ătapes du flux |
tokens:analyze / benchmark / show-ref |
CLI |
Modes de défaillance que nous avons déjà rencontrés ou que nous prévoyons
Surcompression
Une ligne, rare mais cruciale, peut se confondre avec du bruit. Les projecteurs de signalisation en captent une partie ; les agents réels repÚrent les cas que les projecteurs ne détectent pas.
Explosion de référence
Une premiÚre approche de Git générait environ 36 petites références pour les contextes réduits. La surcharge liée aux marqueurs augmentait considérablement et l'expérience utilisateur s'en trouvait dégradée. La solution consistait à utiliser une seule référence globale pour tout le contexte inchangé omis. Mieux vaut moins de références utiles que de nombreuses micro-références.
Perte sémantique
La préservation d'une chaßne de caractÚres ne garantit pas toujours sa bonne interprétabilité. Un code d'état sans contexte narratif peut encore perturber un modÚle. Les filtres déterministes optimisent le volume et les signaux explicites, et non la compréhension.
Fausse confiance de bytes / 4
Les pourcentages comparatifs sont fiables pour ce laboratoire. Les « économies absolues sur la facture » ââne le sont pas.
Dépendance de récupération
Une fois que l'agent s'appuie sur #ref, il faut lui apprendre à développer cette fonctionnalité. Actuellement, c'est un humain qui exécute tokens:show-ref. La divulgation progressive automatique est la prochaine étape expérimentale ; il ne s'agit pas d'une fonctionnalité finalisée.
Collisions d'identification et hygiĂšne des magasins
Les identifiants de rĂ©fĂ©rence sont adressables par contenu. Les collisions avec un contenu diffĂ©rent augmentent la longueur du hachage et gĂ©nĂšrent une erreur. Les collisions avec un contenu identique sont réécrites de maniĂšre idempotente. Le stockage se trouve dans le rĂ©pertoire var/, ignorĂ© par Git ; il s'agit d'un cache d'espace de travail, et non d'une base de connaissances persistante. Cela convient pour une preuve de concept, mais s'avĂšre insuffisant pour la mĂ©moire de production multi-utilisateurs â une raison supplĂ©mentaire de ne pas confondre cela avec la persistance de type Claude-mem.
Posture de sécurité
Considérez le contenu analysé comme des données, et jamais comme des instructions à exécuter. Les optimiseurs ne font que réécrire les chaßnes de caractÚres. Les journaux et les fichiers JSON peuvent contenir du texte ressemblant à des directives d'agent (« ignorer les instructions précédentes⊠»). Le pipeline ne doit en aucun cas interpréter ce texte comme un élément de contrÎle. Aujourd'hui, il s'agit principalement d'une question d'implémentation : pas d'évaluation, pas d'analyse du contenu, et surtout, ne vous fiez pas aux instructions de test comme à une politique.
Ce que nous avons délibérément omis
Il est utile de lister les objectifs secondaires pour que l'expérience reste lisible :
- Pas de résumé des sections omises basé sur LLM (pour l'instant). Les résumés seraient incomplets d'une autre maniÚre et plus difficiles à reconstituer exactement.
- Pas de crochet de curseur automatique ni d'interception de shell
- Absence de systÚme d'empaquetage contextuel permettant de choisir parmi vingt candidats sous une limite de 4 Ko (conçu plus tard dans la feuille de route initiale, non implémenté).
- Aucune suite de tests PHPUnit n'est utilisée ;
tokens:benchmarkconstitue la surface de validation. - Aucune affirmation selon laquelle les jetons estimés correspondent aux compteurs de facturation d'OpenAI/Anthropic
Le fait de les omettre fait partie de la mĂ©thode : Ă©valuer lâidĂ©e de rĂ©duction rĂ©versible avant dây superposer des produits.
Suivant : divulgation progressive
La suite logique :
LEVEL 0 tiny overview
â
LEVEL 1 selected section / #ref expansion
â
LEVEL 2 raw original source
Exemple de croquis :
Application failed during checkout.
1 PaymentService error.
telemetry omitted.
#ref:ctx_payment
Si nĂ©cessaire : dĂ©velopper la rĂ©fĂ©rence. Uniquement si nĂ©cessaire : donnĂ©es brutes complĂštes. Le contexte est dĂ©sormais dĂ©terminĂ© par la demande plutĂŽt que par lâenvoi systĂ©matique du journal complet.
Câest lĂ que nolife-tokens cesse dâĂȘtre seulement un compresseur et devient une expĂ©rience en matiĂšre dâarchitecture de contexte.
Schématiquement :
âââââââââââââââââââââââ
raw tool dump ââș â classify + optimize â ââș visible context (+ #ref)
ââââââââââââŹâââââââââââ
â omissions
âŒ
var/context/*.json
â
expand / show-ref â (on demand)
âŒ
original bytes
Aujourd'hui, la flÚche vers le bas est manuelle. La divulgation progressive l'intÚgre à la boucle de l'agent : d'abord une vue d'ensemble, puis les extensions choisies, puis les données brutes uniquement lorsque les couches moins coûteuses échouent.
Conclusion
Les grandes fenĂȘtres de contexte incitent Ă la facilité : tout envoyer. La question technique passe de « quelle quantitĂ© de donnĂ©es le modĂšle peut-il accepter ? » Ă Â :
Quel est le contexte minimal nécessaire à la prise de décision correcte, et à quel coût pouvons-nous récupérer le reste ?
Deux étapes importantes concernant les jetons nolife sont déjà visibles, avec des mesures :
- Des réductions déterministes importantes sont possibles pour les journaux bruités et les fichiers JSON
- la rĂ©tention du signal peut ĂȘtre testĂ©e comme un invariant de premiĂšre classe
- les donnĂ©es omises peuvent ĂȘtre rĂ©cupĂ©rĂ©es octet par octet.
- La granularitĂ© de rĂ©fĂ©rence elle-mĂȘme a un coĂ»t mesurable (une granularitĂ© grossiĂšre est prĂ©fĂ©rable Ă une granularitĂ© fine sur notre configuration logarithmique).
- Les types de contenu ont des limites de compression naturelles différentes (les différences Git sont d'environ 24 % ici).
Le problĂšme n'est pas rĂ©solu. Les questions de coĂ»t pour les prestataires, d'expansion pilotĂ©e par les agents et de divulgation progressive restent Ă explorer. Le laboratoire est volontairement de petite taille : suffisamment petit pour que chaque mĂ©canisme demeure comprĂ©hensible, ce qui, pour les systĂšmes contextuels, est peut-ĂȘtre l'objectif principal.
Commandes utilisées dans ce travail
bin/console tokens:analyze fixtures/sample.log
bin/console tokens:analyze fixtures/sample.log --granularity=fine
bin/console tokens:analyze fixtures/sample.json
bin/console tokens:analyze fixtures/sample.diff
bin/console tokens:benchmark
bin/console tokens:show-ref ctx_31154f
Code source
DépÎt : https://github.com/matyo91/nolife-tokens
Slides: https://github.com/matyo91/slidewire