Aller au contenu

Explorer des données dans un noyau persistant

Ce guide vous montre comment faire charger une ressource coûteuse une seule fois par un agent, puis lui poser plusieurs questions à son sujet, plutôt que de payer ce chargement à chaque question.

L’outil eval exécute un interpréteur persistant. Les variables, les imports et les données chargées survivent d’un appel à l’autre.

Un python -c ponctuel paie la mise en place à chaque question. Charger un dataframe coûte des secondes et une centaine de mégaoctets ; le représenter sous forme de graphique coûte des millisecondes. Quand chaque question coûte le chargement, un agent pose moins de questions qu’il ne le devrait — et chaque réponse revient sous forme de texte qu’il doit ensuite réanalyser.

Chargez data/runs.csv et indiquez-moi la durée p95 par agent.

import pandas as pd
runs = pd.read_csv("data/runs.csv")
len(runs)

La dernière expression est renvoyée comme le ferait un notebook, si bien que len(runs) indique le nombre de lignes sans print.

Puis, dans un appel distinct :

runs.groupby("agent")["duration_ms"].quantile(0.95).sort_values()

runs est toujours chargé. Un dataframe pandas revient rendu sous forme de tableau plutôt que sous forme de repr.

import matplotlib.pyplot as plt
runs.groupby("agent")["duration_ms"].mean().plot.bar()

Les figures sont capturées après la cellule et renvoyées sous forme d’images, que la transcription affiche. Vous voyez le graphique ; l’agent aussi.

Les figures sont fermées après leur capture. Une figure non collectée serait redessinée et renvoyée aussi par la cellule suivante : une session de cinq cellules émettrait donc cinq fois le premier graphique.

Ils sont réécrits en code ordinaire plutôt que de provoquer une erreur :

Vous tapez Ce qui se passe
%pip install x Exécute pip, puis vide le cache des modules pour que le prochain import le voie
%cd path os.chdir
!command Un sous-processus capturé
%%bash Le reste de la cellule comme script shell
%%timeit 1000 exécutions chronométrées

L’éviction du cache de %pip est celle qui compte : installer dans un interpréteur actif n’a aucun effet sur un module déjà importé, si bien que sans elle, la version que vous venez d’installer n’est pas celle qui s’exécute.

paths = tool("find", {"pattern": "**/*.dart", "path": "lib"})
for path in paths.splitlines()[:100]:
contents = tool("read", {"path": path})
...

tool(name, args) re-entre dans le registre d’outils de l’agent lui-même. Une cellule peut donc se ramifier sur une centaine de fichiers, ou déléguer via task, sans que le modèle dépense un tour par élément.

Un appel via la passerelle suspend le délai d’expiration de la cellule tant qu’il est en cours. Le budget mesure l’inactivité, pas le temps d’horloge — une cellule qui se ramifie vers des sous-agents et attend quatre minutes n’est pas bloquée, et la tuer en pleine ramification perdrait tout l’état du noyau.

eval(code: "…", reset: true, description: "Start a clean interpreter")

Abandonne l’interpréteur et en démarre un vierge. Tout ce qui était chargé disparaît, et c’est bien là l’intérêt.

Dans l’enceinte de la conversation quand elle en a une, sur l’hôte sinon.

Un interpréteur persistant piloté par un modèle qui lit un dépôt non fiable est exactement ce pour quoi l’exécution en enceinte seule existe : un appel bash ponctuel finit au moins, alors qu’un noyau est un shell qui se souvient.

Il ne démarre pas d’enceinte pour y parvenir — la même règle qui empêche un onglet rig de démarrer tout seul. Si vous voulez que le noyau soit enfermé, ouvrez d’abord le shell de la conversation. Voir Donner une machine de test à un agent.

  • Une cellule à la fois par noyau. Deux cellules qui se disputent un même interpréteur, ce sont deux moitiés de deux programmes entrelacées dans un seul espace de noms.
  • La sortie est plafonnée. Une cellule prise dans une boucle de print est tronquée plutôt que de remplir la fenêtre de contexte.
  • Quatre images par cellule. Une boucle de tracé peut émettre une figure à chaque itération, et chaque image représente un coût fixe élevé dans la requête qui la transporte.
  • Le noyau meurt avec l’exécution. Un noyau qui retient encore un dataframe après la fin de la conversation est une fuite.