Le recall de votre provider de mémoire était tronqué : spill opt-in et deux autres échecs silencieux

Vous branchez un provider de mémoire externe dans Hermes — Hindsight, Mem0, Honcho, n’importe lequel. Pendant deux semaines, il se souvient vraiment de vous. Puis un jour vous mentionnez le contrat d’API convenu la semaine dernière, et il répond comme s’il n’en avait jamais entendu parler. Vous accusez le modèle, ou vous supposez que le provider a perdu l’écriture. La vraie histoire est plus agaçante : le recall a bien été récupéré, puis Hermes lui-même l’a remplacé par les premiers paragraphes, les derniers paragraphes et un chemin de fichier.
Ce comportement a changé le 9 octobre, à travers trois correctifs fusionnés couvrant trois échecs silencieux autour des plugins de mémoire externe : un recall tronqué, des providers qui disparaissent de hermes memory status, et une installation qui réessaie à chaque démarrage. Voici chaque symptôme, son mécanisme et ce qu’il faut faire.
Symptôme un : le recall est tronqué sans avertissement
Hermes dispose d’un mécanisme général de « spill » : quand la sortie d’un hook dépasse un seuil de taille, le texte complet n’est pas placé dans le contexte. Un aperçu tête-et-queue le remplace, avec un chemin de fichier pour le reste. Pour des hooks de plugin ordinaires, ce défaut est sensé — un seul plugin bavard ne devrait pas dévorer votre context window.
Le hic, c’est que le même mécanisme s’appliquait au recall de prefetch de mémoire externe. Ainsi, un provider qui avait déjà appliqué son propre budget de recall était rogné une seconde fois par Hermes : le milieu classé par pertinence disparaissait de la vue, et le modèle ne voyait que le début et la fin.
Le correctif en fait un choix explicite (PR #135157) :
# ~/.hermes/config.yaml
memory:
prefetch_spill_enabled: false # par défaut : le recall externe reste intact
Les détails valent la peine d’être connus :
- Défaut à false. Le recall externe atteint le modèle en entier. Un plafond de sécurité existe toujours : un recall supérieur à 10×
hooks.output_spill.max_chars(au moins 100 000 caractères) est tout de même spillié, afin qu’un provider emballé ne puisse pas faire déborder le context window. - Mettez-le à true pour retrouver l’ancien comportement : un recall surdimensionné devient un aperçu tête/queue plus un chemin de fichier. Il réutilise le seuil partagé
hooks.output_spill, les longueurs d’aperçu et le répertoire — ethooks.output_spill.enabled: falsecontinue de tout surcharger. - Le spill normal des hooks de plugin est inchangé et reste activé par défaut, indépendamment de ce réglage de mémoire.
- Le réglage est figé par snapshot au moment où le provider s’enregistre, donc redémarrez Hermes après l’avoir modifié ; une conversation en cours ne changera pas de comportement en plein tour.
- La documentation officielle le dit clairement : quand votre provider a son propre budget de recall, préférez-le. Il sait mieux qu’une règle de troncature quoi garder.
Un compromis honnête : conserver le recall complet rend les prompts plus gros. Si vous êtes sur des tokens facturés au compteur et faites tourner un service de mémoire bavard, réactiver prefetch_spill_enabled peut faire économiser de l’argent — à condition d’accepter que le milieu du recall soit moins visible.
Symptôme deux : votre provider disparaît de hermes memory status
Le deuxième correctif (PR #135524) tient en une ligne modifiée plus une ligne de docstring, et il compte plus que sa taille ne le suggère.
Pour décider si un dossier de plugin est un provider de mémoire, Hermes cherche register_memory_provider ou MemoryProvider dans son __init__.py. C’est un scan bon marché sans imports — mais il ne lisait que les 8 premiers KB du fichier. Un provider parfaitement fonctionnel avec une longue docstring de module était ignoré : absent de hermes memory status, et hermes memory setup <name> répondait « not found ».
Le plugin de catalogue excel_line a rencontré exactement cela, et le contournement de l’époque était sinistre : raccourcir la docstring pour que le code d’enregistrement tombe dans les 8 premiers KB (#135442 / #135473). Ce contournement n’est plus nécessaire. Le chemin rapide reste (toujours une vérification de texte de 8 KB bon marché) ; ce n’est que lorsqu’il ne trouve rien qu’il retombe sur le _defines_memory_provider existant, qui analyse les modules de premier niveau du paquet sans les importer. Les providers qui correspondaient déjà ne paient rien de plus — seuls les dossiers qui manquent paient une analyse AST.
Si vous avez écrit un plugin qui s’installe mais n’est jamais détecté : arrêtez de supprimer vos docs, et mettez à jour à la place.
Symptôme trois : le provider se réinstalle à chaque démarrage
Le troisième correctif (PR #135529) traite une boucle de nouvelle tentative qui s’emballe.
L’installation automatique d’un provider de mémoire s’exécute au démarrage de l’agent — hermes chat, un run cron, un agent du gateway, un redémarrage du Desktop, chacun compte. Avant le correctif, une installation échouée était réessayée à chacun de ces démarrages, et chaque nouvelle tentative était enregistrée comme un nouvel échec. La télémétrie de la PR (14 jours, dédupliquée) montre ce que cela produisait :
| plugin | ok | échecs | jours d’installation en échec | échecs par jour d’installation en échec |
|---|---|---|---|---|
| hindsight | 130 | 1048 | 204 | 5.1 |
| mnemosyne | 47 | 288 | 83 | 3.5 |
Les échecs quotidiens de Hindsight ont grimpé de 8 → 47 → 170 → 204 entre le 09-29 et le 10-02, pour l’essentiel la même poignée d’échecs comptée encore et encore, par-dessus un plus petit ensemble de vraies erreurs réseau (uv lock du PM, git clone). La conclusion de la PR est sans détour : c’est une boucle de nouvelle tentative, pas 1 048 défauts distincts.
La sémantique est désormais propre : STARTUP_RETRY_SECONDS = 3600.0 dans hermes_cli/memory_provider_migration.py — un provider dont l’installation automatique a échoué n’est réessayé par aucun processus pendant une heure. Quand il réapparaît, il vous indique quoi exécuter :
Its automatic install failed less than an hour ago: run `hermes plugins install hindsight`
La même PR a corrigé un diagnostic erroné connexe : quand plugins/<name> existe déjà comme symlink vers un autre emplacement, une installation depuis le catalogue dit désormais que l’emplacement est pris au lieu d’incriminer le manifeste du plugin.
Comment vérifier votre propre configuration
hermes memory status # quel provider externe est actif, et s'il est installé
hermes memory setup # choisir ou configurer un provider (mentionne aussi une installation manquante)
hermes memory off # désactiver le provider externe
hermes plugins list # le plugin est-il présent ?
Un ordre de triage pratique pour « il a oublié quelque chose » : confirmez que le provider est présent avec hermes memory status, puis lisez memory.provider et prefetch_spill_enabled dans config.yaml, puis déterminez si le modèle n’a jamais reçu le recall (configuration) ou si le provider ne l’a jamais stocké (service). Pour un rappel sur le choix d’un backend de mémoire, voir notre parcours pas à pas de la mémoire sémantique et la fonctionnalité dreaming / consolidation de mémoire ; la surface complète des commandes se trouve dans la référence hermes memory.
Statut des releases
Les trois correctifs ont été fusionnés le 2026-10-09, après la sortie de la v0.21.6 le 8 octobre, donc ils sont uniquement sur origin/main pour l’instant — attendez la prochaine release ou exécutez hermes update. Pour savoir comment les plugins sont examinés et épinglés au départ, voir le guide du catalogue de plugins.
À retenir
Les trois sont des échecs silencieux : pas d’erreur, pas de ligne de log, juste un comportement discrètement moins bon — un recall rogné, un plugin ignoré, un échec compté mille fois. Ils ont survécu si longtemps parce que chacun vit dans une couche que les utilisateurs ne voient pas. Si vous faites tourner un provider de mémoire externe, la chose la plus utile que vous puissiez faire aujourd’hui est d’ouvrir config.yaml, de regarder memory.prefetch_spill_enabled, et de décider si vous voulez un recall complet ou un prompt plus petit.