Reasoning and Acting AI Agent using LangGraph
A hands-on guide to implementing autonomous AI Agent with function tools and reasoning loops in LangGraph.
Introduction
This is the second article in the AI Agents series, where we will delve deeper into the integration of AI Agents using LangGraph tools. In this article, let’s explore tools and techniques that enable more sophisticated AI agent implementations.
Intro to AI Agents:
👉 Article 1: Simple ReAct Agent from Scratch
👉 (This) Article 2: ReAct Agent in LangGraph
👉 Article 3: Persistence (memory) and streaming in Langgraph
👉 Article 4: Human in the loop
[Github] The Jupiter Notebook of this implementation
[Inspired by] Deeplearning.ai course
Common Tools of LangChain
Prompt Templates
Cyclic graphs (LangGraphs)
Built-in persistence
Human-in-the-loop
Prompt templates
Langchain hub is one of the centralized to store templatized prompt
Here is an example of a ReAct prompt that we used in part 1:
React System PromptAs you can see, we can pass tools, tool names and input as parameters
The main advantage of using prompts from the centralized data store is that we can adapt the prompt on the fly without having to make a new software deployment just to update the prompt
LangGraph
As most of the AI agents are implemented as cyclic graph, Langchain introduced LangGraph to abstract the AI agent implementation.
LangGraph terminologies
Nodes: LLM Agents or Functions
Edges: connect nodes
Conditional edges: decision
Here is how we represent the implementation of AI Agents in Agentic AI [Part 1]:- Simple ReAct Agent from Scratch in LangGraph terminology
Langchain Tools
Langchain supports numerous tools out of the box. This article uses the Tavily Search tool to search the internet before giving back the result.
Implementation of AI Agent
Agent State
It is local to the graph and accessible to all parts of the graph
It can be stored in a persistent layer
Here is an example of a simple state manager that appends all the conversations in the messages variable
class AgentState(TypedDict):
messages: Annotated[list[AnyMessage], operator.add]
Agent declaration
We can implement the graph mentioned above in the __init__
function of the Agent class.
class Agent:
def __init__(self, model, tools, system=""):
self.system = system
graph = StateGraph(AgentState) # A simple message store
graph.add_node("llm", self.call_openai)
graph.add_node("action", self.take_action)
graph.add_conditional_edges(
"llm",
self.exists_action,
{True: "action", False: END}
)
graph.add_edge("action", "llm")
graph.set_entry_point("llm")
# Returns a runnable graph
self.graph = graph.compile()
# Let the model know about all the available tools
self.tools = {t.name: t for t in tools}
self.model = model.bind_tools(tools)
Exist action
This is a condition for the conditional edege. If the last message has tool_calls it returns True if not false
def exists_action(self, state: AgentState):
result = state['messages'][-1]
return len(result.tool_calls) > 0
Call Open AI
Simply calls the LLM (with system message) and return the message. As the state has an operator.add
the return value gets appended the the messages
def call_openai(self, state: AgentState):
messages = state['messages']
if self.system:
messages = [SystemMessage(content=self.system)] + messages
message = self.model.invoke(messages)
return {'messages': [message]}
Take action
Executes the tool with name and argument
def take_action(self, state: AgentState):
tool_calls = state['messages'][-1].tool_calls
results = []
for t in tool_calls:
print(f"Calling: {t}")
# check for bad tool name from LLM
if not t['name'] in self.tools:
print("\n ....bad tool name....")
# instruct LLM to retry if bad
result = "bad tool name, retry"
else:
# Call the tool and get back an answer
result = self.tools[t['name']].invoke(t['args'])
results.append(ToolMessage(tool_call_id=t['id'], name=t['name'], content=str(result)))
print("Back to the model!")
return {'messages': results}
Execution of Agent
Here is the prompt that we will use for the agent.
You are a smart research assistant. Use the search engine to look up information.
You are allowed to make multiple calls (either together or in sequence).
Only look up information when you are sure of what you want.
If you need to look up some information before asking a follow up question, you are allowed to do that!
We can then execute the graph with the following code
model = ChatOpenAI(model="gpt-3.5-turbo")
abot = Agent(model, [tool], system=prompt)
# Execute the graph
messages = [HumanMessage(content="What is the weather in Zurich?")]
result = abot.graph.invoke({"messages": messages})
The result is the final state of the system with all the messages
We can get the last message content to see the response
Complex query (Parallel function call)
Let’s now challenge the agent with a complex query. Asking the agent about the weather in Zurich and Berlin.
As you can see, the system now does two queries even before going back to the model. This enables a parallel function cal,l and the model makes the final answer with both of the results
An even more complex example (sequential function call)
Let’s now stress test the agent with the need for a sequential function call. In a sequential function call, the LLM would need the result of the first query in order to answer the second query.
Let's now ask it:
Which country won Euro Cup in 2024 and whats the GDP of that country?
As you can see, the Agent understood that it should first understand the winner of Eurocup, then go back to the model.
The model then made a second query with Spain's GDP for 2024 and then returned the result.
Conclusion
In this continuation of our exploration into AI Agents within LangGraph, we've leveraged LangChain tools to enhance our agent's capabilities. By building on the basics from Part 1, we've seen how to implement complex AI workflows utilizing both parallel and sequential function calls. LangGraph provides a robust framework for developing AI agents that can handle intricate tasks with ease.
Intro to AI Agents:
👉 Article 1: Simple ReAct Agent from Scratch
👉 (This) Article 2: ReAct Agent in LangGraph
👉 Article 3: Persistence (memory) and streaming in Langgraph
👉 Article 4: Human in the loop