Parallel workflows allow multiple independent nodes to execute concurrently within a LangGraph application. They are useful when several tasks can process the same input without depending on one another.
A typical parallel workflow follows this pattern
This is known as fan-out → parallel execution → fan-in.
1. When to Use Parallel Workflows #
Use parallel execution when tasks are independent.
Common examples:
- Generate a summary and keywords from the same document.
- Run multiple evaluation criteria simultaneously.
- Ask multiple agents for independent perspectives.
- Perform independent data-processing operations.
If one task requires the output of another, a sequential workflow is usually more appropriate.
2. State Updates #
LangGraph nodes communicate through a shared state
from typing import TypedDict
class State(TypedDict):
text: str
summary: str
sentiment: str
keyword: str
final_answer: str
# Parallel nodes should normally return partial state updates—only the fields they produce.
def summarize(state: State):
result = llm.invoke(
f"Summarize this text into 2 sentences: {state['text']}"
)
return {
"summary": result.content
}
Here, the node updates only summary; it does not return the entire state.
3. Example: Parallel LLM Workflow #
The following example uses Ollama with gemma3:1b, but you can use any compatible LLM, such as OpenAI, Anthropic, Google Gemini, or other LangChain-supported models, to perform the three independent analysis tasks.
Create the Model #
from langchain_ollama import ChatOllama
llm = ChatOllama(model="gemma3:1b)
Define the Nodes #
def summarize(state: State):
result = llm.invoke(
f"Summarize this text into 2 sentences: {state['text']}"
)
return {"summary": result.content}
def sentiment(state: State):
result = llm.invoke(
f"Analyze the sentiment of this text. "
f"Return Positive, Negative, or Neutral with a short reason:\n"
f"{state['text']}"
)
return {"sentiment": result.content}
def extract_keyword(state: State):
result = llm.invoke(
f"Extract the 5 most important keywords "
f"from this text:\n{state['text']}"
)
return {"keyword": result.content}
These three nodes are independent because they all require only state["text"].
4. Fan-Out and Fan-In #
Create the graph and add the nodes:
from langgraph.graph import StateGraph, START, END
builder = StateGraph(State)
builder.add_node("summarize", summarize)
builder.add_node("sentiment", sentiment)
builder.add_node("keyword", extract_keyword)
builder.add_node("final_report", final_report)
Fan-Out #
Connect START to the independent nodes:
builder.add_edge(START, "summarize")
builder.add_edge(START, "sentiment")
builder.add_edge(START, "keyword")
This creates the parallel branches.
Fan-In #
Connect the branches to the final node:
builder.add_edge("summarize", "final_report")
builder.add_edge("sentiment", "final_report")
builder.add_edge("keyword", "final_report")
builder.add_edge("final_report", END)
The final node runs after the required upstream branches have completed.
5. Aggregating the Results #
The final node can use all results stored in the state:
def final_report(state: State):
prompt = f"""
Create a final report.
Original Text:
{state['text']}
Summary:
{state['summary']}
Sentiment:
{state['sentiment']}
Keywords:
{state['keyword']}
"""
result = llm.invoke(prompt)
return {
"final_answer": result.content
}
Compile and execute the graph:
graph = builder.compile()
result = graph.invoke({
"text": "Artificial Intelligence is changing software development."
})
print(result["final_answer"])
6. Reducers #
A reducer is required when multiple parallel nodes write to the same state key and their results need to be combined.
For example:
from typing import Annotated, TypedDict
import operator
class State(TypedDict):
scores: Annotated[list[int], operator.add]
If parallel nodes return:
Node A → [8]
Node B → [7]
Node C → [9]
the reducer combines them into:
[8, 7, 9]
Without a reducer, multiple writes to the same state key can conflict.
Reducers are not needed when each node updates a different key:
Summary → summary
Sentiment → sentiment
Keywords → keyword
Complete Code
from langchain_ollama import ChatOllama
from langgraph.graph import StateGraph, START, END
from typing import TypedDict
# 1. Create LLM
llm = ChatOllama(
model="gemma3:1b"
)
# 2. Define State
class State(TypedDict):
text: str
summary: str
sentiment: str
keyword: str
final_answer: str
# 3. Parallel Node 1
def summarize(state: State):
result = llm.invoke(
f"Summarize this text into 2 sentences: {state['text']}"
)
return {
"summary": result.content
}
# 4. Parallel Node 2
def sentiment(state: State):
result = llm.invoke(
f"Analyze the sentiment of this text. "
f"Return Positive, Negative, or Neutral with a short reason:\n"
f"{state['text']}"
)
return {
"sentiment": result.content
}
# 5. Parallel Node 3
def extract_keyword(state: State):
result = llm.invoke(
f"Extract the 5 most important keywords "
f"from this text:\n{state['text']}"
)
return {
"keyword": result.content
}
# 6. Final Node
def final_report(state: State):
prompt = f"""
Create a final report of this text.
Original Text:
{state['text']}
Summary:
{state['summary']}
Sentiment:
{state['sentiment']}
Keywords:
{state['keyword']}
Give a clear final response.
"""
result = llm.invoke(prompt)
return {
"final_answer": result.content
}
# 7. Build Graph
builder = StateGraph(State)
builder.add_node("summarize", summarize)
builder.add_node("sentiment", sentiment)
builder.add_node("keyword", extract_keyword)
builder.add_node("final_report", final_report)
# START → Parallel Nodes
builder.add_edge(START, "summarize")
builder.add_edge(START, "sentiment")
builder.add_edge(START, "keyword")
# Parallel Nodes → Final Node
builder.add_edge("summarize", "final_report")
builder.add_edge("sentiment", "final_report")
builder.add_edge("keyword", "final_report")
# Final Node → END
builder.add_edge("final_report", END)
# 8. Compile Graph
graph = builder.compile()
# 9. Run Graph
result = graph.invoke({
"text": "Artificial Intelligence is changing software development."
})
# 10. Print Final Answer
print(result["final_answer"])
Key Concepts #
| Concept | Description |
|---|---|
| Parallel Workflow | Executes independent nodes concurrently |
| Fan-Out | Splits execution into multiple branches |
| Fan-In | Converges branches into a later node |
| Partial Update | Node returns only the fields it changes |
| Reducer | Defines how multiple updates to the same field are combined |
The core principle is:
Use parallel execution for independent work, partial state updates for clean state management, and reducers when multiple branches must contribute to the same state field.
Q.1 In a parallel LangGraph workflow, why does returning the entire state from multiple nodes often cause an InvalidUpdateError?
The graph encounters a conflict because multiple nodes attempt to overwrite the same state keys simultaneously.
Parallel nodes are not allowed to access the same state dictionary at the same time.
Returning the full state exceeds the memory limit allocated for parallel branches.
The StateGraph class only supports partial updates for its internal nodes.
Explanation
When multiple parallel nodes return the entire state, they may overwrite the same keys at the same time. LangGraph detects this conflicting update and raises an InvalidUpdateError to prevent inconsistent state.
Q.2 Which strategy is recommended to prevent conflicts when updating state in parallel workflows?
Wrap every node in a try-except block.
Use global variables instead of LangGraph state.
Return a partial dictionary containing only the keys updated by that node.
Insert a Wait node between every parallel branch.
Explanation
Each parallel node should return only the specific state fields it modifies. Returning partial updates minimizes conflicts and allows LangGraph to merge updates correctly.
Q.3 When defining a state key that aggregates results from multiple parallel nodes into a list, which Python component is commonly used as the reducer?
operator.add
functools.reduce
math.sum
collections.Counter
Explanation
LangGraph commonly uses operator.add as a reducer to merge list outputs from multiple parallel branches by appending each branch’s results into a single shared list.
Q.4 What is the main purpose of a Parallel Workflow in LangGraph?
To execute all tasks strictly one after another.
To execute independent tasks concurrently.
To remove the need for a state.
To replace the LLM with a database.
Explanation
A Parallel Workflow allows independent nodes to execute concurrently, which can reduce execution time compared with running the same tasks sequentially.
Q.5 Why is structured output especially important in automated LangGraph workflows?
It reduces internet bandwidth usage.
It guarantees that the LLM returns predictable fields that downstream nodes can process reliably.
It automatically trains the language model during execution.
It eliminates the need for prompt engineering.
Explanation
Structured output ensures every node receives data in a consistent format, reducing parsing errors and making automated workflows more reliable and production-ready.
Q.6 Which specific LangGraph function generates the final executable graph after all nodes and edges are defined?
graph.build()
graph.run()
graph.compile()
graph.initialize()
Explanation
The compile() method validates the graph structure and produces an executable graph object that supports methods such as invoke(), stream(), and batch().
Q.7 What do Fan-Out and Fan-In represent in a LangGraph parallel workflow?
Fan-Out combines results and Fan-In splits the workflow.
Fan-Out and Fan-In both execute a single node.
Fan-Out splits the workflow into parallel branches, while Fan-In brings the branches together.
Fan-Out and Fan-In are used only for error handling.
Explanation
Fan-Out splits execution into multiple independent branches, while Fan-In brings those branches together at a later node, such as a final aggregation or reporting node.
Q.8 Why are reducers particularly useful when implementing parallel execution?
They automatically create new graph nodes.
They resolve concurrent updates by defining how multiple values should be combined.
They eliminate the need for shared state.
They improve GPU performance.
Explanation
Without reducers, simultaneous writes to the same key would conflict. Reducers provide deterministic merge logic so parallel outputs can be safely combined.
Q.9 In a parallel essay evaluation system, why might different evaluator nodes run simultaneously?
To evaluate different aspects of the essay, such as grammar, content, and structure, at the same time.
To duplicate identical work for backup.
Because LangGraph requires every node to execute in parallel.
To reduce the size of the state dictionary.
Explanation
Parallel execution allows specialised evaluators to assess different dimensions of the essay independently, reducing overall execution time before their results are merged.
Q.10 What happens if you use a standard LLM without with_structured_output() in an automated workflow?
The LLM refuses to provide scores.
The model cannot process long inputs.
LangGraph raises a ModelNotFoundError.
The LLM may return inconsistent text formats that cause parsing errors in downstream code.
Explanation
Without structured output, the model may produce responses in different formats each time, making it difficult for subsequent nodes to reliably extract fields such as score, feedback, or recommendations.