Building a LangGraph Agent from Scratch
Everything you need to know to get started

Introduction
The term "AI agent" is one of the most popular right now. They emerged after the LLM hype, when people realized that the latest LLM capabilities are impressive but that they can only perform tasks on which they have been explicitly trained. In that sense, normal LLMs do not have tools that would allow them to do anything outside their scope of knowledge.
RAG
To address this, Retrieval-Augmented Generation (RAG) was later introduced to retrieve additional context from external data sources and inject it into the prompt, so the LLM becomes aware of more context. We can roughly say that RAG made the LLM more knowledgeable, but for more complex problems, the LLM + RAG approach still failed when the solution path was not known in advance.

Agents
Agents are a remarkable concept built around LLMs that introduce state, decision-making, and memory. Agents can be thought of as a set of predefined tools for analyzing results and storing them in memory for later use before producing the final answer.
LangGraph
LangGraph is a popular framework used for creating agents. As the name suggests, agents are constructed using graphs with nodes and edges.
Nodes represent the agent's state, which evolves over time. Edges define the control flow by specifying transition rules and conditions between nodes.
To better understand LangGraph in practice, we will go through a detailed example. While LangGraph might seem too verbose for the problem below, it usually has a much larger impact on complex problems with large graphs.
First, we need to install the necessary libraries.
Then we import the necessary modules.
We would also need to create an .env file and add an OPENAI_API_KEY there:
Then, with load_dotenv(), we can load the environment variables into the system.
Extra functionalities
The function below will be useful for us to visually display constructed graphs.
Agent
Let us initialize an agent based on GPT-5-nano using a simple command:
State
In our example, we will construct an agent capable of answering questions about soccer. Its thought process will be based on retrieved statistics about players.
To do that, we need to define a state. In our case, it will be an entity containing all the information an LLM needs about a player. To define a state, we need to write a class that inherits from pydantic.BaseModel:
When moving between LangGraph nodes, each node takes as input an instance of PlayerState that specifies how to process the state. Our task will be to define how exactly that state is processed.
Tools
First, we will define some of the tools an agent can use. A tool can be roughly thought of as an additional function that an agent can call to retrieve the information needed to answer a user's question.
To define a tool, we need to write a function with a @tool decorator. It is important to use clear parameter names and function docstrings, as the agent will consider them when deciding whether to call the tool based on the input context.
To make our examples simpler, we are going to use mock data instead of real data retrieved from external sources, which is usually the case for production applications.
In the first tool, we will return information about a player's club and country by name.
You might be asking why we place a tool inside another function, which seems like over-engineering. In fact, these two functions have different responsibilities.
The function fetch_player_information() takes a state as a parameter and is compatible with the LangGraph framework. It extracts the name field and calls a tool that operates on the parameter level.
It provides a clear separation of concerns and allows easy reuse of the same tool across multiple graph nodes.
Then we have an analogous function that retrieves a player's jersey number:
For the third tool, we will be fetching the player's FIFA rating:
Now, let us write several more graph node functions that will retrieve external data. We are not going to label them as tools as before, which means they won't be something the agent decides to call or not.
Here is a graph node that retrieves the number of minutes played over the last several seasons.
Below is a node that extracts a player's name from a user question.
Now is the time when things get interesting. Do you remember the three tools we defined above? Thanks to them, we can now create a planner that will ask the agent to choose a specific tool to call based on the context of the situation:
In our case, we will ask the agent to create a summary of a soccer player. It will decide on its own which tool to call to retrieve additional data. Docstrings under tools play an important role: they provide the agent with additional context about the tools.
Below is our final graph node, which will take multiple fields retrieved from previous steps and call the LLM to generate final summary.
Graph construction
We now have all the elements to build a graph. Firstly, we initialize the graph using the StateGraph constructor. Then, we add nodes to that graph one by one using the add_node() method. It takes two parameters: a string used to assign a name to the node, and a callable function associated with the node that takes a graph state as its only parameter.
Right now, our graph consists only of nodes. We need to add edges to it. The edges in LangGraph are oriented and added via the add_edge() method, specifying the names of the start and end nodes.
The only thing we need to take into account is the planner, which behaves slightly differently from other nodes. As shown above, it can return the selected_tools field, which contains 0 to 3 output nodes.
For that, we need to use the add_conditional_edges() method taking three parameters:
The planner node name;
A callable function taking a LangGraph node and returning a list of strings indicating the list of node names should be called;
A dictionary mapping strings from the second parameter to node names.
In our case, we will define the route_tools() node to simply return the state.selected_tools field as a result of a planner function.
Then we can construct nodes:
START and END are LangGraph constants used to define the graph's start and end points.
The last step is to compile the graph. We can optionally visualize it using the helper function defined above.

Example
We are now finally able to use our graph! To do so, we can use the invoke method and pass a dictionary containing the question field with a custom user question:
And here is an example result we can obtain!
A cool thing is that we can observe the entire state of the graph and analyze the tools the agent has chosen to generate the final answer. The final summary looks great!
Conclusion
In this article, we have examined AI agents that have opened a new chapter for LLMs. Equipped with state-of-the-art tools and decision-making, we now have much greater potential to solve complex tasks.
An example we saw in this article introduced us to LangGraph — one of the most popular frameworks for building agents. Its simplicity and elegance allow to construct complex decision chains. While, for our simple example, LangGraph might seem like overkill, it becomes extremely useful for larger projects where state and graph structures are much more complex.
Resources
All images unless otherwise noted are by the author.








