Back to blog

Product · Jun 8, 2026

Facio's Notebook Editing: How AI Agents Read and Modify Jupyter Notebooks at the Cell Level

Jupyter notebooks are a mess for AI agents. The JSON structure obscures the actual code, cells are interleaved with outputs and metadata, and most agents treat the whole notebook as opaque text. Facio's notebook_edit tool works at the cell level — read, replace, insert, and delete individual code and markdown cells with surgical precision. Here's why notebook-aware editing matters for data science agents.

JupyterNotebooksData ScienceCode EditingAgent Tooling

Facio's Notebook Editing: How AI Agents Read and Modify Jupyter Notebooks at the Cell Level

Jupyter notebooks are the lingua franca of data science. Every data scientist, ML engineer, and analytics professional has notebooks in their workflow — exploratory analysis, model training, results reporting, stakeholder presentations. But to most AI agents, notebooks are an opaque blob: a .ipynb file is JSON wrapped around interleaved code, outputs, and metadata, and the agents that try to edit them typically break them in subtle ways.

Facio's notebook_edit tool fixes this by working at the cell level, not the file level. The agent reads, replaces, inserts, and deletes individual code and markdown cells — preserving notebook structure, keeping outputs intact, and never accidentally clobbering metadata. Here's why notebook-aware editing matters and how it enables data science agent workflows.

Why Notebooks Are Hard for Agents

A Jupyter notebook file is JSON. Inside the JSON, cells are an array of objects — each with a cell type (code or markdown), a source array, execution counts, metadata, and (for code cells) an outputs array. The structure is precise, version-sensitive, and easy to break.

When an agent uses a generic file editing tool on a notebook, several things go wrong:

  1. The agent edits "the code" but doesn't realize it's modifying a string array. Jupyter source fields are arrays of strings (one per line), not single strings. Naive text replacement can produce invalid JSON.

  2. Outputs get clobbered. Outputs include data, metadata, and sometimes large base64-encoded images. A careless overwrite destroys the rendered output that took minutes to generate.

  3. Execution counts get reset. A notebook that took 200 minutes of kernel time to run gets its execution_count set to null because the agent rewrote the source.

  4. Cell types get confused. Replacing a code cell with markdown (or vice versa) silently changes what executes when the notebook is run.

The result: notebooks that look right but are subtly broken. Code that "should" work produces different results. Outputs that "should" be there are missing. The notebook no longer reproduces.

Cell-Level Operations

Facio's notebook_edit tool supports four operations, each at the cell level:

OperationWhat it doesUse case
replaceReplace the content of an existing cell at a specific indexFix a bug, update a function, add a parameter
insertAdd a new cell after a specific indexAdd a new analysis step, prepend documentation
deleteRemove a cell at a specific indexDrop a dead-end exploration, clean up scratch work

The tool handles all the structural details: keeping source as an array of strings, preserving execution counts and metadata, maintaining the cell type, and keeping outputs intact for cells that aren't being modified.

# Replace cell #5 (a code cell with a buggy function)
notebook_edit(
    path="projects/analysis/main.ipynb",
    cell_index=5,
    new_source="def calculate_metrics(data):\n    return data.agg(['mean', 'std', 'count'])\n",
    cell_type="code",
    edit_mode="replace"
)

The cell type and edit mode are explicit. The tool never infers them from context — which is exactly what prevents the silent type confusion that breaks notebooks in less-aware agents.

The Three Edit Modes

Replace

Replace the source of an existing cell, preserving all other fields (cell type, execution count, outputs, metadata). The agent modifies code or markdown in place — outputs from the original cell (if any) stay attached to the new source, which is usually what you want for a code fix that produces different but still-valid output.

For code cells, replacing the source means a subsequent kernel run will produce fresh outputs — but the old outputs remain in the file until a kernel execution regenerates them. This is correct behavior: the file accurately reflects what the cell produced last, even if the source has since been updated.

Insert

