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.'
15 questions · take it digitally for instant feedback at tendril.neural-forge.io/learn/quiz/end-prog-python-agent-creators
What is the core idea behind "Build It: A Minimal AI Agent Loop From Scratch"?
Which term best describes a foundational idea in "Build It: A Minimal AI Agent Loop From Scratch"?
A learner studying Build It: A Minimal AI Agent Loop From Scratch would need to understand which concept?
Which of these is directly relevant to Build It: A Minimal AI Agent Loop From Scratch?
Which of the following is a key point about Build It: A Minimal AI Agent Loop From Scratch?
Which of these does NOT belong in a discussion of Build It: A Minimal AI Agent Loop From Scratch?
What is the key insight about "Safety guardrails matter" in the context of Build It: A Minimal AI Agent Loop From Scratch?
What is the recommended tip about "Always review AI output" in the context of Build It: A Minimal AI Agent Loop From Scratch?
Which statement accurately describes an aspect of Build It: A Minimal AI Agent Loop From Scratch?
What does working with Build It: A Minimal AI Agent Loop From Scratch typically involve?
Which best describes the scope of "Build It: A Minimal AI Agent Loop From Scratch"?
Which section heading best belongs in a lesson about Build It: A Minimal AI Agent Loop From Scratch?
Which section heading best belongs in a lesson about Build It: A Minimal AI Agent Loop From Scratch?
Which of the following is a concept covered in Build It: A Minimal AI Agent Loop From Scratch?
Which of the following is a concept covered in Build It: A Minimal AI Agent Loop From Scratch?