Módulo 11: Deep Agents

Virtual Filesystem

Descripción de la cápsula

El virtual filesystem le da al agente herramientas para leer, escribir y editar archivos. En lugar de meter toda la información en el context window (caro y limitado a 128K-200K tokens), el agente escribe outputs a archivos y lee solo lo que necesita para el paso actual. Esto habilita investigaciones de cualquier tamaño sin explotar costos ni degradar calidad. La analogía directa es con lo que construiste en el Módulo 8: allá configuraste checkpointers para persistir estado. Aquí, el framework te da un filesystem completo como abstracción de persistencia — el agente decide qué guardar, dónde guardarlo, y cuándo leerlo.


El concepto: context offloading

El problema del context window

Imagina un agente de investigación que busca información en 10 fuentes. Cada fuente produce ~2,000 tokens de resultados. Sin filesystem:

Turno 1: Buscar fuente 1 → 2,000 tokens en context
Turno 2: Buscar fuente 2 → 4,000 tokens en context
Turno 3: Buscar fuente 3 → 6,000 tokens en context
...
Turno 10: Buscar fuente 10 → 20,000 tokens en context

Context window al sintetizar: 20,000+ tokens de resultados + system prompt + historial

Tres problemas:

  1. Costo: cada turno paga por todos los tokens acumulados. El turno 10 paga por 20,000 tokens de input que son mayormente resultados anteriores que no necesita
  2. Calidad: los modelos pierden atención en contextos largos. Los resultados de la fuente 1 están "lejos" cuando el agente sintetiza en el turno 10
  3. Límite duro: 128K tokens es un máximo. Con fuentes largas o muchas fuentes, simplemente no cabe

La solución: filesystem como memoria externa

Con filesystem:

Turno 1: Buscar fuente 1 → write_file("research/source_01.md", resultados) → context limpio
Turno 2: Buscar fuente 2 → write_file("research/source_02.md", resultados) → context limpio
...
Turno 10: Buscar fuente 10 → write_file("research/source_10.md", resultados) → context limpio

Turno 11 (síntesis):
  read_file("research/source_01.md")  ← lee solo lo que necesita
  read_file("research/source_05.md")  ← selecciona las fuentes más relevantes
  write_file("output/report.md", reporte)

El context window se mantiene pequeño en cada turno. El agente tiene acceso a toda la información a través de archivos, pero solo carga lo necesario. Esto es context offloading: mover datos del context window (caro, limitado) al filesystem (barato, ilimitado).

Impacto concreto en costos

Sin filesystem (10 fuentes, ~2K tokens cada una):
  Total tokens de input a lo largo de la ejecución:
  2K + 4K + 6K + 8K + ... + 20K = ~110K tokens
  Costo aproximado con GPT-4.1: ~$0.22

Con filesystem:
  Cada turno: ~2K tokens de input (solo el resultado actual + instrucciones)
  Turno de síntesis: ~6K tokens (3 fuentes seleccionadas)
  Total: ~26K tokens
  Costo aproximado con GPT-4.1: ~$0.05

Ahorro: ~75%

Los números varían según el modelo y la tarea, pero el patrón es consistente: el filesystem reduce costos significativamente cuando hay acumulación de datos.


Las herramientas del filesystem

Deep Agents inyecta cuatro herramientas de filesystem automáticamente:

ls(path) — listar archivos y directorios

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=(
        "Eres un agente que organiza información en archivos. "
        "Usa el filesystem para almacenar y leer datos."
    ),
)

result = agent.run(
    "Crea una estructura de directorios para un proyecto de investigación "
    "con carpetas research/, analysis/, y output/. "
    "En cada carpeta, crea un archivo README.md que describa su propósito."
)

print("=== Archivos generados ===")
for path, content in sorted(result.files.items()):
    print(f"  {path} ({len(content)} chars)")
# Output esperado (varía):
# === Archivos generados ===
#   analysis/README.md (89 chars)
#   output/README.md (76 chars)
#   research/README.md (112 chars)

