Build an AI Agent from Scratch: Understanding the Core Principles with Pure Python

Build a minimal AI Agent in pure Python by mastering the LLM + Tools + Loop formula from the ground up.
Forget memorizing framework APIs that go stale overnight — build a minimal Agent engine yourself to truly understand the underlying mechanics. This article distills the essence of intelligent agents into one formula: Agent = LLM + Tools + Loop (decision-making, action, and task progression). Tools are JSON Schema menus; safety boundaries must block direct shell/SQL execution and return errors to the model for self-correction. The core loop runs four steps — query, check, execute, log — with a Max Steps brake to prevent runaway API costs. The recommended path: handcraft first to master the principles, then adopt frameworks like LangChain for production speed.
Forget the Frameworks — Rediscover What an Agent Really Is
Open up any mainstream AI framework's architecture diagram (LangChain, for example), and you'll find a dense web of arrows and abstract concepts that looks more like a plate of spaghetti than an engineering blueprint. But the message here is direct: frameworks will change, APIs update every other day, and only the underlying mechanisms stay constant. If you want to truly understand agents, the fastest path is to ignore the complexity and build a minimal engine yourself.
So where's the real dividing line between a regular LLM and an AI Agent? A standard large language model is like a super-brain floating in a glass jar: you ask, it answers, task done — a one-shot, single-pass interaction. A true Agent, on the other hand, has multi-step reasoning and judgment. It actively considers whether it needs to call a tool, goes and fetches information, performs calculations, synthesizes the results, and keeps going until it arrives at a final answer.
In one sentence: An Agent is a brain that has grown hands and feet — and knows how to reflect on itself.
The Core Formula: Agent = LLM + Tools + Loop
The entire agent ecosystem boils down to one fundamental formula:
Agent = LLM + Tools + Loop
- LLM: handles thinking and decision-making;
- Tools: handle interaction with the outside world and taking action;
- Loop: keeps the task moving forward until it's complete.

