Blog

Garder les tours d'agent riches en images dans la mémoire

En production, un tour d'agent a manqué de mémoire à 952 Mo en découpant des produits dans six scans de catalogue et en examinant ses découpes. Il n'y avait pas une seule fuite, mais trois. Chaque appel au modèle renvoyait les images du tour en taille réelle ; la bibliothèque que nous utilisons pour appeler le modèle gardait chaque requête en vie jusqu'à la fin du tour ; et le traçage du framework de l'agent conservait, pour tout le tour, les entrées de chaque appel, images comprises. Nous avons corrigé les trois : les images sont réduites avant l'envoi, et les images de chaque appel sont libérées dès que l'appel est terminé.

Comment l'agent voit les images

Sur Mythex, l'agent travaille dans le bac à sable cloud de votre projet. Il peut regarder des images, une capture d'écran que vous joignez, une maquette, un fichier de votre projet, ou une image qu'il vient de produire lui-même, en les envoyant au modèle avec la conversation.

Un tour est tout ce que fait l'agent en réponse à un message de votre part. Au sein d'un tour, l'agent appelle le modèle de nombreuses fois : il réfléchit, lance un outil, regarde le résultat, réfléchit à nouveau. Chacun de ces appels envoie la conversation jusque-là, y compris les images du tour, encodées en texte (base64, ce qui les rend environ un tiers plus lourdes).

Le tour qui a planté faisait quelque chose de raisonnable. Il a pris six scans de catalogue, d'environ 2,4 Mo chacun, en a découpé des produits individuels, et a relu ses découpes pour les vérifier. Cela représente de nombreux appels au modèle, chacun transportant plusieurs grandes images.

Cause 1 : chaque appel renvoyait les images en taille réelle

Chaque appel au modèle reconstruisait le corps de sa requête avec les images du tour en taille réelle : environ 20 Mo de base64 par appel. Le corps est construit comme une seule chaîne dans la mémoire de l'agent, donc chaque appel allouait encore 20 Mo.

Envoyer en taille réelle n'apportait même rien. Le modèle réduit lui-même les images à environ 1 300 pixels sur le grand côté ; les pixels supplémentaires ne coûtaient donc que de la mémoire et du temps d'envoi.

La correction : réduire une fois, envoyer petit

  • Les images de plus de 400 Ko sont désormais converties en JPEG d'au plus 1 568 pixels sur le grand côté avant l'envoi. Les plus petites partent telles quelles.
  • La conversion utilise ImageMagick, présent dans chaque bac à sable Mythex, et s'exécute une fois par fichier. Un tour qui montre les mêmes scans à chaque appel réutilise la copie réduite. Ce cache est indexé par le chemin, la taille et la date de modification du fichier, donc un fichier modifié est reconverti, et il a sa propre limite de taille pour ne pas devenir lui-même ce qui remplit la mémoire.
  • Le budget d'images pour un seul appel au modèle passe de 16 Mo à 6 Mo.

Les six mêmes scans coûtent désormais 4,4 Mo par appel au lieu de 19,7 Mo.

Cause 2 : le client du modèle ne libérait jamais une requête

La taille de chaque requête n'était qu'une partie du problème. La mémoire augmentait aussi d'appel en appel. Sur notre environnement de préproduction, le même type de tour gardait 369 Mo de mémoire vive même après un ramasse-miettes complet, l'instant où tout ce qui est inaccessible a été libéré. L'échantillonnage du tas a attribué toute la croissance à la partie de la bibliothèque cliente du modèle qui encode les corps de requête.

La cause était un écouteur d'abandon. Lorsque vous appuyez sur Stop, le signal d'abandon du tour se déclenche, et chaque requête à l'écoute s'annule. Nous transmettions ce signal unique, à l'échelle du tour, à chaque requête. La bibliothèque cliente ajoute un écouteur « abort » à tout signal qu'on lui donne, et ne le retire jamais.

Ainsi, chaque requête d'un tour restait accessible depuis le signal du tour, pendant toute la durée du tour : l'écouteur, qui retenait le contrôleur de la requête, qui retenait la requête, qui retenait tout le corps de la requête avec ses images.

La correction : un signal par requête

Chaque requête reçoit désormais son propre signal. Il n'est relié au signal du tour que pendant l'exécution de cette requête, pour une réponse en flux jusqu'à la lecture du dernier fragment, puis il est détaché aussitôt. Stop atteint toujours la requête en cours, mais le signal du tour ne retient plus qu'un écouteur à la fois.

Les tests couvrent les trois volets :

  • Après 12 appels, aucun écouteur ne subsiste sur le signal du tour. Avant la correction, il y en avait 12.
  • Sur 15 requêtes de 8 Mo chacune, la mémoire reste stable. Avant, elle augmentait de 128 Mo.
  • Appuyer sur Stop interrompt toujours une requête en plein milieu de son flux.

Cause 3 : le traçage conservait les entrées de chaque appel

Un instantané du tas a révélé un troisième détenteur : environ 6 Mo conservés par appel au modèle. Cela venait du flux d'événements du framework de l'agent. Il enregistre chaque appel au modèle d'un tour comme enfant du tour, entrées comprises, jusqu'à la fin du tour. Chaque image jointe à chaque appel restait donc en mémoire avec lui.

La correction : vider les images dès le retour de l'appel

Les images ne sont jamais attachées qu'aux messages construits pour un appel précis au modèle. Elles ne sont jamais stockées dans l'état sauvegardé de la conversation. Une fois l'appel terminé, on peut donc vider les données d'image de ces messages en toute sécurité. Le traceur conserve une trace textuelle de l'appel ; les pixels disparaissent.

Un détail était crucial : les images doivent être vidées sur place. Le traceur peut atteindre les mêmes données d'image par plusieurs références sur un message ; remplacer la liste par une nouvelle liste vide laisserait l'original, pixels compris, accessible. L'état de conversation sauvegardé n'est jamais modifié.

Un test exécute le graphe de l'agent sous ce même flux d'événements avec une image de 4 Mo à chaque appel. Sans la correction, la mémoire augmentait de 77 Mo sur 16 appels. Avec elle, elle reste stable.

Ce que nous en retenons

  • Les problèmes de mémoire peuvent avoir plusieurs couches. Trois causes distinctes gardaient les images trop longtemps, et il a fallu supprimer les trois pour que le tour reste dans la mémoire.
  • Tout ce que vous confiez à une bibliothèque peut être conservé. Un signal et une trace ont survécu aux appels auxquels on les avait donnés. Quand la mémoire croît par appel, regardez quels objets à longue durée de vie cet appel touche.
  • N'envoyez pas ce que l'autre côté jettera. Des pixels que le modèle écarterait de toute façon nous coûtaient de la mémoire à chaque appel.
  • Testez la mémoire directement. Chaque correction s'accompagne d'un test qui mesure la croissance du tas ou le nombre d'écouteurs, si bien qu'une régression apparaît comme un chiffre, et non comme un plantage.

C'est la deuxième histoire de mémoire liée à la sortie de l'agent en peu de temps ; la première était un libellé de progression renvoyé 41 000 fois. Pour en savoir plus sur ce qu'est l'agent et son fonctionnement, voir notre guide sur les agents IA et les docs du chat.

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 简体中文 繁體中文(香港) 繁體中文(台灣)