Filesystem Tools & Backends

The agent’s ability to read, write, and navigate files is what makes it useful for real development tasks. In this module, you’ll explore the built-in file tools and learn how backends control where and how file operations happen.

Exercise 1: Built-in File Tools

Let’s exercise the full range of file tools by having the agent create a small project structure.

  1. Create a workspace directory:

    mkdir -p workspace
  2. Create an agent that uses the filesystem backend:

    • Run

    • Code Preview

    cat > file_tools.py << 'EOF'
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import FilesystemBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    WORKSPACE = os.path.abspath("./workspace")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}. All file operations should use paths within this directory.",
        backend=FilesystemBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "Create a directory called 'myproject' with a README.md and a main.py file. "
        "The README should describe the project. The main.py should have a hello world function. "
        "Then list the files you created and read back the README to verify it."
    )]})
    
    print(agent_response(result))
    EOF
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import FilesystemBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    WORKSPACE = os.path.abspath("./workspace")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}. All file operations should use paths within this directory.",
        backend=FilesystemBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "Create a directory called 'myproject' with a README.md and a main.py file. "
        "The README should describe the project. The main.py should have a hello world function. "
        "Then list the files you created and read back the README to verify it."
    )]})
    
    print(agent_response(result))

    Two things to notice here:

    • virtual_mode=False tells the backend to work directly with the real filesystem (required to avoid deprecation warnings)

    • The system prompt tells the agent its working directory — without this, the agent may write files to /tmp or other locations. The root_dir parameter configures the backend, but the agent also needs to know where to work via its prompt.

  3. Run the agent script:

    uv run file_tools.py
    Sample output (your results may vary)
    I've successfully created the project structure. Here's what I did:
    
    1. Created a directory called 'myproject'
    2. Created README.md with a project description
    3. Created main.py with a hello world function
    4. Listed the files to verify creation
    5. Read back the README content
    
    The directory contains:
    - README.md
    - main.py
    
    The README.md file contains:
    # MyProject
    
    This is a simple Python project demonstrating basic project structure.
    It includes a main.py file with a hello world function.

Look at the agent’s response. You’ll see it used multiple tools in sequence:

  • write_file — created both README.md and main.py

  • ls — listed the directory contents to verify

  • read_file — read back the README to confirm the content

This is the filesystem pillar in action. The agent understands how to combine these primitives to accomplish complex file manipulation tasks.

Exercise 2: The Execute Tool

The execute tool lets the agent run shell commands. This is powerful but requires a specific backend — LocalShellBackend — which extends FilesystemBackend with shell execution capabilities.

The execute tool is only available when using a backend that implements SandboxBackendProtocol. The standard FilesystemBackend provides file operations only (read, write, edit, ls, glob, grep). To add shell execution, use LocalShellBackend.
  1. Create a script to test the execute tool:

    • Run

    • Code Preview

    cat > execute_tool.py << 'EOF'
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import LocalShellBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    WORKSPACE = os.path.abspath("./workspace")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}.",
        backend=LocalShellBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "Use your execute tool to run 'ls -la' to show the contents of the current directory, "
        "then run 'date' to show the current date and time. "
        "Then find out which python is being used and what version it is."
    )]})
    
    print(agent_response(result))
    EOF
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import LocalShellBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    WORKSPACE = os.path.abspath("./workspace")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}.",
        backend=LocalShellBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "Use your execute tool to run 'ls -la' to show the contents of the current directory, "
        "then run 'date' to show the current date and time. "
        "Then find out which python is being used and what version it is."
    )]})
    
    print(agent_response(result))
  2. Run the script:

    uv run execute_tool.py
    Sample output (your results may vary)
    Here are the results:
    
    Directory Contents:
    total 0
    drwxr-xr-x  3 user  staff   96 Apr 05 07:35 .
    drwxr-xr-x  8 user  staff  768 Apr 05 07:37 ..
    drwxr-xr-x  4 user  staff  128 Apr 05 07:35 myproject
    
    Current Date and Time:
    Sun Apr  5 07:38:03 MDT 2026
    
    Python Information:
    - The `python` command is not available (command not found)
    - Python 3.12.8 is available via `python3`
    - Located at /usr/local/bin/python3

The agent ran real shell commands via the execute tool and captured their output. LocalShellBackend gives the agent everything FilesystemBackend has (file read/write/edit) plus shell execution.

Notice the Python result — the agent discovered that python isn’t in the subprocess PATH, but python3 is. This reveals an important characteristic of the execute tool: it runs in a minimal subprocess environment that doesn’t inherit your full shell configuration. The agent adapted by searching for alternatives, which is exactly the kind of problem-solving behavior you want.

The execute tool’s subprocess has a minimal PATH. Commands like python, uv, or node may not be found even though they work in your terminal. The agent can usually figure this out (as it did above), but if you need specific tools, provide absolute paths in your prompt or system prompt.

Sandboxing considerations: The execute tool runs real commands with real side effects. While file operations are scoped to root_dir, shell commands via execute can access the broader system. Use caution when giving agents shell access, especially with untrusted prompts. For production use, consider a sandboxed backend (Docker container, remote sandbox) instead of LocalShellBackend.

Exercise 3: Understanding Backend Security Boundaries

