Skip to content

Daytona Executor: Sandbox Code Execution for AG2 Agents#

Open In Colab Open on GitHub

This notebook demonstrates how to use the Daytona Executor with AG2 agents for secure, isolated code execution in the cloud. Daytona spins up persistent sandbox environments that run LLM-generated code safely.

Key Benefits#

  • Isolated Environments: Code runs in secure, isolated sandbox environments — LLM-generated code never touches your local machine
  • Fast Startup: Sandboxes spin up on demand with near-instant startup
  • Multi-Language Support: Python, Bash, JavaScript, and TypeScript
  • Named Snapshots: Boot from a pre-configured snapshot template for instant, reproducible environments
  • Custom Images: Use any Docker image or build one declaratively via the Daytona Image object
  • Environment Variables: Inject environment variables into the sandbox at creation time — keep credentials out of your code
  • Persistent State: Files and installed packages persist across all executions in the same session
  • Clean Restart: Call restart() to wipe state and get a fresh sandbox

Prerequisites#

  1. Install AG2 with Daytona and OpenAI support:

    pip install "ag2[daytona,openai]"
    
  2. Get a Daytona API key from app.daytona.io

  3. Set your API keys as environment variables:

    export DAYTONA_API_KEY=your_daytona_api_key
    export OPENAI_API_KEY=your_openai_api_key  # or another LLM provider
    
import os

from autogen import ConversableAgent, LLMConfig
from autogen.coding import DaytonaCodeExecutor

# Ensure required API keys are set
assert os.getenv("DAYTONA_API_KEY"), "Please set DAYTONA_API_KEY environment variable"
assert os.getenv("OPENAI_API_KEY"), "Please set OPENAI_API_KEY environment variable (or configure another LLM provider)"

Example 1: Basic Sandbox Setup and Direct Code Execution#

Let’s start with the simplest usage: create a DaytonaCodeExecutor, attach it to an agent, and execute a Python snippet directly.

# Create a Daytona executor — a sandbox is provisioned immediately
executor = DaytonaCodeExecutor(timeout=60)

# Create an executor agent that runs code but doesn't call an LLM
code_executor_agent = ConversableAgent(
    "code_executor_agent",
    llm_config=False,
    code_execution_config={"executor": executor},
    human_input_mode="NEVER",
)

print(f"Sandbox ready — ID: {executor._sandbox.id}")
# Execute a simple Python block
task = """
Run this Python code:
```python
import datetime
import math

now = datetime.datetime.now()
print(f"Current time (inside Daytona sandbox): {now}")
print(f"Pi to 10 decimal places: {math.pi:.10f}")
print(f"Square root of 1764: {math.sqrt(1764):.1f}")
```
"""

reply = code_executor_agent.generate_reply(messages=[{"role": "user", "content": task}])
print(reply)
# Don't forget to clean up when done with a standalone executor
executor.delete()

Example 2: Full AI Agent Conversation (Code Writer + Executor)#

The real power comes from combining a code-writing LLM agent with the Daytona executor. Here we use the context manager form so the sandbox is automatically deleted when the with block exits.

llm_config = LLMConfig({
    "model": "gpt-4o-mini",
    "api_key": os.environ["OPENAI_API_KEY"],
})

code_writer_system_message = """
You are a helpful AI assistant that writes and executes Python code.
When writing code:
- Use only the Python standard library or packages available in the default Python environment
- Include informative print statements so results are clearly visible
- Write complete, runnable code blocks
Reply TERMINATE when the task is complete.
"""

with DaytonaCodeExecutor(timeout=120) as executor:
    code_writer_agent = ConversableAgent(
        "code_writer",
        system_message=code_writer_system_message,
        llm_config=llm_config,
        max_consecutive_auto_reply=5,
        code_execution_config=False,
    )

    code_executor_agent = ConversableAgent(
        "code_executor",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
        is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    )

    chat_result = code_executor_agent.run(
        recipient=code_writer_agent,
        message=(
            "Generate a list of the first 20 Fibonacci numbers, "
            "compute their sum and mean, and print a formatted table."
        ),
        max_turns=6,
    ).process()

