> ## Documentation Index
> Fetch the complete documentation index at: https://docs.langchain.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Build a data analysis agent from scratch

> Build a data analysis agent step by step using create_agent and Deep Agents middleware.

This guide builds a data analysis agent from first principles using `create_agent` and Deep Agents middleware.

Both `create_agent` and `create_deep_agent` provide you with fine-grained control over tools, memory, and more.
The main difference between both is that Deep Agents comes with a range of commonly useful capabilities already built in, such as planning, file system tools, and subagents.
If the Deep Agents default harness does not fit your needs, this guide shows you how to start with `create_agent` and assemble the harness one piece at a time, so you can see exactly what each component adds and swap in only what your use case needs.

Follow this guide to build an agent that:

1. Accepts a CSV file for analysis
2. Writes and executes Python code in an isolated sandbox
3. Delegates visualization work to a specialized subagent
4. Loads data analysis patterns from a skills file

The final stack mirrors what `create_deep_agent` assembles by default.

## What you will learn

Each step adds one capability to the same data analysis agent:

| Step                 | Problem without it                     | What you add                                                                                                 |
| -------------------- | -------------------------------------- | ------------------------------------------------------------------------------------------------------------ |
| Minimal agent        | —                                      | Baseline loop: model + tools, no harness                                                                     |
| Sandbox + filesystem | Agent cannot read CSVs or run Python   | Isolated [backend](/oss/javascript/deepagents/backends) + file and execute tools                             |
| Summarization        | Long sessions hit context limits       | Automatic history compression                                                                                |
| Skills               | Domain rules bloat the system prompt   | On-demand expertise via [progressive disclosure](/oss/javascript/langchain/multi-agent/skills-sql-assistant) |
| Subagent             | Chart iteration crowds the main thread | Isolated worker + parallel delegation                                                                        |

## Setup

<Steps>
  <Step title="Install packages">
    Install the packages for this tutorial:

    ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    npm install deepagents langsmith
    ```
  </Step>

  <Step title="Set up LangSmith API keys">
    This tutorial uses [`LangSmithSandbox`](https://reference.langchain.com/javascript/deepagents/backends/LangSmithSandbox), which provisions sandboxes through `SandboxClient`. That client authenticates with LangSmith using `LANGSMITH_API_KEY` from your environment, so an API key is required to run the tutorial. Setting up LangSmith also allows you to see traces of what happens when your agent runs.

    1. [Sign up for a free account](https://smith.langchain.com?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=oss-langchain-deep-agent-from-scratch). You can use Google, GitHub, or email.
    2. [Create an API key](/langsmith/create-account-api-key) in **Settings > API Keys**.
    3. Export the LangSmith API key:

    ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    export LANGSMITH_API_KEY=...
    ```

    4. Enable tracing to inspect tool calls, middleware steps, and subagent delegation as you add each piece:

    ```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
    export LANGSMITH_TRACING=true
    ```
  </Step>

  <Step title="Add a model provider API key">
    Export the API key for the model provider you use in the code samples:

    <CodeGroup>
      ```bash Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export GOOGLE_API_KEY=...
      ```

      ```bash OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export OPENAI_API_KEY=...
      ```

      ```bash Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export ANTHROPIC_API_KEY=...
      ```

      ```bash OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export OPENROUTER_API_KEY=...
      ```

      ```bash Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export FIREWORKS_API_KEY=...
      ```

      ```bash Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      export BASETEN_API_KEY=...
      ```

      ```bash Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
      # Local: Ollama must be running on your machine
      # Cloud: Set your Ollama API key for hosted inference
      export OLLAMA_API_KEY=...
      ```
    </CodeGroup>
  </Step>
</Steps>

## Build the agent

## Create the minimal agent

A data analysis agent needs more than a chat loop, but to begin with, start with the baseline: only a model and a loop.

