GenAI Exam Prep
Home Mock Exam
⚡ LECTURE 19

Agentic AI — Control Flow

How agents decide what to do next. Learn the ReAct framework, agent state management, complex conditional control flow, and building stateful agents with LangGraph.

Syllabus topics 75–78 ⏱ ~26 min read 12 practice questions

19.1 The ReAct Framework

ReAct (Reason + Act) — a framework where the agent repeatedly cycles through Think → Act → Observe. It reasons about the situation, takes an action, observes the result, then reasons again — adapting until the task is complete.
THINK → ACT → OBSERVE → (THINK again) → …
🧩 ReAct in action Think: "I need to search for 'best Italian restaurants'." → Act: search executed. → Observe: "No results found." → Think again: "Try 'top rated pasta places' instead." → ActObserve … The loop repeats, each observation feeding back into reasoning, so the agent adapts when things go wrong.
🔑 Why ReAct matters A plain LLM gives one answer and stops. ReAct lets an agent self-correct — if an action fails, the next "Think" step reconsiders and tries a different approach. This adaptability is what makes agents reliable on multi-step real-world tasks.

19.2 State Management of AI Agents

State — a memory object that stores all the context an agent needs throughout an interaction. Like a waiter's order pad, constantly updated as the conversation progresses.

State management lets the agent: prevent forgetting previous context, support multi-turn conversations, track progress through complex workflows, and validate inputs.

🧩 State validation — the Pizza Bot User: "Order a pizza with shoe topping." → State check: "shoe" not in valid toppings → reject & loop back ("Please choose from Cheese, Pepperoni…"). User: "Cheese." → State check: valid ✓ → accept & proceed. State both remembers the order and guards the workflow.

Types of state (recap from Lecture 15)

19.3 Complex Control Flow

Simple chatbots are linear — one straight path. Real agents need complex control flow:

💡 Tip — Human-in-the-Loop (HITL) Agents can make mistakes (book the wrong ticket, email the wrong person). HITL pauses the workflow before a critical or irreversible action and waits for a human to approve it — e.g. an agent drafts an email, pauses, a manager clicks "Approve", then the agent sends it.

19.4 LangGraph: Graphs, Nodes, Edges, State

LangGraph — a Python library for building stateful, multi-actor agent applications. Instead of a linear chat, you build a graph of logic — a decision tree where the agent can route itself based on conditions, loop, and maintain shared state.

LangGraph has four core components:

1. State — the shared diary

A typed Python dictionary (TypedDict) passed between every step. Each node reads from it and writes to it, carrying context forward. TypedDict gives type safety and prevents key errors. The add_messages reducer appends new messages instead of overwriting the history.

2. Nodes — the workers

Python functions that perform work. Each node receives the current state, performs an operation (call an LLM, run a tool, compute), and returns updates to the state. Keep each node to a single responsibility.

3. Edges — the logic gates

4. The StateGraph — the builder

The StateGraph assembles nodes and edges into an executable workflow. Three essential methods: add_node() (register a worker), add_edge() / add_conditional_edges() (connect them), and set_entry_point() (define where execution starts). Finally .compile() turns it into a runnable app.

🔑 Why graphs beat linear code for agents A graph allows cycles (loop back for the ReAct loop or user input), conditional routing (different paths per state), and deterministic, debuggable execution. State updates happen incrementally at each node; checkpointing lets a workflow resume after a failure.

19.5 Building an Agent Graph

Python · defining the State
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages

# State = a typed dictionary shared across all nodes
class AgentState(TypedDict):
    messages: Annotated[list, add_messages]  # add_messages APPENDS
    flight_price: int                        # other fields OVERWRITE
    decision: str
Python · nodes and conditional routing
# Node: receives state, returns state updates
def check_price(state: AgentState):
    if state["flight_price"] > 500:
        return {"decision": "ask_user"}
    return {"decision": "book"}

# Routing function for the conditional edge
def route(state: AgentState):
    return "ask" if state["decision"] == "ask_user" else "book"
Python · building & compiling the graph
from langgraph.graph import StateGraph, START, END

builder = StateGraph(AgentState)

# 1. Register nodes (workers)
builder.add_node("find_flight", find_flight)
builder.add_node("check_price", check_price)
builder.add_node("book_flight", book_flight)
builder.add_node("ask_user", ask_user)

# 2. Connect with edges
builder.add_edge(START, "find_flight")
builder.add_edge("find_flight", "check_price")
builder.add_conditional_edges("check_price", route,
                              {"book": "book_flight", "ask": "ask_user"})
builder.add_edge("book_flight", END)

# 3. Compile into a runnable app
app = builder.compile()
result = app.invoke({"flight_price": 0, "decision": "", "messages": []})
Python · a ReAct-style tool-using agent
from langgraph.prebuilt import ToolNode
from langgraph.graph import StateGraph, START, END

def assistant_node(state):                      # the "Think" node
    return {"messages": [llm_with_tools.invoke(state["messages"])]}

tool_node = ToolNode([multiply])                # the "Act" node

def should_continue(state):                     # router: loop or stop?
    last = state["messages"][-1]
    return "tools" if last.tool_calls else END

builder = StateGraph(State)
builder.add_node("assistant", assistant_node)
builder.add_node("tools", tool_node)
builder.add_edge(START, "assistant")
builder.add_conditional_edges("assistant", should_continue)
builder.add_edge("tools", "assistant")          # loop back -> ReAct cycle
graph = builder.compile()
Output (for "Calculate 50 times 173 then divide by 5")The result of 50 times 173 is 8650. Dividing 8650 by 5 gives 1730.

