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

To migrate from the legacy graph.py or 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, and define your workflow nodes in 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 or 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: Implemented a simple "plan-and-execute" graph running a single researcher LLM with manually wired search tool calls.
  • 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) replaces these with a LangGraph-based design featuring a langgraph.json declarative schema, a centralized Config dataclass in src/open_deep_research/configuration.py, and a strictly typed State object from 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.


# 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 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.

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 for LLM calls and prompt formatting.

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 at the repository root and define your node sequence to mirror the legacy workflow logic. For a simple migration from graph.py, you might define:

{
  "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 and 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):

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):

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, handling parallelism and state persistence internally.

Key Files in the New Architecture

File Purpose
src/open_deep_research/deep_researcher.py Main entry point exposing the compiled LangGraph (deep_researcher).
src/open_deep_research/configuration.py Config dataclass for centralized environment and runtime settings.
src/open_deep_research/state.py State class defining the typed schema for graph state.
src/open_deep_research/utils.py Helper utilities for LLM calls, prompt rendering, and search result parsing.
langgraph.json Declarative graph definition consumed by LangGraph Studio.
src/legacy/graph.py Original plan-and-execute script (deprecated).
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.
  • Centralize configuration using Config.load() and src/open_deep_research/configuration.py.
  • Define workflows declaratively in 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 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, 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 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 required manual supervisor logic to coordinate parallel researchers. In the new architecture, you define parallel branches directly in langgraph.json or use LangGraph's Send API within node implementations, allowing the framework to handle concurrency and state merging automatically.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →