Deep Research LangGraph
What it does
A planner splits a broad question into focused sub-questions. Several searchers research them in parallel so the wall-clock stays low. A synthesizer merges the findings, removes overlap, resolves contradictions, and writes a brief with citations back to the sources. For questions a single search cannot answer. Point the searchers at your own search or retrieval server.
The cast · 5 agents
Break the question into focused, non-overlapping sub-questions that together cover it. Assign them across three lanes as evenly as you can. Do not research yet.
Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes.
Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes.
Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes.
Merge the three lanes' findings into one brief. Remove overlap, resolve contradictions by weighing sources, and cite a source for every claim. Note anything the research could not settle.
Flow
Interface
Depends on · 1 MCP server
io.github.brave/brave-search-mcpnot in the registry yetsearch# Generated by @socketcat/compiler for target: langgraph
# blueprint: com.socketcat/deep-research v1.0.0 schema: socketcat.dev/blueprint/v0
# This code is yours. Edit it freely. The socketcat_runtime helper is optional and can be vendored.
from typing import TypedDict
from langgraph.graph import StateGraph, START, END
import socketcat_runtime as _rt
class State(TypedDict, total=False):
question: object
brief: object
subquestions: object
findingsA: object
findingsB: object
findingsC: object
def node_AgentInvoke_0(state):
return _rt.run_agent("planner", state, {"question":"question"}, "subquestions")
def node_Fork_1(state):
return {}
def node_Join_2(state):
return {}
def node_AgentInvoke_3(state):
return _rt.run_agent("searcherA", state, {"subquestions":"subquestions"}, "findingsA")
def node_AgentInvoke_4(state):
return _rt.run_agent("searcherB", state, {"subquestions":"subquestions"}, "findingsB")
def node_AgentInvoke_5(state):
return _rt.run_agent("searcherC", state, {"subquestions":"subquestions"}, "findingsC")
def node_AgentInvoke_6(state):
return _rt.run_agent("synthesizer", state, {"findingsA":"findingsA","findingsB":"findingsB","findingsC":"findingsC"}, "brief")
def build():
b = StateGraph(State)
b.add_node("AgentInvoke_0", node_AgentInvoke_0)
b.add_node("Fork_1", node_Fork_1)
b.add_node("Join_2", node_Join_2)
b.add_node("AgentInvoke_3", node_AgentInvoke_3)
b.add_node("AgentInvoke_4", node_AgentInvoke_4)
b.add_node("AgentInvoke_5", node_AgentInvoke_5)
b.add_node("AgentInvoke_6", node_AgentInvoke_6)
b.add_edge(START, "AgentInvoke_0")
b.add_edge("AgentInvoke_0", "Fork_1")
b.add_edge("Fork_1", "AgentInvoke_3")
b.add_edge("Fork_1", "AgentInvoke_4")
b.add_edge("Fork_1", "AgentInvoke_5")
b.add_edge("Join_2", "AgentInvoke_6")
b.add_edge("AgentInvoke_3", "Join_2")
b.add_edge("AgentInvoke_4", "Join_2")
b.add_edge("AgentInvoke_5", "Join_2")
b.add_edge("AgentInvoke_6", END)
return b.compile()
INPUTS = ["question"]
OUTPUTS = ["brief"]
if __name__ == "__main__":
_rt.main(build, INPUTS, OUTPUTS)
▸blueprint.json (the portable format)
{
"id": "com.socketcat/deep-research",
"flow": {
"type": "sequence",
"blocks": [
{
"in": {
"question": "question"
},
"out": "subquestions",
"use": "planner",
"type": "agent"
},
{
"type": "parallel",
"blocks": [
{
"in": {
"subquestions": "subquestions"
},
"out": "findingsA",
"use": "searcherA",
"type": "agent"
},
{
"in": {
"subquestions": "subquestions"
},
"out": "findingsB",
"use": "searcherB",
"type": "agent"
},
{
"in": {
"subquestions": "subquestions"
},
"out": "findingsC",
"use": "searcherC",
"type": "agent"
}
]
},
{
"in": {
"findingsA": "findingsA",
"findingsB": "findingsB",
"findingsC": "findingsC"
},
"out": "brief",
"use": "synthesizer",
"type": "agent"
}
]
},
"tags": [
"research",
"parallel",
"citations",
"synthesis"
],
"title": "Deep Research",
"agents": {
"planner": {
"model": {
"hint": "reasoning"
},
"title": "Planner",
"instructions": "Break the question into focused, non-overlapping sub-questions that together cover it. Assign them across three lanes as evenly as you can. Do not research yet."
},
"searcherA": {
"model": {
"hint": "fast"
},
"title": "Searcher A",
"tools": [
"search.web"
],
"instructions": "Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes."
},
"searcherB": {
"model": {
"hint": "fast"
},
"title": "Searcher B",
"tools": [
"search.web"
],
"instructions": "Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes."
},
"searcherC": {
"model": {
"hint": "fast"
},
"title": "Searcher C",
"tools": [
"search.web"
],
"instructions": "Research the sub-questions in your assigned lane. Report findings with a source for each claim. Do not stray into the other lanes."
},
"synthesizer": {
"model": {
"hint": "reasoning"
},
"title": "Synthesizer",
"instructions": "Merge the three lanes' findings into one brief. Remove overlap, resolve contradictions by weighing sources, and cite a source for every claim. Note anything the research could not settle."
}
},
"estate": {
"findingsA": {
"type": "object",
"description": "First searcher's findings."
},
"findingsB": {
"type": "object",
"description": "Second searcher's findings."
},
"findingsC": {
"type": "object",
"description": "Third searcher's findings."
},
"subquestions": {
"type": "array",
"description": "The planner's focused sub-questions."
}
},
"$schema": "socketcat.dev/blueprint/v0",
"authors": [
{
"url": "https://socketcat.com",
"name": "SocketCat"
}
],
"license": "MIT",
"servers": [
{
"ref": "io.github.brave/brave-search-mcp",
"alias": "search"
}
],
"summary": "Plans sub-questions, researches them in parallel, and synthesizes a single cited brief.",
"targets": [
"langgraph",
"*"
],
"version": "1.0.0",
"interface": {
"inputs": {
"question": {
"type": "string",
"description": "The broad question to research."
}
},
"outputs": {
"brief": {
"type": "string",
"description": "A synthesized brief with citations."
}
}
},
"extensions": {
"dev.langgraph": {
"checkpointer": "memory"
}
},
"description": "A planner splits a broad question into focused sub-questions. Several searchers research them in parallel so the wall-clock stays low. A synthesizer merges the findings, removes overlap, resolves contradictions, and writes a brief with citations back to the sources. For questions a single search cannot answer. Point the searchers at your own search or retrieval server."
}More in Core patterns
Browse all →Supervisor Team
A supervisor decides who works next — a researcher who searches or a coder who edits the repo — and assembles the result when the work is done.