0% found this document useful (0 votes)
2 views8 pages

Introduction

The document discusses the concept of AI agents built around large language models (LLMs) and introduces Retrieval-Augmented Generation (RAG) to enhance LLM capabilities by incorporating external data. It highlights the LangGraph framework for creating agents that utilize state, decision-making, and memory, exemplified through a soccer player information retrieval system. The document details the construction of an agent, including defining states and tools for fetching player statistics, and outlines the process of planning tool selection based on user queries.

Uploaded by

anupamabhan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
2 views8 pages

Introduction

The document discusses the concept of AI agents built around large language models (LLMs) and introduces Retrieval-Augmented Generation (RAG) to enhance LLM capabilities by incorporating external data. It highlights the LangGraph framework for creating agents that utilize state, decision-making, and memory, exemplified through a soccer player information retrieval system. The document details the construction of an agent, including defining states and tools for fetching player statistics, and outlines the process of planning tool selection based on user queries.

Uploaded by

anupamabhan
Copyright
© All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as DOCX, PDF, TXT or read online on Scribd

ntroduction

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.

RAG pipeline

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.


langgraph==1.0.5

langchain-community==0.4.1

jupyter==1.1.1

notebook==7.5.1

langchain[openai]

Then we import the necessary modules.

import os

from dotenv import load_dotenv

import json

import random

from pydantic import BaseModel

from typing import Optional, List, Dict, Any

from [Link] import StateGraph, START, END

from langchain_openai import ChatOpenAI

from langchain_core.messages import HumanMessage

from langchain.chat_models import init_chat_model

from [Link] import tool

from [Link] import Image, display

We would also need to create an .env file and add


an OPENAI_API_KEY there:

OPENAI_API_KEY=...

Then, with load_dotenv(), we can load the environment variables into


the system.

load_dotenv()

Extra functionalities

The function below will be useful for us to visually display constructed


graphs.

def display_graph(graph):

return display(Image(graph.get_graph().draw_mermaid_png()))

Agent
Let us initialize an agent based on GPT-5-nano using a simple command:

llm = init_chat_model("openai:gpt-5-nano")

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 [Link]:

class PlayerState(BaseModel):

question: str

selected_tools: Optional[List[str]] = None

name: Optional[str] = None

club: Optional[str] = None

country: Optional[str] = None

number: Optional[int] = None

rating: Optional[int] = None

goals: Optional[List[int]] = None

minutes_played: Optional[List[int]] = None

summary: Optional[str] = None

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.

@tool

def fetch_player_information_tool(name: str):

"""Contains information about the football club of a player and its


country"""

data = {

'Haaland': {

'club': 'Manchester City',

'country': 'Norway'

},

'Kane': {

'club': 'Bayern',

'country': 'England'

},

'Lautaro': {

'club': 'Inter',

'country': 'Argentina'

},

'Ronaldo': {

'club': 'Al-Nassr',

'country': 'Portugal'

if name in data:

print(f"Returning player information: {data[name]}")

return data[name]
else:

return {

'club': 'unknown',

'country': 'unknown'

def fetch_player_information(state: PlayerState):

return fetch_player_information_tool.invoke({'name': [Link]})

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:

@tool

def fetch_player_jersey_number_tool(name: str):

"Returns player jersey number"

data = {

'Haaland': 9,

'Kane': 9,

'Lautaro': 10,

'Ronaldo': 7

if name in data:

print(f"Returning player number: {data[name]}")

return {'number': data[name]}

else:
return {'number': 0}

def fetch_player_jersey_number(state: PlayerState):

return fetch_player_jersey_tool.invoke({'name': [Link]})

For the third tool, we will be fetching the player’s FIFA rating:

@tool

def fetch_player_rating_tool(name: str):

"Returns player rating in the FIFA"

data = {

'Haaland': 92,

'Kane': 89,

'Lautaro': 88,

'Ronaldo': 90

if name in data:

print(f"Returning rating data: {data[name]}")

return {'rating': data[name]}

else:

return {'rating': 0}

def fetch_player_rating(state: PlayerState):

return fetch_player_rating_tool.invoke({'name': [Link]})

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.

def retrieve_goals(state: PlayerState):

name = [Link]

data = {

'Haaland': [25, 40, 28, 33, 36],

'Kane': [33, 37, 41, 38, 29],


'Lautaro': [19, 25, 27, 24, 25],

'Ronaldo': [27, 32, 28, 30, 36]

if name in data:

return {'goals': data[name]}

else:

return {'goals': [0]}

Here is a graph node that retrieves the number of minutes played over the
last several seasons.

def retrieve_minutes_played(state: PlayerState):

name = [Link]

data = {

'Haaland': [2108, 3102, 3156, 2617, 2758],

'Kane': [2924, 2850, 3133, 2784, 2680],

'Lautaro': [2445, 2498, 2519, 2773],

'Ronaldo': [3001, 2560, 2804, 2487, 2771]

if name in data:

return {'minutes_played': data[name]}

else:

return {'minutes_played': [0]}

Below is a node that extracts a player’s name from a user question.

def extract_name(state: PlayerState):

question = [Link]

prompt = f"""

You are a football name extractor assistant.

Your goal is to just extract a surname of a footballer in the following


question.

User question: {question}

You have to just output a string containing one word - footballer surname.
"""

response = [Link]([HumanMessage(content=prompt)]).content

print(f"Player name: ", response)

return {'name': response}

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:

def planner(state: PlayerState):

question = [Link]

prompt = f"""

You are a football player summary assistant.

You have the following tools available: ['fetch_player_jersey_number',


'fetch_player_information', 'fetch_player_rating']

User question: {question}

Decide which tools are required to answer.

Return a JSON list of tool names, e.g. ['fetch_player_jersey_number',


'fetch_rating']

"""

response = [Link]([HumanMessage(content=prompt)]).content

try:

selected_tools = [Link](response)

except:

selected_tools = []

return {'selected_tools': selected_tools}

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.

You might also like