print("\nSandbox automatically deleted — context manager exited.")

Example 3: Named Snapshots#

A Daytona snapshot is a pre-configured environment template registered in your Daytona account. Using a snapshot is the fastest way to get a fully reproducible environment — no image build step, no pip install at runtime. Snapshots take priority over image; you cannot set both.

Replace "my-data-science-snapshot" below with a snapshot name from your Daytona account.

SNAPSHOT_NAME = os.getenv("DAYTONA_SNAPSHOT", "my-data-science-snapshot")

with DaytonaCodeExecutor(snapshot=SNAPSHOT_NAME, timeout=120) as executor:
    agent = ConversableAgent(
        "snapshot_agent",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
    )

    reply = agent.generate_reply(
        messages=[
            {
                "role": "user",
                "content": """
Run this Python code:
```python
import sys, platform
print(f"Python  : {sys.version}")
print(f"Platform: {platform.platform()}")
print("Snapshot sandbox is ready!")
```
""",
            }
        ]
    )
    print(reply)

Example 4: Custom Docker Image#

You can provide any Docker image as a string, or use Daytona’s declarative Image builder to compose an image with pre-installed packages. This is useful when you need specific libraries and don’t have a snapshot.

from daytona import Image

# Declarative image build: start from a slim Python base and add packages
custom_image = Image.base("python:3.12-slim").pip_install("numpy", "pandas", "scipy")

with DaytonaCodeExecutor(image=custom_image, timeout=180) as executor:
    agent = ConversableAgent(
        "data_agent",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
    )

    task = """
Run this Python code:
```python
import numpy as np
import pandas as pd
from scipy import stats

# Generate random sales data
rng = np.random.default_rng(42)
sales = rng.normal(loc=500, scale=150, size=200).clip(0)

df = pd.DataFrame({"sales": sales})
print(df["sales"].describe().round(2).to_string())
print(f"\nSkewness : {stats.skew(sales):.4f}")
print(f"Kurtosis : {stats.kurtosis(sales):.4f}")
```
"""

    reply = agent.generate_reply(messages=[{"role": "user", "content": task}])
    print(reply)

You can also pass a plain Docker image name as a string:

with DaytonaCodeExecutor(image="python:3.12-slim", timeout=60) as executor:
    agent = ConversableAgent(
        "slim_agent",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
    )

    reply = agent.generate_reply(
        messages=[
            {
                "role": "user",
                "content": """
Run this Python code:
```python
import sys
print(f"Python {sys.version}")
print("Running in a slim Python 3.12 sandbox!")
```
""",
            }
        ]
    )
    print(reply)

Example 5: Multi-Language Execution#

The Daytona executor supports Python, Bash, JavaScript, and TypeScript out of the box. All languages can be used within the same sandbox session.

from autogen.coding import CodeBlock

with DaytonaCodeExecutor(timeout=60) as executor:
    # Bash — list files and show system info
    bash_result = executor.execute_code_blocks([
        CodeBlock(language="bash", code="echo 'Hello from Bash!' && uname -a && echo 'Disk usage:' && df -h /"),
    ])
    print("=== Bash ===")
    print(bash_result.output)

    # JavaScript — run via Node.js
    js_result = executor.execute_code_blocks([
        CodeBlock(
            language="javascript",
            code=(
                "const nums = Array.from({length: 10}, (_, i) => i + 1);\n"
                "const sum = nums.reduce((a, b) => a + b, 0);\n"
                "console.log('Numbers:', nums.join(', '));\n"
                "console.log('Sum:', sum);\n"
                "console.log('Mean:', sum / nums.length);"
            ),
        )
    ])
    print("\n=== JavaScript ===")
    print(js_result.output)

    # TypeScript — transpiled on-the-fly via ts-node
    ts_result = executor.execute_code_blocks([
        CodeBlock(
            language="typescript",
            code=(
                "function greet(name: string): string {\n"
                "  return `Hello, ${name}! Running TypeScript in Daytona.`;\n"
                "}\n"
                "console.log(greet('AG2'));"
            ),
        )
    ])
    print("\n=== TypeScript ===")
    print(ts_result.output)

