Notes pratiques : Mettez à jour votre agent Deep Agent avec un environnement de test open source local
Guide pratique pas à pas : Améliorez votre Deep Agent avec un environnement de test open source local : contrats, vérifications et emplacements pour du code à intégrer destinés aux équipes utilisant ce modèle.
Utilisez ceci comme une version révisée destinée aux opérateurs des idées présentées dans « Améliorez votre agent avancé grâce à un environnement de test open source local » : étapes claires, emplacements de code organisés et notes de récupération qui survivent au transfert de responsabilités. L’étape « Aperçu » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.
Qu’est-ce qu’un environnement de test, et pourquoi les agents en ont-ils besoin
Pour l’étape « Qu’est-ce qu’un sandbox ? », définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documentez ensemble le parcours idéal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations ultérieures. Imposez une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. Une configuration en temps de compilation ne garantit pas la complétude du fonctionnement commercial.
Les pièges
Pour l’étape de capture, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Faites approuver par un humain les actions qui entraînent des dépenses ou modifient des données de production. La connexion en temps de compilation ne garantit pas la complétude du processus métier.
Entrer dans OpenSandbox
Pour l’étape Enter OpenSandbox, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute exécution partielle silencieuse. Faites approuver manuellement les actions qui entraînent des dépenses ou modifient des données de production. Une connexion en temps de compilation ne garantit pas l’exhaustivité du processus métier. Pour l’étape Enter OpenSandbox, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.
Point d’extension du sandbox pour les Deep Agents
Lors de la phase d’extension du sandbox pour les Deep Agents, notez d’abord le contrat : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de maintenir l’honnêteté des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations ultérieures. Créez des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur tente à nouveau un nœud ultérieur.
class BaseSandbox(ABC):
def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse: ...
@property
def id(self) -> str: ...
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]: ...
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]: ...
Construire l’intégration
Lors de la phase de mise en place de l’intégration, notez d’abord les exigences du contrat : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une seule responsabilité et non un processus embrouillé. Faites des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel d’LLM lorsque l’opérateur réessaie un nœud ultérieur.
Comment fonctionne OpenSandbox ?
Lors de l’étape « Comment OpenSandbox fonctionne », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle garantit l’honnêteté des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminations partielles silencieuses. Faites des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel d’LLM lorsque l’opérateur réessaie un nœud ultérieur. Lors de l’étape « Comment OpenSandbox fonctionne », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle garantit l’honnêteté des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent se trouver en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du graphe.
# Generate a starter config
uvx opensandbox-server init-config ~/.sandbox.toml --example docker
# Start the server
uvx opensandbox-server
Code d’intégration simplifié
La phase de code d’intégration simplifié fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et le traitement des messages non livrés font partie du produit, et non d’une mise en forme ultérieure. Gardez l’état des graphes plat et typé : les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.
.create()
La phase de création fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Maintenez l’état du graphe simple et typé. Les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.
class MinimalOpenSandboxBackend(BaseSandbox):
def __init__(self, sandbox: Sandbox, runner: AsyncRunner):
self._sandbox = sandbox
self._runner = runner
from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
IMAGE = "opensandbox/code-interpreter:v1.1.0"
ENTRYPOINT = ["/opt/code-interpreter/code-interpreter.sh"]
@classmethod
def create(cls, api_key: str | None = None) -> "MinimalOpenSandboxBackend":
runner = AsyncRunner()
config = ConnectionConfig(domain="localhost:8080", api_key=api_key)
sandbox = runner.run(
Sandbox.create(IMAGE, entrypoint=ENTRYPOINT, connection_config=config, timeout=timedelta(minutes=30))
)
return cls(sandbox, runner)
.id()
Cette étape fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemplaire idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Traitez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définites des vérifications de succès et refusez toute complétion partielle silencieuse. Maintenez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption. Cette étape fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemplaire idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent se trouver en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du graphe.
@property
def id(self) -> str:
return self._sandbox.id
.execute()
Pour l’étape d’exécution, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’une mise en forme ultérieure. Imposez une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. La configuration en temps de compilation ne garantit pas la complétude du processus métier.
from deepagents.backends.protocol import ExecuteResponse
def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
execution = self._runner.run(self._sandbox.commands.run(command))
stdout = "\n".join(c.text for c in execution.logs.stdout)
stderr = "\n".join(c.text for c in execution.logs.stderr)
output = "\n".join(p for p in (stdout, stderr) if p)
return ExecuteResponse(output=output, exit_code=execution.exit_code or 0)
.upload_files()
Pour l’étape d’uploadfiles, définissez les entrées, le responsable de cette étape ainsi que les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un processus embrouillé. Faites approuver par des humains les actions qui entraînent des dépenses ou modifient des données de production. Une connexion en temps de compilation ne garantit pas la complétude du processus métier.
from opensandbox.models import WriteEntry
from deepagents.backends.protocol import FileUploadResponse
def upload_files(self, files: list[tuple[str, bytes]]) -> list[FileUploadResponse]:
entries = [WriteEntry(path=path, data=data, mode=644) for path, data in files]
try:
self._runner.run(self._sandbox.files.write_files(entries))
return [FileUploadResponse(path=p) for p, _ in files]
except Exception as exc:
return [FileUploadResponse(path=p, error=str(exc)) for p, _ in files]
.download_files()
Pour l’étape de téléchargement des fichiers, définissez les entrées, le responsable de l’étape et les critères de fin avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminations partielles silencieuses. Faites approuver par un humain les cas où de l’argent est dépensé ou où des données de production sont modifiées. La connexion en temps de compilation ne revient pas à une complétude opérationnelle.
from deepagents.backends.protocol import FileDownloadResponse
def download_files(self, paths: list[str]) -> list[FileDownloadResponse]:
results = []
for path in paths:
try:
content = self._runner.run(self._sandbox.files.read_bytes(path))
results.append(FileDownloadResponse(path=path, content=content))
except Exception as exc:
results.append(FileDownloadResponse(path=path, error=str(exc)))
return results
Pour l’étape de téléchargement des fichiers, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.
.kill()
Lorsque vous travaillez sur l’étape de suppression, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Documentez à la fois le parcours normal et les scénarios de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’améliorations ultérieures. Créez un point de contrôle après les étapes coûteuses. La reprise ne doit pas facturer à nouveau la même appel de LLM lorsque l’opérateur tente à nouveau un nœud ultérieur.
def kill(self) -> None:
self._runner.run(self._sandbox.kill())
self._runner.shutdown()
Le pont synchronisé/asynchrone
Lors de la phase du « Pont synchronisé/asynchrone », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de maintenir l’honnêteté des modifications ultérieures du code. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Faites des points de contrôle après les étapes coûteuses. Le mécanisme de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur réessaie un nœud ultérieur.
Un agent d’analyse de données dans le sandbox
Lors du traitement de l’étape de l’agent d’analyse de données A, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminations partielles silencieuses. Créez des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur réessaie un nœud ultérieur. Lors du traitement de l’étape de l’agent d’analyse de données A, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.
import asyncio
import nest_asyncio
import threading
from datetime import timedelta
from pathlib import Path
from deepagents import create_deep_agent
from deepagents.backends.protocol import ExecuteResponse, FileDownloadResponse, FileUploadResponse
from deepagents.backends.sandbox import BaseSandbox
from langchain.chat_models import init_chat_model
from opensandbox import Sandbox
from opensandbox.config import ConnectionConfig
from opensandbox.models import WriteEntry
# nest_asyncio for running async functions in Jupyter.
nest_asyncio.apply()
IMAGE = "opensandbox/code-interpreter:v1.1.0"
ENTRYPOINT = ["/opt/code-interpreter/code-interpreter.sh"]
backend = MinimalOpenSandboxBackend.create(api_key="SANDBOX_API_KEY")
print("Sandbox ready:", backend.id)
llm = init_chat_model(
model="gemini-3.5-flash",
model_provider="google_genai",
api_key=os.environ["GOOGLE_API_KEY"],
max_tokens=14750,
max_retries=5,
)
agent = create_deep_agent(
model=llm,
system_prompt=(
"You are a Python coding assistant with sandbox access. "
"You specialize in performing data analysis and data visualization with python,"
"you generate clear reports with good looking charts using seaborn."
),
backend=backend,
)
csv_bytes = Path("customers-1000.csv").read_bytes()
results = backend.upload_files([("/workspace/customers-1000.csv", csv_bytes)])
for r in results:
if r.error:
print(f"Upload failed for {r.path}: {r.error}")
else:
print(f"Uploaded {r.path}")
result = agent.invoke({
"messages": "Perform a deep exploratory data analysis on the customers-1000.csv file "
"and summarize the findings in a markdown report with clear charts."
})
# Deep Exploratory Data Analysis: Customer Acquisition and Profiling
**Dataset:** `customers-1000.csv`
**Analysis Period:** Jan 2020 – May 2022
---
## 1. Executive Summary
This report presents a comprehensive exploratory data analysis (EDA) of a customer database containing 1,000 unique records. The analysis delves into geographical distributions, sign-up temporal trends, domain & technical alignments, and name demographics to uncover actionable insights for strategic growth.
### Key Takeaways
1. **Unprecedented Global Reach:** The customer base is extraordinarily decentralized, spanning **240 countries** across all **7 continents** (including Antarctica). No single country represents more than 1.2% of the customer base. Africa (24.7%) and Asia (22.6%) are the leading regions, followed by Europe (18.5%) and North America (16.1%).
2. **Stable Acquisition Trends:** Customer subscriptions are remarkably stable, averaging roughly **34-35 new customers per month** across 2020 and 2021. This consistency is maintained across all continents year-over-year, indicating a highly standardized, globally distributed customer acquisition channel.
3. **Mid-Week and Weekend Consistency:** Subscriptions are evenly spread across the days of the week, with a minor peak on Friday and Saturday, and a minor trough on Thursday.
4. **B2B / Synthetic Profile Characteristics:** The dataset shows zero domain overlap between customer email domains and company websites (0.0% exact match across 923 unique domains). Combined with the near 1-to-1 ratio of customers to companies, this suggests a highly B2B-centric profile (one representative per enterprise) or synthetically generated profiles with randomized fields.
5. **Standardized TLD Footprint:** The `.com` top-level domain (TLD) dominates both emails (61.2%) and corporate websites (58.8%). The remaining distribution is evenly split among `.org`, `.net`, `.biz`, and `.info`.
---
## 2. Dataset Structure & Data Integrity
The initial dataset contains **1,000 rows** and **12 columns**. An inspection of data integrity reveals excellent completeness:
- **Zero Missing Values:** Every column is 100% populated.
- **Zero Duplicates:** There are no duplicate rows, and the `Customer Id` column contains 1,000 unique identifiers.
- **Data Types:** All columns are stored as object/string types except for `Index` (integer).
### Data Preprocessing & Feature Engineering
To enable deep exploratory analysis, several features were engineered:
1. **Temporal Features:** `Subscription Date` was parsed as a datetime object, allowing the extraction of `Sub_Year`, `Sub_Month`, `Sub_Month_Name`, `Sub_Day_of_Week`, and `Sub_Year_Month` (period).
2. **Geographical Mapping:** Using the `pycountry` and `pycountry-convert` libraries, coupled with a manual fallback dictionary for territories, each of the 240 countries was successfully mapped to its respective **Continent**.
3. **Domain & Technical Profiles:** Email domains (`Email_Domain`), email TLDs (`Email_TLD`), and website TLDs (`Website_TLD`) were extracted to analyze the technical profiling of users.
---
## 3. Geographical Analysis
### Continent-Level Distribution
The geographic reach of this customer base is truly global. Rather than being concentrated in a single dominant market like North America or Europe, customers are spread across all continents:
| Continent | Customer Count | Percentage |
| :--- | :---: | :---: |
| **Africa** | 247 | 24.7% |
| **Asia** | 226 | 22.6% |
| **Europe** | 185 | 18.5% |
| **North America** | 161 | 16.1% |
| **Oceania** | 107 | 10.7% |
| **South America** | 54 | 5.4% |
| **Antarctica** | 20 | 2.0% |
#### Chart 1: Customer Distribution by Continent