Memoizing this formula is worth far more than cramming framework API syntax that gets deprecated every few months. The development environment can be equally minimal: no heavy frameworks needed — just install a base model library (like Anthropic's official SDK), set your API key as an environment variable, and you're ready. A clean Python environment with a basic API connection is all you need to start.
Loop is an indispensable part of the formula because most real-world tasks can't be completed in a single model call. Each LLM call produces only one "next action" — which might be a tool call or a direct answer. The loop chains these single-step decisions into a coherent reasoning sequence. This design draws from the ReAct (Reasoning + Acting) paradigm: the model alternates between generating a "Thought" and taking an "Action" each round, with the action's result appended to the context as an "Observation" for the next iteration — until the model determines the task is complete and produces a final answer. This "Think → Act → Observe → Think again" iterative loop is what gives Agents the ability to handle multi-step, multi-dependency tasks, and is the most fundamental architectural difference from single-turn LLMs.
Configuring the Toolbox and Safety Boundaries
Many people assume "giving an AI tools" just means passing it a bunch of Python functions. That's not quite right. From the AI's perspective, a tool is essentially a highly detailed menu — typically described using JSON Schema. It's like ordering at a restaurant: you can't just say "get me something to eat." You need to spell out the dish name, ingredients, and preparation method. The more precisely the menu is written, the more accurately the model can "order" the right tool.

A classic example is giving a model a calculator. Everyone knows LLMs can hallucinate on arithmetic — they might confidently tell you "13 times 42 equals 999." So we define a tool called Calculator with a description that reads "only for evaluating simple mathematical expressions." The AI relies entirely on reading that natural language description to decide whether to reach for the calculator.
A Safety Line That Must Never Be Crossed
Here's a serious warning: never let a large language model directly execute raw shell commands or SQL statements on your system. LLMs are highly non-deterministic — left to improvise, a model could wipe out your entire production database.
This calls for a three-layer safety architecture: the model produces an "intent" on one side, the local program does the "execution" on the other, and in between sits a locked boundary interceptor. This lock completely isolates the model's chaotic, unpredictable intentions from your fragile, precious local environment — the model only proposes "what I want to do"; whether it actually happens is determined entirely by hardcoded local logic.
In practice, a whitelist using regex allows only expressions containing numbers and basic operators to pass through, defending against malicious code injection. There's another key mechanism: when an exception is caught, the error message is sent back to the model verbatim rather than being swallowed. When the AI sees the error, it self-reflects, adjusts the format, and tries again.
JSON Schema is a standard specification for describing JSON data structures, and in Agent tool definitions it acts as a "user manual." Developers use it to declare a tool's name, parameter types (string, number, boolean, etc.), which parameters are required, and natural-language descriptions of each parameter. When an LLM decides which tool to call, it reads this Schema — not the function's source code. The model never sees the Python implementation details; it can only infer the tool's purpose and calling convention from the text descriptions. This means the more precise your parameter descriptions, the fewer errors the model makes when filling them in. Vague or missing descriptions cause the model to guess at parameter formats, leading to parse failures or logic errors. All major model providers (OpenAI, Anthropic, Google) use this mechanism, making JSON Schema fluency a fundamental skill for engineering production-grade Agent tools.
Returning error messages to the model instead of silently discarding them is a frequently overlooked but critical detail in Agent engineering, rooted in the self-correction mechanism of prompt chaining. When the model receives context containing error information, it can — much like a human developer reading a compiler error — identify formatting issues or parameter problems and generate a corrected call in the next round. This "Execute → Error → Reflect → Retry" self-healing loop dramatically reduces the likelihood of a single malformed output derailing an entire task. Conversely, if the program swallows the exception and returns an empty result, the model assumes the tool call succeeded but returned nothing — leading to faulty inferences that are extremely difficult to trace in logs. Structurally injecting exception information into the message history is therefore standard practice for building robust Agents.
Ignition: The Core Loop Engine in ~10 Lines of Code
Once all the components are in place, what truly brings an Agent to life is the Core Loop. It consists of four steps that form a continuous reasoning cycle:
- Query the model — send the current context to the LLM;
- Check for action — determine whether the model wants to call a tool;
- Execute the tool — run the tool safely in the local environment;
- Log history — append the result back to the message list, building up memory.

One critical detail to never overlook: Max Steps — your emergency brake. Without a hard upper limit on the loop count, if the AI gets stuck in an infinite loop, you might wake up to an API bill large enough to buy a yacht for the CEO of your model provider.
About ten lines of core Python code is all it takes to drive the entire Agent: a loop as the engine skeleton, a clean exit condition, local execution whenever the model requests a tool, and results snowballing back into the context. The logic is crystal clear with no black-box abstraction.
In a live test, a user throws a complex arithmetic request at the Agent. Internally, it generates a thought, decides to call the calculator, the signal reaches the Python layer for execution, a precise result is returned, and the Agent wraps it into fluent natural language — driven entirely by bare-metal logic, with data flowing smoothly through a pipeline you built yourself.
Handcrafted vs. Heavy Frameworks: Understand the Principles Before Optimizing for Speed
Finally, let's compare the "raw API + loop" approach against industrial-grade heavy frameworks like LangChain and CrewAI.

Heavy frameworks have vast ecosystems and are great for rapidly prototyping business features in a team setting — but their thick abstraction layers and fast-moving versioning make them a nightmare for learning the underlying mechanics. Building with raw code means you handle context management and error feedback yourself, but everything is fully transparent and trivially debuggable.
The recommended growth path is clear: handcraft it first to truly understand the principles, then use frameworks to boost production efficiency. The Agent core has four dimensions — the model handles decisions, tools handle actions, the loop drives progress, and boundaries enforce safety. Once you've internalized this engine, the next steps open up naturally: give the AI tools to read local files, plug in search tools to give it access to the entire web, or even implement a file-based memory system for persistent long-term conversations.
Anything is possible. Strike while the iron is hot — open your code editor and write your first tool for your very own Agent.
Related articles

Random Pruning of the DPO Computation Graph: 1.4x Training Speedup and Lower Peak Memory
DPO training too slow and memory-hungry? This paper proposes a random pruning framework for the DPO computation graph, sampling independently across position, sequence, and branch dimensions; branch pruning achieves a 1.426x step-time speedup and cuts training time by about 30%, with in-depth attribution of memory and time gains.

Why Does JEPA Underperform MLM in Longitudinal EHR Representation Learning?
New study compares JEPA and MLM for longitudinal EHR representation learning on MIMIC-IV: three JEPA variants train stably without collapse but underperform MLM; target size, not heterogeneity, drives prediction error.

Transfer Learning + Visible Light Communication: A New Approach to Energy-Efficient Indoor Positioning
A study proposes a transfer learning-based visible light communication indoor positioning method, achieving 47% accuracy improvement on the BOSCH factory dataset, 67.5% model efficiency under high noise, and introducing the ME composite metric for energy-efficient positioning in energy-constrained industrial IoT scenarios.