~/TechPurAI
~/tutorials/building-ai-agents/build-your-first-ai-agent-with-python
intermediate·part 6 of 22·4 min read

Build Your First AI Agent with Python

Updated Aug 31, 2026AI

Parts 1 through 5 built every conceptual and technical piece. This part assembles them into one, real, complete, runnable agent — checking Bright Leaf Coffee's actual inventory and creating a real reorder request when genuinely needed.

Real, practical setup

bash
pip install anthropic python-dotenv

The real, actual tool implementations

python
# tools.py
INVENTORY = {"eth-light-roast": 14, "gift-sub-bag": 82}
SUPPLIERS = {"addis-imports": {"lead_time_days": 12}}

def check_inventory(product_id: str) -> dict:
    stock = INVENTORY.get(product_id)
    if stock is None:
        return {"error": f"Unknown product: {product_id}"}
    return {"product_id": product_id, "stock": stock}

def get_supplier_lead_time(supplier_id: str) -> dict:
    supplier = SUPPLIERS.get(supplier_id)
    if supplier is None:
        return {"error": f"Unknown supplier: {supplier_id}"}
    return {"supplier_id": supplier_id, "lead_time_days": supplier["lead_time_days"]}

def create_reorder_request(product_id: str, quantity: int) -> dict:
    # a real, actual reorder request — in production this would
    # write to a real database or call a real supplier API
    print(f"REORDER CREATED: {quantity} units of {product_id}")
    return {"status": "created", "product_id": product_id, "quantity": quantity}

These real functions use an in-memory dictionary standing in for a real database — genuinely simplified for this part's teaching purpose, but structurally identical to what a real production version would do, just swapping these for actual database queries.

The real, complete agent, assembled from part 5

python
# agent.py
from anthropic import Anthropic
from tools import check_inventory, get_supplier_lead_time, create_reorder_request

client = Anthropic()

tools = [ ... ]  # part 5's real, complete tool schemas
tool_functions = {
    "check_inventory": check_inventory,
    "get_supplier_lead_time": get_supplier_lead_time,
    "create_reorder_request": create_reorder_request,
}

SYSTEM_PROMPT = """You manage inventory for Bright Leaf Coffee.
When asked to check a product, use check_inventory. If stock is
below 20 units, check the supplier's lead time, then create a
reorder request for enough units to reach 100 in stock. Explain
your reasoning at each step."""

def run_inventory_agent(request: str) -> str:
    messages = [{"role": "user", "content": request}]
    for _ in range(5):
        response = client.messages.create(
            model="claude-sonnet-5", max_tokens=1024,
            system=SYSTEM_PROMPT, tools=tools, messages=messages,
        )
        messages.append({"role": "assistant", "content": response.content})
        if response.stop_reason != "tool_use":
            return response.content[0].text
        tool_call = next(b for b in response.content if b.type == "tool_use")
        result = tool_functions[tool_call.name](**tool_call.input)
        messages.append({
            "role": "user",
            "content": [{"type": "tool_result", "tool_use_id": tool_call.id, "content": str(result)}],
        })
    return "This task needs human review — exceeded expected steps."

if __name__ == "__main__":
    print(run_inventory_agent("Check the Ethiopian Light Roast and reorder if needed, using supplier addis-imports."))

Running it, and the real, actual execution trace

text
Iteration 1: model calls check_inventory("eth-light-roast")
  → real result: {"stock": 14}
Iteration 2: model reasons "14 is below 20" → calls
  get_supplier_lead_time("addis-imports")
  → real result: {"lead_time_days": 12}
Iteration 3: model calls
  create_reorder_request("eth-light-roast", 86)
  → real result: {"status": "created", ...}
  → prints "REORDER CREATED: 86 units of eth-light-roast"
Iteration 4: model has enough real information → generates a final,
  real summary
Why it matters

Notice the model correctly calculated 86 as the reorder quantity (100 target minus 14 current stock) — genuine, real reasoning over the actual retrieved numbers, not a hardcoded calculation anywhere in the code. This is precisely the practical value part 1 described conceptually, now demonstrably working.

What this real, working agent is still honestly missing

text
No real, human confirmation before create_reorder_request actually
  executes — a genuinely consequential real action running fully
  automated (part 18 of the LLM & Advanced AI series flagged exactly
  this concern for a similar real case)
No real error handling if a tool call itself fails
No real logging of what the agent actually did and why

This part's real, complete, working code is deliberately a genuine first version — parts 18 through 20 of this series add exactly these production safeguards directly on top of this actual, working foundation, the same incremental, honest approach this site's other AI series have used throughout.

FAQ

Why does for _ in range(5) use 5 specifically? It's a reasonable ceiling for this task's expected 3-4 iterations, with a little headroom — not a value derived from a formula. A task expected to need more real steps would use a higher limit; the actual number should reflect what a successful run of that specific task normally takes, checked against Mistake 7 of the common mistakes page if it's ever left unset entirely.

What happens if the model tries to call two tools in the same response? This code's next(b for b in response.content if b.type == "tool_use") only picks up the first one — a real limitation worth knowing, not a hidden feature. Handling multiple tool calls per turn means looping over every tool_use block in response.content and returning a tool_result for each, which later, more complete examples in this series do.

Why does create_reorder_request just print instead of calling a real supplier API? Because this part's goal is the agent loop itself, not a working supplier integration — the comment in the code says as much. Swapping the print for an actual API call doesn't change anything about how the agent reasons; it's a drop-in replacement once a real supplier's API is available.

Next: AI agent memory explained — how an agent tracks real, working state across a task, and what happens once that task ends.

VK

Vijay Kumar

Founder of TechPurAI — writing hands-on tutorials and honest tool breakdowns.

LinkedIn ↗
← previous5. AI Agent Tools and Function Calling Explainednext →7. AI Agent Memory Explained