Every framework, every job posting, and about half of LinkedIn wants to tell you what an "AI agent" is. Most of the definitions are marketing. Here is the one that fits on an index card: an agent is a language model, a short list of functions it is allowed to ask for, and a while loop.
I'm going to prove that by building one in under 70 lines of Python with no framework. Then I'm going to hide one paragraph in a web page and watch the agent hand over my API key. Then we fix it, and the fixes are the interesting part, because none of them involve the model.
You need one semester of Python. If you know what a function, a dict, and a while loop are, you're fine. You don't need Docker, a cloud account, or a credit card.
Disclosure: I work at Tigera, on the Kubernetes end of this exact problem. Nothing in this post needs anything we make.
Python 3.10 or newer, and Ollama, which runs open models on your own machine. Install Ollama, then pull a model that knows how to call tools:
That is a 4.7 GB download. If your laptop has 8 GB of RAM or less, llama3.2:3b is about 2 GB and also works. Any model is fine as long as ollama show lists tools under capabilities.
Why the OpenAI package for a local model? Because Ollama speaks the same HTTP API as OpenAI. Point the client at localhost and everything else is identical. When you want a hosted model later, you change two lines and keep the rest.
Which is a very polite way of saying it has no clock. The model is a function from text to text, and it has no way to look anything up. Everything an agent can do that a chatbot can't comes from what we add next.
Read runagent slowly, because every agent framework you will ever use is this function with more features bolted on. Send the conversation to the model, along with a list of tools it may ask for. If the model replies with plain text, we're done. Return it. If the model replies with a tool call instead, look the function up by name, run it, append the result to the conversation as a message with the role tool, and go around again.
The model never runs anything. It replies with a bit of JSON that means "I would like you to call gettime with these arguments." Your Python decides whether to do it. Hold on to that thought, because it is the basis for every fix later in this article.
The TOOLS list is all the model knows about your functions. It never sees the code. The description string is how it decides when to use a tool, so it's worth writing carefully. It's like documenting a library for a coworker who reads the docs and nothing else.
A clock is cute. Swap it for a tool that reads files and a tool that fetches web pages, and you have something that can do actual research. Replace agent.py with this.
The [:4000] on the web page is there because small local models get confused by huge inputs, and because you'll be glad of it when you accidentally point this at Wikipedia.
It called readfile, got the text back, and summarized it. That's an agent. 68 lines.
A warning about speed. My laptop has no GPU worth mentioning, and each model call took between 25 seconds and two minutes. Local models on a CPU are slow. That's the price of free.
Now imagine the agent reads something you didn't write. A web page, a PDF a classmate sent you, an email. Anything that came from outside.
First, a fake secret in the project root, the kind of file every real project has:
Then a web page. Most of it is ordinary. One paragraph is styled so a person reading it in a browser will never see it.