Use [`create_agent`](https://reference.langchain.com/javascript/langchain/index/createAgent) and specify the model that you want to use:

<CodeGroup>
  ```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "google-genai:gemini-3.6-flash",
    tools: [],
  });
  ```

  ```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "openai:gpt-5.5",
    tools: [],
  });
  ```

  ```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "anthropic:claude-sonnet-5",
    tools: [],
  });
  ```

  ```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "openrouter:z-ai/glm-5.2",
    tools: [],
  });
  ```

  ```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "fireworks:accounts/fireworks/models/glm-5p2",
    tools: [],
  });
  ```

  ```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "baseten:zai-org/GLM-5.2",
    tools: [],
  });
  ```

  ```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createAgent } from "langchain";

  let agent = createAgent({
    model: "ollama:north-mini-code-1.0",
    tools: [],
  });
  ```
</CodeGroup>

This runs, but the agent has no filesystem and no way to execute code. If you ask it to analyze a CSV, it can only guess from the prompt. The next steps add real file access and code execution.

## Add a sandbox backend

To analyze data efficiently, the agent needs to run code on files. This requires two things:

* An isolated [sandbox](/oss/javascript/deepagents/sandboxes) where the agent can place files and run code on the files without giving the agent access to your host machine.

* A [backend](/oss/javascript/deepagents/backends) which provides the file system tools to work with the sandbox (`read_file`, `write_file`, `edit_file`, `glob`, `grep`) using the [`FilesystemMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createFilesystemMiddleware):\*\*. Because the `LangSmithSandbox` backend implements the sandbox protocol, [`FilesystemMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createFilesystemMiddleware) also adds the `execute` tool, which allows the agent to run shell commands.

[`LangSmithSandbox`](https://reference.langchain.com/javascript/deepagents/backends/LangSmithSandbox) is where files live and commands run. [`FilesystemMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createFilesystemMiddleware) is what exposes that environment to the model as tools. The same middleware works with other backends if you swap the backend later.

[`LangSmithSandbox`](https://reference.langchain.com/javascript/deepagents/backends/LangSmithSandbox) gives the agent an isolated environment with a filesystem and an `execute` tool for running shell commands. With it, the agent can install packages, write scripts, and run them without touching the host. To boot from a custom image instead of the default runtime, pass `snapshotId` to `LangSmithSandbox.create()`; see [Sandbox snapshots](/langsmith/sandbox-snapshots).

Replace the agent from the previous step with one that includes [`FilesystemMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createFilesystemMiddleware):

<CodeGroup>
  ```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "google-genai:gemini-3.6-flash",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "openai:gpt-5.5",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "anthropic:claude-sonnet-5",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "openrouter:z-ai/glm-5.2",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "fireworks:accounts/fireworks/models/glm-5p2",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "baseten:zai-org/GLM-5.2",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```

  ```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createFilesystemMiddleware, LangSmithSandbox } from "deepagents";
  import { SandboxClient } from "langsmith/sandbox";

  const client = new SandboxClient();
  const sandbox = await client.createSandbox({
    name: "langchain-docs",
    snapshotName: "docs-test-ci",
  });
  const backend = new LangSmithSandbox({ sandbox });

  agent = createAgent({
    model: "ollama:north-mini-code-1.0",
    tools: [],
    middleware: [createFilesystemMiddleware({ backend })],
  });
  ```
</CodeGroup>

The sandbox filesystem is separate from your laptop. You must upload the files you need to it before you invoke the agent:

```ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
const rows = [
  ["Date", "Product", "Units", "Revenue"],
  ["2025-08-01", "Widget A", "10", "250"],
  ["2025-08-02", "Widget B", "5", "125"],
  ["2025-08-03", "Widget A", "7", "175"],
  ["2025-08-04", "Widget C", "3", "90"],
];

const csv = rows.map((row) => row.join(",")).join("\n");
const encoder = new TextEncoder();
await backend.uploadFiles([["/sales.csv", encoder.encode(csv)]]);

const uploadStream = await agent.streamEvents(
  {
    messages: [
      {
        role: "user",
        content:
          "Read /sales.csv and summarize total revenue by product in one sentence. Do not run shell commands.",
      },
    ],
  },
  { version: "v3", recursionLimit: 8 },
);

await Promise.all([
  (async () => {
    for await (const message of uploadStream.messages) {
      console.log(await message.text);
    }
  })(),
  uploadStream.output,
]);
```

<Note>
  With [`LangSmithSandbox`](https://reference.langchain.com/javascript/deepagents/backends/LangSmithSandbox), upload paths must be absolute POSIX paths (for example, `/sales.csv`). Relative paths such as `sales.csv` are rejected with `invalid_path` and the file is not written to the sandbox.
</Note>

Combine the code from the previous steps into one script and run it:

```bash theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
npx tsx analyze-sales.ts
```

On the first run, LangSmith provisions a sandbox (this can take a few seconds). The script uploads `sales.csv`, streams the agent run, and prints assistant messages as they arrive. You should see an analysis of the sample sales data: product-level revenue, which widgets sold most, and brief trend notes. Exact wording varies by model run.

Open the run in [LangSmith](https://smith.langchain.com/?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=oss-langchain-deep-agent-from-scratch) and watch the agent use filesystem tools (`read_file`, and `execute` if it runs Python in the sandbox) before it replies.

## Add context management

After step 2, every tool result stays in the message history. A real analysis session (multiple plots, failed scripts, large `read_file` output) fills the context window quickly.

[`SummarizationMiddleware`](https://reference.langchain.com/javascript/langchain/index/summarizationMiddleware) compresses older turns when history grows too large, so the agent keeps working without you manually trimming messages. This matters less on the first `sales.csv` question and more on follow-ups such as "Now segment by product and plot monthly trends."

Update your agent from step 2 by adding [`SummarizationMiddleware`](https://reference.langchain.com/javascript/langchain/index/summarizationMiddleware) to the middleware list:

<CodeGroup>
  ```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "google-genai:gemini-3.6-flash";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "openai:gpt-5.5";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "anthropic:claude-sonnet-5";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "openrouter:z-ai/glm-5.2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "fireworks:accounts/fireworks/models/glm-5p2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "baseten:zai-org/GLM-5.2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```

  ```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSummarizationMiddleware } from "deepagents";

  let model = "ollama:north-mini-code-1.0";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({
        model,
        backend,
      }),
    ],
  });
  ```
</CodeGroup>

Run a multi-turn session to see summarization in action. After the initial analysis, ask follow-up questions that trigger more file reads or script runs. In LangSmith, look for a summarization step before later model calls. For more information, [Context engineering](/oss/javascript/langchain/context-engineering).

## Add skills

[Skills](/oss/javascript/langchain/multi-agent/skills-sql-assistant) provide a way to give an agent on-demand domain knowledge when needed using progressive disclosure. Skills can include multi-step workflows, rules, and conventions. By placing this information in a skill, it isn't added to the system prompt by default which ensures the tokens are only used when the information from the skill is needed for a task.

When the agent starts, it sees only lightweight metadata about each skill. When a task needs a skill, the agent loads the full skill file on demand.

Create a skill file in a skills directory:

```
skills/
  pandas-patterns/
    SKILL.md
```

```markdown theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
---
name: pandas-patterns
description: Common pandas and matplotlib patterns for data analysis and visualization
---

## Data loading
Use `pd.read_csv()` for CSV files. Always check `df.info()` and `df.describe()` first.

## Visualization
Use `matplotlib` for bar charts, `seaborn` for statistical plots.
Save figures with `plt.savefig("output.png", dpi=150, bbox_inches="tight")`.

## Reporting
Write a markdown summary to `report.md` alongside any generated charts.
```

This skill contains information on how the visualization should be done.

With [`LangSmithSandbox`](https://reference.langchain.com/javascript/deepagents/backends/LangSmithSandbox), skill paths resolve on the sandbox filesystem, not your local machine. Upload your local `skills/` directory before configuring [`SkillsMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createSkillsMiddleware):

```ts theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
import { readFileSync, readdirSync, statSync } from "node:fs";
import { join, relative, resolve } from "node:path";
import { fileURLToPath } from "node:url";

const skillsDir = resolve(
  fileURLToPath(new URL(".", import.meta.url)),
  "skills",
);
const skillFiles: Array<[string, Uint8Array]> = [];

function collectSkillFiles(dir: string): void {
  for (const entry of readdirSync(dir)) {
    const fullPath = join(dir, entry);
    if (statSync(fullPath).isDirectory()) {
      collectSkillFiles(fullPath);
    } else {
      const rel = relative(skillsDir, fullPath).replace(/\\/g, "/");
      skillFiles.push([`/skills/${rel}`, readFileSync(fullPath)]);
    }
  }
}

collectSkillFiles(skillsDir);
await backend.uploadFiles(skillFiles);
```

Then create your agent with your skills by adding [`SkillsMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createSkillsMiddleware):

<CodeGroup>
  ```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "google-genai:gemini-3.6-flash";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "openai:gpt-5.5";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "anthropic:claude-sonnet-5";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "openrouter:z-ai/glm-5.2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "fireworks:accounts/fireworks/models/glm-5p2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "baseten:zai-org/GLM-5.2";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```

  ```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { createSkillsMiddleware } from "deepagents";

  model = "ollama:north-mini-code-1.0";

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
    ],
  });
  ```
</CodeGroup>

You can try a prompt such as "Analyze sales.csv using our pandas patterns." The agent will load the skill when it needs plotting or reporting guidance. If you ask a different question that does not need the skill, the agent will not load it.

## Add a visualization subagent

Some tasks produce large intermediate output (script drafts, failed runs, file reads) that would crowd the main agent's context if kept in one thread. A [subagent](/oss/javascript/deepagents/subagents) runs in its own context window so the supervisor sees only the final result, not every tool call along the way. That keeps the main analysis focused and leaves room for follow-up questions.

One example where using a subagent makes sense is chart generation. Plotting often means iterating on Python scripts, installing packages, and reading error output before a figure is ready. The following `visualizer` subagent can handle that work in isolation while the main agent continues planning and analysis. With [`TodoListMiddleware`](https://reference.langchain.com/javascript/langchain/index/todoListMiddleware), the main agent can also delegate that chart work in parallel instead of blocking on each plot.

Update your agent from step 4 by adding [`TodoListMiddleware`](https://reference.langchain.com/javascript/langchain/index/todoListMiddleware) and [`SubAgentMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createSubAgentMiddleware):

<CodeGroup>
  ```ts Google theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "google-genai:gemini-3.6-flash";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts OpenAI theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "openai:gpt-5.5";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts Anthropic theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "anthropic:claude-sonnet-5";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts OpenRouter theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "openrouter:z-ai/glm-5.2";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts Fireworks theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "fireworks:accounts/fireworks/models/glm-5p2";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts Baseten theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "baseten:zai-org/GLM-5.2";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```

  ```ts Ollama theme={"theme":{"light":"catppuccin-latte","dark":"catppuccin-mocha"}}
  import { todoListMiddleware } from "langchain";
  import { createSubAgentMiddleware, type SubAgent } from "deepagents";

  model = "ollama:north-mini-code-1.0";

  const visualizer: SubAgent = {
    name: "visualizer",
    description:
      "Generates charts and visualizations from data files in the sandbox.",
    systemPrompt:
      "You are a data visualization specialist. Write Python scripts using matplotlib and seaborn. Save all figures as PNG files.",
    tools: [],
    model,
  };

  agent = createAgent({
    model,
    tools: [],
    middleware: [
      createFilesystemMiddleware({ backend }),
      createSummarizationMiddleware({ model, backend }),
      createSkillsMiddleware({ backend, sources: ["/skills/"] }),
      todoListMiddleware(),
      createSubAgentMiddleware({
        defaultModel: model,
        defaultTools: [],
        subagents: [visualizer],
      }),
    ],
  });
  ```
</CodeGroup>

Try a prompt such as "Analyze sales.csv, then create a bar chart of revenue by product." The main agent handles analysis and planning and delegates chart generation to the `visualizer` subagent via the `task` tool.

If you enabled tracing in [Setup](#setup), open the run in [LangSmith](https://smith.langchain.com/?utm_source=docs\&utm_medium=cta\&utm_campaign=langsmith-signup\&utm_content=oss-langchain-deep-agent-from-scratch). You should see a `task` call to `visualizer`, a separate sub-run with its own tool loop, and a short result returned to the supervisor.

## What you built

You've built a customized agent with the following middleware:

| Middleware                                                                                                                                                                                                                | What it adds                         |
| ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [`FilesystemMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createFilesystemMiddleware) + `LangSmithSandbox`                                                                                | Isolated filesystem + `execute` tool |
| [`SummarizationMiddleware`](https://reference.langchain.com/javascript/langchain/index/summarizationMiddleware)                                                                                                           | Automatic context compression        |
| [`SkillsMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createSkillsMiddleware)                                                                                                             | Domain knowledge loaded on demand    |
| [`TodoListMiddleware`](https://reference.langchain.com/javascript/langchain/index/todoListMiddleware) + [`SubAgentMiddleware`](https://reference.langchain.com/javascript/deepagents/middleware/createSubAgentMiddleware) | Parallel visualization subagent      |

This is the same foundation as [`createDeepAgent`](https://reference.langchain.com/javascript/deepagents/agent/createDeepAgent): assembled manually so you control exactly what's included.

The possibilities don't end here: see [Prebuilt middleware](/oss/javascript/langchain/middleware/built-in) for the full list of composable capabilities, and the [`create_agent`](https://reference.langchain.com/javascript/langchain/index/createAgent) reference for all configuration options.

To work with the pre-assembled version, see [Customize Deep Agents](/oss/javascript/deepagents/customization). For the full data analysis example using `createDeepAgent`, see [Data analysis](/oss/javascript/deepagents/data-analysis).

***

<div className="source-links">
  <Callout icon="terminal-2">
    [Connect these docs](/use-these-docs) to your agent of choice via MCP for real-time answers.
  </Callout>

  <Callout icon="edit">
    [Edit this page on GitHub](https://github.com/langchain-ai/docs/edit/main/src/oss/langchain/deep-agent-from-scratch.mdx) or [file an issue](https://github.com/langchain-ai/docs/issues/new/choose).
  </Callout>
</div>
