Blog

Garder les longs tours d'agent dans la fenêtre de contexte

Un modèle ne peut lire qu'une quantité limitée à la fois : sa fenêtre de contexte. Le travail long de l'agent Mythex consiste à lire de nombreux fichiers, lancer des commandes et confier des tâches bornées à des sous-agents. Nous avons repéré trois façons dont ce travail long pouvait échouer : des sous-agents qui grossissaient jusqu'à ce que le modèle refuse toutes leurs requêtes, un sous-agent qui s'arrêtait tôt parce qu'il croyait manquer de place, et un décompte de tokens très faux pour le russe, le chinois et le japonais. Voici ce que nous avons changé.

Quelques termes d'abord

  • Les tokens sont les morceaux que lit un modèle. Les limites et les coûts se comptent en tokens, et non en caractères ou en mots.
  • La fenêtre de contexte est la quantité maximale qu'un modèle peut lire dans une requête. Nous fixons aussi un budget plus bas pour chaque requête, afin de maîtriser coûts et délais.
  • Les résultats d'outils sont ce que l'agent reçoit en retour de ses outils : le contenu d'un fichier lu, la sortie d'une commande exécutée. Dans un long travail, ce sont la plus grande part de ce qui est envoyé.
  • Un sous-agent est un assistant que l'agent principal lance pour une tâche autonome, par exemple « vérifie ces chapitres par rapport aux notes ». Il reçoit une seule instruction, puis enchaîne seul de nombreux appels d'outils.

Problème 1 : des sous-agents qui grossissaient jusqu'au refus du modèle

À chaque étape, l'agent envoie au modèle la conversation jusque-là. Quand elle devenait trop grosse, notre code la tronquait, mais seulement au début d'un message utilisateur, pour qu'un appel d'outil et son résultat ne soient jamais séparés. Il gardait le premier message, ajoutait une note indiquant que la conversation antérieure avait été tronquée, et conservait les tours les plus récents qui tenaient entièrement.

Un sous-agent n'a qu'un seul message utilisateur : son instruction. Il n'y avait aucun endroit où couper. Il envoyait donc tout son historique à chaque étape, en grossissant à chaque fois, jusqu'à ce que la requête dépasse ce que le modèle accepte du tout, soit environ un million de tokens. Dans un test avec 8 sous-agents de ce type, les 8 ont été refusés.

La correction : effacer d'abord les anciens résultats d'outils

Les requêtes tiennent désormais dans le budget en trois étapes, chacune essayée seulement si la précédente ne suffit pas :

  1. Effacer les plus anciens résultats d'outils. Leur contenu est remplacé par une courte note : le résultat a été effacé pour garder la conversation dans la fenêtre du modèle, et l'agent peut relancer l'outil s'il en a besoin. Un fichier lu il y a une heure est souvent le plus gros et le moins utile de la requête, et le fichier reste disponible pour être relu. Les résultats les plus récents restent entiers.
  2. Garder des tours utilisateur entiers. Comme avant : le premier message, une note, et les tours complets les plus récents qui tiennent.
  3. Garder des cycles d'outils entiers. Dans un long tour, le seul type qu'un sous-agent connaisse, on garde le premier message, une note, et les cycles les plus récents de « appeler un outil, obtenir son résultat ».

Cela ne change que ce qui est envoyé. L'historique sauvegardé de l'agent garde tout. Dans le même test, 0 requête sur 8 a été refusée et les 8 se sont terminées.

Effacer par lots, pour que le cache continue de fonctionner

Les fournisseurs de modèles peuvent réutiliser le travail effectué sur le début d'une requête si elle est identique à la précédente : c'est un cache de préfixe. Si nous effacions un résultat de plus à chaque appel, le début de la requête changerait à chaque fois et le cache ne servirait jamais.

Nous effaçons donc les plus anciens résultats quatre à la fois, et non un par un. La partie effacée reste identique pendant plusieurs appels. Un résultat à peine plus grand que la note n'est pas touché : l'effacer ferait gagner presque rien tout en modifiant le début de la requête. Ce qui reste entier est décidé par la taille et non par le nombre : les résultats les plus récents qui tiennent dans un quart du budget restent, avec au moins un toujours conservé. Une version antérieure gardait six résultats entiers, valable pour de courtes sorties de commandes, mais qui, avec de longues lectures de fichiers, laissait l'essentiel du budget à d'anciens résultats.

Les refus disent maintenant pourquoi

