Skip to main content

Building Sequential Workflows In LangGraph

Introduction

In the previous lessons, we explored the theoretical foundations of Agentic AI and LangGraph. We learned how Agentic AI differs from traditional Generative AI, why workflow orchestration is important, and why LangGraph was introduced even though LangChain already existed. We also discussed the core concepts of LangGraph such as Graphs, Nodes, Edges, State, and Reducers.

Now it is time to move from theory to practice.

In this tutorial, we will learn how to build Sequential Workflows using LangGraph. A sequential workflow is the simplest type of workflow where tasks execute one after another in a linear order. There are no branches, loops, or parallel executions. Every step completes before the next step begins.

Although the examples in this tutorial are intentionally simple, they will help you understand the coding pattern used in LangGraph. Once you understand these fundamentals, you will be able to build significantly more complex workflows involving multiple LLM calls, conditional routing, agents, tools, and memory.

What is a Sequential Workflow?

A sequential workflow is a workflow where execution follows a fixed path. Consider a simple process:

Step 1 → Step 2 → Step 3 → Step 4

Each step waits for the previous step to finish before starting. In LangGraph, such workflows are represented as graphs.

START

Node A

Node B

Node C

END

This may seem unnecessarily complex for small tasks, but as workflows become larger, the graph representation provides significantly better control, visibility, and maintainability. Before building complex Agentic AI systems, it is important to become comfortable with this basic execution model.

Installing LangGraph

Before writing any code, we need to install the required libraries. First, create and activate the virtual environment.

python -m venv myenv
myenv\Scripts\activate

Now install dependencies.

pip install langgraph langchain python-dotenv ollama langchain-ollama

The Standard LangGraph Development Flow

Almost every LangGraph application follows the same sequence of steps.

Define State

Create Graph

Add Nodes

Add Edges

Compile Graph

Execute Graph

If you remember this flow, understanding LangGraph becomes much easier because every workflow, whether simple or complex, follows the same structure.

Our First Workflow: BMI Calculator

To avoid introducing LLM complexity immediately, we will start with a simple BMI Calculator workflow. The workflow accepts weight and height. It calculates BMI.

The workflow looks like this:

START

Calculate BMI

END

This workflow is intentionally simple because the goal is not to build an advanced BMI application. The goal is to understand how LangGraph code is written.

Understanding the State

Every LangGraph workflow revolves around a State.

The State acts as a shared memory object that travels through the graph. Every node receives the current state, performs some work, updates the state, and returns it.

For our BMI workflow, the state needs three values:

  • Weight
  • Height
  • BMI

We define it using a TypedDict.

from typing import TypedDict

class BMIState(TypedDict):
weight: float
height: float
bmi: float

You can think of this state as a structured dictionary that evolves as the workflow executes. Initially, only weight and height are available. After the BMI calculation node runs, the BMI value is added to the state.

Creating the Graph

Once the state is defined, we create a graph.

from langgraph.graph import StateGraph

graph = StateGraph(BMIState)

Notice that the StateGraph constructor receives the state definition. This tells LangGraph what type of data will flow through the workflow.

Creating the BMI Node

In LangGraph, nodes are simply Python functions. Our node needs to:

  1. Read weight and height from the state.
  2. Calculate BMI.
  3. Store BMI back into the state.
  4. Return the updated state.
def calculate_bmi(state: BMIState) -> BMIState:

weight = state["weight"]
height = state["height"]

bmi = weight / (height ** 2)

state["bmi"] = round(bmi, 2)

return state

Notice the pattern carefully. The function receives state as input and returns state as output. This pattern remains consistent throughout LangGraph applications.

BMI Formula

The BMI calculation is based on the following formula:

BMI=Weight/Height2BMI=Weight/Height^2

​ Where:

  • Weight is measured in kilograms.
  • Height is measured in meters.

For example:

  • Weight = 80 kg
  • Height = 1.73 m

The calculated BMI becomes approximately 26.73.

Registering the Node

Now we add the node to the graph.

graph.add_node(
"calculate_bmi",
calculate_bmi
)

The first argument is the node name. The second argument is the Python function that should execute when the node is triggered.

Creating the Edges

Next, we connect the nodes.

from langgraph.graph import START, END

graph.add_edge(
START,
"calculate_bmi"
)

graph.add_edge(
"calculate_bmi",
END
)

The graph now looks like this:

START

Calculate BMI

END

