An "AI agent" sounds complicated, but at its core it is one simple idea: an LLM in a loop that can call tools and act on the results. Once you build the loop yourself, the magic disappears and you can reason about any agent framework. Here is the whole thing.
Step 1: Define Tools the Model Can Call
A tool is just a function plus a description the model can read. The description is what the model uses to decide when to call it, so make it clear:
tools = [{ "name": "get_weather", "description": "Get the current weather for a city.", "parameters": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } }]
Step 2: Let the Model Choose a Tool
You send the user's request plus the tool definitions. Instead of answering, the model may respond with a request to call a tool and the arguments to use. Your job is to notice that and run the function:
response = model.generate(messages=messages, tools=tools) if response.tool_call: result = run_tool(response.tool_call.name, response.tool_call.args)
Step 3: Feed the Result Back and Loop
This is the key step that makes it an agent: you append the tool's result to the conversation and call the model again. Now it can use the result to answer โ or call another tool:
messages.append({"role": "tool", "content": result}) response = model.generate(messages=messages, tools=tools) # Repeat until the model returns a final answer instead of a tool call
Step 4: Stop Safely
The loop ends when the model returns a normal answer instead of a tool call. Always add a maximum iteration count so a confused agent can't loop forever:
for _ in range(10): # hard ceiling ... if not response.tool_call: break
Summary
An agent is an LLM in a loop: define tools with clear descriptions, let the model choose one, run it, feed the result back, and repeat until the model gives a final answer โ with a hard iteration cap so it can't spin forever. Every agent framework you'll meet is an elaboration of exactly this loop. Build it once by hand and the rest is just convenience on top.