How to Integrate a Custom Backend Implementation Using the BackendProtocol in DeepAgents
To integrate a custom backend in DeepAgents, implement the BackendProtocol interface (or SandboxBackendProtocol for command execution), override the required file operation methods, and pass the instance or a factory function to DeepAgent.
The langchain-ai/deepagents repository provides a pluggable backend architecture that abstracts file-system operations behind the BackendProtocol interface. When you integrate a custom backend implementation using the BackendProtocol in DeepAgents, you can connect agents to diverse storage systems—from in-memory mocks and cloud object stores to remote containerized sandboxes—without modifying the core agent logic.
Understanding the BackendProtocol Architecture
DeepAgents defines a pluggable backend architecture that abstracts all file-system-like operations (list, read, write, edit, upload, download, grep, glob) behind the BackendProtocol interface. Backends that also need to run shell commands implement the derived SandboxBackendProtocol.
Core Protocol Definitions in protocol.py
The BackendProtocol in libs/deepagents/deepagents/backends/protocol.py (lines 44–512) declares the core contract for any backend. All file-related methods are declared here and raise NotImplementedError by default. Key methods include:
ls_info(path: str) -> LsResultread(file_path: str, offset: int = 0, limit: int = 2000) -> ReadResultwrite(file_path: str, content: str) -> WriteResultedit(file_path: str, old_string: str, new_string: str, replace_all: bool = False) -> EditResult
SandboxBackendProtocol for Command Execution
For backends that support command execution, SandboxBackendProtocol (same file, lines 512–558) extends BackendProtocol with the execute(command: str, *, timeout: int | None = None) -> ExecuteResponse method. This is essential for remote containers, VMs, or any sandboxed runtime.
The BaseSandbox Mixin
The BaseSandbox class in libs/deepagents/deepagents/backends/sandbox.py (lines 217–260) provides a reusable mix-in that implements all file operations by delegating to execute(). Custom sandboxes usually subclass this instead of re-implementing every method, as it automatically translates file commands into shell commands.
Implementing a Custom Backend
To add a custom backend, choose the appropriate base protocol, implement the abstract methods your use-case requires, and optionally provide a factory that binds the backend to the runtime.
Minimal Read-Only Backend Example
The following example implements a read-only in-memory backend by subclassing BackendProtocol:
# file: my_backend.py
from __future__ import annotations
from typing import Final
from deepagents.backends.protocol import BackendProtocol, LsResult, ReadResult, WriteResult, EditResult, FileInfo
class InMemoryBackend(BackendProtocol):
"""A simple read‑only backend that stores files in a dict."""
def __init__(self, files: dict[str, str]) -> None:
# `files` maps absolute paths → file content (UTF‑8 string)
self._store: Final = files
# ---------- required file operations ----------
def ls_info(self, path: str) -> LsResult:
if not path.endswith("/"):
path = path + "/"
entries = [
FileInfo(path=p, is_dir=False)
for p in self._store
if p.startswith(path)
]
return LsResult(entries=entries)
def read(self, file_path: str, offset: int = 0, limit: int = 2000) -> ReadResult:
content = self._store.get(file_path)
if content is None:
return ReadResult(error="file_not_found")
# emulate cat‑n format
lines = content.splitlines()[offset : offset + limit]
numbered = "\n".join(f"{i+1}\t{ln}" for i, ln in enumerate(lines, start=offset))
return ReadResult(file_data={"content": numbered, "encoding": "utf-8",
"created_at": "", "modified_at": ""})
# The backend is read‑only, so write/edit just return an error.
def write(self, file_path: str, content: str) -> WriteResult:
return WriteResult(error="permission_denied")
def edit(self, file_path: str, old_string: str, new_string: str,
replace_all: bool = False) -> EditResult:
return EditResult(error="permission_denied")
Using the backend in a DeepAgent
Pass the backend as a factory or instance when creating the agent:
from deepagents import DeepAgent
from deepagents.backends.protocol import BackendFactory
from my_backend import InMemoryBackend
def my_factory(runtime) -> InMemoryBackend: # type: ignore[override]
# `runtime` can be used to read env vars, tool configs, etc.
demo_files = {
"/app/hello.py": "print('Hello, DeepAgents!')\n",
"/app/readme.md": "# Demo project\n\nThis is a demo.\n",
}
return InMemoryBackend(demo_files)
agent = DeepAgent(backend=my_factory) # pass a factory or an instance
agent.run("list the files in /app")
Sandbox Backend with Command Execution
If you need to run shell commands, inherit from SandboxBackendProtocol or the helper BaseSandbox. Below is a mock sandbox that executes commands locally using subprocess.run:
# file: mock_sandbox.py
import subprocess
from deepagents.backends.protocol import SandboxBackendProtocol, ExecuteResponse
class MockLocalSandbox(SandboxBackendProtocol):
@property
def id(self) -> str:
return "mock-local-sandbox"
def execute(self, command: str, *, timeout: int | None = None) -> ExecuteResponse:
try:
completed = subprocess.run(
command,
shell=True,
capture_output=True,
text=True,
timeout=timeout,
check=False,
)
return ExecuteResponse(
output=completed.stdout + completed.stderr,
exit_code=completed.returncode,
truncated=False,
)
except subprocess.TimeoutExpired:
return ExecuteResponse(
output="",
exit_code=None,
truncated=True,
)
Note – The
BaseSandboxclass (see [libs/deepagents/deepagents/backends/sandbox.py](https://github.com/langchain-ai/deepagents/blob/main/libs/deepagents/deepagents/backends/sandbox.py#L217-L260)) already implements all file methods (ls_info,read,write, …) by delegating toexecute. If you subclassBaseSandboxyou only need to provideidandexecute.
Registering the sandbox for the agent
from deepagents import DeepAgent
from mock_sandbox import MockLocalSandbox
agent = DeepAgent(backend=MockLocalSandbox())
agent.run("run `ls -R /` and show the first 20 lines")
Runtime Configuration with BackendFactory
When your backend requires runtime values (e.g., API keys or environment variables), use a BackendFactory instead of a direct instance:
def s3_backend_factory(runtime) -> BackendProtocol:
bucket = runtime.env.get("S3_BUCKET") # runtime.env is a dict‑like view
aws_key = runtime.env.get("AWS_ACCESS_KEY_ID")
# Assume `S3Backend` is a concrete implementation you wrote elsewhere.
return S3Backend(bucket_name=bucket, aws_key=aws_key)
agent = DeepAgent(backend=s3_backend_factory)
The runtime object provides access to environment variables and tool configurations, allowing lazy initialization of the backend when the agent starts.
Reference Implementations and Key Files
Study these concrete implementations to understand the minimal set of methods required for different backend types:
| File | Role | Link |
|---|---|---|
libs/deepagents/deepagents/backends/protocol.py |
Core BackendProtocol & SandboxBackendProtocol definitions |
protocol.py |
libs/deepagents/deepagents/backends/sandbox.py |
BaseSandbox mix‑in that implements file ops via execute |
sandbox.py |
libs/deepagents/deepagents/backends/filesystem.py |
Real filesystem backend – good reference for path handling | filesystem.py |
libs/deepagents/deepagents/backends/local_shell.py |
Combines FilesystemBackend with SandboxBackendProtocol – shows hybrid implementation |
local_shell.py |
libs/harbor/deepagents_harbor/backend.py |
Example of a sandbox backend that talks to a remote Harbor service | Harbor backend |
libs/deepagents/tests/unit_tests/backends/test_protocol.py |
Test suite that validates the abstract contract – useful to see expected behaviours | test_protocol.py |
Summary
- Choose the right protocol: Use
BackendProtocolfor file-only backends orSandboxBackendProtocolif you need to execute shell commands. - Implement required methods: Override
ls_info,read,write,edit, and other file operations declared inlibs/deepagents/deepagents/backends/protocol.py. - Leverage BaseSandbox: Subclass
BaseSandboxfromlibs/deepagents/deepagents/backends/sandbox.pyto get default file operation implementations that delegate to yourexecutemethod. - Use factories for runtime config: Pass a
BackendFactoryfunction toDeepAgentwhen your backend requires environment variables or lazy initialization. - Register and run: Pass your backend instance or factory to the
backendparameter ofDeepAgent, and the runtime automatically wires it into all middleware and tools.
Frequently Asked Questions
What is the difference between BackendProtocol and SandboxBackendProtocol?
BackendProtocol defines the core contract for file-system-like operations such as read, write, and ls_info. SandboxBackendProtocol extends this interface with an execute(command: str) method for running shell commands. If your backend needs to support command execution (like a Docker container or remote VM), implement SandboxBackendProtocol or subclass BaseSandbox.
Do I need to implement every method in BackendProtocol?
No. You only need to implement the methods your use case requires. The base protocol in libs/deepagents/deepagents/backends/protocol.py raises NotImplementedError by default for all methods. However, for a complete backend, you should implement at least ls_info, read, write, and edit to support basic file operations.
How do I access environment variables inside my backend?
Use a BackendFactory instead of instantiating the backend directly. The factory receives a runtime object (of type ToolRuntime) that exposes runtime.env, a dict-like view of environment variables. This pattern allows you to configure API keys, bucket names, or connection strings at runtime rather than hard-coding them.
Can I combine file operations with command execution without writing boilerplate?
Yes. Subclass BaseSandbox from libs/deepagents/deepagents/backends/sandbox.py. This mixin implements all file methods (ls_info, read, write, etc.) by translating them into shell commands and delegating to your execute method. You only need to implement id and execute to get a fully functional sandbox backend.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →