Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions src/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -225,7 +225,7 @@
"root": "oss/python/deepagents/interpreters",
"expanded": true,
"pages": [
"oss/python/deepagents/programmatic-subagents"
"oss/python/deepagents/dynamic-subagents"
]
},
"oss/python/deepagents/profiles",
Expand Down Expand Up @@ -696,7 +696,7 @@
"root": "oss/javascript/deepagents/interpreters",
"expanded": true,
"pages": [
"oss/javascript/deepagents/programmatic-subagents"
"oss/javascript/deepagents/dynamic-subagents"
]
},
"oss/javascript/deepagents/profiles",
Expand Down Expand Up @@ -2316,6 +2316,14 @@
"source": "/oss/deepagents/harness",
"destination": "/oss/deepagents/overview"
},
{
"source": "/oss/python/deepagents/programmatic-subagents",
"destination": "/oss/python/deepagents/dynamic-subagents"
},
{
"source": "/oss/javascript/deepagents/programmatic-subagents",
"destination": "/oss/javascript/deepagents/dynamic-subagents"
},
{
"source": "/oss/python/deepagents/harness",
"destination": "/oss/python/deepagents/overview"
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
---
title: Programmatic subagents
title: Dynamic subagents
description: Use interpreters to dispatch and orchestrate Deep Agents subagents from code
---

Programmatic subagents let an agent dispatch [subagents](/oss/deepagents/subagents) from interpreter code. Instead of asking the model to choose one subagent call at a time, the agent can use JavaScript loops, branches, and parallel batches to route work across configured subagents and synthesize the results.
Dynamic subagents let an agent dispatch [subagents](/oss/deepagents/subagents) from interpreter code. Instead of asking the model to choose one subagent call at a time, the agent can use JavaScript loops, branches, and parallel batches to route work across configured subagents and synthesize the results.

Use this pattern when work spans many independent units, needs multiple perspectives, or benefits from recursive analysis. For general interpreter setup, see [Interpreters](/oss/deepagents/interpreters).

<Warning>
Programmatic subagents use the interpreter runtime, which is in [**beta**](/oss/versioning). APIs and lifecycle behavior may change between releases.
Dynamic subagents use the interpreter runtime, which is in [**beta**](/oss/versioning). APIs and lifecycle behavior may change between releases.
</Warning>

:::python
Expand Down Expand Up @@ -52,15 +52,29 @@ const critical = review.issues.filter((issue) => issue.severity === "high");

When you pass `responseSchema`, the resolved value is already a typed JavaScript object; only call `JSON.parse` if a subagent intentionally returned a JSON string.

## Guide orchestration
## Trigger dynamic orchestration

The interpreter middleware ships orchestration guidance in the system prompt, so the agent already knows how to fan out in bounded batches, filter between passes, and synthesize results. You do not hand-write that logic or prompt for it turn by turn.
Dynamic subagents are dispatched implicitly: the agent decides to fan work out from code based on the shape of the task, rather than being switched on by a configuration flag. The interpreter middleware's built-in system prompt already steers the agent toward this behavior for work that spans many independent units.

To shape what the agent orchestrates, work through the inputs it already responds to:
The interpreter system prompt strongly hints that a "workflow" equates to organizing work through the interpreter—dispatching subagents with `task()` and assembling the results, rather than grinding through items one model-chosen tool call at a time. So phrasing a request as a "workflow" nudges the agent toward dynamic orchestration.

- **The subagents you configure.** Their `name` and `description` define the roles available. A `reviewer` paired with a `verifier` invites a two-pass check; a single `analyzer` invites a straight fan-out.
- **The task message.** Phrasing like "I only want confirmed issues, not maybes" or "be exhaustive" nudges the agent toward verification or an open-ended sweep.
- **The system prompt.** Use `systemPrompt` (or the agent's instructions) to add standing guidance when you want a consistent strategy across runs.
:::python
```python
# Phrasing that mentions a "workflow" nudges the agent toward dynamic dispatch.
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "Run a workflow that reviews every file in src/routes/ and summarizes the top risks."}]
})
```
:::

:::js
```typescript
// Phrasing that mentions a "workflow" nudges the agent toward dynamic dispatch.
const result = await agent.invoke({
messages: [{ role: "user", content: "Run a workflow that reviews every file in src/routes/ and summarizes the top risks." }],
});
```
:::

## Patterns

Expand Down Expand Up @@ -610,7 +624,7 @@ found;
</Warning>
:::

## Disable programmatic subagents
## Disable dynamic subagents

Subagent dispatch is on by default whenever the agent has subagents. Disable it if you want subagents to be available only through the normal `task` tool path.

Expand Down
18 changes: 9 additions & 9 deletions src/oss/deepagents/interpreters.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ Interpreters give the agent a runtime for that work. A loop runs every iteration
<Card title="Programmatic tool calling (PTC)" icon="tool" href="#programmatic-tool-calling-ptc">
Call selected tools from interpreter code, including loops, retries, branching, and parallel batches.
</Card>
<Card title="Programmatic subagents" icon="arrows-split" href="/oss/deepagents/programmatic-subagents">
<Card title="Dynamic subagents" icon="arrows-split" href="/oss/deepagents/dynamic-subagents">
Dispatch subagents from code for fan-out, verification, and recursive workflows over large inputs.
</Card>
<Card title="Stateful work" icon="database" href="#how-interpreters-work">
Expand All @@ -48,7 +48,7 @@ Use interpreters for code inside the agent loop: composing tools, preserving sta
| One or two simple external calls | Normal tool calling |
| A small program that loops, branches, retries, or aggregates results | Interpreter |
| Many selected tool calls that should run from code | Interpreter with [programmatic tool calling (PTC)](#programmatic-tool-calling-ptc) |
| Many independent units of work, multiple perspectives, or recursive analysis over large inputs | Interpreter with [programmatic subagents](/oss/deepagents/programmatic-subagents) |
| Many independent units of work, multiple perspectives, or recursive analysis over large inputs | Interpreter with [dynamic subagents](/oss/deepagents/dynamic-subagents) |
| Shell commands, package installs, tests, or full OS filesystem access | [Sandboxes](/oss/deepagents/sandboxes) |

## Quickstart
Expand Down Expand Up @@ -134,7 +134,7 @@ Code runs against [**QuickJS**](https://github.com/quickjs-ng/quickjs), a lightw
Two explicit bridges extend that reach:

- **Tools**, through [programmatic tool calling (PTC)](#programmatic-tool-calling-ptc). Expose an allowlist of tools as async functions under the `tools` namespace. These can be the agent's own tools or standalone tools you define and pass in.
- **Subagents**, through [programmatic subagents](/oss/deepagents/programmatic-subagents). Dispatch configured subagents from code and orchestrate them in plain JavaScript.
- **Subagents**, through [dynamic subagents](/oss/deepagents/dynamic-subagents). Dispatch configured subagents from code and orchestrate them in plain JavaScript.

Programmatic tool calling is off until you [enable it](#enable-ptc). Subagent dispatch is on by default whenever the agent has subagents, and you can turn it off. Nothing else crosses the QuickJS boundary unless you expose it.

Expand Down Expand Up @@ -205,17 +205,17 @@ results.join("\n\n");
</Warning>
:::

## Programmatic subagents
## Dynamic subagents

Programmatic subagents let the interpreter dispatch configured [subagents](/oss/deepagents/subagents) from code using the built-in `task()` global. A task that spans many independent units, such as reviewing every file in a directory or triaging a batch of tickets, becomes a loop that fans work out and synthesizes the results.
Dynamic subagents let the interpreter dispatch configured [subagents](/oss/deepagents/subagents) from code using the built-in `task()` global. A task that spans many independent units, such as reviewing every file in a directory or triaging a batch of tickets, becomes a loop that fans work out and synthesizes the results.

Use programmatic subagents for:
Use dynamic subagents for:

- **Fan-out and synthesize**: Run the same kind of work across many items in parallel, then combine the results.
- **Verification**: Send findings to independent verifier subagents and keep only confirmed results.
- **Recursive workflows**: Keep a working set in interpreter variables, select slices, call subagents, and refine the result.

For configuration, examples, orchestration patterns, and safety notes, see [Programmatic subagents](/oss/deepagents/programmatic-subagents).
For configuration, examples, orchestration patterns, and safety notes, see [Dynamic subagents](/oss/deepagents/dynamic-subagents).

:::python
## Persistence
Expand Down Expand Up @@ -314,7 +314,7 @@ Every tool you expose through PTC is an outside capability that interpreter code
| `tool_name` | `"eval"` | Name of the interpreter tool exposed to the model. |
| `max_result_chars` | `4000` | Maximum characters returned from result and stdout blocks. |
| `capture_console` | `True` | Whether `console.log`, `console.warn`, and `console.error` output is captured. |
| `subagents` | `True` | Expose the built-in `task()` global for [programmatic subagents](/oss/deepagents/programmatic-subagents). Set to `False` to require subagent dispatch through the normal `task` tool path. |
| `subagents` | `True` | Expose the built-in `task()` global for [dynamic subagents](/oss/deepagents/dynamic-subagents). Set to `False` to require subagent dispatch through the normal `task` tool path. |
| `ptc` | `None` | PTC allowlist: list of tool names or `BaseTool` instances. |
| `snapshot_between_turns` | `True` | Whether interpreter state snapshots persist across agent turns. |
| `max_snapshot_bytes` | `None` | Maximum serialized snapshot size. Defaults to `memory_limit`. |
Expand All @@ -334,5 +334,5 @@ Every tool you expose through PTC is an outside capability that interpreter code
| `maxResultChars` | `4000` | Maximum characters retained from console output, result, and error strings. |
| `toolName` | `"eval"` | Name of the interpreter tool exposed to the model. |
| `captureConsole` | `true` | Whether `console.log`, `console.warn`, and `console.error` output is captured. |
| `subagents` | `true` | Expose the built-in `task()` global for [programmatic subagents](/oss/deepagents/programmatic-subagents). Set to `false` to require subagent dispatch through the normal `task` tool path. |
| `subagents` | `true` | Expose the built-in `task()` global for [dynamic subagents](/oss/deepagents/dynamic-subagents). Set to `false` to require subagent dispatch through the normal `task` tool path. |
:::
134 changes: 134 additions & 0 deletions src/oss/deepagents/subagents.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -207,6 +207,140 @@ const agent = createDeepAgent({
```
:::

## Dynamic subagents

By default, the main agent delegates to subagents through `task` tool calls (it can issue several in a single turn to run them in parallel). With an [interpreter](/oss/deepagents/interpreters) attached, the agent can instead dispatch subagents **from code**—using loops, branches, and parallel batches to fan work out across many items and synthesize the results programmatically. This is called [dynamic subagents](/oss/deepagents/dynamic-subagents).

Reach for dynamic subagents when work spans many independent units (reviewing every file in a directory, triaging a batch of tickets), needs multiple perspectives, or benefits from recursive analysis.

<Warning>
Dynamic subagents use the interpreter runtime, which is in [**beta**](/oss/versioning). APIs and lifecycle behavior may change between releases.
</Warning>

### Enable dynamic subagents

Dynamic subagents become available as soon as the agent has both subagents and the interpreter middleware. Install the QuickJS interpreter package, then add `CodeInterpreterMiddleware` to your agent.

:::python
<CodeGroup>
```bash pip
pip install -U "deepagents[quickjs]"
```

```bash uv
uv add "deepagents[quickjs]"
```
</CodeGroup>

```python
from deepagents import create_deep_agent
from langchain_quickjs import CodeInterpreterMiddleware

agent = create_deep_agent(
model="openai:gpt-5.5",
subagents=[{
"name": "reviewer",
"description": "Reviews code for security issues, citing lines and severity",
"system_prompt": "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware=[CodeInterpreterMiddleware()],
)
```

<Note>
Dynamic subagent dispatch is on by default whenever the agent has subagents and the interpreter middleware. Pass `CodeInterpreterMiddleware(subagents=False)` to require dispatch through the normal `task` tool path. Interpreters require `langchain-quickjs>=0.1.0` and Python `>=3.11`.
</Note>
:::

:::js
<CodeGroup>
```bash npm
npm install deepagents @langchain/quickjs
```

```bash pnpm
pnpm add deepagents @langchain/quickjs
```

```bash yarn
yarn add deepagents @langchain/quickjs
```
</CodeGroup>

```typescript
import { createDeepAgent } from "deepagents";
import { createCodeInterpreterMiddleware } from "@langchain/quickjs";

const agent = createDeepAgent({
model: "openai:gpt-5.5",
subagents: [{
name: "reviewer",
description: "Reviews code for security issues, citing lines and severity",
systemPrompt: "You are a security-focused code reviewer. Report issues with line numbers and severity.",
}],
middleware: [createCodeInterpreterMiddleware()],
});
```

<Note>
Dynamic subagent dispatch is on by default whenever the agent has subagents and the interpreter middleware. Pass `createCodeInterpreterMiddleware({ subagents: false })` to require dispatch through the normal `task` tool path.
</Note>
:::

### Trigger dynamic orchestration

Dynamic dispatch is implicit: the agent decides to fan work out from code based on the shape of the task, not a per-call flag.

<Tip>
**The word "workflow" is a useful trigger.** The built-in interpreter system prompt treats a "workflow" as a signal to organize work through the interpreter—dispatching subagents with `task()` from code. Phrasing a request as a "workflow" is a deliberate lever you can pull to opt into dynamic orchestration: include it when you want the agent to fan work out from code. For a single, direct delegation, phrase the request plainly instead.
</Tip>

For example, phrasing the request as a "workflow" opts into fan-out from code:

:::python
```python
result = await agent.ainvoke({
"messages": [{"role": "user", "content": "Run a workflow that reviews every file in src/routes/ and summarizes the top risks."}]
})
```
:::

:::js
```typescript
const result = await agent.invoke({
messages: [{ role: "user", content: "Run a workflow that reviews every file in src/routes/ and summarizes the top risks." }],
});
```
:::

For configuration, advanced orchestration patterns, and safety notes, see [Dynamic subagents](/oss/deepagents/dynamic-subagents).

### Use with a coding agent

The fastest way to try dynamic subagents is with `dcode`, the LangChain terminal coding agent built on a Deep Agent. It ships with the code interpreter enabled, so dynamic subagents works out of the box with nothing to wire up.

Install `dcode`:

```bash
curl -LsSf https://langch.in/dcode | bash
```

Run it:

```bash
dcode
```

To trigger dynamic subagents, ask for a "workflow". Instead of grinding through the work itself or managing fan-out through its native `task` tool, the agent writes an orchestration script that calls the built-in `task()` global and runs it in the code interpreter. For example: "Run a workflow to review every file in src/ for SQL injection."

As subagents spawn, `dcode` shows them live in the dynamic subagents panel, grouped into phases by dispatch.

<Frame>
![The dcode dynamic subagents panel showing spawned subagents grouped into phases by dispatch](/oss/images/deepagents/dcode-dynamic-subagents-panel.png)
</Frame>

`dcode` is the fastest way to try this, but you can also use dynamic subagents in the coding agent of your choice over [ACP](/oss/deepagents/acp) (for example, Zed).

## Streaming

Deep Agents support streaming updates from both the coordinator and every delegated subagent.
Expand Down
Loading