Blog14 min readBy Ravi Shankar

Build Your First AI Agent: Step-by-Step Tutorial

The best way to understand agentic AI is to build something. This tutorial walks you through creating a working AI agent from scratch — one that can search the web, read documents, perform calculations, and report results autonomously.

By the end of this tutorial, you will have a functional agent you can extend and adapt for real use cases.


Prerequisites

  • Python 3.10+
  • Basic familiarity with Python
  • An OpenAI API key (or Anthropic API key)
  • 30-60 minutes

We'll use LangChain because it has the most comprehensive documentation and the largest community for troubleshooting.


Step 1: Set Up Your Environment

Create a new directory and virtual environment:

mkdir my-first-agent
cd my-first-agent
python -m venv venv
source venv/bin/activate  # On Windows: venv\Scripts\activate

Install dependencies:

pip install langchain langchain-openai langchain-community python-dotenv

Create a .env file:

OPENAI_API_KEY=your-api-key-here

Step 2: Understand the Core Concept

Before writing code, let's understand what makes an agent different from a simple LLM call:

Simple LLM call: You send a prompt, you get a response. One turn, done.

Agent: You give a goal. The agent:

  1. Decides what actions to take
  2. Calls tools (functions, APIs, searches)
  3. Observes the results
  4. Decides what to do next
  5. Repeats until the goal is achieved

The key innovation is the tool use loop: the LLM can call external functions and use their results to reason further.


Step 3: Create Your First Tool

Tools are functions the agent can call. Let's start with a simple calculator tool:

from langchain.tools import tool

@tool
def calculate(expression: str) -> str:
    """Evaluate a mathematical expression. Input should be a valid Python math expression."""
    try:
        result = eval(expression, {"__builtins__": {}}, {})
        return str(result)
    except Exception as e:
        return f"Error: {str(e)}"

The docstring is critical — the LLM reads it to understand when and how to use the tool.


Step 4: Add a Web Search Tool

Real agents need to access information. Let's add a search capability:

pip install duckduckgo-search
from langchain_community.tools import DuckDuckGoSearchRun

search = DuckDuckGoSearchRun()

DuckDuckGo search requires no API key, making it ideal for tutorials.


Step 5: Add a Text File Reader

Agents that can read documents are far more useful:

@tool
def read_file(filename: str) -> str:
    """Read the contents of a text file. Input should be the filename."""
    try:
        with open(filename, 'r') as f:
            return f.read()
    except FileNotFoundError:
        return f"File not found: {filename}"
    except Exception as e:
        return f"Error reading file: {str(e)}"

Step 6: Create the Agent

Now we assemble the agent:

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

load_dotenv()

# Initialize the LLM
llm = ChatOpenAI(model="gpt-4o", temperature=0)

# Define the tools
tools = [calculate, search, read_file]

# Create the prompt
prompt = ChatPromptTemplate.from_messages([
    ("system", """You are a helpful AI assistant with access to tools.

Use tools to find accurate information and complete tasks.
Always verify information before reporting it.
Think step by step about what you need to do to complete each task."""),
    MessagesPlaceholder("chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),
])

# Create the agent
agent = create_openai_tools_agent(llm, tools, prompt)

# Create the executor
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,  # Shows the agent's reasoning
    max_iterations=10,
    handle_parsing_errors=True,
)

Step 7: Run Your Agent

# Simple calculation task
result = agent_executor.invoke({
    "input": "What is 15% of 2,847?"
})
print(result["output"])

# Research task
result = agent_executor.invoke({
    "input": "Search for the current population of Tokyo and calculate how many people that is per square kilometer (Tokyo area is 2,194 sq km)."
})
print(result["output"])

Run it:

python agent.py

You'll see the agent's reasoning process printed in the terminal (because verbose=True). This is invaluable for understanding and debugging agent behavior.


Step 8: Add Memory

The agent currently has no memory between conversations. Let's fix that:

from langchain.memory import ConversationBufferMemory

# Add memory
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True
)

# Update the executor
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    verbose=True,
    memory=memory,
    max_iterations=10,
    handle_parsing_errors=True,
)

# Now the agent remembers context
result1 = agent_executor.invoke({"input": "My name is Alex."})
result2 = agent_executor.invoke({"input": "What is my name?"})
print(result2["output"])  # Should say "Alex"