The edge tools → assistant creates the loop — the agent keeps reasoning and acting until should_continue returns END. That loop is the ReAct cycle implemented as a graph.

💡 Tip — checkpointing for reliability Add a checkpointer (e.g. MemorySaver) when compiling the graph to save state snapshots. The agent can then recover from crashes, resume multi-turn conversations after a restart, and avoid recomputing completed work.
? Practice Questions

ReAct and LangGraph components are heavily tested.

MCQQ1ReAct

The ReAct framework cycles through which three steps?

  • A Read → Encode → Generate
  • B Think → Act → Observe
  • C Train → Test → Deploy
  • D Retrieve → Augment → Generate
Answer: B

ReAct = Reason + Act: Think (decide), Act (execute), Observe (review the result) — then think again. (Retrieve→Augment→Generate is RAG.)

MCQQ2ReAct benefit

The main benefit of the ReAct loop is that the agent can:

  • A Answer instantly without reasoning
  • B Adapt and self-correct when an action fails
  • C Avoid using any tools
  • D Skip the observe step
Answer: B

Each Observe feeds back into Think, so when an action fails the agent reconsiders and tries a different approach — it self-corrects.

MCQQ3State

In an AI agent, "state" is best described as:

  • A The model's weights
  • B A memory object holding all context the agent needs during an interaction
  • C The temperature setting
  • D A type of activation function
Answer: B

State is the constantly-updated "order pad" that stores messages, intermediate results and progress so the agent stays coherent.

MCQQ4LangGraph

In LangGraph, a node is:

  • A A connection between two functions
  • B A Python function that receives state and returns state updates
  • C The final answer
  • D A type of embedding
Answer: B

A node is a worker function: it takes the current state, does an operation, and returns updates. Edges connect nodes.

MCQQ5Edges

A conditional edge in LangGraph:

  • A Always sends the flow to a fixed next node
  • B Uses a routing function to choose the next node based on the state
  • C Deletes the state
  • D Trains the model
Answer: B

A conditional edge runs a routing function that inspects the state and dynamically picks which node executes next — like a GPS rerouting.

MCQQ6add_messages

Annotating the messages field with add_messages ensures that new messages are:

  • A Deleted
  • B Appended to the existing list, not overwriting history
  • C Translated
  • D Sent to a different graph
Answer: B

add_messages is a reducer that appends new messages, preserving the full conversation history instead of replacing it.

MCQQ7HITL

Human-in-the-Loop (HITL) is used to:

  • A Make the agent run faster
  • B Pause for human approval before a critical or risky action
  • C Remove the LLM from the system
  • D Increase the temperature
Answer: B

HITL inserts a human checkpoint — the workflow pauses for approval before irreversible actions (sending an email, making a purchase).

MCQQ8Why graphs

Why is a graph-based approach better than linear code for AI agents?

  • A Graphs never use state
  • B Graphs support cycles, conditional routing and deterministic, debuggable flow
  • C Graphs do not need an LLM
  • D Graphs run only once
Answer: B

Graphs allow loops (ReAct), conditional routing based on state, and explicit, predictable execution that is easy to debug and scale.

CodingQ9Define state

Define a LangGraph State TypedDict with a messages list (appended) and a task_status string.

Solution
Python
from typing import TypedDict, Annotated
from langgraph.graph.message import add_messages

class AgentState(TypedDict):
    messages: Annotated[list, add_messages]   # appended each step
    task_status: str                          # overwritten each step

The Annotated[..., add_messages] reducer makes messages append; plain fields like task_status are overwritten on update.

CodingQ10Build a graph

Build a minimal LangGraph with a single node chat connected from START to END, and compile it.

Solution
Python
from langgraph.graph import StateGraph, START, END
from langchain_core.messages import AIMessage

def chat_node(state):
    text = state["messages"][-1].content
    return {"messages": [AIMessage(content=f"I heard: {text}")]}

graph = StateGraph(AgentState)
graph.add_node("chat", chat_node)
graph.add_edge(START, "chat")
graph.add_edge("chat", END)

app = graph.compile()

add_node registers the worker, two add_edge calls wire START → chat → END, and .compile() produces a runnable app.

CodingQ11Conditional edge

Write a routing function and add a conditional edge so that, after a check node, the graph goes to "approve" if state["amount"] > 1000, otherwise to "auto".

Solution
Python
def route(state):
    if state["amount"] > 1000:
        return "approve"      # needs human approval
    return "auto"             # auto-process

builder.add_conditional_edges(
    "check", route,
    {"approve": "approve_node", "auto": "auto_node"}
)

The routing function inspects the state and returns a key; the mapping dictionary sends the flow to the matching node.

Short AnswerQ12Concept

Name LangGraph's four core components and state the role of each in one line.

Model answer

State — a typed dictionary (the shared "diary") passed between steps, holding all context. Nodes — Python worker functions that receive state and return updates. Edges — connections defining flow; conditional edges route dynamically based on state. StateGraph — the builder that registers nodes, connects edges and compiles everything into a runnable app.

🎯 Lecture 19 — must-remember ReAct = Think → Act → Observe (loop, enables self-correction). State = the agent's memory "order pad"; also validates inputs. Complex control flow: branching, loops, conditional routing, HITL (pause for human approval). LangGraph: State (typed dict), Nodes (worker functions), Edges (standard or conditional), StateGraph builder → .compile(). add_messages appends history; checkpointing enables recovery.