Edges define the execution path. When the workflow starts, control moves from START to the BMI node. After the node finishes execution, control moves to END.

Compiling the Graph

Before execution, LangGraph validates the graph structure.

workflow = graph.compile()

Compilation checks whether the graph structure is valid and ready for execution. Think of this step as a sanity check before runtime.

Executing the Workflow

Now we provide an initial state.

initial_state = {
"weight": 80,
"height": 1.73
}

Execute the workflow.

final_state = workflow.invoke(
initial_state
)

print(final_state)

Output:

{
'weight': 80,
'height': 1.73,
'bmi': 26.73
}

An important thing to notice is that LangGraph returns the entire state object, not just the BMI value. The state enters the workflow, evolves throughout execution, and exits as the final state. This concept becomes extremely valuable when workflows contain dozens of nodes.

Extending the Workflow

Now let us make the workflow slightly more interesting. Calculating BMI alone is not particularly useful. Usually, BMI is also categorized as:

  • Underweight
  • Normal
  • Overweight
  • Obese

To achieve this, we add another node. The workflow now becomes:

START

Calculate BMI

Label BMI

END

Updating the State

The state now requires one additional field.

class BMIState(TypedDict):
weight: float
height: float
bmi: float
category: str

Creating the Label BMI Node

def label_bmi(
state: BMIState
) -> BMIState:

bmi = state["bmi"]

if bmi < 18.5:
state["category"] = "Underweight"

elif bmi < 25:
state["category"] = "Normal"

elif bmi < 30:
state["category"] = "Overweight"

else:
state["category"] = "Obese"

return state

This node consumes the BMI value produced by the previous node and adds another piece of information to the state. Notice how nodes communicate indirectly through state rather than calling each other directly. This separation of concerns is one of the strengths of LangGraph.

Understanding State Evolution

The state evolves as execution progresses.

Initially:

{
"weight": 80,
"height": 1.73
}

After the first node:

{
"weight": 80,
"height": 1.73,
"bmi": 26.73
}

After the second node:

{
"weight": 80,
"height": 1.73,
"bmi": 26.73,
"category": "Overweight"
}

This gradual enrichment of state is the foundation of workflow-based programming in LangGraph.

Building Our First LLM Workflow

Just like every other LangGraph workflow, we begin by defining a state.

Our workflow needs only two pieces of information:

  1. The question provided by the user.
  2. The answer generated by the model.
from typing import TypedDict

class LLMState(TypedDict):
question: str
answer: str

Initially, only the question will be present. As the workflow executes, the answer field will be populated by the LLM node.

Loading Gemma 3 12B

The next step is creating a connection to our local model.

Using LangChain's Ollama integration, this is straightforward:

from langchain_ollama import ChatOllama

model = ChatOllama(
model="gemma4:12b"
)

At this point, LangChain knows how to communicate with the Ollama server running on your machine.

Whenever we call model.invoke(), the request will be sent to the local Gemma 4 12B model instead of an external cloud service.

Creating the LLM Node

Now we create the actual node that will interact with the model.

def llm_qa(state: LLMState) -> LLMState:

question = state["question"]

prompt = f"""
Answer the following question:

{question}
"""

response = model.invoke(prompt)

state["answer"] = response.content

return state

Let us carefully understand what happens inside this node.

When the node starts executing, it receives the current workflow state as input. It extracts the question from the state and creates a prompt. That prompt is then sent to Gemma 4 12B through Ollama. Once the model generates a response, the answer is written back into the state. Finally, the updated state is returned to LangGraph.

This pattern is identical to every LangGraph node we have created so far. The only difference is that this node communicates with an LLM before updating the state.

Creating the Graph

Now we can assemble the workflow.

from langgraph.graph import (
StateGraph,
START,
END
)

graph = StateGraph(LLMState)

graph.add_node(
"llm_qa",
llm_qa
)

graph.add_edge(
START,
"llm_qa"
)

graph.add_edge(
"llm_qa",
END
)

workflow = graph.compile()

The execution flow is very simple:

  • Start execution.
  • Call the LLM node.
  • Store the answer.
  • End execution.

Executing the Workflow

Next, we provide an initial state.

initial_state = {
"question":
"What is doublw of 2?"
}

Now invoke the workflow.

final_state = workflow.invoke(
initial_state
)

print(final_state)

A typical output might look similar to:

{
'question': 'What is double of 2',
'answer': 'The double of 2 is **4**.'
}

The exact wording will vary because the response is generated by Gemma 4 12B.