Hey everyone, Emma here from agent101.net!
It’s March 28th, 2026, and if you’re anything like me, you’ve probably spent the last few months feeling like you’re constantly trying to catch up with the AI agent world. Every week there’s a new framework, a new tool, a new philosophical debate about whether agents will take over the world or just book my dentist appointments better. My inbox is a graveyard of “must-read” articles I haven’t gotten to yet, and my browser tabs are a precarious Jenga tower of half-read tutorials.
Seriously, it can feel like everyone else just ‘gets’ it. Meanwhile, I’m over here, still trying to wrap my head around the difference between an ‘agentic workflow’ and just, you know, a regular script that calls an API. Sound familiar? Good, because you’re in the right place.
Today, I want to talk about something that’s been a real turning point for me in understanding AI agents: the humble ‘tool.’ Specifically, how giving an AI agent the *right* tools, and understanding *why* those tools matter, makes all the difference. We’re going to build a super simple, but incredibly insightful, “smart” note-taking agent. It won’t write your novel, but it will show you the core principle of how agents interact with the outside world, which is, frankly, the secret sauce.
Why Tools Are the Agent’s Superpower (and Your Learning Breakthrough)
For a long time, my mental model of an AI agent was basically a really smart chatbot. You talk to it, it talks back, maybe it summarizes some stuff. And while that’s a part of it, it misses the crucial element: agency. An agent isn’t just responding; it’s *acting*. And to act, it needs things to act *with*.
Think about a human assistant. If I ask my assistant, “Hey, can you find me the best flight to Tokyo next month?” they don’t just sit there and think really hard. They open a browser, go to a flight booking site, enter criteria, compare prices, and then come back to me with options. The browser, the booking site – those are their tools.
AI agents are no different. Out of the box, a large language model (LLM) is brilliant at understanding language and generating text. But it can’t *do* much beyond that. It can’t browse the web, it can’t run code, it can’t save files to your hard drive. To perform those actions, you need to give it specific functionalities – its tools.
This realization hit me like a ton of bricks a few months ago when I was trying to get an agent news articles. I kept feeding it articles, and it would summarize them perfectly, but I wanted it to *find* the articles first. I was stuck in the “LLM as a brain” mindset. Once I understood that I needed to give it a “web search tool,” suddenly the whole puzzle clicked into place. It wasn’t about the LLM getting smarter; it was about giving the LLM the ability to interact with the world.
Our Goal: A Smart Note-Taker That *Remembers*
Today, we’re going to build a very basic agent that can take notes. “Big deal, Emma,” you might say, “I can do that with Notepad.” And you’d be right! But our agent will have a crucial difference: it can *remember* and *retrieve* notes, and it will decide *when* to use those capabilities.
This means it needs two fundamental tools:
- A tool to *save* a note.
- A tool to *retrieve* a note.
And because we’re starting simple, we’ll keep our “memory” as basic as possible: a simple text file on your computer.
What You’ll Need (Pre-Flight Checklist):
- Python (I’m using 3.10+, but anything modern should work).
pipfor installing packages.- An API key for an LLM. I’ll be using OpenAI’s API for this example, specifically `gpt-3.5-turbo`, because it’s cost-effective and very capable for this kind of task. If you don’t have one, you can get it from the OpenAI platform. Remember to keep it safe!
- The
openaiPython library. - The
langchainlibrary. Yes, I know, LangChain can feel like a whole beast itself, but for this, we’ll use just a tiny, focused part to demonstrate tools cleanly.
Let’s get those packages installed:
pip install openai langchain
And set up your OpenAI API key. The safest way is usually through an environment variable:
export OPENAI_API_KEY="your_api_key_here"
Or, if you’re just messing around locally, you can put it directly in your script (though not recommended for production!):
import os
os.environ["OPENAI_API_KEY"] = "your_api_key_here"
Step 1: Define Our Tools
This is where the magic starts. We’re going to create Python functions that represent our agent’s capabilities. LangChain has a nice way to wrap these functions into “tools” that the LLM can understand.
Tool 1: Saving a Note
This tool will take a note’s title and its content, and save it to a simple text file. Each note will be saved in its own file, named after its title, in a `notes/` directory.
import os
def save_note(title: str, content: str) -> str:
"""
Saves a note to a text file.
Args:
title (str): The title of the note.
content (str): The content of the note.
Returns:
str: A confirmation message.
"""
notes_dir = "notes"
if not os.path.exists(notes_dir):
os.makedirs(notes_dir)
filename = os.path.join(notes_dir, f"{title.replace(' ', '_').lower()}.txt")
try:
with open(filename, "w") as f:
f.write(content)
return f"Note '{title}' saved successfully to {filename}."
except Exception as e:
return f"Error saving note '{title}': {e}"
# Example of how to call it directly
# print(save_note("My First Note", "This is the content of my very first note."))
A few things to notice here:
- The function has a clear docstring. This is SUPER important because the LLM will read this docstring to understand what the tool does and how to use it.
- It takes specific arguments (`title`, `content`) with type hints. This helps LangChain (and the LLM) understand the expected input.
- It returns a string. This is the output the LLM will see after the tool runs.
- We’re making sure a `notes` directory exists. Good practice!
Tool 2: Retrieving a Note
This tool will take a note’s title and try to read its content from the corresponding text file.
def retrieve_note(title: str) -> str:
"""
Retrieves the content of a previously saved note.
Args:
title (str): The title of the note to retrieve.
Returns:
str: The content of the note, or an error message if not found.
"""
notes_dir = "notes"
filename = os.path.join(notes_dir, f"{title.replace(' ', '_').lower()}.txt")
if not os.path.exists(filename):
return f"Note '{title}' not found."
try:
with open(filename, "r") as f:
content = f.read()
return f"Content of '{title}':\n---\n{content}\n---"
except Exception as e:
return f"Error retrieving note '{title}': {e}"
# Example of how to call it directly
# print(retrieve_note("My First Note"))
Similar principles here: clear docstring, type hints, and a useful return message.
Step 2: Wiring Up Our Agent
Now that we have our functions, we need to tell LangChain to turn them into tools that our LLM can use. Then, we’ll create an agent that can decide *when* to use these tools based on our input.
from langchain.agents import initialize_agent, AgentType
from langchain_openai import ChatOpenAI
from langchain.tools import Tool
import os # Make sure this is imported if not already
# Initialize the LLM (our agent's "brain")
llm = ChatOpenAI(temperature=0, model="gpt-3.5-turbo") # temperature=0 makes it more deterministic
# Define our tools for LangChain
tools = [
Tool(
name="SaveNote",
func=save_note,
description="Useful for saving new notes or updating existing ones. Input should be a JSON string with 'title' and 'content' keys."
),
Tool(
name="RetrieveNote",
func=retrieve_note,
description="Useful for retrieving the content of a previously saved note. Input should be the exact 'title' of the note."
)
]
# Initialize the agent
# AgentType.OPENAI_FUNCTIONS is excellent because it uses OpenAI's native function calling capabilities.
# This means the LLM is explicitly trained to decide when to call functions and what arguments to pass.
agent = initialize_agent(
tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True, # Set to True to see the agent's thought process
handle_parsing_errors=True # Good for debugging
)
print("Agent initialized! Type 'exit' to quit.")
# Main interaction loop
while True:
user_input = input("\nWhat do you want to do with your notes? (Type 'exit' to quit) > ")
if user_input.lower() == 'exit':
break
try:
result = agent.run(user_input)
print(f"\nAgent's response: {result}")
except Exception as e:
print(f"An error occurred: {e}")
Let’s break down the important bits:
- `ChatOpenAI(temperature=0, model=”gpt-3.5-turbo”)`: This is our LLM. `temperature=0` makes it less creative and more focused on logical task completion, which is usually what you want for agents.
- `Tool(…)`: This is how we wrap our Python functions into something LangChain can use.
- `name`: A unique identifier for the tool.
- `func`: The actual Python function we defined.
- `description`: THIS IS CRITICAL! The LLM reads this to understand *when* and *how* to use your tool. Be clear and specific about its purpose and expected input format. I’ve specified JSON for `SaveNote` because it has multiple arguments.
- `initialize_agent(…)`: This is where we create our agent.
- `tools`: Our list of tools.
- `llm`: Our LLM brain.
- `agent=AgentType.OPENAI_FUNCTIONS`: This is the specific type of agent that leverages OpenAI’s function-calling feature. It’s really good at this!
- `verbose=True`: This is your best friend for debugging! It shows you the agent’s “thought process” – what it’s thinking, which tool it’s calling, and the tool’s output.
Let’s Take Our Agent for a Spin!
Save the above code as something like `note_agent.py` and run it from your terminal:
python note_agent.py
Now, try these prompts:
Scenario 1: Saving a new note
What do you want to do with your notes? > I need to save a note about my grocery list. The content is: milk, eggs, bread, coffee.
Watch the verbose output! You should see the agent realize it needs to use the `SaveNote` tool, formulate the arguments, call the tool, and then report the tool’s output back to you.
Check your `notes/` directory. You should see a file named `grocery_list.txt` with the content!
Scenario 2: Retrieving a note
What do you want to do with your notes? > Can you show me the grocery list note?
Again, observe the agent’s thought process. It should identify `RetrieveNote` as the appropriate tool and return the content.
Scenario 3: A more complex request (decision making)
What do you want to do with your notes? > I just remembered I need to add butter to the grocery list. Then, I need to remember a meeting note I saved called 'Project Alpha Kickoff'.
Here, the agent should first decide to *update* the grocery list (by saving a new version) and *then* retrieve the Project Alpha note. This demonstrates its sequential reasoning and tool-use capabilities.
Scenario 4: Something it can’t do (and how it handles it)
What do you want to do with your notes? > Can you send an email to my boss about the grocery list?
The agent doesn’t have an “email sending” tool. It should acknowledge this and respond appropriately, explaining that it can’t perform that action because it lacks the necessary tools.
What We Just Built and Why It Matters
You’ve just built an AI agent that can:
- Understand your intent: It figured out if you wanted to save or retrieve.
- Make decisions: It chose the correct tool based on your request.
- Execute actions: It called the Python functions you provided.
- Process results: It read the output of the tool and responded to you.
This simple note-taker demonstrates the core loop of almost every advanced AI agent out there. The difference between this and a super complex agent isn’t the fundamental principle; it’s the *number* and *sophistication* of the tools it has access to. Imagine if our agent also had tools for:
- Searching the web
- Sending emails
- Setting calendar reminders
- Running Python code snippets
- Interacting with a database
The possibilities explode once you start thinking about agents as “brains with hands” (those hands being the tools).
Actionable Takeaways for Your Agent Journey:
- Start Simple with Tools: Don’t try to build a world-domination agent on day one. Pick one or two specific external actions you want an agent to perform, define them as Python functions, and then wire them up.
- Master the Description: The `description` field for your tools is paramount. Spend time crafting clear, concise descriptions that tell the LLM exactly what the tool does, when to use it, and what kind of input it expects. This is how the LLM “understands” your tools.
- Use `verbose=True`: Seriously, this is your best friend. It demystifies the agent’s process and helps you understand why it’s making certain decisions (or getting stuck).
- Think “Tool-First”: When you encounter a task you want an agent to do, first ask yourself: “What external actions or information does this task require?” Each of those external actions is a potential tool.
- Experiment with Input Formats: For tools with multiple arguments, specifying that the input should be a JSON string often works very well with `AgentType.OPENAI_FUNCTIONS`, as the LLM is adept at generating structured JSON.
I hope this deep dive into tools helps demystify AI agents for you as much as it did for me. It’s not about magic; it’s about giving a powerful language model the right interfaces to interact with the world. Go forth, build some tools, and let your agents get to work!
Until next time, happy agent building!
Emma
agent101.net
đź•’ Published: