Guide #3 Getting Started 20 min read March 2026

LangChain Agent with
Browser and Terminal

LangChain has 47 million PyPI downloads. It's running in production at Cisco, Uber, JPMorgan, and hundreds more. It gives your agent memory, tools, and reasoning chains. What it doesn't give your agent is a computer.

This guide builds LangChain tools that let a ReAct agent create sandboxes, execute shell commands, launch browsers, and manage files — all in isolated cloud environments. The agent decides when to browse and when to code.

The missing piece in every LangChain tutorial

Every LangChain tutorial shows you how to build an agent that calls tools. But when it comes to code execution, they all do the same thing: use PythonREPLTool which runs exec() inline in your process, or point at a Jupyter kernel on your laptop. No isolation, no browsers, no real file system, no parallel execution.

The "code interpreter" tools in LangChain are toys. They run in your process memory. They can't install system packages, can't git clone, can't open a browser, can't persist files between runs. The moment you need your agent to do anything real — research the web, clone a repo, run a test suite — those tools break down.

Sandbox Platform fills this gap. Your LangChain agent gets tools that provision real Linux VMs, execute arbitrary shell commands in isolation, launch cloud browsers controllable via CDP, and read/write files to a persistent workspace.

LangChain ReAct Agent → tool calls → Sandbox Platform API VMs / Browsers / Files The agent reasons. The sandbox executes. LangChain orchestrates the loop.

Setup

bash
# Install dependencies
pip install langchain langchain-openai langchain-anthropic httpx

# Set your keys
export OPENAI_API_KEY="sk-..."          # or ANTHROPIC_API_KEY for Claude
export SANDBOX_API_KEY="ab0t_sk_live_..."
export SANDBOX_URL="https://sandbox.dev.ab0t.com"

Building the tools

LangChain's @tool decorator turns any Python function into a tool the agent can call. The docstring becomes the tool description that the LLM reads to decide when to use it.

Sandbox client

python
# sandbox_tools.py
import os, httpx
from langchain_core.tools import tool

_URL = os.environ.get("SANDBOX_URL", "https://sandbox.dev.ab0t.com")
_KEY = os.environ["SANDBOX_API_KEY"]
_client = httpx.Client(base_url=_URL, headers={"Authorization": f"Bearer {_KEY}"}, timeout=120)
_sandbox_id: str | None = None

Tool definitions

python
@tool
def create_sandbox(name: str, instance_type: str = "ab0t.medium") -> str:
    """Create an isolated Linux VM for code execution. Must be called before
    execute_command or save_file. Use ab0t.micro for light tasks, ab0t.medium
    for builds, ab0t.gpu for GPU. Returns sandbox ID when ready."""
    global _sandbox_id
    r = _client.post("/api/sandboxes", json={
        "name": name, "instance_type": instance_type, "auto_stop_minutes": 60
    })
    r.raise_for_status()
    d = r.json()
    _sandbox_id = d["sandbox_id"]
    return f"Sandbox {_sandbox_id} created ({d['status']}). Cost: ${d.get('hourly_cost','0.04')}/hr."

@tool
def execute_command(command: str) -> str:
    """Run a shell command in the active sandbox. Use for git, pip, npm, pytest,
    any CLI tool. Returns stdout, stderr, exit code. Install packages as needed."""
    if not _sandbox_id: return "No sandbox. Call create_sandbox first."
    r = _client.post(f"/api/sandboxes/{_sandbox_id}/execute", json={"command": command})
    r.raise_for_status()
    d = r.json()
    out = d.get("stdout", "")
    err = d.get("stderr", "")
    code = d.get("exit_code", 0)
    return f"{out}\n{err}\nExit: {code}".strip()

@tool
def launch_browser(url: str = "") -> str:
    """Launch a cloud Chrome browser. Returns a CDP URL for programmatic control
    and an access URL for visual viewing. Use for web research, scraping, testing."""
    r = _client.post("/api/browsers", json={"browser_type": "chrome", "homepage_url": url})
    r.raise_for_status()
    d = r.json()
    return f"Browser {d['container_id']} launched.\nCDP: {d.get('cdp_url','pending')}\nView: {d.get('access_url','pending')}"

@tool
def save_file(filename: str, content: str) -> str:
    """Write a file to the sandbox workspace. Use for scripts, configs, data."""
    if not _sandbox_id: return "No sandbox."
    _client.post(f"/api/sandboxes/{_sandbox_id}/files", json={"filename": filename, "content": content})
    return f"Saved /workspace/{filename}"

@tool
def stop_sandbox() -> str:
    """Stop the sandbox. Data persists. Billing stops."""
    global _sandbox_id
    if _sandbox_id:
        _client.post(f"/api/sandboxes/{_sandbox_id}/stop")
        sid = _sandbox_id; _sandbox_id = None
        return f"Sandbox {sid} stopped."
    return "No active sandbox."

ALL_TOOLS = [create_sandbox, execute_command, launch_browser, save_file, stop_sandbox]

Creating the ReAct agent

A ReAct agent reasons step by step and calls tools as needed. LangChain's create_react_agent handles the loop — the model decides what to do next based on tool outputs.

python
# agent.py
from langchain_openai import ChatOpenAI
from langchain.agents import create_react_agent, AgentExecutor
from langchain_core.prompts import ChatPromptTemplate
from sandbox_tools import ALL_TOOLS