It’s important to understand what root_dir does and doesn’t do. Let’s test it.

  1. Create a script that asks the agent to read a file outside the workspace:

    • Run

    • Code Preview

    cat > scoped_backend.py << 'EOF'
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import FilesystemBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    WORKSPACE = os.path.abspath("./workspace/myproject")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}. All file operations should use paths within this directory.",
        backend=FilesystemBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "List all files in the current directory."
    )]})
    
    print(agent_response(result))
    EOF
    import os
    from deepagents import create_deep_agent
    from deepagents.backends import FilesystemBackend
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    WORKSPACE = os.path.abspath("./workspace/myproject")
    
    agent = create_deep_agent(
        model=MODEL,
        system_prompt=f"Your working directory is {WORKSPACE}. All file operations should use paths within this directory.",
        backend=FilesystemBackend(root_dir=WORKSPACE, virtual_mode=False),
    )
    
    result = agent.invoke({"messages": [("user",
        "List all files in the current directory."
    )]})
    
    print(agent_response(result))
  2. Run the scoped backend test:

    uv run scoped_backend.py
    Sample output (your results may vary)
    Here are the files in the current directory:
    - README.md
    - main.py

The agent lists files within the workspace as expected.

With virtual_mode=False, the root_dir parameter is a hint to the agent, not a hard security boundary. The system prompt tells the agent where to work, and well-behaved models will respect it. However, with virtual_mode=False there is no enforcement — the agent could read files outside root_dir if it chose to. This is by design for development flexibility.

For stronger isolation, Deep Agents provides:

  • StateBackend — ephemeral in-memory filesystem (no real disk access at all)

  • virtual_mode=True (coming in deepagents 0.5.0) — enforces path containment within root_dir

  • Container sandboxing — run the agent in a Docker/Podman container with limited filesystem access (see the deployment modules)

In development, the system prompt approach works well. In production, combine it with container-level isolation for defense in depth.

Exercise 4: StateBackend (Ephemeral In-Memory)

Sometimes you want the agent to work with files without touching the real filesystem. StateBackend stores everything in LangGraph’s state — ephemeral and in-memory.

StateBackend is actually the default backend when you don’t pass one to create_deep_agent(). It requires a runtime argument internally, so you don’t instantiate it directly — just omit the backend parameter:

  1. Create a script to test the default StateBackend:

    • Run

    • Code Preview

    cat > state_backend.py << 'EOF'
    import os
    from deepagents import create_deep_agent
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    # No backend parameter = StateBackend (ephemeral, in-memory)
    agent = create_deep_agent(model=MODEL)
    
    result = agent.invoke({"messages": [("user",
        "Create a file called 'notes.txt' with some content. "
        "Then read it back to verify."
    )]})
    
    print(agent_response(result))
    EOF
    import os
    from deepagents import create_deep_agent
    from utils import agent_response
    
    MODEL = os.environ.get("DEEPAGENTS_MODEL", "anthropic:claude-sonnet-4-6")
    
    # No backend parameter = StateBackend (ephemeral, in-memory)
    agent = create_deep_agent(model=MODEL)
    
    result = agent.invoke({"messages": [("user",
        "Create a file called 'notes.txt' with some content. "
        "Then read it back to verify."
    )]})
    
    print(agent_response(result))
  2. Run the StateBackend test:

    uv run state_backend.py
    Sample output (your results may vary)
    I've successfully created and verified the file:
    
    1. Created 'notes.txt' with the following content:
       "These are my notes for the deep agents workshop.
       The StateBackend stores files in memory only."
    
    2. Read back the file to verify - the content matches what was written.
    
    The file was successfully created and read, though it only exists in memory.

The agent successfully created and read the file, but it only exists in the LangGraph state — check your filesystem and you won’t find notes.txt anywhere. This is the same backend that was silently used in Modules 1-2 when you called create_deep_agent() without a backend parameter.

StateBackend is ideal for:

  • Testing — verify agent behavior without filesystem side effects

  • Sandboxing — let the agent manipulate "files" safely

  • Temporary workspaces — files only live for the duration of the conversation

When the agent run completes, the in-memory filesystem disappears.

Exercise 5: CompositeBackend Preview

For advanced use cases, you can layer multiple backends using CompositeBackend:

from deepagents.backends import CompositeBackend, FilesystemBackend, StateBackend

backend = CompositeBackend(
    mounts={
        "/workspace": FilesystemBackend(root_dir="./workspace", virtual_mode=False),
        "/scratch": StateBackend(),
    }
)

This setup routes different paths to different storage:

  • Files under /workspace go to the real filesystem

  • Files under /scratch stay in memory

You could combine FilesystemBackend for persistent project files with StateBackend for temporary scratch work, or mix in custom backends for database access, S3 storage, or anything else.

We’ll explore CompositeBackend in depth during the capstone project. For now, just know it’s available when you need sophisticated storage routing.

CLI Default: The CLI uses FilesystemBackend rooted at the current working directory by default. When you run deepagents, file operations happen in the directory where you launched the command.

Module Summary

In this module, you accomplished:

  • Exercised the full suite of file tools — write, read, ls, grep, execute

  • Explored FilesystemBackend’s root_dir scoping for safety and isolation

  • Used StateBackend for ephemeral in-memory file operations

  • Previewed CompositeBackend for advanced storage routing

Key takeaways:

  • File tools are the foundation of real-world agent tasks

  • Backends control where file operations happen and enforce boundaries

  • FilesystemBackend scopes to a root directory; StateBackend keeps everything in memory

  • The execute tool is powerful but requires careful sandboxing

  • Backends are pluggable — you can route different paths to different storage systems

Next up: you’ll learn how to create custom tools, extending your agent’s capabilities beyond the built-in defaults.