# Building an Agent in 50 Lines of Code

> A ground-up look at what an agent really is under the hood: an inference client, a set of tools, and a while loop, using Hugging Face's Tiny Agents as a concrete reference.

- **Source:** https://ainativesoftware.engineering/roadmap/day-2/building-an-agent-in-50-lines-of-code
- **Site:** AI-Native Software Engineering — https://ainativesoftware.engineering/book

- **Day:** 2 · position 4 of 7
- **Reading time:** 3 minutes
- **Day overview:** [Day 2](https://ainativesoftware.engineering/roadmap/day-2.md)

A ground-up look at what an agent really is under the hood: an inference client, a set of tools, and a while loop, using Hugging Face's Tiny Agents as a concrete reference.

## Theory

The best way to understand agents is to build one from scratch. Julien Chaumond from Hugging Face did exactly that and distilled it into a key insight:

> Once you have an MCP client, an agent is literally just a while loop on top of it.

Strip away the frameworks and you're left with three pieces:

1. **An LLM inference client**: something that can send a list of messages and receive a response
2. **A set of tools**: functions with a name, description, and JSON schema for their parameters
3. **A while loop**: the agentic loop that keeps calling the LLM, executing tool calls, and feeding results back until the task is done

### The anatomy of a tool

A tool is just a function described in a way the LLM can understand:

```json
{
  "type": "function",
  "function": {
    "name": "get_weather",
    "description": "Get current temperature for a given location.",
    "parameters": {
      "type": "object",
      "properties": {
        "location": {
          "type": "string",
          "description": "City and country e.g. Bogotá, Colombia"
        }
      }
    }
  }
}
```

You pass a list of these to the LLM alongside your messages. The LLM decides when to call one and with what arguments. You execute it, capture the result, append it to the message history as a `tool` role message, and loop.

### The while loop

The core of any agent is this pattern:

```
while true:
  response = llm.chat(messages, tools=available_tools)
  if response has no tool calls:
    break  // task is done or the agent is stuck
  for each tool_call in response:
    result = execute(tool_call)
    messages.append(tool_result(result))
```

That's it. Everything else (memory management, MCP integration, multi-agent coordination) is built on top of this loop.

### Why this matters for engineers

Knowing the raw loop helps you:

- **Debug agent failures**: when an agent loops forever or stops too early, you can trace exactly which message caused it
- **Evaluate frameworks**: any framework (LangChain, CrewAI, BMAD, etc.) is an abstraction over this loop: you can ask what it adds and whether the complexity is worth it
- **Understand tool design**: because the LLM picks tools based on their description, a well-named, well-described tool will be called correctly; a vague one won't
- **Reason about cost and latency**: every iteration of the loop is an LLM call; knowing this helps you design agents that exit cleanly rather than running indefinitely

**Theory resources**

- [Tiny Agents: an MCP-powered agent in 50 lines of code – Hugging Face](https://huggingface.co/blog/tiny-agents)

## Practice

Run this in a repository you already know, not a toy project.

Reimplement the agentic loop from scratch in your language or framework of choice. You don't need MCP support. The goal is to feel how the loop works, not to build a production tool.

**What to build:**
1. Define 2–3 simple tools (e.g., `get_current_time`, `add_numbers`, `reverse_string`). Each tool is just a real function plus a JSON schema description.
2. Write a function that calls an LLM (OpenAI-compatible API, Anthropic, Ollama, etc.) with a list of messages and a list of tool schemas.
3. Parse the response: if it contains tool calls, execute them and append the results to the message list. If it contains a plain text response, print it and stop.
4. Wrap steps 2–3 in a while loop that runs until the LLM stops calling tools.
5. Test it with a prompt that requires using at least one of your tools (e.g., "What is 17 + 38?").

**What to observe:**
- How many loop iterations did it take?
- What did the raw messages array look like at each step?
- What happened when you gave it a goal that needed no tools?
- What happened when you gave it a goal it couldn't achieve with your tools?

This exercise is deliberately low-level. The point is not to ship something; it's to internalize the loop so you can reason about any framework built on top of it.

- **Previous topic:** [From Autocomplete to Agents](https://ainativesoftware.engineering/roadmap/day-2/from-autocomplete-to-agents.md)
- **Next topic:** [Memory and State in Agent Workflows](https://ainativesoftware.engineering/roadmap/day-2/memory-and-state-in-agent-workflows.md)

---

_AI-Native Software Engineering by Alfonso Graziano (O'Reilly Media, Early Release; print edition February 2027). Every page of ainativesoftware.engineering is also served as Markdown: append `.md` to any URL. Index: https://ainativesoftware.engineering/llms.txt — whole site in one file: https://ainativesoftware.engineering/llms-full.txt._
