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.graphorsrc.legacy.multi_agenttosrc.open_deep_research.deep_researcher. - Adopt typed state by replacing dictionaries with the
Stateclass fromsrc/open_deep_research/state.py. - Centralize configuration using
Config.load()andsrc/open_deep_research/configuration.py. - Define workflows declaratively in
langgraph.jsoninstead 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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →