Twenty-nine chapters and two capstones. One while loop that becomes Claude Code.
Read the diagram → break the simulator → run the file → take the quiz.
Everyone can call a model. Almost nobody can explain why their agent works in the demo and dies on Tuesday, or why the twelve-step task costs forty times what they estimated. Closing that gap is what this course is for.
It starts with a while loop that calls a model. It ends with a context manager,
a durable event log, a sandbox, a patch engine, an approval layer, a message-passing
runtime, an eval harness and two complete agents. Each of those arrives only after
you have felt the specific pain that made it necessary.
// Chapter 4. This is the entire agent.
while (true) {
const reply = await model(messages, tools); // the model decides
messages.push(reply);
if (reply.stop) return reply.text; // it decided to stop
for (const call of reply.toolCalls) { // it decided to act
messages.push(await runTool(tools, call)); // the world answers back
}
}Twenty chapters later that loop is unrecognisable, and you will be able to point at any line of LangGraph, AutoGen or the Claude Agent SDK and say what it is for.
No framework. No API key. No vector database. 6,191 lines of dependency-free TypeScript that run on a laptop in a few seconds.
| You used a framework and it felt like magic. | Build the loop once and StateGraph and RoutedAgent stop being vocabulary. C04 is the whole idea in 120 lines. |
| Your agent works in the demo and not on Tuesday. | C13 is the failure taxonomy, retries, loop detection and budget enforcement — the four things that separate a demo from a system. |
| You have to sign off on shipping one. | C24 is prompt injection, the lethal trifecta, least privilege, egress control and an approval design that actually holds. |
| You learn by breaking things. | Twenty-nine simulators. Starve the context budget and watch the agent forget its goal. |
Prerequisites: TypeScript or JavaScript, and having called an LLM API once. No machine learning background — nothing here trains a model.
| 📊 Diagrams of the mechanism | Where tokens, latency, money and trust enter the path — not boxes and logos. |
| 🎛 Simulators you can break | Twenty-nine real implementations running in the page. Overflow the context window, corrupt a tool result, push a tool registry to 50 and watch selection collapse. |
| ✅ 174 quiz questions | Six graded per chapter, each with an explanation naming the section to reread. |
| 🟦 Code that runs | 31 self-contained TypeScript files. All 31 execute in CI on every push, offline, with a deterministic mock model. |
| 📌 Outputs that are real | Every "run it" block is generated by executing that chapter's file. CI fails if one drifts. |
| 🌏 English and 中文 | C00–C04 translated; the rest falls back to English with an honest banner. |
| Layer | Chapters | You learn |
|---|---|---|
| The Shape of the Thing | C00 | Agent vs chain vs pipeline · the agency dial · blast radius · why the p99 is the number that matters |
| The Model | C01–C04 | The model call · structured output & the repair ladder · tools & dispatch · the agent loop |
| Context & Memory | C05, C06, C07, C08, C09 | Context engineering · retrieval as a tool · multimodal observations · memory & contradiction · durable state and resume |
| Reasoning & Control | C10–C13 | Planning · verification & the reflection ladder · composition primitives · failure & recovery |
| The Environment | C14, C15, C16, C17, C18, C19 | Code execution & sandboxing · the action space (code actions vs tool calls) · files, shell and apply_patch · MCP · skills (progressive disclosure) · human approval |
| Systems & Production | C20–C24, C25, C26 | Multi-agent topology · the actor runtime · evals · tracing · security · the interactive loop (steering, interrupts) · the server |
| Capstone I | C27 | A deep-research agent — plans, searches, verifies claims against evidence spans, cites, surfaces conflicts |
| Capstone II | C28 | A coding agent — orients in a repo, patches, runs the tests, reads the failure, repairs |
Each layer answers a question the previous one created:
- An agent is a loop with a decision in it. …which tells you what an agent is. Now build the smallest one that works. So:
- The model, typed and tooled. …which leaves you with an agent that works and forgets everything. So:
- Context and memory. …which gives the agent a past. It still has no plan. So:
- Reasoning and control. …which makes it reliable inside its own head. Now let it touch the world. So:
- The environment. …which is one capable agent. Production needs more than one, and needs proof. So:
- Systems and production. …which is everything the course has to teach. Now assemble it twice.
Read them in order the first time. The dependencies are real.
git clone https://github.com/xinbetween/learn-ai-agent-from-scratch
cd learn-ai-agent-from-scratch
# --- the code (no dependencies, no key) ---
node --experimental-strip-types code/c04_agent_loop.ts # start here
npm run check:code # all 31 files
# --- the site ---
npm install # only for `npm run check` — the site itself has no deps
npm run dev # http://localhost:4321Node 22.6+ runs TypeScript directly. On 22.18+ and Node 24 you can drop the flag:
node code/c04_agent_loop.ts
npm run agent code/c04_agent_loop.ts # either versionEvery file ships a deterministic mock model, so the whole course runs offline with
no API key and no spend. C01 also includes an opt-in live-client demonstration that
reads ANTHROPIC_API_KEY or OPENAI_API_KEY; the chapter programs remain deterministic.
Node's type stripping only erases types — it does not transform — so enum,
namespace, decorators and parameter properties are unsupported. Every file avoids
them, and npm run check:code proves it by executing all 31.
Each chapter ends with a "run it" block. Those are not illustrative: they are generated by running that chapter's file and pasting what it printed.
npm run sync # re-run every code file, rewrite the output blocks
npm run sync -- --check # fail if any block has drifted (this runs in CI)npm run verify is the whole contract in one command — execute all 27 files,
rebuild the site, and assert no output block has drifted:
27/27 runnable files execute cleanly.
built 29 chapters + 9 pages + 23 runnable files × 2 locales → dist/ (zh: 5/29 chapters translated)
The build refuses to ship an incomplete chapter: fewer than four sections, not exactly six quiz questions, a quiz answer index out of range, or a translation whose shape has drifted from the chapter it mirrors are all build failures, not review comments.
code/ ALL chapter code lives here
c00..c07_*.ts one runnable file per chapter
c27_research/ Capstone I — the deep-research agent
c28_coder/ Capstone II — the coding agent
content/
chapters/c00..c28.ts chapter prose, exercises, Q&A, projects, quizzes
pages/ map, glossary, answers, Q&A, projects, setup, timeline
zh/ Chinese translations — see below
src/
build.ts the generator: writes dist/ for every locale
render.ts shell, nav, sidebar, rail, chapter and page renderers
landing.ts the home page
i18n.ts locales, URL shapes, UI strings, landing copy
curriculum.ts layers and site metadata
ui.ts code(), fig(), lab(), note(), table() — the HTML helpers
types.ts the content model everything is typed against
static/ styles.css, app.js, favicon.svg
scripts/ output sync, code check, dev server
Chapter ids are URLs, so C15–C07 were appended rather than inserted, and
content/chapters/index.ts places them in reading order. Ids therefore run out of order in the sidebar gutter, on
purpose: renumbering would have moved eleven chapters and broken every link
anyone had shared.
The site is a static generator in about 1,500 lines with no dependencies. It builds to a folder of HTML you can host anywhere.
Every push to main builds and publishes to GitHub Pages via
.github/workflows/deploy.yml; no build output is
committed. Two environment variables are the whole deployment configuration:
| Variable | What it does |
|---|---|
SITE_URL |
Absolute origin baked into the sitemap, robots.txt and canonical URLs. |
SITE_BASE_PATH |
The sub-path Pages serves from — /<repo> for project hosting, empty for a custom domain at its origin root. |
Every internal URL goes through url() or localePath() in
src/i18n.ts, so those two variables are the only things that have
to know where the site lives. To move to a custom domain: clear SITE_BASE_PATH,
point SITE_URL at the domain, and add a CNAME file to static/ so it survives
every deploy.
Translation is partial by design. A chapter that has not been translated falls
back to the English body with a banner saying so, which keeps /zh/ complete and
navigable at every point instead of shipping a half-built second site.
To add a language:
-
Add its code to
LOCALESinsrc/i18n.tsand fill inLOCALE_META. -
Add a dictionary beside
enandzhin the same file. That covers the site chrome, the landing copy and the curriculum layer names:Where What it holds CATALOGUENav, footer, rail, quiz UI, section labels, the fallback banner LANDINGHome-page prose — hero, cards, curriculum intro, CTAs LAYER_TEXTLayer names, blurbs and the bridge sentence between layers -
Translate chapters into
content/<code>/chapters/and register them incontent/<code>/index.ts. A translated chapter spreads the English one and overrides the text, so it inherits the diagrams and the runnable-file references rather than duplicating them:import en, { DIAL_SVG } from "../../chapters/c00.ts"; const chapter: Chapter = { ...en, title: "智能体到底是什么", sections: [ /* … */ ] };
-
Run
npm run check, thennpm run build. The build asserts every translation keeps the original's shape — same section, exercise, Q&A and quiz counts, and identical quiz answer indices.
Shape is not freshness, though, and the second one is what actually rots. Editing
an English paragraph leaves its translation untouched and every check passing, so
content/zh/translation-lock.json records a hash of the English behind each
translated field:
npm run lock # after re-translating, record what you translated from
npm run check:i18n # fail if any English source moved (runs inside `verify`)It distinguishes the two kinds of section. A spread section ({ ...explore }) is
byte-identical to the English, so edits flow through and nothing is locked. A
section with its own prose is locked, and an edit to the English side fails the
check naming the exact field:
Chinese translations are out of date with the English source.
The English changed; the translation did not:
- c00 · section.core-idea
Simulator sections are deliberately shared with the English chapter. Their labels
live inside the SVG and the client script, and splitting them would decouple the
simulator from the code/ file it mirrors.
Conventions. Code identifiers (stopReason, tool_use, apply_patch), model
and framework names, and paper titles stay in English — they are what the reader
will type or search for. Everything else is translated.
Issues and PRs welcome. Particularly useful:
- Corrections. If a claim is wrong, open an issue with the evidence. This is the most valuable contribution there is.
- Translations. See above — the build will tell you exactly what is missing.
- Quiz questions. Six per chapter; more good ones are always welcome.
- A chapter this course is missing. Voice agents, computer use, RL-trained tool use and cost-aware model routing are all absent and all interesting.
Built on the work of the people who actually solved these problems. Particular debts
to Anthropic's Building Effective Agents, OpenAI Codex's apply_patch, Microsoft's
autogen-core runtime, the Model Context Protocol specification, and Simon Willison's
writing on prompt injection and the lethal trifecta. Chapter references are on the
references page.
This is an educational reimplementation and is not affiliated with any of them. Production frameworks are the real thing; this teaches you how to read them.
If this helped, a ⭐ makes it findable for the next person.