El agente usó write_file para crear la estructura. Internamente, ls le permite verificar qué ya existe.

read_file(path) — leer contenido de un archivo

El agente lee un archivo específico cuando necesita esa información:

# Internamente, el agente hace:
# content = read_file("research/source_01.md")
# → Retorna el contenido del archivo como string

El punto clave: read_file carga el contenido en el context window solo cuando el agente lo llama. No está siempre presente — el agente decide cuándo necesita qué información.

write_file(path, content) — crear o sobrescribir un archivo

# Internamente, el agente hace:
# write_file("research/web_results.md", "# Resultados de búsqueda web\n\n...")
# → Crea el archivo (o sobrescribe si ya existe)

write_file reemplaza el contenido completo del archivo. Si necesitas preservar contenido existente y hacer cambios puntuales, usa edit_file.

edit_file(path, edits) — ediciones puntuales

# Internamente, el agente hace:
# edit_file("output/report.md", [
#     {"old": "## Conclusión\n\nPendiente.", "new": "## Conclusión\n\nEl análisis revela..."}
# ])
# → Modifica solo la sección especificada

edit_file es para modificaciones quirúrgicas: actualizar una sección del reporte, corregir un dato, agregar un párrafo. No reescribe todo el archivo.


Cómo el agente usa el filesystem

Patrón: investigación multi-fuente

El uso más natural del filesystem es almacenar resultados de investigación:

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=(
        "Eres un investigador. Para cada tema:\n"
        "1. Planifica con write_todos\n"
        "2. Para cada fuente, escribe los resultados en research/[fuente].md\n"
        "3. Analiza los resultados y escribe el análisis en analysis/synthesis.md\n"
        "4. Genera el reporte final en output/report.md\n\n"
        "IMPORTANTE: no acumules resultados en el context. "
        "Escribe cada resultado a un archivo inmediatamente."
    ),
)

result = agent.run("Investiga los frameworks de agentes de IA más populares en 2025")

print("=== Estructura de archivos ===")
for path in sorted(result.files.keys()):
    content = result.files[path]
    lines = content.count("\n") + 1
    print(f"  {path} ({lines} líneas, {len(content)} chars)")

print(f"\n=== Contenido de output/report.md (primeras 5 líneas) ===")
if "output/report.md" in result.files:
    for line in result.files["output/report.md"].split("\n")[:5]:
        print(f"  {line}")
# Output esperado (varía):
# === Estructura de archivos ===
#   analysis/synthesis.md (45 líneas, 2103 chars)
#   output/report.md (78 líneas, 4521 chars)
#   research/academic_papers.md (32 líneas, 1876 chars)
#   research/industry_tools.md (28 líneas, 1654 chars)
#   research/web_search.md (35 líneas, 1932 chars)
#
# === Contenido de output/report.md (primeras 5 líneas) ===
#   # Frameworks de Agentes de IA en 2025
#
#   ## Resumen Ejecutivo
#
#   El ecosistema de agentes de IA en 2025 se ha consolidado alrededor de...

El agente creó una estructura organizada: fuentes en research/, análisis en analysis/, reporte final en output/. Cada paso escribió a un archivo separado en vez de acumular en el context.

Patrón: leer selectivamente

No todo lo que el agente escribe necesita leerse para cada paso. El agente selecciona:

Paso "sintetizar":
  - Lee research/academic_papers.md  ← relevante para la síntesis
  - Lee research/industry_tools.md   ← relevante para la síntesis
  - NO lee research/web_search.md    ← ya cubierto en los otros archivos
  - Escribe analysis/synthesis.md

