# How to Migrate from Legacy graph.py or multi_agent.py in Open Deep Research

> Easily migrate from legacy graph.py or multi_agent.py in Open Deep Research. Learn how to update imports, refactor state, and centralize configuration for a smoother transition.

- Repository: [LangChain/open_deep_research](https://github.com/langchain-ai/open_deep_research)
- Tags: migration-guide
- Published: 2026-07-23

---

**To migrate from the legacy [`graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/graph.py) or [`multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/multi_agent.py) implementations, replace direct LangChain imports with the LangGraph-based `deep_researcher` entry point, refactor state dictionaries to the typed `State` class, centralize configuration in [`configuration.py`](https://github.com/langchain-ai/open_deep_research/blob/main/configuration.py), and define your workflow nodes in [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json).**

The `langchain-ai/open_deep_research` repository has transitioned from early prototype scripts to a production-ready, LangGraph-powered research framework. If your project currently depends on the legacy [`src/legacy/graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/graph.py) or [`src/legacy/multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/multi_agent.py) files, migrating to the modern architecture unlocks declarative graph definitions, type-safe state management, and native parallel execution support.

## Overview of Legacy vs. New Architecture

The original codebase provided two distinct research patterns that required manual orchestration:

- **[`src/legacy/graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/graph.py)**: Implemented a simple "plan-and-execute" graph running a single researcher LLM with manually wired search tool calls.
- **[`src/legacy/multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/multi_agent.py)**: Implemented a supervisor-researcher architecture where a supervisor LLM coordinated multiple researcher agents in parallel using loosely structured dictionaries for state.

The new `deep_researcher` implementation (located in [`src/open_deep_research/deep_researcher.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/deep_researcher.py)) replaces these with a **LangGraph-based** design featuring a [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json) declarative schema, a centralized `Config` dataclass in [`src/open_deep_research/configuration.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/configuration.py), and a strictly typed `State` object from [`src/open_deep_research/state.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/state.py).

## Migration Steps

### 1. Replace Legacy Imports

Remove imports from the legacy modules and import the new LangGraph entry point. The `deep_researcher` object is a compiled graph that replaces both the single-agent and multi-agent legacy classes.

```python

# Remove this

# from src.legacy.graph import Graph

# from src.legacy.multi_agent import MultiAgent

# Use this

from src.open_deep_research.deep_researcher import deep_researcher
from src.open_deep_research.configuration import Config
from src.open_deep_research.state import State

```

### 2. Adapt Configuration

Legacy scripts typically read environment variables ad-hoc. Migrate these settings into the centralized configuration system by ensuring your `.env` file contains the required keys (e.g., `OPENAI_API_KEY`, `SEARCH_API_KEY`), then extend [`src/open_deep_research/configuration.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/configuration.py) if you need custom fields in the `Config` dataclass. The new code loads settings via `Config.load()`.

### 3. Refactor State Handling

Replace manual dictionary state (e.g., `state = {"research_topic": ..., "report": ""}`) with the typed `State` model. This ensures type safety and compatibility with LangGraph's node contracts.

```python
from src.open_deep_research.state import State

# Old way

# state = {"topic": "AI safety", "report": ""}

# New way

state = State(topic="AI safety", report="")

```

### 4. Convert Logic to LangGraph Nodes

Transform custom functions that performed single LLM calls into **LangGraph nodes**. Each node must accept a `State` object and return a modified `State`. Utilize helper functions from [`src/open_deep_research/utils.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/utils.py) for LLM calls and prompt formatting.

```python
from src.open_deep_research.utils import llm_call

def generate_outline(state: State) -> State:
    """Node: Generate report outline."""
    prompt = f"Create an outline for: {state.topic}"
    response = llm_call(prompt, model=Config.load().model)
    state.outline = response
    return state

```

### 5. Update the Graph Definition

Open [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json) at the repository root and define your node sequence to mirror the legacy workflow logic. For a simple migration from [`graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/graph.py), you might define:

```json
{
  "nodes": [
    "generate_outline",
    "run_searches",
    "write_report"
  ]
}

```

This declarative structure replaces the imperative graph construction found in the legacy files.

### 6. Validate with LangGraph Studio

Launch the development server using `uvx langgraph dev` and visually verify the graph execution end-to-end. The Studio interface allows you to inspect state transitions between nodes and identify missing inputs or outputs before production deployment.

### 7. Remove Legacy Files

Once the new graph passes integration tests, delete [`src/legacy/graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/graph.py) and [`src/legacy/multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/multi_agent.py) to prevent accidental usage of obsolete code paths.

## Code Comparison: Before and After

The following examples demonstrate the transformation from the legacy imperative style to the modern LangGraph approach.

**Legacy implementation ([`src/legacy/graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/graph.py)):**

```python
from src.legacy.graph import Graph

graph = Graph(topic="AI safety")
result = graph.run()
print(result)

```

**Modern implementation ([`src/open_deep_research/deep_researcher.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/deep_researcher.py)):**

```python
from src.open_deep_research.configuration import Config
from src.open_deep_research.state import State
from src.open_deep_research.deep_researcher import deep_researcher

# Load centralized configuration

cfg = Config.load()

# Initialize typed state

state = State(topic="AI safety")

# Execute the compiled LangGraph

result_state = deep_researcher.invoke(state, cfg)

print(result_state.report)

```

The `deep_researcher.invoke()` call automatically executes the node sequence defined in [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json), handling parallelism and state persistence internally.

## Key Files in the New Architecture

| File | Purpose |
|------|---------|
| [`src/open_deep_research/deep_researcher.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/deep_researcher.py) | Main entry point exposing the compiled LangGraph (`deep_researcher`). |
| [`src/open_deep_research/configuration.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/configuration.py) | `Config` dataclass for centralized environment and runtime settings. |
| [`src/open_deep_research/state.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/state.py) | `State` class defining the typed schema for graph state. |
| [`src/open_deep_research/utils.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/utils.py) | Helper utilities for LLM calls, prompt rendering, and search result parsing. |
| [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json) | Declarative graph definition consumed by LangGraph Studio. |
| [`src/legacy/graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/graph.py) | Original plan-and-execute script (deprecated). |
| [`src/legacy/multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/legacy/multi_agent.py) | Original supervisor-researcher script (deprecated). |

## Summary

- **Migrate imports** from `src.legacy.graph` or `src.legacy.multi_agent` to `src.open_deep_research.deep_researcher`.
- **Adopt typed state** by replacing dictionaries with the `State` class from [`src/open_deep_research/state.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/state.py).
- **Centralize configuration** using `Config.load()` and [`src/open_deep_research/configuration.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/configuration.py).
- **Define workflows declaratively** in [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json) instead of imperative Python construction.
- **Utilize LangGraph Studio** for visual debugging and validation of the migrated workflow.

## Frequently Asked Questions

### What is the main difference between legacy graph.py and the new deep_researcher?

The legacy [`graph.py`](https://github.com/langchain-ai/open_deep_research/blob/main/graph.py) used imperative Python code to chain LLM calls and search tools manually, while `deep_researcher` leverages LangGraph to define nodes and edges declaratively in [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json), enabling automatic parallelization, better state management, and visual debugging through LangGraph Studio.

### Do I need to rewrite my custom search tools when migrating?

No, you can reuse your existing search tool implementations by importing them into [`src/open_deep_research/utils.py`](https://github.com/langchain-ai/open_deep_research/blob/main/src/open_deep_research/utils.py) or referencing them directly within your new LangGraph nodes. The migration primarily affects orchestration logic, not the underlying tool implementations.

### Can I run the new implementation without LangGraph Studio?

Yes, the `deep_researcher` graph is a standard LangGraph compiled object that can be invoked programmatically via `deep_researcher.invoke()` or deployed as an API endpoint. LangGraph Studio is only required for visual development and debugging.

### How does the new architecture handle parallel agent execution compared to multi_agent.py?

The legacy [`multi_agent.py`](https://github.com/langchain-ai/open_deep_research/blob/main/multi_agent.py) required manual supervisor logic to coordinate parallel researchers. In the new architecture, you define parallel branches directly in [`langgraph.json`](https://github.com/langchain-ai/open_deep_research/blob/main/langgraph.json) or use LangGraph's `Send` API within node implementations, allowing the framework to handle concurrency and state merging automatically.