Une requête que le modèle rejette comme invalide n'est plus relancée : renvoyer la même requête obtient la même réponse. Si la conversation est trop longue pour le modèle, la personne voit : « Cette conversation est devenue trop longue pour que le modèle la lise d'un seul coup. Commencez un nouveau chat pour l'étape suivante : tout ce qui a été construit jusqu'ici est conservé. » C'est à la personne d'agir, ce n'est pas une panne, donc cela ne déclenche pas d'alerte pour nous.

Problème 2 : l'agent s'arrêtait pour « gagner de la place »

Un sous-agent chargé de lire 40 longs chapitres s'est arrêté après 2, en disant qu'il voulait économiser de la place. Il avait utilisé environ 100 000 tokens sur un budget de 400 000.

Il n'avait aucun moyen de savoir que les anciens résultats seraient désormais effacés pour lui. Nous le lui avons donc dit. Les instructions de l'agent précisent maintenant que, lorsque la conversation dépasse ce que le modèle peut prendre, les plus anciens résultats d'outils sont effacés et les plus récents restent entiers : ne vous arrêtez donc pas au milieu d'une longue tâche pour gagner de la place ; continuez, et relisez un fichier si vous avez besoin de ce qui a été effacé.

Une correction que le modèle ne voit pas n'en est pas une de son point de vue. Le mécanisme n'aide que si l'agent lui fait confiance.

Problème 3 : compter les tokens comme si tout était en anglais

Nous estimions les tokens sans exécuter de tokeniseur, en divisant les caractères par quatre. C'est une règle empirique raisonnable pour l'anglais, mais pas pour d'autres écritures : le russe tient environ 2,2 caractères par token, et le chinois ou le japonais environ un seul.

Ainsi, un sous-agent lisant des chapitres en russe envoyait une requête d'environ 640 000 tokens alors que notre estimation indiquait moins que le budget de 400 000. L'estimation pouvait tout aussi bien laisser passer une requête au-delà de la limite stricte du modèle.

L'estimation tient désormais compte de l'écriture : l'ASCII simple à quatre caractères par token, le chinois, le japonais et le coréen à un caractère par token, et tout le reste (cyrillique, grec, arabe, lettres accentuées, emoji) à deux. Elle est conçue pour surestimer plutôt que sous-estimer. Sur un chapitre en russe, elle donne désormais 43 423 tokens contre environ 44 600 mesurés ; avant, elle en annonçait 25 023. Comme un message ne change jamais une fois écrit, son estimation est calculée une seule fois et mémorisée, au lieu de l'être à chaque étape.

Ce que nous en retenons

  • Tronquer là où se trouve réellement la taille. Dans le travail d'un agent, c'est dans les résultats d'outils, pas dans les messages utilisateur.
  • Dire à l'agent l'existence de ses filets de sécurité. Sinon, il planifie en fonction de limites qui ne s'appliquent plus.
  • « Caractères ÷ 4 » est une hypothèse anglophone. Tout ce qui compte les tokens pour des utilisateurs multilingues doit compter selon l'écriture, ou utiliser un vrai tokeniseur.
  • Surveillez le cache. Une requête qui change en tête à chaque appel coûte plus cher et s'exécute plus lentement.

Si vous intégrez des fonctionnalités d'IA à votre propre app, nos guides sur ce qu'est un LLM et comment utiliser les API LLM expliquent dès le départ les fenêtres de contexte et les tokens. Pour en savoir plus sur le fonctionnement des sous-agents dans Mythex, voir les docs sur les sous-agents.

Mythex · Pricing · Créez tout avec l'IA · Modèles de sites et d'applications · Mythex comparé · Glossaire de la création d'applications avec l'IA · Outils gratuits pour créer des applications avec l'IA · English

English አማርኛ العربية Български বাংলা Bosanski Català Čeština Dansk Deutsch Ελληνικά Español Español (España) Eesti فارسی Suomi Français (Canada) ગુજરાતી हिन्दी Hrvatski Magyar Հայերեն Bahasa Indonesia Íslenska Italiano 日本語 ქართული Қазақ тілі ಕನ್ನಡ 한국어 Lietuvių Latviešu Македонски മലയാളം Монгол मराठी Bahasa Melayu မြန်မာ Norsk bokmål Nederlands ਪੰਜਾਬੀ Polski Português (Brasil) Português (Portugal) Română Русский Slovenčina Slovenščina Soomaali Shqip Srpski Svenska Kiswahili தமிழ் తెలుగు ไทย Tagalog Türkçe Українська اردو Tiếng Việt 简体中文 繁體中文(香港) 繁體中文(台灣)