llm = ChatOpenAI(model="gpt-4.1", temperature=0)
# Or: from langchain_anthropic import ChatAnthropic
# llm = ChatAnthropic(model="claude-sonnet-4-6")

prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a developer with access to cloud sandboxes.

RULES:
1. Always create_sandbox before running commands.
2. Use execute_command for shell operations (git, pip, tests, builds).
3. Use launch_browser for web research, scraping, UI testing.
4. Use save_file to write code or data.
5. Always stop_sandbox when the task is done.

You have full root access on the sandbox. Install anything you need."""),
    ("human", "{input}"),
    ("placeholder", "{agent_scratchpad}"),
])

agent = create_react_agent(llm, ALL_TOOLS, prompt)
executor = AgentExecutor(agent=agent, tools=ALL_TOOLS, verbose=True, max_iterations=25)

Example: research + code in one task

This is the use case that shows why a LangChain agent needs both a browser and a terminal. The agent researches a topic on the web, then writes code based on what it found.

python
# run.py
result = executor.invoke({
    "input": """
    1. Create a sandbox.
    2. Launch a browser and research the top 5 Python web frameworks in 2026.
       Visit their official sites and GitHub repos. Note star counts and key features.
    3. In the sandbox terminal, write a Python script that generates a comparison
       table as a Markdown file, using the data you collected.
    4. Run the script and show me the output.
    5. Stop the sandbox.
    """
})
print(result["output"])

The agent will:

  1. Call create_sandbox("research-workspace")
  2. Call launch_browser("https://github.com/topics/python-web-framework")
  3. Navigate to each framework's page, extract star counts and features
  4. Call save_file("compare.py", "...") with a Python script
  5. Call execute_command("python compare.py")
  6. Call stop_sandbox()
  7. Return the Markdown comparison table
Browser + terminal in the same task

This is what makes the sandbox different from a code interpreter or a scraping API. The agent has a browser for the web AND a terminal for code, and it switches between them as the task requires. No other LangChain tool gives you both.

LangGraph version: stateful workflows

For more complex workflows, LangGraph gives you a directed graph with explicit state management. Each node in the graph can use sandbox tools, and the state persists between nodes.

python
from langgraph.graph import StateGraph, MessagesState
from langgraph.prebuilt import ToolNode
from sandbox_tools import ALL_TOOLS

# Bind tools to the model
model_with_tools = llm.bind_tools(ALL_TOOLS)

def agent_node(state: MessagesState):
    return {"messages": [model_with_tools.invoke(state["messages"])]}

def should_continue(state: MessagesState):
    last = state["messages"][-1]
    if last.tool_calls:
        return "tools"
    return "end"

# Build the graph
graph = StateGraph(MessagesState)
graph.add_node("agent", agent_node)
graph.add_node("tools", ToolNode(ALL_TOOLS))
graph.set_entry_point("agent")
graph.add_conditional_edges("agent", should_continue, {"tools": "tools", "end": "__end__"})
graph.add_edge("tools", "agent")

app = graph.compile()

# Run it
result = app.invoke({
    "messages": [("human", "Create a sandbox, clone fastapi, run tests, report failures.")]
})
print(result["messages"][-1].content)

Works with any model

The tools are model-agnostic. Swap the LLM and everything else stays the same:

ProviderImportModel
OpenAIlangchain_openai.ChatOpenAIgpt-4.1, gpt-4o
Anthropiclangchain_anthropic.ChatAnthropicclaude-sonnet-4-6, claude-opus-4-6
Googlelangchain_google_genai.ChatGoogleGenerativeAIgemini-2.5-pro
Locallangchain_ollama.ChatOllamallama3, codestral
Model choice matters for tool use

Opus 4.6 and GPT-4.1 are the most reliable at multi-step tool-calling workflows. Smaller models (Haiku, GPT-4o-mini) work for simple tasks but may need more explicit instructions for complex research-and-code sequences.

Cost

TaskSandboxDurationCost
Research + code (browser + terminal)ab0t.medium + Chrome~20 min$0.03
Clone + test a repoab0t.medium~15 min$0.01
10-site parallel scrape10x Chrome~10 min$0.07
Long research sessionab0t.medium + Chrome~2 hrs$0.12

Tips

Set max_iterations

AgentExecutor(max_iterations=25) prevents the agent from looping forever. For complex tasks, increase to 40-50. For simple tasks, 10 is enough.

Use verbose=True during development

It shows every tool call, every observation, every reasoning step. Essential for debugging when the agent goes off-track.

Break big tasks into sub-tasks

Instead of "research 50 companies and write a report", give the agent a list of 5 and call it 10 times. Each run gets a fresh sandbox and clean state.

Combine with LangChain memory

Use ConversationBufferMemory to let the agent remember results from previous tool calls in the same session. This helps it build on earlier findings without re-researching.

Troubleshooting

Agent doesn't create sandbox first

Put it in the system prompt in bold: "ALWAYS call create_sandbox before any other tool." Or make the first message explicitly ask for it.

Tool output too long

Command outputs can be thousands of lines. Truncate in the tool: return out[:3000]. LLMs don't need the full npm install log.

Agent gets stuck in a loop

max_iterations prevents infinite loops. If the agent retries the same failing command, add error-handling guidance to the system prompt: "If a command fails twice, try a different approach."

What's next

Give your LangChain agent a computer

Browsers, terminals, files. Real compute for real agents.

Get Started Free