Python & AI
Your First AI Agent in Python: a Chatbot Answers, an Agent Acts
By the FUTEAC Team · 22 October 2026 · 8 min read
In short
An AI agent is a program that does not just reply — it decides which tools to use, uses them, reads the results, and keeps going until the job is done. A chatbot answers in one step; an agent works in a loop. In this article you build a small agent in Python that saves flashcards and counts words in your study notes, first with a simple loop, then the standard way: as an MCP server that any AI app can plug into.
A chatbot answers. An agent acts.
Ask a chatbot to "make a flashcard for photosynthesis and count the words in my note" and you get one block of text back. Nothing is saved. Nothing is counted. If you want the work done, you do it yourself, copying and pasting between apps.
An agent handles the same request differently. It breaks the request into steps, picks a tool for the first step, runs it, looks at what came back, and uses that to decide the next step:
- Request: *"Make a flashcard for photosynthesis and count the words in my note."*
- Agent decides: a flashcard tool is needed → runs it → "Saved flashcard #1".
- Agent decides: now the counting tool → runs it → "11 words".
- Agent answers: "Done — flashcard saved, your note has 11 words."
That loop — decide, act, observe, repeat — is the whole idea. Everything else in this article is detail: what the tools are, who does the deciding, and how to keep the agent safe.
Why everyone is suddenly talking about agents
This is not just fashion; two things happened in the last few weeks. On 29 September 2026, at its DevDay event, OpenAI expanded its Agents API — with tool use, multi-agent orchestration and support for MCP, the tool standard explained below — and put it in public beta. And the Stack Overflow Developer Survey 2026, published on 6 October with about 31,000 developers responding, found agent workflows already in regular use and coding assistants/agents used by two-thirds of developers — while also finding that trust in them is conditional: developers check what agents produce.
That combination is exactly why this is worth learning now, and learning properly: agents are becoming a normal part of the job, and the people who do well with them are the ones who understand the loop and verify the output — not the ones who paste in a magic library and hope.
The four parts of every agent
Whatever the framework, an agent has the same four parts:
- A brain — something that reads the request and chooses the next action: a real AI model, or (for learning) a small stand-in you write yourself.
- Tools — ordinary functions the agent is allowed to call: save a flashcard, count words, search a file, read the time.
- A loop — code that asks the brain what to do, runs the chosen tool, feeds the result back to the brain, and repeats until the brain says "done".
- Guardrails — limits you set: a maximum number of steps, only approved tools, and a human check before anything important (sending, deleting, spending).
You will build all four parts now. No account, no API key, nothing to pay for.
Build 1: the agent loop (pure Python, no downloads)
Save this as agent.py and run it with python agent.py. The "brain" is a small scripted planner — deliberately simple, so every step is visible. Read the comments; they mark the four parts.
"""Your first AI agent: a study-notes helper that uses tools.
The 'brain' below is a small scripted planner so this example runs free,
with no API key. The loop around it — ask the brain, run the tool it picks,
feed the result back — is exactly the loop a real AI agent runs.
"""
flashcards = [] # our tiny "database": a list of {"term": ..., "definition": ...}
# ---- Tools: plain Python functions the agent is allowed to use ----
def count_words(text: str) -> str:
return f"{len(text.split())} words"
def make_flashcard(term: str, definition: str) -> str:
flashcards.append({"term": term, "definition": definition})
return f"Saved flashcard #{len(flashcards)}: {term}"
def list_flashcards() -> str:
if not flashcards:
return "No flashcards yet."
return "\n".join(f"{i}. {c['term']} — {c['definition']}" for i, c in enumerate(flashcards, 1))
TOOLS = {"count_words": count_words, "make_flashcard": make_flashcard, "list_flashcards": list_flashcards}
# ---- The stand-in brain: reads the request, returns ONE decision ----
def brain(request: str, last_result: str | None) -> dict:
# Step 2 of the demo request: after the flashcard is saved, count the note's words.
if last_result and last_result.startswith("Saved flashcard"):
return {"tool": "count_words", "args": {"text": "Plants use sunlight water and air to make their own food"}}
# Final step: once we have the count, answer the learner.
if last_result and last_result.endswith("words"):
return {"final": f"Done! Your flashcard is saved and your note has {last_result}."}
# Step 1: the request asks for a flashcard, so make it first.
if "flashcard" in request:
return {"tool": "make_flashcard",
"args": {"term": "Photosynthesis",
"definition": "How plants use sunlight, water and air to make their own food"}}
return {"final": "I can make flashcards and count words in your notes. Try asking for a flashcard."}
# ---- The agent loop: the part that makes it an agent, not a chatbot ----
def run_agent(request: str) -> None:
print(f"You: {request}\n")
result = None
for step in range(5): # a safety limit: real agents always cap their steps
decision = brain(request, result)
if "final" in decision:
print(f"Agent: {decision['final']}")
return
name, args = decision["tool"], decision["args"]
print(f"Agent (step {step + 1}): using tool '{name}' with {args}")
result = TOOLS[name](**args)
print(f"Tool returned: {result}\n")
print("Agent: stopped — step limit reached.")
if __name__ == "__main__":
run_agent("Make a flashcard for photosynthesis and count the words in my note")
print("\n--- Your flashcards now ---")
print(list_flashcards())Expected output (this is the real output — the program was run before this article was delivered):
You: Make a flashcard for photosynthesis and count the words in my note
Agent (step 1): using tool 'make_flashcard' with {'term': 'Photosynthesis', 'definition': 'How plants use sunlight, water and air to make their own food'}
Tool returned: Saved flashcard #1: Photosynthesis
Agent (step 2): using tool 'count_words' with {'text': 'Plants use sunlight water and air to make their own food'}
Tool returned: 11 words
Agent: Done! Your flashcard is saved and your note has 11 words.
--- Your flashcards now ---
1. Photosynthesis — How plants use sunlight, water and air to make their own foodThree things to notice. First, the agent did two jobs in the right order, and the second job was chosen because of what the first one returned — that is the "observe" step doing real work. Second, the flashcard is genuinely saved: it is still there when the program lists flashcards at the end. Third, the loop cannot run forever — the for step in range(5) cap is a guardrail, and real agents need the same discipline.
Build 2: offer the same tools the standard way — a tiny MCP server
Your agent works, but its tools are trapped inside it. If tomorrow you switch to a different AI app, you would rewrite them. The industry's answer — the one OpenAI's Agents API, LangChain and others all adopted this autumn — is MCP (Model Context Protocol): a standard way to offer tools from a small server program, so *any* AI client can discover and call them.
Install the official Python SDK and save this as study_server.py:
pip install mcp"""A tiny MCP server: the same study tools, offered the standard way.
Run it with: python study_server.py
Any MCP client (an AI app, the MCP Inspector, your own agent) can then
discover and call these tools — you never re-write them per app.
"""
from mcp.server.mcpserver import MCPServer
server = MCPServer("study-tools")
flashcards = []
@server.tool()
def count_words(text: str) -> str:
"""Count the words in a study note."""
return f"{len(text.split())} words"
@server.tool()
def make_flashcard(term: str, definition: str) -> str:
"""Save a flashcard for a term."""
flashcards.append({"term": term, "definition": definition})
return f"Saved flashcard #{len(flashcards)}: {term}"
@server.tool()
def list_flashcards() -> str:
"""List all saved flashcards."""
if not flashcards:
return "No flashcards yet."
return "\n".join(f"{i}. {c['term']} — {c['definition']}" for i, c in enumerate(flashcards, 1))
if __name__ == "__main__":
server.run() # serves the tools over stdio, the standard local transportThe functions are the same three tools. The differences are the point: each one is decorated with @server.tool(), its docstring becomes the description AI clients read when deciding when to call it, and server.run() serves them over stdio — the standard local connection, where the client starts your server as a subprocess and talks to it.
Test it like a professional: the MCP Inspector is a free testing tool that connects to your server and lists its tools, so you can click and call each one yourself before any AI touches it:
npx @modelcontextprotocol/inspector python study_server.pyThis is also how the server was verified for this article: a small test client connected, discovered all three tools, called make_flashcard ("Saved flashcard #1: Gravity"), count_words ("8 words") and list_flashcards — the same results as Build 1, now through the standard protocol. Any MCP-capable AI app can now use your study tools without you changing a line.
Swapping in a real AI brain
Your stand-in brain follows rules you wrote. A real agent replaces brain() with a call to an AI model: you send the model the request, the conversation so far, and the list of tools; the model replies either with an answer or with a tool call in a fixed format; your loop runs the tool and sends the result back. The loop, the tools and the guardrails do not change — which is why learning them first, with a brain you can read, is the right order.
In practice you would use a provider's agents SDK (OpenAI's Agents API is the one from DevDay; LangChain added native MCP support in its 1.4 release this October) and point it at your MCP server instead of hand-writing tools into the app. That step needs an API key and a small amount of credit, so it is the natural second project — after the free one above works. The FUTEAC course AI & Machine Learning: First Steps covers the model concepts (what a model is doing when it "chooses" a tool) that make that step click.
Guardrails: the part beginners skip and professionals don't
The Stack Overflow survey's finding — wide use, conditional trust — is the professional norm, and your builds already contain its seeds:
- Cap the steps. An agent that can loop forever will, one day, loop forever.
range(5)today; a budget in real systems. - Approve the tool list, not each action — until it matters. Reading and counting are safe to automate. Sending email, deleting files or spending money deserve a human confirmation step in the loop.
- Check the output. Print every tool call and result while developing (Build 1 does). An agent you cannot observe is an agent you cannot trust.
- Small, honest tools. Each tool does one thing and returns a clear result. Vague tools ("do the homework") produce vague behaviour.
Try it yourself
- Add a fourth tool to both builds:
longest_word(text)that returns the longest word in a note. Run Build 1 with a request that needs it. - Break the agent on purpose: remove the step cap, make the brain always ask for a tool, and watch what happens. Then put the cap back. Understanding the failure is the lesson.
- Connect the MCP Inspector to your server and call each tool by hand. Write down what a *bad* input does (empty text, a very long note) — handling that gracefully is your next improvement.
Common mistakes
- Calling any chatbot an agent. If it cannot act — call tools and use what comes back — it is a chatbot. The loop is the difference.
- Starting with a framework before understanding the loop. Frameworks hide exactly the part you need to learn. Build the 60-line version first; the framework will then read as a shortcut, not magic.
- Giving an agent dangerous tools on day one. Start with tools that can only read or add. Delete/send/spend tools come later, behind a confirmation step.
- No step limit. See Try It #2. Every production agent has one; yours should too, from the first version.
Follow the Python + AI/ML Beginner Roadmap
Learn Python from zero and take your first steps into AI and machine learning. Free, step by step, at your pace.
View the RoadmapFrequently asked questions
What is the difference between a chatbot and an AI agent?
A chatbot produces a reply. An agent produces a result: it works in a loop — choosing tools, running them, reading their output and deciding the next step — until the task is complete or a limit stops it. The same AI model can sit inside both; the loop and the tools are what make it an agent.
Do I need to pay for an API to learn agents?
No. Both builds in this article run free on your own computer: the first uses a stand-in brain you can read, and the MCP server needs no model at all. You only need an API key when you swap in a real AI model as the brain — a deliberate second step, not the starting point.
What is MCP, in one sentence?
MCP (Model Context Protocol) is a standard plug for AI tools: you offer your tools from a small server once, and any AI app that speaks MCP can discover and use them — which is why OpenAI, LangChain and others adopted it through 2026.
How much Python do I need before trying this?
Functions, dictionaries, lists and f-strings — the ground covered in the first half of **Python Programming Foundations**. If `TOOLS[name](**args)` looks mysterious, do the functions module first and come back; the article will wait.
Is this the same as the "AI agents" in the news?
Yes, at a learning scale. The systems announced at events like OpenAI's DevDay (29 September 2026) add bigger models, more tools, memory and multi-agent teams — but the decide → act → observe loop, the tool discipline and the guardrails are the same ones you just built. --- *Draft for Abd's review — not published. Nothing in this draft goes live until Abd approves it in chat.*