Loading lesson…
An agent is a loop: model decides, tool runs, model reads result, decides again. You'll build one in 100 lines without a framework.
Every 'agent' library — LangChain, CrewAI, LangGraph — is built on the same 20-line core. Writing it by hand once makes every library make sense forever. We'll build an agent that can search the web, read files, and do math.
# pyproject.toml: anthropic, httpx import json import httpx from anthropic import Anthropic client = Anthropic() MODEL = "claude-opus-4-7" # --- Tools the agent can use --- def tool_calculate(expression: str) -> str: # Pretend-safe eval for a DEMO — never use eval on untrusted input in production. allowed = set("0123456789+-*/(). ") if not set(expression).issubset(allowed): return "Error: expression has disallowed characters" try: return str(eval(expression)) except Exception as e: return f"Error: {e}" def tool_fetch(url: str) -> str: try: r = httpx.get(url, timeout=10, headers={"User-Agent": "tendril-agent/1.0"}) r.raise_for_status() return r.text[:4000] except Exception as e: return f"Error fetching: {e}" TOOLS = { "calculate": tool_calculate, "fetch_url": tool_fetch, }Two tools: a sandboxed calculator and a URL fetcher. Real tools would be broader.TOOL_SPECS = [ { "name": "calculate", "description": "Evaluate a basic arithmetic expression. Only digits, +,-,*,/,()", "input_schema": { "type": "object", "properties": {"expression": {"type": "string"}}, "required": ["expression"], }, }, { "name": "fetch_url", "description": "Fetch the first 4KB of text from a URL.", "input_schema": { "type": "object", "properties": {"url": {"type": "string"}}, "required": ["url"], }, }, ]Tool specs — what the model sees when deciding which tool to call.def run_agent(task: str, max_steps: int = 8) -> str: messages = [{"role": "user", "content": task}] for step in range(max_steps): response = client.messages.create( model=MODEL, max_tokens=1500, tools=TOOL_SPECS, messages=messages, ) # Save assistant turn messages.append({"role": "assistant", "content": response.content}) if response.stop_reason == "end_turn": # Agent is done — return final text for block in response.content: if block.type == "text": return block.text return "(no text)" # Otherwise gather tool calls and run them tool_results = [] for block in response.content: if block.type == "tool_use": func = TOOLS.get(block.name) result = func(**block.input) if func else f"No such tool: {block.name}" tool_results.append({ "type": "tool_result", "tool_use_id": block.id, "content": str(result), }) messages.append({"role": "user", "content": tool_results}) return "Agent exceeded max steps without finishing." if __name__ == "__main__": print(run_agent("What is 25 * 34, and what's on https://example.com — give one sentence about each."))The loop: call model → if tools, run them and feed back → if done, return. That's the entire agent.# Save the messages list to disk so the agent can resume a conversation import json from pathlib import Path def save_state(messages, path=Path("agent_state.json")): # Serialize tool_use/tool_result blocks into dicts serialized = [] for m in messages: if isinstance(m["content"], str): serialized.append(m) else: serialized.append({ "role": m["role"], "content": [b.model_dump() if hasattr(b, "model_dump") else b for b in m["content"]] }) path.write_text(json.dumps(serialized, indent=2))Turning agent state into JSON lets you pause and resume — the foundation of every production agent.| Chatbot | Agent |
|---|---|
| One response per turn | Many steps per turn |
| No side effects | Calls tools, touches the world |
| Linear | Branches until goal met |
| Predictable | Needs guardrails |
Big idea: an agent is shockingly simple code. Frameworks add conveniences (retries, logging, streaming), but the core is a for-loop with two cases: 'call a tool' or 'stop.'
8 questions · take it digitally for instant feedback at tendril.neural-forge.io/learn/quiz/end-prog-python-agent-creators
What is the main idea of "Build It: A Minimal AI Agent Loop From Scratch"?
Which concept is most central to "Build It: A Minimal AI Agent Loop From Scratch"?
Which use of AI fits this topic best?
What should a careful learner remember about "Safety guardrails matter"?
You want to use AI after this lesson. What is the safest next step?
How should AI output about agent loop be treated?
Name one way to verify an AI answer about agent loop.
Which action would help you apply "Build It: A Minimal AI Agent Loop From Scratch" responsibly?