Introduction to LangGraph
LangGraph is built on top of LangChain and is primarily focused on creating agents or agentic workflows. While it has other uses, agents are its main purpose.
What Are Agents?
Agents utilize a large language model (LLM) to decide on a sequence of actions.
An LLM serves as a reasoning engine to:
- Determine which actions to take.
- Decide in what order to take these actions.
LangGraph also supports:
- Agent Swarms: Combines multiple agents to enable data sharing, perform various tasks, and allow large language models to interact with each other.
Comparison with Other Frameworks
- Autogen and Crew AI: High-level frameworks that are easier to use but may lack detailed customization.
- LangGraph:
- Has a steeper learning curve but offers a lower-level framework.
- Provides more control and flexibility for fine-tuning workflows.
- Ideal for meeting specific and complex requirements.
Core Concepts of LangGraph
Understanding LangGraph requires familiarity with three key concepts: Nodes, Edges, and State.
1. Nodes
- A node is a function or runnable that performs a specific task.
- Each node:
- Processes input.
- Updates the state based on its operation.
2. Edges
- Edges define the connection and flow between nodes.
- They determine the order of execution.
- Conditional edges can route execution based on the current state.
3. State
- Represents the data passed between nodes during execution.
- Each node updates the state with its return value, enabling:
- Persistent workflows.
- Dynamic updates.
Workflow Structure
- Nodes change the state.
- Edges direct the flow to the next node.
- The process continues until an end node is reached, marking workflow completion.
Hands-On Learning Approach
- Start with basic examples that don't use LLMs.
- Focus on understanding the core concepts (Nodes, Edges, and State).
- Gradually move to real-world examples involving LLMs.
Key Classes in LangGraph
MessageGraph
:- A predefined class in LangGraph to create workflows.
- Provides a simple interface for interacting with LLMs.
END
:- A special node that marks the end of a workflow.
Creating and Executing a LangGraph Workflow
Example Workflow Code
from langchain_core.messages import HumanMessage
from langgraph.graph import END, MessageGraph
import time
# Function to process messages
def add_one(input: list[HumanMessage]):
input[0].content = input[0].content + "a"
time.sleep(1)
return input
# Create a new graph
graph = MessageGraph()
# Define nodes and edges
graph.add_node("branch_a", add_one)
graph.add_edge("branch_a", "branch_b")
graph.add_edge("branch_a", "branch_c")
graph.add_node("branch_b", add_one)
graph.add_node("branch_c", add_one)
graph.add_edge("branch_b", "final_node")
graph.add_edge("branch_c", "final_node")
graph.add_node("final_node", add_one)
graph.add_edge("final_node", END)
# Set entry point and compile the graph
graph.set_entry_point("branch_a")
runnable = graph.compile()
# Visualize the graph
from IPython.display import Image, display
display(Image(runnable.get_graph().draw_mermaid_png()))
# Execute the workflow
runnable.invoke("a")
Explanation of the Code
- Define the Function:
- The
add_one
function modifies the content of the first message by appending an "a" and pauses for 1 second.
- The
- Create the Graph:
- An instance of
MessageGraph
is created to define the workflow.
- An instance of
- Add Nodes and Edges:
- Nodes: Represent tasks to execute (
add_one
). - Edges: Define the flow between nodes.
- Nodes: Represent tasks to execute (
- Set Entry Point:
- Specify the starting node of the workflow using
set_entry_point
.
- Specify the starting node of the workflow using
- Compile the Graph:
- Use
graph.compile()
to create a runnable workflow.
- Use
- Visualize the Workflow:
- Visualize the graph using the
get_graph
method in combination with IPython's display tools.
- Visualize the graph using the
- Execute the Workflow:
- Use the
invoke
method to execute the workflow.
- Use the
- HumanMessage:
- HumanMessages are messages that are passed in from a human to the model.
Workflow Visualization
The workflow consists of:
- Branch A: Entry point that splits into Branch B and Branch C.
- Branch B and Branch C: Perform tasks and route to the final node.
- Final Node: Combines results and marks the workflow as complete.
Graph Overview
- Entry point: branch_a
- Workflow flow:
branch_a --> branch_b --> final_node --> branch_c --> final_node
- End node: END
Output
When executed with the input "a"
, the workflow:
- Adds
"a"
in each step (total of 5 additions). - Produces the output:
"aaaaa"
.
Notes and Tips
- Visualization: Use the
get_graph
method to generate a visual representation of the workflow. This is helpful for debugging and understanding complex workflows. - Flexibility: LangGraph supports custom functions and LangChain runnables, enabling dynamic and customizable workflows.
- Future Enhancements: You can integrate LLMs or other models for real-world applications.
This example demonstrates the foundational concepts of LangGraph. By mastering nodes, edges, and workflows, you can build sophisticated and scalable agentic systems.
Working with Conditional Edges
Conditional edges allow branching in the workflow based on input data. Here's a step-by-step explanation of how to work with conditional edges:
Functions Used
entry(input)
:- Takes an input list of
HumanMessage
objects. - Simply returns the input without any modifications.
- Takes an input list of
work_with_b(input)
:- Executes when the workflow branches to "Branch B".
- Prints
"Using branch B"
.
work_with_c(input)
:- Executes when the workflow branches to "Branch C".
- Prints
"Using branch C"
.
router(input)
:- Determines the branching logic.
- If
"use_b"
is found in the input's content, it routes to "Branch B"; otherwise, it routes to "Branch C".
Steps to Build the Workflow
Create a MessageGraph:
- Initialize a
MessageGraph
object.
- Initialize a
Add Nodes:
- Add nodes for each branch and the entry point:
"branch_a"
: Executes theentry
function."branch_b"
: Executes thework_with_b
function."branch_c"
: Executes thework_with_c
function.
- Add nodes for each branch and the entry point:
Add Conditional Edges:
- Use
add_conditional_edges()
to route based on the output of therouter
function. - Provide a mapping of output strings to target nodes:
"branch_b"
→"branch_b"
"branch_c"
→"branch_c"
- Use
Set End Nodes:
- Add edges from
"branch_b"
and"branch_c"
to theEND
node.
- Add edges from
Set Entry Point:
- Define
"branch_a"
as the entry point usingset_entry_point()
.
- Define
Compile and Visualize:
- Compile the graph with
compile()
to create a runnable workflow. - Visualize the graph using
draw_mermaid_png()
to understand its structure.
- Compile the graph with
Graph Visualization
Conditional edges are visualized with dashed lines, distinguishing them from standard edges. This helps in understanding how the workflow branches based on input conditions.
Example Invocations
Input:
"hello"
:- Since
"use_b"
is not in the input, the workflow branches to"branch_c"
. - Output:
"Using branch C"
- Since
Input:
"I want to use_b"
:- The input contains
"use_b"
, so the workflow branches to"branch_b"
. - Output:
"Using branch B"
- The input contains
Code Implementation
from langchain_core.messages import HumanMessage
from langgraph.graph import END, MessageGraph
def entry(input: list[HumanMessage]):
return input
def work_with_b(input: list[HumanMessage]):
print("Using branch B")
return input
def work_with_c(input: list[HumanMessage]):
print("Using branch C")
return input
def router(input: list[HumanMessage]):
if "use_b" in input[0].content:
return "branch_b"
else:
return "branch_c"
graph = MessageGraph()
graph.add_node("branch_a", entry)
graph.add_node("branch_b", work_with_b)
graph.add_node("branch_c", work_with_c)
graph.add_conditional_edges(
"branch_a", router, {"branch_b": "branch_b", "branch_c": "branch_c"}
)
graph.add_edge("branch_b", END)
graph.add_edge("branch_c", END)
graph.set_entry_point("branch_a")
runnable = graph.compile()
from IPython.display import Image, display
display(Image(runnable.get_graph().draw_mermaid_png()))
runnable.invoke("hello")
runnable.invoke("I want to use_b")
Key Takeaways
- Conditional Edges: Essential for branching workflows based on dynamic inputs.
- Graph Visualization: Aids in understanding workflow structures and debugging.
- Reusable Functions: Functions like entry, work_with_b, and router can be customized for different use cases.
LangGraph and Cycles
Key Differences Between LangGraph and LCEL
LangGraph allows the creation of cycles in workflows, which LCEL does not. Cycles enable workflows to loop back and repeat steps based on conditional logic. This functionality is useful for creating iterative or repetitive workflows.
Example: Cyclic Workflow with LangGraph
This example demonstrates a cyclic workflow using LangGraph:
Entry Point:
- A function (
entry
) that simply takes an input and returns the same output without modification.
- A function (
Action Node:
- A function (
action
) that:- Checks the number of input messages.
- If the number exceeds 5, appends a human message with the content
end
. - Otherwise, appends a message with the content
continue
. - Uses the content of the last message to determine whether the workflow should continue or terminate.
- A function (
Should Continue Function:
- A function (
should_continue
) that evaluates the content of the last message:- If the content is
end
, the function returns__end__
, signaling the workflow to terminate. - Otherwise, it returns
action
, signaling the workflow to continue.
- If the content is
- A function (
Graph Structure:
- Nodes:
agent
: Performs theentry
function.action
: Executes theaction
function.
- Conditional Edge:
- From
agent
:- If the output of
should_continue
isaction
, it routes to theaction
node. - If the output is
__end__
, it routes to the termination node (END
).
- If the output of
- From
- Cycle:
- The
action
node routes back to theagent
node, forming a cyclic structure.
- The
- Nodes:
Visualization of the Workflow
The compiled graph visualizes the cyclic workflow:
- The
agent
node conditionally routes to either:- The
action
node for further processing. - The
END
node to terminate the workflow.
- The
- The
action
node routes back to theagent
node, enabling iterative processing.
Execution of the Workflow
Input:
- Start with the input
"Hello"
.
- Start with the input
Process:
- The workflow repeatedly adds the message
continue
until the input message list reaches a length of 5. - At this point, the message
end
is added, signaling the workflow to terminate.
- The workflow repeatedly adds the message
Termination:
- When the last message contains
end
, the workflow routes to the termination node (END
).
- When the last message contains
Code Example
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langgraph.graph import END, MessageGraph
model = ChatOpenAI(temperature=0)
def entry(input: list[HumanMessage]):
return input
def action(input: list[HumanMessage]):
print("Action taken:", [msg.content for msg in input])
if len(input) > 5:
input.append(HumanMessage(content="end"))
else:
input.append(HumanMessage(content="continue"))
return input
def should_continue(input: list):
last_message = input[-1]
if "end" in last_message.content:
return "__end__"
return "action"
graph = MessageGraph()
graph.add_node("agent", entry)
graph.add_node("action", action)
graph.add_conditional_edges(
"agent", should_continue, {"action": "action", "__end__": END}
)
graph.add_edge("action", "agent")
graph.set_entry_point("agent")
runnable = graph.compile()
display(Image(runnable.get_graph().draw_mermaid_png()))
runnable.invoke("Hello")
Key Takeaways
- LangGraph is powerful for creating workflows with cyclic behavior.
- Cycles allow workflows to iterate based on conditional logic until a specific termination condition is met.
- This feature distinguishes LangGraph from LCEL and expands its applicability for complex workflows.
Source
The notes has been taken from this video from Coding Crash Courses YouTube channel.
[YouTube]