How Deep Agents Manages Files with Built-in Filesystem Tools
When I first started building AI agents, I kept running into the same problem: agents would work fine during a conversation, but they couldn’t persist anything. Every session started from scratch, and complex multi-step workflows were impossible because there was no way to save state or read existing project files.
Then I discovered Deep Agents has filesystem tools built right in. Let me show you how they work.
The Problem with Stateless Agents
Most agent frameworks treat files as an afterthought. You pass context through conversation memory, which works for simple tasks but breaks down quickly:
- Large codebases: You can’t read an entire project into context
- Generated outputs: Where do agents save their work?
- Multi-session workflows: How do you resume work later?
- Collaboration: How do multiple agents share state?
Deep Agents solves this with FilesystemMiddleware and a set of dedicated file tools.
Getting Started with Filesystem Tools
First, let’s see what tools are available:
- read_file : Read file contents (with pagination)- write_file : Create or overwrite files- edit_file : Make targeted edits to existing files- ls : List directory contents- glob : Pattern-based file discovery- grep : Content search within filesThe middleware is applied automatically when you create a Deep Agent:
from deepagents import create_agent, FilesystemMiddleware
agent = create_agent( model="openai/gpt-4", middlewares=[FilesystemMiddleware()])Backend Options: Memory vs. Disk
Deep Agents supports multiple backend types for filesystem operations:
from deepagents import StateBackend, FilesystemBackend
# Option 1: In-memory (default)# Files are passed via invoke() and kept in statebackend = StateBackend()
# Option 2: Real filesystem access# Agents can read/write actual files on diskbackend = FilesystemBackend(base_path="/projects/my-app")
# Option 3: Custom backend# Implement BackendProtocol for your own storageI started with StateBackend for testing, then switched to FilesystemBackend for production. The in-memory backend is great for isolated agent runs where you want explicit control over what files the agent can access.
Reading Files: The Foundation
The read_file tool is more sophisticated than a simple cat command:
# Agent can read files with pagination for large filesresult = agent.invoke({ "messages": [ {"role": "user", "content": "Read the first 50 lines of src/main.py"} ]})What I found useful is the pagination support. When working with large files:
User: Read the config file at /etc/app/settings.yaml
Agent: I'll read the config file for you.[Uses read_file with pagination to avoid context overflow]
The file contains database settings, API keys, and feature flags...The agent automatically handles pagination when needed, preventing context window exhaustion.
Writing Files: Saving Agent Output
The write_file tool creates or overwrites files:
result = agent.invoke({ "messages": [ {"role": "user", "content": "Create a new file at output/summary.md with a project overview"} ]})I use this constantly for:
- Generated documentation: Agents write README files
- Code generation: Agents create new modules
- Report output: Research agents save findings
Editing Files: Precise Modifications
This is where things get interesting. The edit_file tool makes targeted changes without rewriting entire files:
result = agent.invoke({ "messages": [ {"role": "user", "content": "Update the version number in package.json from 1.0.0 to 1.1.0"} ]})The agent will:
- Read the file to find the exact location
- Make a surgical edit to just that line
- Preserve everything else
I initially tried having agents rewrite entire files for every change, but that was error-prone. The edit tool maintains file integrity and reduces the chance of unintended modifications.
Discovery Tools: ls, glob, grep
These three tools help agents navigate codebases:
ls - List directory contents:
result = agent.invoke({ "messages": [ {"role": "user", "content": "What files are in the src/components directory?"} ]})glob - Pattern-based discovery:
result = agent.invoke({ "messages": [ {"role": "user", "content": "Find all Python test files in the project"} ]})# Agent uses glob: **/*_test.py or **/test_*.pygrep - Content search:
result = agent.invoke({ "messages": [ {"role": "user", "content": "Find where the API_KEY variable is used"} ]})# Agent uses grep to search file contentsA Real Workflow Example
Let me show you how these tools work together. I wanted an agent to analyze a codebase and create documentation:
from deepagents import create_agent
agent = create_agent( model="openai/gpt-4", backend=FilesystemBackend(base_path="/my-project"))
result = agent.invoke({ "messages": [ {"role": "user", "content": """ Analyze this project and create comprehensive documentation: 1. List all Python files using glob 2. Read the main entry points 3. Find all function definitions 4. Create a README.md with installation and usage 5. Create a docs/API.md for any API endpoints """} ]})The agent’s execution flow looked like this:
1. glob: **/*.py -> Found 23 Python files2. ls: src/ -> Identified main modules3. read_file: src/main.py -> Found entry point4. grep: "def " -> Found 47 function definitions5. read_file: src/api.py -> Analyzed API endpoints6. write_file: README.md -> Created documentation7. edit_file: README.md -> Added missing sectionNotice the last step: the agent wrote the README, then realized it missed a section and used edit_file to add it. This is the kind of iterative workflow that filesystem tools enable.
Best Practices I Learned
After using these tools extensively, here are my recommendations:
1. Use specialized tools over shell commands
The Deep Agents documentation explicitly recommends this:
Use specialized tools over shell equivalents when available (e.g.,
read_fileovercat,edit_fileoversed)
The specialized tools have better error handling, pagination, and integration with the agent’s state.
2. Paginate large file reads
# BAD: Read entire 10MB log file at onceresult = agent.invoke({ "messages": [{"role": "user", "content": "Read /var/log/app.log"}]})
# BETTER: Agent reads in chunksresult = agent.invoke({ "messages": [{"role": "user", "content": "Read the last 100 lines of /var/log/app.log"}]})3. Choose the right backend
| Backend | Use Case |
|---|---|
StateBackend | Testing, isolated runs, explicit file control |
FilesystemBackend | Production, real project work, persistent storage |
| Custom | S3, databases, version control systems |
4. Let agents discover before acting
I’ve found the best results when I let agents use ls and glob to explore before making changes:
result = agent.invoke({ "messages": [ {"role": "user", "content": "First explore the project structure, then add type hints to all Python files"} ]})Integration with Sub-Agents
Filesystem tools are also available to sub-agents through the middleware:
from deepagents import create_agent, FilesystemMiddleware
agent = create_agent( model="openai/gpt-4", middlewares=[FilesystemMiddleware(backend=FilesystemBackend())], sub_agents=["researcher", "coder"] # Both inherit filesystem access)This enables coordinated workflows where one agent researches and another writes code, both working with the same files.
Sandbox Support
For running shell commands safely, Deep Agents provides sandbox capabilities:
# If backend implements SandboxBackendProtocol,# agents can use the 'execute' tool for shell commandsresult = agent.invoke({ "messages": [ {"role": "user", "content": "Run the tests and save the output to test-results.txt"} ]})This is separate from filesystem tools but complementary - useful when you need git operations, package management, or test runners.
Common Pitfalls
I made these mistakes so you don’t have to:
Pitfall 1: Forgetting the backend
# WRONG: No backend specified, filesystem tools won't workagent = create_agent(model="openai/gpt-4")
# CORRECT: Explicitly configure filesystem accessagent = create_agent( model="openai/gpt-4", middlewares=[FilesystemMiddleware(backend=FilesystemBackend())])Pitfall 2: Assuming write permissions
With FilesystemBackend, the agent has the same permissions as the process running it. On production systems, consider:
- Running with limited user permissions
- Setting
base_pathto a sandboxed directory - Using
StateBackendwith explicit file control
Pitfall 3: Not handling errors
File operations can fail. Good agent prompts include error handling:
result = agent.invoke({ "messages": [ {"role": "user", "content": """ Read the config file at /etc/app/config.yaml. If it doesn't exist, create a default configuration. """} ]})When to Use Filesystem Tools
Filesystem tools shine in these scenarios:
- Coding agents - Read existing code, make edits, create new files
- Research agents - Save findings, generate reports, maintain context
- Data pipelines - Process files, transform data, write outputs
- Documentation - Analyze codebases, generate docs
- Multi-agent coordination - Share state via files
They’re less useful for:
- Simple Q&A tasks
- Stateless operations
- Pure reasoning tasks without persistence needs
Final Words + More Resources
My intention with this article was to help others share my knowledge and experience. If you want to contact me, you can contact by email: Email me
Here are also the most important links from this article along with some further resources that will help you in this scope:
Oh, and if you found these resources useful, don’t forget to support me by starring the repo on GitHub!
Comments