Example 6: Injecting Secrets via Environment Variables#

Pass secrets (API keys, database URLs, etc.) to the sandbox at creation time using the env_vars parameter. The secrets are never written to disk — they live only as environment variables inside the sandbox.

# Secrets are injected into the sandbox environment at creation time
with DaytonaCodeExecutor(
    timeout=60,
    env_vars={
        "MY_API_KEY": "super-secret-key-123",
        "DATABASE_URL": "postgresql://user:pass@host/db",
        "APP_ENV": "production",
    },
) as executor:
    agent = ConversableAgent(
        "secrets_agent",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
    )

    reply = agent.generate_reply(
        messages=[
            {
                "role": "user",
                "content": """
Run this Python code:
```python
import os

api_key = os.environ.get("MY_API_KEY", "not set")
db_url = os.environ.get("DATABASE_URL", "not set")
app_env = os.environ.get("APP_ENV", "not set")

# Mask the secret values in output
print(f"MY_API_KEY   : {api_key[:8]}... (masked)")
print(f"DATABASE_URL : {db_url[:20]}... (masked)")
print(f"APP_ENV      : {app_env}")
```
""",
            }
        ]
    )
    print(reply)

Example 7: Full Data Analysis Workflow#

Putting it all together: an LLM agent that writes data analysis code, executed securely inside a Daytona sandbox with pre-installed scientific libraries.

data_science_image = Image.base("python:3.12-slim").pip_install("numpy", "pandas", "scipy", "scikit-learn")

llm_config = LLMConfig({
    "model": "gpt-4o-mini",
    "api_key": os.environ["OPENAI_API_KEY"],
})

system_message = """
You are a data science assistant. Write Python code using numpy, pandas, scipy,
and scikit-learn. The sandbox has these libraries pre-installed.
Include clear print statements and format numbers to 4 decimal places.
When the results of your code are executed by the executor and have a successful execution result, reply with TERMINATE to end the workflow.
"""

with DaytonaCodeExecutor(image=data_science_image, timeout=180) as executor:
    writer = ConversableAgent(
        "data_scientist",
        system_message=system_message,
        llm_config=llm_config,
        max_consecutive_auto_reply=5,
        code_execution_config=False,
    )

    runner = ConversableAgent(
        "executor",
        llm_config=False,
        code_execution_config={"executor": executor},
        human_input_mode="NEVER",
        is_termination_msg=lambda x: x.get("content", "").rstrip().endswith("TERMINATE"),
    )

    chat_result = runner.run(
        writer,
        message=(
            "Generate a synthetic dataset with 300 samples and 4 features "
            "(make_classification from sklearn). "
            "Train a logistic regression model, evaluate it with cross-validation, "
            "and print the mean accuracy, precision, recall, and F1 score."
        ),
        max_turns=8,
    ).process()

print("\nSandbox deleted — workflow complete.")

Summary#

This notebook covered the key capabilities of DaytonaCodeExecutor for AG2:

Feature How
Basic execution DaytonaCodeExecutor(timeout=60)
Auto cleanup with DaytonaCodeExecutor(...) as executor:
Named snapshot snapshot="my-snapshot"
Custom Docker image (string) image="python:3.12-slim"
Declarative image build Image.base("python:3.12-slim").pip_install(...)
Python / Bash / JS / TS execution CodeBlock(language="python", ...)
Inject environment variables env_vars={"API_KEY": "..."}
Fresh sandbox executor.restart()

Next Steps#

  1. Get your Daytona API key at app.daytona.io
  2. Install the integration: pip install ag2[daytona]
  3. Browse the AG2 Code Execution guide for more patterns
  4. See the full Daytona SDK docs for advanced sandbox configuration