Add a new cell after a specific index. The new cell has no execution count and no outputs (it hasn't been run yet). Insert preserves the relative order of existing cells and slots the new cell in at the requested position.

This is the operation for adding a new analysis step, injecting a debugging cell, or prepending documentation. The agent can build up notebooks incrementally — cell by cell, step by step — without rewriting the entire file.

Delete

Remove a cell at a specific index. The deletion is final (no soft-delete or undo), and the remaining cells shift their indices accordingly. The agent can clean up exploratory cells that didn't lead anywhere, remove duplicate imports, or strip scratch work from a final-report notebook.

Integration with the Workspace and Credential Systems

Notebook editing slots naturally into the broader Facio workspace and credential systems:

  • Notebooks live in projects/. Following the WORKSPACE.md conventions, substantial multi-file work — including notebooks — belongs in projects/<project-name>/. The agent finds the notebook with glob or grep, edits the specific cell, and continues the workflow.
  • Notebooks can reference credentials. A data pipeline notebook that pulls from a database can use ${credentials.DATABASE_URL} placeholders in the code, resolved at execution time. The agent edits the cell, the placeholder stays in place, the kernel resolves it.
  • Notebooks integrate with the file tool surface. read_file(path="...") returns the raw notebook JSON, but notebook_edit provides the structured alternative. The agent picks the right tool for the operation.

Production Patterns

Pattern 1: Automated Notebook Cleanup

After running an exploratory analysis, the notebook has 50 cells — some with the actual analysis, some with dead-end explorations, some with debugging prints. The agent:

  1. read_file to see the structure.
  2. notebook_edit(action="read", cell_index=N) to inspect each cell.
  3. notebook_edit(edit_mode="delete", cell_index=M) to remove dead cells.
  4. notebook_edit(edit_mode="replace", cell_index=K, new_source="...") to clean up the surviving cells.
  5. The result: a focused, publishable notebook — produced by the agent, not the human.

Pattern 2: Versioned Analysis Updates

A quarterly report notebook has been running for a year. Q3 2026 needs a new metric. The agent:

  1. Greps the notebook to find the metrics section.
  2. notebook_edit(edit_mode="insert", cell_index=42, new_source="new_metric = ...", cell_type="code") to add the new metric calculation.
  3. notebook_edit(edit_mode="replace", cell_index=44, new_source="updated_table", cell_type="code") to update the visualization cell.
  4. The notebook now has the new analysis, the old analysis is preserved, and outputs will regenerate on the next kernel run.

Pattern 3: Documentation Generation

A complex data pipeline notebook needs documentation. The agent:

  1. Reads the notebook cell by cell.
  2. For each code cell, generates a markdown explanation.
  3. notebook_edit(edit_mode="insert", cell_index=N, new_source="## Explanation\n\nThis cell calculates...", cell_type="markdown") to insert a markdown cell above each code cell.
  4. The result: a self-documenting notebook, generated by the agent from the code itself.

Pattern 4: Multi-Agent Notebook Collaboration

A parent agent spawns three sub-agents — one for data loading, one for feature engineering, one for model training. Each sub-agent works on a different section of the same notebook:

  1. Parent agent creates the initial notebook structure.
  2. Sub-agent 1: notebook_edit(insert, cell_index=5, ...) adds data loading cells.
  3. Sub-agent 2: notebook_edit(insert, cell_index=10, ...) adds feature engineering cells.
  4. Sub-agent 3: notebook_edit(insert, cell_index=20, ...) adds model training cells.
  5. Parent agent reads the final notebook and presents it for human review.

The sub-agents don't step on each other because they work on different cell ranges. The notebook is a shared collaboration surface with deterministic ordering.

What notebook_edit Doesn't Do

The tool has deliberate constraints:

  • It doesn't execute notebooks. The agent edits the file structure, not the kernel state. For execution, the agent uses exec to run Python scripts directly, or spawns sub-agents that have their own kernel access.
  • It doesn't manage kernels. Jupyter kernels (Python 3, R, Julia) are external to the notebook file. The agent edits the file; a separate process (or human) runs it.
  • It doesn't validate syntax. If the agent writes invalid Python, the tool writes it anyway. The error surfaces when the cell runs. This is correct behavior — the agent should be able to write experimental code that fails to run, and learn from the error.

Bottom Line

Data science lives in notebooks. AI agents that can't edit notebooks cleanly aren't really data science agents — they're text agents that happen to know what .ipynb is. The difference matters when notebooks are the deliverable: a quarterly analysis report, a model training pipeline, a reproducible research artifact.

Facio's notebook_edit gives agents true notebook awareness: cell-level operations, structural preservation, output-aware updates. The agent modifies a single function without destroying the outputs of every other cell. It inserts a documentation cell without resetting execution counts. It deletes a dead-end exploration without breaking the JSON.

Because a notebook that runs is worth more than a notebook that's "fixed" but no longer reproduces.


See the notebook editing documentation for cell type handling, execution count preservation, and multi-agent collaboration patterns.

Keep reading

More on Product

View category
Jun 7, 2026Product

Facio's Media Generation Tools: How AI Agents Create Images and Videos Programmatically

AI agents that can only produce text are leaving half their potential on the table. Facio's generate_image and generate_video tools let agents create visual content programmatically — across OpenAI, Google Gemini, Replicate, and fal.ai — with provider-agnostic APIs, HITL approval gates, and direct delivery to any channel. Here's how autonomous visual content creation works in production.

Jun 6, 2026Product

Facio's Workspace System: How File Tools and Layout Conventions Keep Agent Work Organized

AI agents that can read, write, and edit files are common. Agents that understand where files belong — following project conventions, keeping roots clean, and organizing deliverables — are rare. Facio's workspace system combines a full file tool surface (read_file, write_file, edit_file, grep, glob, patch_file) with WORKSPACE.md layout rules that give agents structural awareness. Here's how it turns file access into file discipline.

Jun 5, 2026Product

Facio's Built-in Log System: How read_logs Makes Agent Execution Auditable in Real Time

When an AI agent makes a mistake at 4 AM, you need to know what happened — not wait for a human to grep through server logs. Facio's read_logs tool gives agents access to their own persistent execution log, with level filtering, time-range queries, and regex search. The agent diagnoses its own failures. Here's how the architecture works and why self-auditability matters for production autonomy.