Building AI Agents from Scratch

Building a Useful Agent

Combine the loop, tools, and results into a practical project assistant — not a toy calculator.

Weather and arithmetic are demos. A useful agent has a job a teammate would type: “What does this repo expose over HTTP, and which files implement it?”

One job

Build a project scout: it may list files under the project root, read a small text file, and GET one allow-listed URL (httpbin or your own health endpoint). It may not write, delete, or fetch arbitrary URLs.

def read_file(path: str) -> dict:
    target = (ROOT / path).resolve()
    if ROOT not in target.parents and target != ROOT:
        return {"ok": False, "error": "path escapes the project"}
    if target.suffix.lower() not in {".md", ".txt", ".py", ".json"}:
        return {"ok": False, "error": "suffix not allowed"}
    text = target.read_text(encoding="utf-8", errors="replace")
    return {"ok": True, "path": path, "text": text[:4000]}

System prompt: “You scout a local project. Prefer list_dir, then read_file on the promising names. Use http_get only when the user asks about the demo endpoint. Quote paths. Do not invent files.”

What you skip

  • A shell tool. You do not need cat if read_file exists.
  • A general browser. One GET is enough for this lesson.
  • Writing files. That waits for approval.

Check it

  1. Ask it to summarize the README and name the Python entrypoints. It should call tools, then answer with paths that exist.
  2. Ask it to fetch the demo URL and relate the JSON to a file it read — two tools, one paragraph.
  3. Ask it to read /etc/passwd or ../../. The tool refuses; the model should say so.

Keep this repo. Later lessons hang memory, planning, and MCP off the same run(). Next: stop losing the last turn.