Paso "generar reporte":
  - Lee analysis/synthesis.md        ← necesita el análisis
  - NO lee research/*                ← ya sintetizado
  - Escribe output/report.md

Esta lectura selectiva es lo que mantiene el context window pequeño. El agente no carga todos los archivos — solo los que necesita para el paso actual.


Archivos como memoria de trabajo

El filesystem no es solo almacenamiento — es la memoria de trabajo del agente. Mientras el context window es memoria de corto plazo (lo que el agente "tiene en mente" ahora), los archivos son memoria de mediano plazo (lo que el agente "escribió en su cuaderno" y puede consultar).

La analogía del investigador

Un investigador humano:
  - Lee un paper → toma notas en su cuaderno (write_file)
  - Lee otro paper → más notas en otra página (write_file)
  - Abre el cuaderno en la página relevante (read_file)
  - Escribe la sección de su tesis consultando solo las notas que necesita
  - No intenta recordar todo de memoria — para eso están las notas

Un agente con filesystem:
  - Busca información → escribe en research/source_01.md
  - Busca más información → escribe en research/source_02.md
  - Lee solo el archivo relevante para el paso actual
  - Genera el output consultando archivos específicos
  - No intenta mantener todo en el context window

Ejemplo: agente que escribe un reporte multi-sección

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=(
        "Eres un escritor de reportes técnicos. Para cada reporte:\n"
        "1. Planifica las secciones con write_todos\n"
        "2. Escribe cada sección como un archivo separado en sections/\n"
        "3. Al final, lee todas las secciones y compón el reporte final en output/\n\n"
        "Cada sección debe ser autocontenida: título, contenido, conclusión parcial. "
        "El reporte final los integra con transiciones."
    ),
)

result = agent.run(
    "Escribe un reporte técnico sobre las diferencias entre "
    "microservicios y monolitos para un equipo de ingeniería"
)

print("=== Archivos generados ===")
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=== Reporte final: {len(report)} chars, {report.count(chr(10))+1} líneas ===")
    for line in report.split("\n")[:10]:
        print(f"  {line}")
# Output esperado (varía):
# === Archivos generados ===
#   output/report.md
#   sections/01-introduccion.md
#   sections/02-arquitectura-monolitica.md
#   sections/03-arquitectura-microservicios.md
#   sections/04-comparacion.md
#   sections/05-recomendaciones.md
#
# === Reporte final: 5234 chars, 98 líneas ===
#   # Microservicios vs Monolitos: Guía para Equipos de Ingeniería
#
#   ## Introducción
#
#   La decisión entre arquitectura monolítica y microservicios es...

El agente escribió cada sección como archivo independiente y luego las compuso en un reporte final. Sin filesystem, intentaría generar todo el reporte en un paso, lo cual produce resultados menos estructurados.


Organización de outputs: directory structure

Un buen agente organiza sus archivos como un buen desarrollador organiza su código. Estos patrones emergen con las instrucciones correctas:

Para investigación

workspace/
├── research/              ← Datos crudos de fuentes
│   ├── source_academic.md
│   ├── source_industry.md
│   └── source_news.md
├── analysis/              ← Procesamiento intermedio
│   ├── comparison.md
│   └── synthesis.md
└── output/                ← Entregables finales
    └── report.md

Para generación de código

workspace/
├── specs/                 ← Especificaciones y requerimientos
│   └── requirements.md
├── src/                   ← Código generado
│   ├── main.py
│   ├── models.py
│   └── utils.py
├── tests/                 ← Tests generados
│   └── test_main.py
└── docs/                  ← Documentación
    └── README.md

Para análisis comparativo

workspace/
├── data/                  ← Datos por entidad comparada
│   ├── option_a.md
│   ├── option_b.md
│   └── option_c.md
├── comparison/            ← Matrices y análisis cruzado
│   └── comparison_matrix.md
└── output/                ← Recomendación final
    └── recommendation.md

La estructura no es mágica — la defines en las instrucciones:

instructions = (
    "Organiza tus archivos en tres carpetas:\n"
    "- research/ para datos crudos\n"
    "- analysis/ para procesamiento intermedio\n"
    "- output/ para entregables finales\n"
    "Nunca escribas directamente en output/ sin antes tener archivos en research/ y analysis/."
)

Impacto en costos y calidad: comparación concreta

Escenario: investigación de 5 fuentes

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=(
        "Investiga el tema. Escribe CADA resultado de búsqueda "
        "en un archivo separado en research/. "
        "Al final, lee los archivos relevantes y genera el reporte en output/report.md."
    ),
)

agent_without_fs = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[web_search],
    name="Without Filesystem",
    instructions=(
        "Investiga el tema. Acumula todos los resultados en tu contexto. "
        "NO uses archivos. Genera el reporte directamente al final."
    ),
)

task = "Investiga las tendencias principales en AI engineering en 2025"

result_fs = agent_with_fs.run(task)
result_no_fs = agent_without_fs.run(task)

print("=== Con filesystem ===")
print(f"  Archivos: {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=== Sin filesystem ===")
print(f"  Archivos: {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"\nAhorro con filesystem: {saving:.0f}%")
# Output esperado (varía):
# === Con filesystem ===
#   Archivos: 5
#   Token usage: 18,432 tokens
#   Output length: 3,847 chars
#
# === Sin filesystem ===
#   Archivos: 0
#   Token usage: 31,205 tokens
#   Output length: 2,156 chars
#
# Ahorro con filesystem: 41%

Dos beneficios visibles: menos tokens consumidos Y output más largo (más completo). El filesystem permite que el agente procese más información sin el penalty de un context window saturado.


Seguridad: sandboxing del virtual filesystem

Una pregunta legítima: si el agente puede escribir archivos, ¿puede escribir en tu sistema de archivos real?

No. El virtual filesystem de Deep Agents es un sandbox:

Filesystem real:                    Virtual filesystem:
/home/user/                         workspace/          ← el agente solo ve esto
├── documents/                      ├── research/
├── code/                           ├── analysis/
├── .ssh/         ← inaccesible     └── output/
└── .env          ← inaccesible

Qué puede hacer el agente

  • ✅ Crear archivos y directorios dentro del workspace virtual
  • ✅ Leer archivos que él mismo creó
  • ✅ Editar archivos dentro del workspace
  • ✅ Listar el contenido del workspace

Qué NO puede hacer

  • ❌ Acceder a archivos fuera del workspace virtual
  • ❌ Leer variables de entorno del sistema
  • ❌ Ejecutar comandos del sistema operativo
  • ❌ Acceder a la red directamente (solo a través de tools que tú le das)

Configuración del 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="Escribe un archivo de prueba.",
    filesystem_config={
        "max_file_size": 100_000,      # Máximo 100KB por archivo
        "max_total_size": 10_000_000,  # Máximo 10MB total
        "max_files": 50,               # Máximo 50 archivos
    },
)

result = agent.run("Crea un archivo con un resumen de IA generativa")
print(f"Archivos: {list(result.files.keys())}")
print(f"Tamaño total: {sum(len(c) for c in result.files.values()):,} chars")
# Output esperado (varía):
# Archivos: ['output/summary.md']
# Tamaño total: 1,243 chars

Los límites previenen que el agente consuma recursos excesivos. Si intenta crear 100 archivos cuando el límite es 50, el tool call falla con un error claro.


Conexión con M8: filesystem como abstracción de persistencia

En el Módulo 8 construiste persistencia con checkpointers:

# M8: Persistencia manual con checkpointer
checkpointer = MemorySaver()
graph = builder.compile(checkpointer=checkpointer)
result = graph.invoke(input, config={"configurable": {"thread_id": "session_1"}})

El virtual filesystem abstrae eso a un nivel más alto:

AspectoM8 (Checkpointers)M11 (Virtual Filesystem)
Qué se persisteEstado completo del grafoArchivos que el agente decide crear
Quién decide qué guardarEl framework (guarda todo)El agente (escribe lo que considera relevante)
GranularidadPor checkpoint (snapshot completo)Por archivo (unidad lógica)
AccesoRestaurar estado completoLeer archivos individuales
Caso de usoCrash recovery, resume, time-travelWorking memory, context offloading

No son competidores — son complementarios. El checkpointer guarda el estado del agente (incluyendo los archivos del filesystem) para crash recovery. El filesystem es la interfaz con la que el agente trabaja.


Troubleshooting

Problema 1: El agente no usa el filesystem

Síntoma: El agente acumula toda la información en el context window en vez de escribir a archivos. Los resultados de búsqueda se quedan en el historial de mensajes. Causa: Las instrucciones no son explícitas sobre cuándo usar el filesystem. Solución: Sé prescriptivo:

instructions = (
    "REGLA: después de cada búsqueda, SIEMPRE escribe los resultados en un archivo. "
    "NUNCA acumules más de 1 resultado de búsqueda en tu contexto sin escribirlo a disco. "
    "Usa write_file('research/[nombre_descriptivo].md', contenido)."
)

Problema 2: Archivos demasiado grandes

Síntoma: El agente escribe un solo archivo de 50K caracteres con toda la investigación, perdiendo los beneficios del context offloading. Causa: El agente no recibió instrucciones sobre granularidad de archivos. Solución: Define la estructura esperada:

instructions = (
    "Cada archivo debe cubrir UNA fuente o UN aspecto del tema. "
    "Si un archivo excede 3,000 caracteres, divídelo en archivos más pequeños. "
    "Usa directorios para organizar: research/ para fuentes, analysis/ para procesamiento."
)

Problema 3: El agente no lee archivos anteriores al sintetizar

Síntoma: El reporte final es superficial porque el agente generó la síntesis de memoria en vez de leer los archivos de investigación. Causa: El agente no fue instruido para consultar sus archivos antes de sintetizar. Solución:

instructions = (
    "Antes de generar el reporte final:\n"
    "1. Usa ls('research/') para ver qué fuentes investigaste\n"
    "2. Lee CADA archivo con read_file()\n"
    "3. Solo entonces escribe el reporte basándote en los datos reales"
)

Problema 4: Conflictos al editar archivos

Síntoma: edit_file falla porque el old text no coincide exactamente con el contenido actual del archivo. Causa: El agente "recuerda" una versión anterior del archivo que ya fue modificada. Solución: Instruye al agente a leer antes de editar:

instructions = (
    "Antes de usar edit_file, SIEMPRE lee el archivo con read_file primero "
    "para tener el contenido actual. Nunca edites basándote en lo que recuerdas."
)

Problema 5: Estructura de directorios desorganizada

Síntoma: El agente crea archivos como file1.md, temp.md, results.md en el directorio raíz sin organización lógica. Causa: Sin estructura definida, el agente usa nombres genéricos. Solución: Define la convención en las instrucciones:

instructions = (
    "Estructura de archivos obligatoria:\n"
    "- research/[fuente]_[tema].md — datos crudos de cada fuente\n"
    "- analysis/[tipo]_analysis.md — procesamiento intermedio\n"
    "- output/[entregable].md — entregables finales\n"
    "Nombres descriptivos, sin espacios, en snake_case."
)

Ejercicios

Ejercicio 1: Crear estructura de archivos (Fácil)

Crea un Deep Agent que genere una estructura de archivos para un proyecto de investigación. Debe crear al menos 3 carpetas con archivos README que expliquen el propósito de cada una.

Ver solución
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=(
        "Crea una estructura de archivos para un proyecto de investigación sobre IA. "
        "Incluye estas carpetas: research/, analysis/, output/. "
        "En cada carpeta, crea un README.md que explique qué tipo de archivos va ahí."
    ),
)

result = agent.run("Crea la estructura de archivos para investigar LLMs en producción")

for path in sorted(result.files.keys()):
    content = result.files[path]
    print(f"\n--- {path} ---")
    print(content[:200])
# Output esperado (varía):
# --- analysis/README.md ---
# # Analysis
# Archivos de análisis y procesamiento intermedio de los datos de investigación.
#
# --- output/README.md ---
# # Output
# Entregables finales: reportes, resúmenes ejecutivos, presentaciones.
#
# --- research/README.md ---
# # Research
# Datos crudos de cada fuente investigada. Un archivo por fuente.

Explicación: El agente usa write_file para crear cada archivo. La estructura refleja un flujo de trabajo organizado: datos crudos → análisis → output. Esto establece las convenciones para los ejercicios siguientes.

Ejercicio 2: Investigación multi-archivo (Fácil)

Crea un agente que investigue 3 aspectos diferentes de un tema y escriba cada aspecto en un archivo separado dentro de research/. Al final, debe crear un archivo output/summary.md con un resumen.

Ver solución
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=(
        "Para el tema proporcionado:\n"
        "1. Identifica 3 aspectos clave\n"
        "2. Escribe cada aspecto en research/aspecto_N.md\n"
        "3. Lee los 3 archivos de research/\n"
        "4. Genera un resumen integrando los 3 aspectos en output/summary.md"
    ),
)

result = agent.run("Investiga el impacto de la IA en el mercado laboral")

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"Archivos de research: {len(research_files)}")
for f in sorted(research_files):
    print(f"  {f} ({len(result.files[f])} chars)")

print(f"\nArchivos de output: {len(output_files)}")
for f in sorted(output_files):
    print(f"  {f} ({len(result.files[f])} chars)")
# Output esperado (varía):
# Archivos de research: 3
#   research/aspecto_1_automatizacion.md (1243 chars)
#   research/aspecto_2_nuevos_roles.md (1087 chars)
#   research/aspecto_3_educacion.md (956 chars)
#
# Archivos de output: 1
#   output/summary.md (2456 chars)

Explicación: El agente escribió cada aspecto en un archivo separado, luego los leyó para generar el resumen. El summary integra los tres aspectos porque el agente consultó los archivos, no su memoria del context window.

Ejercicio 3: Medir impacto del filesystem en tokens (Medio)

Ejecuta la misma tarea con dos agentes: uno que use filesystem y otro que no. Compara el total de tokens consumidos.

Ver solución
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=(
        "Para cada aspecto del tema, escribe un archivo en research/. "
        "Al final, lee los archivos y genera output/report.md. "
        "Investiga al menos 4 aspectos."
    ),
)

agent_no_fs = create_deep_agent(
    "openai:gpt-4.1-mini",
    tools=[],
    name="Without FS",
    instructions=(
        "Investiga al menos 4 aspectos del tema. "
        "NO uses archivos. Mantén toda la información en tu respuesta directa. "
        "Genera un reporte completo al final."
    ),
)

task = "Analiza las ventajas y desventajas de las 4 principales arquitecturas de microservicios"

result_fs = agent_fs.run(task)
result_no_fs = agent_no_fs.run(task)

print("=== Comparación de token usage ===")
print(f"\n  Con filesystem:")
print(f"    Tokens: {result_fs.usage.total_tokens:,}")
print(f"    Archivos: {len(result_fs.files)}")
print(f"    Output: {len(result_fs.output):,} chars")

print(f"\n  Sin filesystem:")
print(f"    Tokens: {result_no_fs.usage.total_tokens:,}")
print(f"    Archivos: {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  Ahorro: {diff:,} tokens ({diff/result_no_fs.usage.total_tokens*100:.0f}%)")
else:
    print(f"\n  Overhead del filesystem: {abs(diff):,} tokens")
# Output esperado (varía):
# === Comparación de token usage ===
#
#   Con filesystem:
#     Tokens: 15,234
#     Archivos: 6
#     Output: 4,102 chars
#
#   Sin filesystem:
#     Tokens: 23,891
#     Archivos: 0
#     Output: 2,567 chars
#
#   Ahorro: 8,657 tokens (36%)

Explicación: El agente con filesystem consume menos tokens porque no arrastra todos los resultados previos en cada turno. La diferencia se amplifica con más fuentes y más datos. Además, el output es más largo porque el context window no estaba saturado.

Ejercicio 4: Edit file para reportes iterativos (Medio)

Crea un agente que escriba un reporte inicial y luego lo refine iterativamente. Debe usar edit_file para mejorar secciones específicas, no reescribir el reporte completo cada vez.

Ver solución
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=(
        "Sigue este proceso para escribir un reporte:\n"
        "1. Escribe un borrador inicial en output/report.md con secciones marcadas [BORRADOR]\n"
        "2. Lee el borrador completo con read_file\n"
        "3. Usa edit_file para mejorar CADA sección individualmente:\n"
        "   - Reemplaza [BORRADOR] con contenido final\n"
        "   - Agrega datos específicos\n"
        "   - Mejora las transiciones entre secciones\n"
        "4. Lee el reporte final para verificar calidad"
    ),
)

result = agent.run(
    "Escribe un reporte sobre las mejores prácticas de prompt engineering para producción"
)

if "output/report.md" in result.files:
    report = result.files["output/report.md"]
    draft_markers = report.count("[BORRADOR]")
    print(f"Marcadores [BORRADOR] restantes: {draft_markers}")
    print(f"Longitud final: {len(report):,} chars")
    print(f"\nPrimeras 10 líneas:")
    for line in report.split("\n")[:10]:
        print(f"  {line}")
# Output esperado (varía):
# Marcadores [BORRADOR] restantes: 0
# Longitud final: 4,892 chars
#
# Primeras 10 líneas:
#   # Mejores Prácticas de Prompt Engineering para Producción
#
#   ## 1. Estructura del Prompt
#
#   Un prompt de producción tiene tres componentes obligatorios...

Explicación: edit_file permite refinar secciones específicas sin reescribir todo el documento. Esto es más eficiente que write_file para documentos largos y produce resultados de mayor calidad porque el agente se enfoca en una sección a la vez.

Ejercicio 5: Filesystem con planning combinado (Avanzado)

Crea un agente que use write_todos para planificar Y el filesystem para almacenar. Cada paso del plan debe corresponder a un archivo. Imprime la tabla de mapeo: paso → archivo.

Ver solución
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=(
        "Para cada tarea:\n"
        "1. Crea un plan con write_todos\n"
        "2. Para CADA paso del plan, escribe el resultado en un archivo:\n"
        "   - Paso de investigación → research/[paso_N].md\n"
        "   - Paso de análisis → analysis/[paso_N].md\n"
        "   - Paso de output → output/[paso_N].md\n"
        "3. En el resultado de cada todo, incluye el path del archivo generado\n"
        "4. Actualiza el progreso en write_todos después de cada paso"
    ),
)

result = agent.run("Compara 3 bases de datos vectoriales para RAG en producción")

print("=== Mapeo: Plan → Archivos ===")
print(f"{'Paso':<50} {'Status':<12} {'Archivo asociado'}")
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=== Archivos generados: {len(result.files)} ===")
for path in sorted(result.files.keys()):
    print(f"  {path}")
# Output esperado (varía):
# === Mapeo: Plan → Archivos ===
# Paso                                               Status       Archivo asociado
# ------------------------------------------------------------------------------------------
# Investigar Pinecone                                 completed    research/pinecone.md
# Investigar Weaviate                                 completed    research/weaviate.md
# Investigar ChromaDB                                 completed    research/chromadb.md
# Crear matriz comparativa                            completed    analysis/comparison.md
# Generar recomendación final                         completed    output/recommendation.md
#
# === Archivos generados: 5 ===
#   analysis/comparison.md
#   output/recommendation.md
#   research/chromadb.md
#   research/pinecone.md
#   research/weaviate.md

Explicación: Planning y filesystem se complementan: write_todos define QUÉ hacer, el filesystem almacena los resultados de CADA paso. Juntos, producen una ejecución organizada y trazable donde puedes ver tanto el plan como los outputs de cada paso.

Ejercicio 6: Configurar límites del filesystem (Avanzado)

Crea un agente con límites de filesystem (max 5 archivos, max 5KB por archivo) y observa cómo se comporta cuando alcanza los límites. ¿El agente se adapta?

Ver solución
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=(
        "Investiga el tema en profundidad. Escribe cada aspecto en un archivo separado. "
        "Si alcanzas el límite de archivos, consolida información en archivos existentes "
        "usando edit_file en vez de crear nuevos."
    ),
    filesystem_config={
        "max_files": 5,
        "max_file_size": 5_000,
    },
)

result = agent.run(
    "Investiga 8 frameworks de machine learning: "
    "TensorFlow, PyTorch, JAX, Keras, scikit-learn, XGBoost, LightGBM, Hugging Face"
)

print(f"=== Resultado con límite de 5 archivos ===")
print(f"  Archivos creados: {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  ✅ El agente respetó el límite de archivos")
    print("  Estrategia: consolidó múltiples frameworks por archivo")
else:
    print("\n  ❌ El agente excedió el límite (edge case)")
# Output esperado (varía):
# === Resultado con límite de 5 archivos ===
#   Archivos creados: 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)
#
#   ✅ El agente respetó el límite de archivos
#   Estrategia: consolidó múltiples frameworks por archivo

Explicación: Con un límite de 5 archivos y 8 temas, el agente se adapta agrupando frameworks relacionados en un solo archivo. Los límites del filesystem fuerzan al agente a ser eficiente con el espacio, similar a cómo un presupuesto limitado fuerza priorización. Esto es especialmente útil para controlar costos en producción.


Resumen

  • El virtual filesystem permite al agente leer, escribir y editar archivos. En lugar de acumular datos en el context window (caro, limitado), el agente escribe outputs a archivos y lee solo lo que necesita — esto es context offloading
  • Cuatro herramientas: ls (listar), read_file (leer), write_file (crear/sobrescribir), edit_file (ediciones puntuales). Deep Agents las inyecta automáticamente
  • Los archivos funcionan como memoria de trabajo del agente: mientras el context window es memoria de corto plazo, los archivos son un cuaderno de notas persistente que el agente consulta selectivamente
  • Impacto en costos: en investigaciones multi-fuente, el filesystem puede reducir tokens consumidos un 30-75% porque cada turno solo carga los datos que necesita
  • Impacto en calidad: con un context window más lean, el modelo mantiene mejor atención sobre los datos relevantes, produciendo outputs más completos y coherentes
  • El filesystem es un sandbox: el agente no puede acceder a archivos reales del sistema, ejecutar comandos, ni salir del workspace virtual. Configura límites de archivos y tamaño para controlar recursos
  • Conexión con M8: el filesystem abstrae la persistencia que construiste manualmente con checkpointers. El checkpointer guarda el estado completo (incluyendo archivos) para crash recovery; el filesystem es la interfaz con la que el agente trabaja

Próxima cápsula: Subagent Spawning y Delegación — cómo el agente crea subagentes especializados on-demand para manejar subtareas, con contexto aislado y límites de control.


Recursos adicionales

  1. Deep Agents — Filesystem — Documentación oficial del virtual filesystem y sus herramientas
  2. Context Window Management — Anthropic — Guía de Anthropic sobre cómo manejar contextos largos eficientemente
  3. LangGraph — Checkpointing — La base de persistencia sobre la que el filesystem opera
  4. The Needle in a Haystack Test — Paper sobre cómo los modelos pierden información en contextos largos, motivación directa para context offloading
  5. Retrieval-Augmented Generation Survey — RAG es otra forma de context offloading (external knowledge); el filesystem es offloading de working data
  6. Sandboxing AI Agents — Safety Considerations — Consideraciones de seguridad al dar a agentes acceso a herramientas de filesystem

Módulo 11 — LangChain & LangGraph: From Chains to Agents

Siguiente cápsula: Subagent Spawning y Delegación — aprenderás cómo el agente crea subagentes especializados on-demand, con contexto aislado para cada subtarea, y cómo configurar límites para controlar costos y complejidad.