--- title: "LangChain Deep Agents" description: "Add sandboxed code execution to your LangChain Deep Agent in 3 lines. BaponiSandbox gives your agent file read/write, code execution, grep, and glob with persistent state, per-call overrides, real-time streaming, and lifecycle management." url: https://baponi.ai/docs/guides/deep-agents lastUpdated: 2026-03-22 --- # LangChain Deep Agents Give your LangChain Deep Agent a full sandbox environment with file operations, code execution, and persistent state in 3 lines of code. ## Get started 1. **Install** ```bash pip install baponi[deepagents] ``` 2. **Set your API keys** ```bash export BAPONI_API_KEY=sk-your-key-here export ANTHROPIC_API_KEY=your-anthropic-key-here ``` Get a Baponi key from [console.baponi.ai](https://console.baponi.ai). 1,000 free credits/month, no credit card. 3. **Create and run your agent** ```python from baponi.deepagents import BaponiSandbox from deepagents import create_deep_agent with BaponiSandbox() as sandbox: agent = create_deep_agent("anthropic:claude-sonnet-4-6", backend=sandbox) result = agent.invoke({"messages": [("human", "Use Python to calculate the SHA-256 hash of 'hello world'")]}) print(result["messages"][-1].content) ``` The agent writes a Python script, executes it inside an isolated sandbox, and returns the result. Files, installed packages, and environment changes persist across calls. ## What your agent can do Your agent gets these tools automatically: | Tool | Example | |------|---------| | **execute** | Run `pip install pandas`, compile code, start servers | | **read_file** | Inspect source files, configs, logs | | **write_file** | Generate code, create configs, save results | | **edit_file** | Refactor code, fix bugs, update settings | | **ls** | Explore project structure | | **grep** | Find function definitions, error patterns, TODOs | | **glob** | Find all `*.py` files, match naming patterns | ## Persistent sessions Files and installed packages persist automatically across calls within the same sandbox. Your agent can install dependencies, write code, then test it, all building on previous steps. To resume a session from a separate process (e.g., a new API request or a cron job), pass the same `thread_id`: ```python # Process A with BaponiSandbox(thread_id="my-project") as sandbox: agent = create_deep_agent("anthropic:claude-sonnet-4-6", backend=sandbox) agent.invoke({"messages": [("human", "Install pandas and write a CSV parser")]}) # Process B - picks up right where A left off with BaponiSandbox(thread_id="my-project") as sandbox: agent = create_deep_agent("anthropic:claude-sonnet-4-6", backend=sandbox) agent.invoke({"messages": [("human", "Add error handling to the CSV parser you wrote")]}) ``` ## Configuration ```python sandbox = BaponiSandbox( thread_id="data-pipeline-run-42", # session ID for persistent state timeout=60, # default 60s, Pro plan supports up to 3600 metadata={"user": "agent-1"}, # attached to every execution for audit env_vars={"ENV": "prod"}, # environment variables injected into every execution sub_paths=["/data/shared"], # storage path scoping for BYOB mounts ) ``` For self-hosted Baponi deployments: ```python sandbox = BaponiSandbox( base_url="https://baponi.internal.company.com", api_key="sk-your-enterprise-key", ) ``` ```python # Before agent = create_deep_agent(model, backend=OtherSandbox()) # After from baponi.deepagents import BaponiSandbox agent = create_deep_agent(model, backend=BaponiSandbox()) ``` ## Per-call overrides Override constructor defaults on individual `execute()` or `aexecute()` calls. Pass `None` (the default) to use the constructor value. Pass an explicit value to replace the constructor default for that call only - values are replaced, not merged. ```python sandbox = BaponiSandbox( env_vars={"ENV": "prod"}, metadata={"team": "data"}, sub_paths=["/data/shared"], ) # Override env_vars for this call only result = sandbox.execute("echo $ENV", env_vars={"ENV": "staging"}) # Override metadata for this call only result = sandbox.execute("cmd", metadata={"step": "cleanup"}) # Narrow storage scope to a specific subdirectory for this call only result = sandbox.execute("ls /data/shared", sub_paths=["/data/shared/2026-03-22"]) # Override timeout for this call only result = sandbox.execute("long-running-task", timeout=300) ``` All four parameters (`timeout`, `env_vars`, `metadata`, `sub_paths`) are available as per-call overrides on `execute()`, `aexecute()`, and `execute_stream()`. ## Context managers `BaponiSandbox` supports `with` and `async with` to ensure HTTP connections are properly closed: ```python # Sync with BaponiSandbox(api_key="sk-...", thread_id="session-1") as sandbox: result = sandbox.execute("echo hello") # connections closed automatically # Async async with BaponiSandbox(api_key="sk-...") as sandbox: result = await sandbox.aexecute("echo hello") ``` You can also call `sandbox.close()` or `await sandbox.aclose()` manually. Both are idempotent. ## Streaming `execute_stream()` provides real-time NDJSON streaming for long-running commands. Events arrive as they happen rather than waiting for the full result. ```python stream = sandbox.execute_stream("pip install -r requirements.txt && python train.py") with stream: for event in stream: print(event) result = stream.get_final_result() ``` `execute_stream()` accepts the same per-call overrides as `execute()`: ```python stream = sandbox.execute_stream( "python train.py", timeout=600, env_vars={"CUDA_VISIBLE_DEVICES": "0"}, ) ``` Streaming requires the Pro plan or higher. See [pricing](/pricing) for details. ## Sandbox provider `BaponiSandboxProvider` manages multiple sandbox instances by ID. Use it when your application needs to create, reuse, and clean up sandboxes programmatically - for example, one sandbox per user session in a server. ```python from baponi.deepagents import BaponiSandboxProvider provider = BaponiSandboxProvider(api_key="sk-...") # Create or reuse sandbox by ID sandbox = provider.get_or_create(sandbox_id="user-123") result = sandbox.execute("echo hello") # Different user gets a different sandbox sandbox_b = provider.get_or_create(sandbox_id="user-456") # Clean up when done provider.delete("user-123") ``` The provider also has async variants: `await provider.aget_or_create()` and `await provider.adelete()`. Constructor arguments (`timeout`, `metadata`, `env_vars`, `sub_paths`) are passed through to every sandbox the provider creates. Configuration falls back to `BAPONI_API_KEY` and `BAPONI_BASE_URL` environment variables when not provided explicitly. ## FAQ ### Do I need to manage sandbox lifecycles? For most use cases, no. Create a `BaponiSandbox` and use it - Baponi handles provisioning, isolation, and cleanup. If you need to manage multiple sandboxes programmatically (e.g., one per user in a multi-tenant server), use `BaponiSandboxProvider`. ### What languages can my agent run? Python, Node.js, and Bash are available in the default sandbox image. ### Does it support async and streaming? Yes. `BaponiSandbox` works with both sync (`execute`) and async (`aexecute`) out of the box. For real-time output, use `execute_stream()` to get NDJSON events as they arrive (Pro plan required). ### Can I override settings per execution? Yes. `execute()`, `aexecute()`, and `execute_stream()` accept `timeout`, `env_vars`, `metadata`, and `sub_paths` as keyword arguments. Passing a value overrides the constructor default for that call only. --- More: [Documentation index](https://baponi.ai/docs.md) · [Pricing](https://baponi.ai/pricing) · Site index: [llms.txt](https://baponi.ai/llms.txt)