### Country-Level Distribution (Top 15 Countries)
The country-level distribution exhibits a heavy tail, with the 1,000 customers distributed across **240 distinct nations**. This indicates that the average number of customers per country is only **4.17**.
The top countries by customer density are:
- **Liechtenstein:** 12 customers (1.2%)
- **Gabon:** 10 customers (1.0%)
- **China, Bangladesh, Reunion, Nigeria, Luxembourg:** 9 customers each (0.9%)
This extreme dispersion suggests a borderless, digital-first product that appeals universally across jurisdictions without localized geographic bias.
#### Chart 2: Top 15 Countries by Customer Count

---
## 4. Temporal Analysis (Subscription Trends)
... (Trimmed to keep blog (estimated read time short))
De cahier de notes à un paquet PyPi
La méthode « De cahier de notes à une étape » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Documentez en même temps le parcours réussi et celui de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’une mise en forme ultérieure. Maintenez l’état des graphes simple et typé. Les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.
!pip install deepagents-opensandbox-backend
Découvrez-le
La phase « Check it out » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Maintenez l’état des graphes simple et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.
Liste de contrôle opérationnelle
Lorsque vous travaillez sur la phase de liste de contrôle opérationnelle, notez d’abord les exigences : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste garantit que les modifications ultérieures du code restent transparentes.
Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des factures inattendues lorsque le processus passe de la démonstration aux environnements partagés.
Point de contrôle après des étapes coûteuses. La reprise ne doit pas facturer à nouveau la même appel de LLM lorsque l’opérateur réessaie un nœud ultérieur.
Fixez les versions des dépendances et enregistrez le digest de l’image ayant exécuté la démonstration. La reproductibilité vaut mieux que les connaissances propres à un groupe.
Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stockages de secrets et les flags fonctionnels doivent se trouver en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du graphe.
Point de contrôle après des étapes coûteuses. La reprise ne doit pas facturer à nouveau la même appel de LLM lorsque l’opérateur réessaie un nœud ultérieur.
Au préalable de promouvoir la pile, figez les versions, capturez une transcription exemplaire pour le chemin critique et confirmez les étapes de rollback. Les environnements partagés nécessitent des limites de débit, des vérifications de location et un responsable clair pour la rotation des secrets. Préférez une fiabilité banale à de brillantes démonstrations ponctuelles.
Note de lot pour 43662eb4f13d : éviter d’inclure les clés du fournisseur dans le répertoire, fixer une limite pour les tokens par session, et stocker les transcriptions à côté des fichiers de test afin que les remplacements ultérieurs de modèles restent comparables.
Lors du traitement de la phase 0 de la note de renforcement de sécurité, écrivez d’abord le contrat : entrées requises, signal de succès, et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications ultérieures du code. Considérez cette phase comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définez des vérifications de succès, et refusez toute exécution partielle silencieuse.
Détail de renforcement 0/814 : mesurez le temps d’exécution, la catégorie de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.