Module 11: Deep Agents
Virtual Filesystem
Capsule overview
The virtual filesystem gives the agent tools to read, write, and edit files. Instead of stuffing all the information into the context window (expensive and capped at 128K-200K tokens), the agent writes outputs to files and reads only what it needs for the current step. That enables research of any size without blowing up costs or degrading quality. The direct analogy is with what you built in Module 8: there you configured checkpointers to persist state. Here, the framework hands you a full filesystem as a persistence abstraction — the agent decides what to save, where to save it, and when to read it.
The concept: context offloading
The context window problem
Imagine a research agent that searches for information across 10 sources. Each source produces ~2,000 tokens of results. Without a filesystem:
Turn 1: Search source 1 → 2,000 tokens in context
Turn 2: Search source 2 → 4,000 tokens in context
Turn 3: Search source 3 → 6,000 tokens in context
...
Turn 10: Search source 10 → 20,000 tokens in context
Context window at synthesis time: 20,000+ tokens of results + system prompt + history
Three problems:
- Cost: every turn pays for all the accumulated tokens. Turn 10 pays for 20,000 input tokens that are mostly earlier results it doesn't need
- Quality: models lose attention over long contexts. The results from source 1 are "far away" when the agent synthesizes on turn 10
- Hard limit: 128K tokens is a ceiling. With long sources or many sources, it simply won't fit
The solution: the filesystem as external memory
With a filesystem:
Turn 1: Search source 1 → write_file("research/source_01.md", results) → clean context
Turn 2: Search source 2 → write_file("research/source_02.md", results) → clean context
...
Turn 10: Search source 10 → write_file("research/source_10.md", results) → clean context
Turn 11 (synthesis):
read_file("research/source_01.md") ← reads only what it needs
read_file("research/source_05.md") ← picks the most relevant sources
write_file("output/report.md", report)
The context window stays small on every turn. The agent has access to all the information through files, but only loads what's necessary. This is context offloading: moving data out of the context window (expensive, limited) and into the filesystem (cheap, unlimited).
Concrete impact on cost
Without a filesystem (10 sources, ~2K tokens each):
Total input tokens across the run:
2K + 4K + 6K + 8K + ... + 20K = ~110K tokens
Approximate cost with GPT-4.1: ~$0.22
With a filesystem:
Each turn: ~2K input tokens (just the current result + instructions)
Synthesis turn: ~6K tokens (3 selected sources)
Total: ~26K tokens
Approximate cost with GPT-4.1: ~$0.05
Savings: ~75%
The numbers vary by model and task, but the pattern is consistent: the filesystem cuts costs significantly whenever data accumulates.
The filesystem tools
Deep Agents injects four filesystem tools automatically:
ls(path) — list files and directories
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Filesystem Demo",
instructions=(
"You are an agent that organizes information into files. "
"Use the filesystem to store and read data."
),
)
result = agent.run(
"Create a directory structure for a research project "
"with research/, analysis/, and output/ folders. "
"In each folder, create a README.md describing its purpose."
)
print("=== Files generated ===")
for path, content in sorted(result.files.items()):
print(f" {path} ({len(content)} chars)")
# Expected output (varies):
# === Files generated ===
# analysis/README.md (89 chars)
# output/README.md (76 chars)
# research/README.md (112 chars)
The agent used write_file to create the structure. Under the hood, ls lets it check what already exists.
read_file(path) — read a file's content
The agent reads a specific file when it needs that information:
# Under the hood, the agent does:
# content = read_file("research/source_01.md")
# → Returns the file's content as a string
The key point: read_file loads the content into the context window only when the agent calls it. It isn't always present — the agent decides when it needs which information.
write_file(path, content) — create or overwrite a file
# Under the hood, the agent does:
# write_file("research/web_results.md", "# Web search results\n\n...")
# → Creates the file (or overwrites it if it already exists)
write_file replaces the file's entire content. If you need to preserve existing content and make targeted changes, use edit_file.
edit_file(path, edits) — targeted edits
# Under the hood, the agent does:
# edit_file("output/report.md", [
# {"old": "## Conclusion\n\nPending.", "new": "## Conclusion\n\nThe analysis reveals..."}
# ])
# → Modifies only the specified section
edit_file is for surgical changes: updating a section of the report, fixing a data point, adding a paragraph. It doesn't rewrite the whole file.
How the agent uses the filesystem
Pattern: multi-source research
The most natural use of the filesystem is storing research results:
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults
web_search = TavilySearchResults(max_results=3)
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[web_search],
name="Filesystem Researcher",
instructions=(
"You are a researcher. For every topic:\n"
"1. Plan with write_todos\n"
"2. For each source, write the results to research/[source].md\n"
"3. Analyze the results and write the analysis to analysis/synthesis.md\n"
"4. Generate the final report in output/report.md\n\n"
"IMPORTANT: do not accumulate results in your context. "
"Write each result to a file immediately."
),
)
result = agent.run("Research the most popular AI agent frameworks in 2025")
print("=== File structure ===")
for path in sorted(result.files.keys()):
content = result.files[path]
lines = content.count("\n") + 1
print(f" {path} ({lines} lines, {len(content)} chars)")
print(f"\n=== Content of output/report.md (first 5 lines) ===")
if "output/report.md" in result.files:
for line in result.files["output/report.md"].split("\n")[:5]:
print(f" {line}")
# Expected output (varies):
# === File structure ===
# analysis/synthesis.md (45 lines, 2103 chars)
# output/report.md (78 lines, 4521 chars)
# research/academic_papers.md (32 lines, 1876 chars)
# research/industry_tools.md (28 lines, 1654 chars)
# research/web_search.md (35 lines, 1932 chars)
#
# === Content of output/report.md (first 5 lines) ===
# # AI Agent Frameworks in 2025
#
# ## Executive Summary
#
# The AI agent ecosystem in 2025 has consolidated around...
The agent created an organized structure: sources in research/, analysis in analysis/, the final report in output/. Each step wrote to a separate file instead of piling up in the context.
Pattern: read selectively
Not everything the agent writes needs to be read for every step. The agent picks:
Step "synthesize":
- Reads research/academic_papers.md ← relevant to the synthesis
- Reads research/industry_tools.md ← relevant to the synthesis
- Does NOT read research/web_search.md ← already covered by the other files
- Writes analysis/synthesis.md
Step "generate report":
- Reads analysis/synthesis.md ← needs the analysis
- Does NOT read research/* ← already synthesized
- Writes output/report.md
That selective reading is what keeps the context window small. The agent doesn't load every file — only the ones it needs for the current step.
Files as working memory
The filesystem isn't just storage — it's the agent's working memory. While the context window is short-term memory (what the agent "has in mind" right now), the files are medium-term memory (what the agent "wrote in its notebook" and can look up).
The researcher analogy
A human researcher:
- Reads a paper → takes notes in a notebook (write_file)
- Reads another paper → more notes on another page (write_file)
- Opens the notebook to the relevant page (read_file)
- Writes a section of the thesis consulting only the notes needed
- Doesn't try to hold everything in memory — that's what the notes are for
An agent with a filesystem:
- Searches for information → writes to research/source_01.md
- Searches for more information → writes to research/source_02.md
- Reads only the file relevant to the current step
- Generates the output by consulting specific files
- Doesn't try to keep everything in the context window
Example: an agent that writes a multi-section report
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[],
name="Report Writer",
instructions=(
"You are a technical report writer. For every report:\n"
"1. Plan the sections with write_todos\n"
"2. Write each section as a separate file in sections/\n"
"3. At the end, read all the sections and compose the final report in output/\n\n"
"Each section must be self-contained: title, content, partial conclusion. "
"The final report ties them together with transitions."
),
)
result = agent.run(
"Write a technical report on the differences between "
"microservices and monoliths for an engineering team"
)
print("=== Files generated ===")
for path in sorted(result.files.keys()):
print(f" {path}")
if "output/report.md" in result.files:
report = result.files["output/report.md"]
print(f"\n=== Final report: {len(report)} chars, {report.count(chr(10))+1} lines ===")
for line in report.split("\n")[:10]:
print(f" {line}")
# Expected output (varies):
# === Files generated ===
# output/report.md
# sections/01-introduction.md
# sections/02-monolithic-architecture.md
# sections/03-microservices-architecture.md
# sections/04-comparison.md
# sections/05-recommendations.md
#
# === Final report: 5234 chars, 98 lines ===
# # Microservices vs Monoliths: A Guide for Engineering Teams
#
# ## Introduction
#
# The decision between a monolithic architecture and microservices is...
The agent wrote each section as an independent file and then composed them into a final report. Without a filesystem, it would try to generate the whole report in a single step, which produces less structured results.
Organizing outputs: directory structure
A good agent organizes its files the way a good developer organizes code. These patterns emerge with the right instructions:
For research
workspace/
├── research/ ← Raw data from sources
│ ├── source_academic.md
│ ├── source_industry.md
│ └── source_news.md
├── analysis/ ← Intermediate processing
│ ├── comparison.md
│ └── synthesis.md
└── output/ ← Final deliverables
└── report.md
For code generation
workspace/
├── specs/ ← Specifications and requirements
│ └── requirements.md
├── src/ ← Generated code
│ ├── main.py
│ ├── models.py
│ └── utils.py
├── tests/ ← Generated tests
│ └── test_main.py
└── docs/ ← Documentation
└── README.md
For comparative analysis
workspace/
├── data/ ← Data per compared entity
│ ├── option_a.md
│ ├── option_b.md
│ └── option_c.md
├── comparison/ ← Matrices and cross-analysis
│ └── comparison_matrix.md
└── output/ ← Final recommendation
└── recommendation.md
The structure isn't magic — you define it in the instructions:
instructions = (
"Organize your files into three folders:\n"
"- research/ for raw data\n"
"- analysis/ for intermediate processing\n"
"- output/ for final deliverables\n"
"Never write directly into output/ without first having files in research/ and analysis/."
)
Impact on cost and quality: a concrete comparison
Scenario: research across 5 sources
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults
web_search = TavilySearchResults(max_results=3)
agent_with_fs = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[web_search],
name="With Filesystem",
instructions=(
"Research the topic. Write EACH search result "
"to a separate file in research/. "
"At the end, read the relevant files and generate the report in output/report.md."
),
)
agent_without_fs = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[web_search],
name="Without Filesystem",
instructions=(
"Research the topic. Accumulate all the results in your context. "
"Do NOT use files. Generate the report directly at the end."
),
)
task = "Research the main trends in AI engineering in 2025"
result_fs = agent_with_fs.run(task)
result_no_fs = agent_without_fs.run(task)
print("=== With filesystem ===")
print(f" Files: {len(result_fs.files)}")
print(f" Token usage: {result_fs.usage.total_tokens:,} tokens")
print(f" Output length: {len(result_fs.output):,} chars")
print("\n=== Without filesystem ===")
print(f" Files: {len(result_no_fs.files)}")
print(f" Token usage: {result_no_fs.usage.total_tokens:,} tokens")
print(f" Output length: {len(result_no_fs.output):,} chars")
if result_fs.usage.total_tokens < result_no_fs.usage.total_tokens:
saving = (1 - result_fs.usage.total_tokens / result_no_fs.usage.total_tokens) * 100
print(f"\nSavings with filesystem: {saving:.0f}%")
# Expected output (varies):
# === With filesystem ===
# Files: 5
# Token usage: 18,432 tokens
# Output length: 3,847 chars
#
# === Without filesystem ===
# Files: 0
# Token usage: 31,205 tokens
# Output length: 2,156 chars
#
# Savings with filesystem: 41%
Two visible benefits: fewer tokens consumed AND longer output (more complete). The filesystem lets the agent process more information without the penalty of a saturated context window.
Security: sandboxing the virtual filesystem
A fair question: if the agent can write files, can it write to your real filesystem?
No. The Deep Agents virtual filesystem is a sandbox:
Real filesystem: Virtual filesystem:
/home/user/ workspace/ ← all the agent sees
├── documents/ ├── research/
├── code/ ├── analysis/
├── .ssh/ ← inaccessible └── output/
└── .env ← inaccessible
What the agent can do
- ✅ Create files and directories inside the virtual workspace
- ✅ Read files it created itself
- ✅ Edit files inside the workspace
- ✅ List the workspace's contents
What it can NOT do
- ❌ Access files outside the virtual workspace
- ❌ Read the system's environment variables
- ❌ Run operating system commands
- ❌ Access the network directly (only through the tools you give it)
Configuring the sandbox
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Sandboxed Agent",
instructions="Write a test file.",
filesystem_config={
"max_file_size": 100_000, # Max 100KB per file
"max_total_size": 10_000_000, # Max 10MB total
"max_files": 50, # Max 50 files
},
)
result = agent.run("Create a file with a summary of generative AI")
print(f"Files: {list(result.files.keys())}")
print(f"Total size: {sum(len(c) for c in result.files.values()):,} chars")
# Expected output (varies):
# Files: ['output/summary.md']
# Total size: 1,243 chars
The limits prevent the agent from consuming excessive resources. If it tries to create 100 files when the limit is 50, the tool call fails with a clear error.
Connection with M8: the filesystem as a persistence abstraction
In Module 8 you built persistence with checkpointers:
# M8: Manual persistence with a checkpointer
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)
result = graph.invoke(input, config={"configurable": {"thread_id": "session_1"}})
The virtual filesystem abstracts that at a higher level:
| Aspect | M8 (Checkpointers) | M11 (Virtual Filesystem) |
|---|---|---|
| What gets persisted | The graph's full state | The files the agent decides to create |
| Who decides what to save | The framework (saves everything) | The agent (writes what it deems relevant) |
| Granularity | Per checkpoint (full snapshot) | Per file (logical unit) |
| Access | Restore the full state | Read individual files |
| Use case | Crash recovery, resume, time-travel | Working memory, context offloading |
They aren't competitors — they're complementary. The checkpointer saves the agent's state (including the filesystem's files) for crash recovery. The filesystem is the interface the agent works with.
Troubleshooting
Problem 1: The agent doesn't use the filesystem
Symptom: The agent piles all the information into the context window instead of writing to files. Search results stay in the message history. Cause: The instructions aren't explicit about when to use the filesystem. Fix: Be prescriptive:
instructions = (
"RULE: after each search, ALWAYS write the results to a file. "
"NEVER accumulate more than 1 search result in your context without writing it to disk. "
"Use write_file('research/[descriptive_name].md', content)."
)
Problem 2: Files that are too large
Symptom: The agent writes one 50K-character file with all the research in it, losing the benefits of context offloading. Cause: The agent got no instructions about file granularity. Fix: Define the expected structure:
instructions = (
"Each file must cover ONE source or ONE aspect of the topic. "
"If a file exceeds 3,000 characters, split it into smaller files. "
"Use directories to organize: research/ for sources, analysis/ for processing."
)
Problem 3: The agent doesn't read earlier files when synthesizing
Symptom: The final report is shallow because the agent generated the synthesis from memory instead of reading the research files. Cause: The agent wasn't instructed to consult its files before synthesizing. Fix:
instructions = (
"Before generating the final report:\n"
"1. Use ls('research/') to see which sources you researched\n"
"2. Read EVERY file with read_file()\n"
"3. Only then write the report, based on the real data"
)
Problem 4: Conflicts when editing files
Symptom: edit_file fails because the old text doesn't exactly match the file's current content.
Cause: The agent "remembers" an earlier version of the file that has already been modified.
Fix: Instruct the agent to read before editing:
instructions = (
"Before using edit_file, ALWAYS read the file with read_file first "
"so you have the current content. Never edit based on what you remember."
)
Problem 5: A disorganized directory structure
Symptom: The agent creates files like file1.md, temp.md, results.md in the root directory with no logical organization.
Cause: With no defined structure, the agent falls back on generic names.
Fix: Define the convention in the instructions:
instructions = (
"Required file structure:\n"
"- research/[source]_[topic].md — raw data from each source\n"
"- analysis/[type]_analysis.md — intermediate processing\n"
"- output/[deliverable].md — final deliverables\n"
"Descriptive names, no spaces, in snake_case."
)
Exercises
Exercise 1: Create a file structure (Easy)
Create a Deep Agent that generates a file structure for a research project. It should create at least 3 folders with README files explaining the purpose of each one.
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Structure Creator",
instructions=(
"Create a file structure for an AI research project. "
"Include these folders: research/, analysis/, output/. "
"In each folder, create a README.md explaining what kind of files belong there."
),
)
result = agent.run("Create the file structure to research LLMs in production")
for path in sorted(result.files.keys()):
content = result.files[path]
print(f"\n--- {path} ---")
print(content[:200])
# Expected output (varies):
# --- analysis/README.md ---
# # Analysis
# Analysis files and intermediate processing of the research data.
#
# --- output/README.md ---
# # Output
# Final deliverables: reports, executive summaries, presentations.
#
# --- research/README.md ---
# # Research
# Raw data from each source researched. One file per source.
Explanation: The agent uses write_file to create each file. The structure reflects an organized workflow: raw data → analysis → output. That sets up the conventions for the following exercises.
Exercise 2: Multi-file research (Easy)
Create an agent that researches 3 different aspects of a topic and writes each aspect to a separate file inside research/. At the end, it should create an output/summary.md file with a summary.
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Multi-File Researcher",
instructions=(
"For the topic you're given:\n"
"1. Identify 3 key aspects\n"
"2. Write each aspect to research/aspect_N.md\n"
"3. Read the 3 files in research/\n"
"4. Generate a summary integrating the 3 aspects in output/summary.md"
),
)
result = agent.run("Research the impact of AI on the job market")
research_files = [f for f in result.files if f.startswith("research/")]
output_files = [f for f in result.files if f.startswith("output/")]
print(f"Research files: {len(research_files)}")
for f in sorted(research_files):
print(f" {f} ({len(result.files[f])} chars)")
print(f"\nOutput files: {len(output_files)}")
for f in sorted(output_files):
print(f" {f} ({len(result.files[f])} chars)")
# Expected output (varies):
# Research files: 3
# research/aspect_1_automation.md (1243 chars)
# research/aspect_2_new_roles.md (1087 chars)
# research/aspect_3_education.md (956 chars)
#
# Output files: 1
# output/summary.md (2456 chars)
Explanation: The agent wrote each aspect to a separate file, then read them back to generate the summary. The summary integrates all three aspects because the agent consulted the files, not its memory of the context window.
Exercise 3: Measure the filesystem's impact on tokens (Medium)
Run the same task with two agents: one that uses the filesystem and one that doesn't. Compare the total tokens consumed.
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent_fs = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="With FS",
instructions=(
"For each aspect of the topic, write a file in research/. "
"At the end, read the files and generate output/report.md. "
"Research at least 4 aspects."
),
)
agent_no_fs = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Without FS",
instructions=(
"Research at least 4 aspects of the topic. "
"Do NOT use files. Keep all the information in your direct response. "
"Generate a complete report at the end."
),
)
task = "Analyze the pros and cons of the 4 main microservices architectures"
result_fs = agent_fs.run(task)
result_no_fs = agent_no_fs.run(task)
print("=== Token usage comparison ===")
print(f"\n With filesystem:")
print(f" Tokens: {result_fs.usage.total_tokens:,}")
print(f" Files: {len(result_fs.files)}")
print(f" Output: {len(result_fs.output):,} chars")
print(f"\n Without filesystem:")
print(f" Tokens: {result_no_fs.usage.total_tokens:,}")
print(f" Files: {len(result_no_fs.files)}")
print(f" Output: {len(result_no_fs.output):,} chars")
diff = result_no_fs.usage.total_tokens - result_fs.usage.total_tokens
if diff > 0:
print(f"\n Savings: {diff:,} tokens ({diff/result_no_fs.usage.total_tokens*100:.0f}%)")
else:
print(f"\n Filesystem overhead: {abs(diff):,} tokens")
# Expected output (varies):
# === Token usage comparison ===
#
# With filesystem:
# Tokens: 15,234
# Files: 6
# Output: 4,102 chars
#
# Without filesystem:
# Tokens: 23,891
# Files: 0
# Output: 2,567 chars
#
# Savings: 8,657 tokens (36%)
Explanation: The agent with a filesystem consumes fewer tokens because it doesn't drag all the previous results along on every turn. The gap widens with more sources and more data. On top of that, the output is longer because the context window wasn't saturated.
Exercise 4: edit_file for iterative reports (Medium)
Create an agent that writes an initial report and then refines it iteratively. It should use edit_file to improve specific sections, not rewrite the whole report every time.
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[],
name="Iterative Writer",
instructions=(
"Follow this process to write a report:\n"
"1. Write an initial draft in output/report.md with sections marked [DRAFT]\n"
"2. Read the full draft with read_file\n"
"3. Use edit_file to improve EACH section individually:\n"
" - Replace [DRAFT] with final content\n"
" - Add specific data\n"
" - Improve the transitions between sections\n"
"4. Read the final report to verify quality"
),
)
result = agent.run(
"Write a report on prompt engineering best practices for production"
)
if "output/report.md" in result.files:
report = result.files["output/report.md"]
draft_markers = report.count("[DRAFT]")
print(f"Remaining [DRAFT] markers: {draft_markers}")
print(f"Final length: {len(report):,} chars")
print(f"\nFirst 10 lines:")
for line in report.split("\n")[:10]:
print(f" {line}")
# Expected output (varies):
# Remaining [DRAFT] markers: 0
# Final length: 4,892 chars
#
# First 10 lines:
# # Prompt Engineering Best Practices for Production
#
# ## 1. Prompt Structure
#
# A production prompt has three mandatory components...
Explanation: edit_file lets the agent refine specific sections without rewriting the entire document. That's more efficient than write_file for long documents and produces higher-quality results because the agent focuses on one section at a time.
Exercise 5: Filesystem combined with planning (Advanced)
Create an agent that uses write_todos to plan AND the filesystem to store. Each step of the plan should correspond to a file. Print the mapping table: step → file.
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
from langchain_community.tools import TavilySearchResults
web_search = TavilySearchResults(max_results=3)
agent = create_deep_agent(
"openai:gpt-4.1",
tools=[web_search],
name="Planning + Filesystem",
instructions=(
"For every task:\n"
"1. Create a plan with write_todos\n"
"2. For EACH step of the plan, write the result to a file:\n"
" - Research step → research/[step_N].md\n"
" - Analysis step → analysis/[step_N].md\n"
" - Output step → output/[step_N].md\n"
"3. In each todo's result, include the path of the file you generated\n"
"4. Update your progress with write_todos after each step"
),
)
result = agent.run("Compare 3 vector databases for RAG in production")
print("=== Mapping: Plan → Files ===")
print(f"{'Step':<50} {'Status':<12} {'Associated file'}")
print("-" * 90)
for todo in result.todos:
file_ref = todo.get("result", "")[:60] if todo.get("result") else "N/A"
print(f"{todo['title']:<50} {todo['status']:<12} {file_ref}")
print(f"\n=== Files generated: {len(result.files)} ===")
for path in sorted(result.files.keys()):
print(f" {path}")
# Expected output (varies):
# === Mapping: Plan → Files ===
# Step Status Associated file
# ------------------------------------------------------------------------------------------
# Research Pinecone completed research/pinecone.md
# Research Weaviate completed research/weaviate.md
# Research ChromaDB completed research/chromadb.md
# Build the comparison matrix completed analysis/comparison.md
# Generate the final recommendation completed output/recommendation.md
#
# === Files generated: 5 ===
# analysis/comparison.md
# output/recommendation.md
# research/chromadb.md
# research/pinecone.md
# research/weaviate.md
Explanation: Planning and the filesystem complement each other: write_todos defines WHAT to do, the filesystem stores the results of EACH step. Together they produce an organized, traceable run where you can see both the plan and the outputs of every step.
Exercise 6: Configure filesystem limits (Advanced)
Create an agent with filesystem limits (max 5 files, max 5KB per file) and watch how it behaves when it hits them. Does the agent adapt?
See solution
from dotenv import load_dotenv
load_dotenv()
from deep_agents import create_deep_agent
agent = create_deep_agent(
"openai:gpt-4.1-mini",
tools=[],
name="Limited FS Agent",
instructions=(
"Research the topic in depth. Write each aspect to a separate file. "
"If you hit the file limit, consolidate information into existing files "
"using edit_file instead of creating new ones."
),
filesystem_config={
"max_files": 5,
"max_file_size": 5_000,
},
)
result = agent.run(
"Research 8 machine learning frameworks: "
"TensorFlow, PyTorch, JAX, Keras, scikit-learn, XGBoost, LightGBM, Hugging Face"
)
print(f"=== Result with a 5-file limit ===")
print(f" Files created: {len(result.files)}")
for path in sorted(result.files.keys()):
size = len(result.files[path])
print(f" {path} ({size:,} chars)")
if len(result.files) <= 5:
print("\n ✅ The agent respected the file limit")
print(" Strategy: it consolidated several frameworks per file")
else:
print("\n ❌ The agent exceeded the limit (edge case)")
# Expected output (varies):
# === Result with a 5-file limit ===
# Files created: 5
# research/deep_learning_frameworks.md (4,230 chars)
# research/ml_traditional_frameworks.md (3,890 chars)
# research/gradient_boosting.md (2,456 chars)
# analysis/comparison.md (4,102 chars)
# output/report.md (4,890 chars)
#
# ✅ The agent respected the file limit
# Strategy: it consolidated several frameworks per file
Explanation: With a 5-file limit and 8 topics, the agent adapts by grouping related frameworks into a single file. Filesystem limits force the agent to be efficient with space, much like a tight budget forces prioritization. That's especially useful for controlling costs in production.
Summary
- The virtual filesystem lets the agent read, write, and edit files. Instead of accumulating data in the context window (expensive, limited), the agent writes outputs to files and reads only what it needs — that's context offloading
- Four tools:
ls(list),read_file(read),write_file(create/overwrite),edit_file(targeted edits). Deep Agents injects them automatically - Files act as the agent's working memory: while the context window is short-term memory, the files are a persistent notebook the agent consults selectively
- Impact on cost: in multi-source research, the filesystem can cut token consumption by 30-75% because each turn only loads the data it needs
- Impact on quality: with a leaner context window, the model keeps better attention on the relevant data, producing more complete and coherent outputs
- The filesystem is a sandbox: the agent can't reach the system's real files, run commands, or leave the virtual workspace. Configure file and size limits to control resources
- Connection with M8: the filesystem abstracts the persistence you built by hand with checkpointers. The checkpointer saves the full state (including files) for crash recovery; the filesystem is the interface the agent works with
Next capsule: Subagent Spawning and Delegation — how the agent creates specialized subagents on demand to handle subtasks, with isolated context and control limits.
Additional resources
- Deep Agents — Filesystem — Official documentation for the virtual filesystem and its tools
- Context Window Management — Anthropic — Anthropic's guide on handling long contexts efficiently
- LangGraph — Checkpointing — The persistence foundation the filesystem runs on
- The Needle in a Haystack Test — Paper on how models lose information in long contexts, the direct motivation for context offloading
- Retrieval-Augmented Generation Survey — RAG is another form of context offloading (external knowledge); the filesystem offloads working data
- Sandboxing AI Agents — Safety Considerations — Security considerations when giving agents access to filesystem tools
Module 11 — LangChain & LangGraph: From Chains to Agents
Next capsule: Subagent Spawning and Delegation — you'll learn how the agent creates specialized subagents on demand, with isolated context for each subtask, and how to configure limits to control cost and complexity.