Step 9: Add Error Handling and Logging

Production agents need robust error handling:

import logging
from langchain.callbacks import FileCallbackHandler

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)

# Log all agent activity to a file
file_handler = FileCallbackHandler("agent.log")

def run_agent(task: str) -> str:
    """Run agent with error handling."""
    try:
        result = agent_executor.invoke(
            {"input": task},
            config={"callbacks": [file_handler]}
        )
        return result["output"]
    except Exception as e:
        logger.error(f"Agent failed on task: {task}. Error: {str(e)}")
        return f"I encountered an error completing this task: {str(e)}"

Step 10: Build a Simple Interface

A command-line interface makes your agent interactive:

def main():
    print("AI Agent ready. Type 'quit' to exit.\n")

    while True:
        user_input = input("You: ").strip()

        if not user_input:
            continue
        if user_input.lower() in ['quit', 'exit', 'bye']:
            print("Goodbye!")
            break

        print("Agent: ", end="", flush=True)
        response = run_agent(user_input)
        print(response)
        print()

if __name__ == "__main__":
    main()

What to Build Next

Now that you have a working agent, here are logical extensions:

Add a database tool: Connect to SQLite or PostgreSQL and let your agent query real data.

Add email integration: Let the agent read and compose emails (using Gmail API or Exchange).

Add a code execution tool: Allow the agent to write and run Python code — this dramatically expands what it can do.

Add a document loader: Connect to Google Drive, SharePoint, or a local folder and let the agent read PDFs, spreadsheets, and Word documents.

Deploy as an API: Wrap the agent in a FastAPI server so other applications can call it.


Common Issues and Solutions

"Context length exceeded": Your agent is trying to process too much text at once. Use chunking strategies or summarize intermediate results.

"Max iterations reached": The agent is going in circles. Improve your tool descriptions and system prompt to give better guidance.

"Tool execution error": Add better error messages to your tools. The agent needs to understand what went wrong to recover.

Slow responses: Use GPT-4o-mini or Claude Haiku for intermediate steps and GPT-4o only for the final response.


The Complete Agent File

import os
from dotenv import load_dotenv
from langchain_openai import ChatOpenAI
from langchain.agents import AgentExecutor, create_openai_tools_agent
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferMemory
from langchain.tools import tool
from langchain_community.tools import DuckDuckGoSearchRun
import logging

load_dotenv()
logging.basicConfig(level=logging.INFO)

@tool
def calculate(expression: str) -> str:
    """Evaluate a mathematical expression."""
    try:
        return str(eval(expression, {"__builtins__": {}}, {}))
    except Exception as e:
        return f"Error: {str(e)}"

@tool
def read_file(filename: str) -> str:
    """Read a text file."""
    try:
        with open(filename, 'r') as f:
            return f.read()
    except Exception as e:
        return f"Error: {str(e)}"

llm = ChatOpenAI(model="gpt-4o", temperature=0)
search = DuckDuckGoSearchRun()
tools = [calculate, search, read_file]

prompt = ChatPromptTemplate.from_messages([
    ("system", "You are a helpful AI assistant. Use tools to complete tasks accurately."),
    MessagesPlaceholder("chat_history", optional=True),
    ("human", "{input}"),
    MessagesPlaceholder("agent_scratchpad"),
])

agent = create_openai_tools_agent(llm, tools, prompt)
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, memory=memory)

def main():
    while True:
        user_input = input("You: ").strip()
        if user_input.lower() in ['quit', 'exit']:
            break
        result = agent_executor.invoke({"input": user_input})
        print(f"Agent: {result['output']}\n")

if __name__ == "__main__":
    main()

Conclusion

You've built a functional AI agent that can search the web, read files, perform calculations, and maintain conversational memory. This foundation can be extended to handle virtually any workflow that can be decomposed into tool-use steps.

The key insight from this tutorial: agents are not magic. They're a loop: the LLM decides what to do, the tools do it, the results inform the next decision. Understanding that loop is the foundation for building sophisticated agentic systems.


Related Reading

Ready to deploy autonomous AI agents?

Our engineers are available to discuss your specific requirements.

Book a Consultation