Stop Importing Bloated Frameworks: Build a Python AI Agent from Scratch
You want to build an AI agent.
So you head to the docs of a popular orchestration framework, copy the boilerplate, import 20 modules, and spin up an agent. It works—until it doesn't.
Suddenly, you're looking at a 50-line stack trace originating from a library wrapper. You don't know where the query failed, what the exact prompt was, or why the tool call failed to parse.
Here is the truth: You don't need AutoGen, LangChain, or CrewAI to build a working AI agent.
You just need vanilla Python and a basic understanding of the three core pillars of agentic design.
The Three Pillars of an AI Agent
Any basic agent can be broken down into three simple components:
role
and content
) passed to and from the LLM.while
loop that calls the LLM, checks if it wants to use a tool, runs the tool if requested, appends the result to the State, and repeats until the LLM returns a final answer.Let’s build one.
This example uses the official openai
SDK, but the same logic applies to Anthropic, Gemini, or local models running via Ollama.
python
import os
import json
from openai import OpenAI
client = OpenAI(api_key=os.environ.get("OPENAI_API_KEY"))
def get_weather(location):
if "tokyo" in location.lower():
return "Tokyo is sunny and 25°C."
return "Cool and rainy, 15°C."
tools_map = {
"get_weather": get_weather
}
tool_definition = {
"type": "function",
"function": {
"name": "get_weather",
"description": "Get the current weather for a location",
"parameters": {
"type": "object",
"properties": {
"location": {"type": "string"}
},
"required": ["location"]
}
}
}
def run_agent(user_prompt):
messages = [
{"role": "system", "content": "You are a helpful assistant. Call tools when necessary."},
{"role": "user", "content": user_prompt}
]
for _ in range(5):
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=messages,
tools=[tool_definition]
)
message = response.choices[0].message
messages.append(message)
if message.tool_calls:
for tool_call in message.tool_calls:
name = tool_call.function.name
args = json.loads(tool_call.function.arguments)
print(f"[*] Calling tool: {name} with args: {args}")
tool_output = tools_map[name](**args)
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": name,
"content": tool_output
})
else:
return message.content
if __name__ == "__main__":
result = run_agent("What is the weather like in Tokyo right now?")
print(f"\n[Agent Response]: {result}")