A research assistant may need to search again when its first results are weak. A linear chain can run search, summarization, and report writing in order, but branching back to an earlier step makes that chain cumbersome. LangGraph extends LangChain with directed graphs for stateful, multi-step workflows. It is especially useful when an application must choose a path, repeat a step, or track information across steps. section 5.2 section 5.3
A LangGraph workflow has a few moving parts:
- State holds information shared across the workflow. A Python TypedDict can specify its fields, making the data expected by each step clearer. section 5.4
- Nodes perform individual tasks, commonly as Python functions that read state and return updates. Edges connect nodes and establish execution order. section 5.4
- Conditional edges select the next node using the current state. Together with entry points and end conditions, they allow a workflow to branch or loop rather than always move forward. section 5.4
Retrying a web research task
A research graph generates queries, searches the web, summarizes results, and evaluates their relevance. If fewer than 50% of summaries are relevant, it returns to query generation. Otherwise, it writes a report. A maximum of three iterations prevents an endless retry. To build this, define the state, turn each task into a node function, connect the nodes with an evaluation-based route, then compile the graph and invoke it with an initial state. section 5.5 section 5.5.3
Listing 5.4 Graph structure
from langgraph.graph import StateGraph, END
graph = StateGraph(ResearchState) #1
graph.add_node("select_assistant", #2
select_assistant) #2
graph.add_node("generate_search_queries", #2
generate_search_queries) #2
graph.add_node("perform_web_searches", #2
perform_web_searches) #2
graph.add_node("summarize_search_results", #2
summarize_search_results) #2
graph.add_node("evaluate_search_relevance", #2
evaluate_search_relevance) #2
graph.add_node("write_research_report", #2
write_research_report) #2
def route_based_on_relevance(state): #3
iteration_count = state.get("iteration_count", 0) + 1
state["iteration_count"] = iteration_count
if iteration_count >= 3:
return "write_research_report"
if state.get("should_regenerate_queries", False):
return "generate_search_queries"
return "write_research_report"
graph.add_edge("select_assistant", #4
"generate_search_queries") #4
graph.add_edge("generate_search_queries", #4
"perform_web_searches") #4
graph.add_edge("perform_web_searches", #4
"summarize_search_results") #4
graph.add_edge("summarize_search_results", #4
"evaluate_search_relevance") #4
graph.add_edge("write_research_report", END) #4
graph.add_conditional_edges( #5
"evaluate_search_relevance",
route_based_on_relevance,
{
"generate_search_queries": "generate_search_queries",
"write_research_report": "write_research_report"
}
)
graph.set_entry_point("select_assistant") #6
The graph listing shows StateGraph, task nodes, and a routing function that chooses between another search and report writing. section 5.5.3
This explicit structure makes intermediate data and execution paths easier to inspect, test, and debug. It also asks for more technical work than a simple chain. For tool-using agents, LangGraph supports both manually wired graphs and prebuilt ReAct agents: the latter reduce orchestration boilerplate, while the former expose the routing decisions directly. Conditional workflow routing should not be confused with an agent’s LLM-driven choice of tools as a request unfolds. section 5.1.4 section 5.5.4 section 11.0 section 11.9






