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

# LangChain Deep Agents

> Trace and replay LangChain Deep Agents, including subagents, the todo list, and file tools

Bitfab integrates with [LangChain Deep Agents](https://docs.langchain.com/oss/python/deepagents/overview) (`deepagents`), the agent harness built on LangGraph, in Python and TypeScript. One integration records each run of a deep agent as a replayable trace: the main agent, every model call, every subagent the `task` tool hands work to, and every tool call nested where Deep Agents runs it, including `write_todos` and the built-in file tools.

**Canonical signatures:** [Python reference](/reference/python#framework-integrations) | [TypeScript reference](/reference/typescript#framework-integrations)

> [!WARNING]
>
> **Experimental (alpha):** The Deep Agents integration builds on the [LangGraph tool replay integration](/frameworks/langgraph#mock-langgraph-tool-calls-on-replay) and shares its matching rules and limitations. Its API may change before it is stable.

## Supported Languages

| Language | Method | Status |
| - | - | - |
| TypeScript | `getDeepAgentsIntegration()` | 🧪 Experimental (alpha) |
| Python | `get_deep_agents_integration()` | 🧪 Experimental (alpha) |
| Ruby | - | Not applicable |
| Go | - | Not applicable |

Python requires Python 3.11+ (the `deepagents` minimum) and `deepagents` 0.7 or later. TypeScript requires `deepagents` 1.0 or later and `langchain` 1.0 or later.

## Quick Start

Build the agent with the integration's `createDeepAgent()` / `create_deep_agent()` in place of Deep Agents' own. It takes the same parameters:

```typescript TypeScript theme={null}
import { Bitfab } from "@bitfab/sdk"

const bitfab = new Bitfab({ apiKey: process.env.BITFAB_API_KEY })
const integration = bitfab.getFunction("research-agent").getDeepAgentsIntegration()

const agent = await integration.createDeepAgent({
  model: "openai:gpt-4o-mini",
  tools: [internetSearch],
  systemPrompt: "You are an expert researcher.",
  subagents: [researchSubagent, critiqueSubagent],
  name: "research-agent",
})

const question = { messages: [{ role: "user", content: "What is LangGraph?" }] }
const result = await agent.invoke(question)

for await (const chunk of await agent.stream(question, { streamMode: "updates" })) {
  console.log(chunk)
}
```

```python Python theme={null}
import os

from bitfab import Bitfab

bitfab = Bitfab(api_key=os.environ["BITFAB_API_KEY"])
integration = bitfab.get_function("research-agent").get_deep_agents_integration()

agent = integration.create_deep_agent(
    model="openai:gpt-4o-mini",
    tools=[internet_search],
    system_prompt="You are an expert researcher.",
    subagents=[research_subagent, critique_subagent],
    name="research-agent",
)

question = {"messages": [{"role": "user", "content": "What is LangGraph?"}]}
result = agent.invoke(question)

for chunk in agent.stream(question, stream_mode="updates"):
    print(chunk)
```

The builder passes every parameter to Deep Agents unchanged, except that it adds the integration's middleware to the main agent and to each subagent you define as a spec, so every one of their tool calls is replayable. It returns the agent itself, traced: still a Deep Agents agent of the same type, so it can be nested in another graph or exported to LangGraph Platform, with every way of running it traced (`invoke`, `stream`, `streamEvents`, and `batch` in TypeScript; `invoke`, `ainvoke`, `stream`, `astream`, `batch`, `stream_events`, and `astream_events` in Python). Each run records the agent input as the trace's replayable root and the final agent state, including `todos` and `files`, as the root's output. They return or yield exactly what the agent's own methods do, and every other property, such as `getState()` / `get_state()`, is the agent's own. Runtime configuration you pass at call time, such as a `thread_id`, is kept. In TypeScript the builder is async, because it loads `deepagents` when it runs.

For a subagent you pass as a compiled `runnable`, build it with the integration's `createAgent()` / `create_agent()`, the same drop-in for LangChain's `createAgent` / `create_agent`, so its tool calls are replayable too. It nests under the `task` call in the same trace.

### Building the agent yourself

If the agent is built somewhere you can't change the call, add the two pieces by hand: pass `integration.middleware` in the agent's middleware (and in each subagent spec's middleware whose tools should be replayable), and wrap the built agent with `instrument()`:

```typescript TypeScript theme={null}
import { createDeepAgent } from "deepagents"

const agent = integration.instrument(
  createDeepAgent({
    model: "openai:gpt-4o-mini",
    tools: [internetSearch],
    middleware: [integration.middleware],
  }),
)
```

```python Python theme={null}
from deepagents import create_deep_agent

agent = integration.instrument(
    create_deep_agent(
        model="openai:gpt-4o-mini",
        tools=[internet_search],
        middleware=[integration.middleware],
    )
)
```

If the agent is called directly instead of through `instrument()`, it still runs, but its tool calls are not recorded as replayable spans, and the SDK logs a one-time warning saying so.

`getDeepAgentsIntegration()` / `get_deep_agents_integration()` is the [LangGraph integration](/frameworks/langgraph#mock-langgraph-tool-calls-on-replay) under a Deep Agents name, so `createAgent()`, `middleware`, and `instrument()` work the same way for LangChain agents.

Passing a `name` to the builder names the main agent's span in the trace. Without it, LangGraph names it `LangGraph`.

## What Gets Captured

| What the agent does | Where it appears in the trace |
| - | - |
| The run | The root span, keyed by the trace function, with the input messages and the final state, including `todos` and `files` |
| Each model call | An `llm` span with messages, model name, and token usage, under the step that made it |
| Each tool call | A `function` span under the `tools` step that ran it, with the tool arguments and result. Calls made by the main agent and by subagents built with the integration are replayable |
| Planning (`write_todos`) | A `function` span whose input is the full todo list the agent wrote at that point, so each update to the plan is its own span |
| File tools (`ls`, `read_file`, `write_file`, `edit_file`, `glob`, `grep`) | A `function` span with the path and content. For the default state backend, the file change the tool made to the agent's `files` is recorded with the call |
| A subagent run (`task`) | A `function` span named `task`, with the subagent's own run nested under it: its model calls and its tool calls |
| Middleware steps | `agent` spans for middleware hooks such as `TodoListMiddleware.after_model` |

## Replay

Replay recorded runs through the method your app calls, or through your own handler that relays the agent's stream. A streamed replay is drained inside the replay, and its result is the agent's final state:

```typescript TypeScript theme={null}
await bitfab.replay("research-agent", (input) => agent.invoke(input), { limit: 10 })
await bitfab.replay("research-agent", (input) => agent.stream(input), { limit: 10 })
```

```python Python theme={null}
bitfab.replay("research-agent", agent.invoke, limit=10)
bitfab.replay("research-agent", agent.astream, limit=10)
```

If the agent has a checkpointer and the run has a `thread_id`, the thread's checkpoint at the start of the run is recorded with the trace. Replay restores it into an in-memory checkpointer, so a later turn of a conversation sees the same history, a run that resumes a human approval (`Command(resume=...)`, including from `interrupt_on`) resumes the same interrupt, and the agent's own checkpointer is never written to during replay.

By default, every tool call the integration handles is marked for replay mocking: the call does not run, and it returns its recorded result instead. That covers `write_todos`, the file tools, your own tools, and `task`. A mocked `task` call returns the subagent's recorded answer without running the subagent, so none of the subagent's model calls or tools run again. File changes recorded with a file tool call are applied to the replayed agent's `files`, so later steps read the same files the recorded run did.

Model calls run live during replay. Use `mockToolsOnReplay` / `mock_tools_on_replay` to choose which tools are mocked:

```typescript TypeScript theme={null}
const integration = researchAgent.getDeepAgentsIntegration({
  mockToolsOnReplay: ["internet_search", "task"],
})
```

```python Python theme={null}
integration = research_agent.get_deep_agents_integration(
    mock_tools_on_replay=["internet_search", "task"],
)
```

`true` / `True` (the default) mocks every tool the integration handles, `false` / `False` mocks none, and a list mocks only the tools it names. With `task` left live, a subagent built through the integration runs again on replay while its own tool calls return their recordings. Replay's `mock: "all"` strategy and matching mock overrides can still substitute any tool the integration handles.

If a tool marked for mocking has no recorded result at that point in the run, Bitfab refuses to run it live and raises an error naming the tool. Recorded results are matched by tool name and the order of calls, as in the [LangGraph integration](/frameworks/langgraph#current-alpha-limitations).

### Current alpha limitations

* **The built-in `general-purpose` subagent's tools are not replayable one by one.** Deep Agents builds that subagent itself and does not pass custom middleware to it, so its tool calls are recorded but cannot be mocked individually. Mocking its `task` call skips it entirely. Subagents you define are covered by the integration's builder.
* **Async subagents are not covered.** Subagents deployed on LangSmith and started as async subagents run outside the traced process.
* **TypeScript restores threads through a LangGraph run setting.** The in-memory checkpointer is lent to the replayed run through the run config key LangGraph uses to lend a checkpointer to subgraphs. A LangGraph version that renames it would replay against the agent's own checkpointer.
* **Model calls are not mocked.** A replay calls the model again, so a model that picks a different tool or calls tools in a different order can reach a tool call with no matching recording.
* **Python file changes are recorded through LangGraph internals.** The Python default state backend saves files through LangGraph's internal state-write function, which Bitfab records for the length of each tool call. If a LangGraph version stops exposing it, the SDK logs a one-time warning and file changes are not restored on replay. In TypeScript the file tools return their file changes, so they are recorded with the result.
* **The todo list needs the todo-list middleware.** Add `todoListMiddleware()` (TypeScript) or `TodoListMiddleware()` (Python) to the middleware if your agent should plan with a todo list; Deep Agents only adds `write_todos` for some model profiles.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.