EVERYTHING AIAI engineering, made visual
0/12 complete
LESSON 01 · SETUP & TOOLING × AI · BUILD

Four layers.
One direction: up.

Python, TypeScript, Rust and Julia on one machine, without version roulette. uv installs the runtime, fnm pins Node, rustup hands you cargo — and a route-aware preflight says exactly what your next lesson needs before lesson one becomes an afternoon of import errors.

45 MIN · 6 CHAPTERS + CHECKPREREQ · NONE
FIG. 01 / A LIVE STACK · FOUR LAYERS, BOTTOM-UP
layer 1/4 · system foundation · xcode-select --install 1 system 2 packages 3 runtimes 4 libraries
LESSON 01TYPE · BUILD~45 MINPREREQ · NONEORIGINAL LESSON ↗
THE 60-SECOND VERSIONSee the problem ↓
01 / ONE DIRECTION: UP

Four layers, and they only stack bottom-up.

System foundation → package managers → language runtimes → AI/ML libraries (artificial intelligence and machine learning). Each layer gains its commands from the layer below, so skipping one turns a five-minute install into an afternoon of `command not found` and `ModuleNotFoundError`. The source's rule: install bottom-up, debug top-down.

SYSTEM → PACKAGES → RUNTIMES → LIBRARIES
02 / ONE INSTALLER PER LANGUAGE

uv, fnm, rustup — an owner for every runtime.

uv installs Python versions, creates the virtual environment and installs packages; fnm pins Node per project (Node 20+, install 22); rustup hands you Rust's `cargo`. Julia is optional, via juliaup. The source's claim for uv over pip: 10–100× faster, because dependency resolution is walked once instead of repeatedly.

uv → python · fnm → node · rustup → cargo
03 / VERIFY BEFORE YOU LEARN

The preflight answers with one exact command.

`verify.py --route <name>` checks only the probes your chosen route needs now, prints PASS or FAIL with the detected path and one corrective command, and finishes with the first runnable lesson. Everything else is reported as LATER — a missing later tool never blocks lesson one.

7 routes · 2–4 required probes each · LATER ≠ FAIL
MENTAL MODEL IN ONE SENTENCE

An AI environment is four layers built bottom-up — system foundation, package managers, language runtimes, AI/ML libraries — so every “it doesn’t work” becomes one question: which layer am I standing on?

By the end you will be able to explain why the install order is bottom-up; install and prove uv, fnm, rustup and (optionally) Julia; activate a virtual environment and confirm it with [1,2,3] · [1,2,3] = 14; verify a graphics processing unit (GPU) with torch.cuda.is_available() versus torch.backends.mps.is_available() (MPS = Metal Performance Shaders) and say why Apple Silicon answers False/True; diagnose the Rosetta trap that makes arch print i386 next to an /opt/homebrew Homebrew; and read a route preflight — PASS, FAIL with its Fix line, LATER, and the Next: command.

WHY SETUP IS A SKILL

A broken environment taxes
every lesson that follows.

The source opens with a warning, not a warm-up: this course runs 500+ lessons in Python, TypeScript, Rust and Julia, and if the environment is broken, every one of those lessons becomes a fight with tooling instead of a lesson about AI.

Here is the source’s opening, verbatim: “You’re about to learn AI engineering across 500+ lessons using Python, TypeScript, Rust, and Julia. If your environment is broken, every single lesson becomes a fight against tooling instead of learning.” Then the sentence that names the real problem: “Most people skip environment setup. Then they spend hours debugging import errors, version conflicts, and missing CUDA drivers.”

Three named pains, three different layers. An import error is usually a package-layer or runtime-layer problem (the library is missing, or a different interpreter ran). A version conflict is a resolver problem. A missing CUDA driver is a system-layer problem — CUDA (Compute Unified Device Architecture) is NVIDIA’s parallel-computing platform for graphics processing units, and no amount of reinstalling Python will make it appear. The reason setup feels endless is that one error message can be born on any of four layers, and nothing in the message says which.

What you seeLayer that usually owns itThe first honest check
command not found: uv2 · packages — or the shell’s PATH (the directories it searches for commands), layer 1which uv — then restart the shell so PATH reloads
ModuleNotFoundError: No module named ’numpy’3 · runtimes (the wrong interpreter), or 4 · librariespython3 -c "import sys; print(sys.executable)"
found v18.20.4; need version 20+3 · runtimes (Node is too old for the route)fnm install 22 && fnm use 22
Cannot install under Rosetta 2 in ARM default prefix1 · system foundation (the shell’s architecture)arch — then the fix in chapter 05
CUDA available: False on a Mac4 · libraries (and a platform fact, not a bug)torch.backends.mps.is_available()

The source gives this lesson ~45 minutes and a promise: set it up once, properly. That is the trade being offered — one bounded session versus an unbounded class of failures. A wrong interpreter takes as long as your patience lasts, because there is no error to tell you when to stop. The preflight you will meet in chapter 06 exists to turn that unbounded search into a bounded read: one line per probe, one Fix: command per failure.

The mindset is the part that generalizes beyond this lesson. Every script, notebook and agent you write later is downstream of a machine whose layers you can name. When something breaks at 2 a.m. in lesson 37, the first useful sentence is not “what is wrong with my code?” but “which layer am I standing on?”

Import error triage

Eight messages a beginner really meets, each traced to the layer that causes it, with the exact command that fixes it and the check that proves it worked. Read the symptom, guess the layer, then look — the messages are shown as the tools print them, with one long resolver error abbreviated.

THE SYMPTOM · no module
$ python3 -c "import numpy"
ModuleNotFoundError: No module named 'numpy'
LAYER 3 · LANGUAGE RUNTIMES

The interpreter that ran is not the one you installed into. NumPy may be sitting in `.venv` while this shell is using the system Python — the same message appears whether the package is missing or you are simply in the wrong environment.

RUN THIS
source .venv/bin/activate   # Windows: .venv\Scripts\activate
HOW YOU KNOW IT WORKED
python3 -c "import sys; print(sys.executable)"  # should end in /.venv/bin/python3
selected no module suspect layer 3 · Language runtimes tools at risk Python 3.11+ · Node 20+ (install 22) · Rust · Julia (optional) the four layers again 1 system git --version 2 packages uv --version · fnm --version 3 runtimes python3 --version · node --version 4 libraries python3 -c "import numpy, torch" the two messages that look alike command not found: uv → the SHELL cannot find the program (PATH / layer 2) No module named 'numpy' → a program ran, and it was the WRONG interpreter (layer 3)

Triage is subtraction, not searching: name the layer first, then run one command to confirm. The preflight does exactly this — shutil.which for programs, importlib.util.find_spec for libraries — which is why it can tell “not installed” from “not importable here”.

Quick check

Your first lesson fails with `ModuleNotFoundError: No module named 'numpy'`. Your teammate says “just pip install numpy”. What is the most useful first check?

FOUR LAYERS, BOTTOM-UP

Every layer stands on the one below.
So install them in that order.

The source draws its environment as four layers, with layer 4 at the top asking layer 3 for a runtime, layer 3 asking layer 2 for an installer, and layer 2 asking layer 1 for a shell to run in. Arrows point down; installation therefore runs up.

The source’s diagram, read bottom-up — the direction you actually install in:

LayerWhat it isWhat installs itProof it works
1 · System foundationThe operating system (OS), your shell, git, an editor, GPU driversxcode-select --install · brew install git curl wget (macOS)git --version
2 · Package managersThe tools that fetch, resolve and pin everything above them: uv for Python, pnpm for Node, cargo for Rust, juliaup for Juliacurl -LsSf https://astral.sh/uv/install.sh | shuv --version
3 · Language runtimesPython 3.11+, Node 20+ (the source installs 22), Rust, and optionally Juliauv python install 3.12 · fnm install 22python3 --version · node --version
4 · AI/ML librariesThe artificial-intelligence and machine-learning packages — PyTorch, NumPy, Matplotlib, Jupyter, transformersuv pip install numpy matplotlib jupyter[1,2,3] · [1,2,3] = 14

Layer 1 is the only layer you do not install — you arrive with it. On macOS, the source’s first command is xcode-select --install, which ships the command-line tools (including git) that every later layer quietly assumes. On Ubuntu/Debian it is apt; on Windows the whole stack lives inside WSL2 (Windows Subsystem for Linux 2), installed with wsl --install -d Ubuntu-24.04.

layer 1 · the system foundation, per platformbash
# macOS
xcode-select --install
brew install git curl wget

# Ubuntu/Debian
sudo apt update && sudo apt install -y build-essential git curl wget

# Windows (use WSL2)
wsl --install -d Ubuntu-24.04
Verbatim from the source. The three lines are not alternatives to each other — they are the same layer, different operating systems.

Why the order is not negotiable. Layer 2 cannot install itself: its installers are shell scripts you download with curl or git, which arrive with layer 1. Layer 3 cannot install itself either: uv python install 3.12 and fnm install 22 name tools from layer 2, and those names do not exist yet. Layer 4 is the layer whose failures you will actually notice — import torch in the middle of a lesson — but it is also the layer with nothing under it if the three below are not in place. Four layers, three dependencies, one legal installation order.

Debugging runs the other way. The symptom always appears above the cause, because the top layer is the one doing work when the bill comes due. A missing package manager surfaces as a missing runtime line; a missing runtime surfaces as an import error; a translated shell surfaces as an installer refusing to run. Read the symptom, then walk down.

Stack assembler

The source’s four layers, drawn bottom-up. Click them in the wrong order and the banner shows the symptom that layer really produces; click bottom-up and the stack completes with a checkmark and a proof command per layer. Required order: system foundation → package managers → language runtimes → ai/ml libraries.

install order bottom-up: system → packages → runtimes → libraries installed 0 / 4 next layer layer 1 · System foundation last outcome OK Click the four layers in the order the source insists on: bottom-up. current proof line xcode-select --install · brew install git curl wget → git --version

The stack is a dependency order, not a preference: each layer gains its commands from the layer below. That is why a top-layer symptom (an import error) so often has a bottom-layer cause (no tool to install with).

Quick check

You run `uv pip install torch` and the shell answers `command not found: uv`. Which layer is missing, and what is the fix?

PYTHON WITH UV

One tool installs the runtime,
the environment and the packages.

Python is where phases 1–12 live, so it gets installed first. The source uses uv for three jobs at once — and the reason to care is not speed for its own sake: fewer tools means fewer places for a layer to be missing.

What uv is. uv is a Python package installer and dependency resolver written in Rust: one binary that installs Python versions, creates virtual environments and installs packages. The source makes a specific, testable claim — it is 10–100× faster than pip — and the reason is worth understanding because it explains the whole shape of the tool: pip re-walks the dependency graph as conflicts are discovered, while uv resolves once and pins the result.

The three jobs, in the source’s own commands. Install uv; install a Python runtime; create a virtual environment; activate it; install the libraries:

layer 2 + 3 · install uv, install Python, make a virtual environmentbash
curl -LsSf https://astral.sh/uv/install.sh | sh

uv python install 3.12

uv venv
source .venv/bin/activate  # or .venv\Scripts\activate on Windows

uv pip install numpy matplotlib jupyter
Verbatim from the source. `uv venv` writes a `.venv/` directory; activating it puts that directory's bin folder first on PATH, so `python3` and `pip` in this shell mean the environment, not the machine.

Why a virtual environment at all. A virtual environment is a per-project box of package versions. Without it, two projects that need different PyTorch versions fight over one global site-packages directory, and upgrading for project B breaks project A. With it, the source’s beginner route can install exactly what its lessons need and nothing else. Check yours any time with python3 -c "import sys; print(sys.executable)" — the path should end in .venv/bin/python3.

The verify snippet is deliberately tiny. The source prints the Python version, the NumPy version, and one dot product:

layer 3 + 4 · the source's verification snippetpython
import sys
print(f"Python {sys.version}")

import numpy as np
print(f"NumPy {np.__version__}")
a = np.array([1, 2, 3])
print(f"Vector: {a}, dot product with itself: {np.dot(a, a)}")
A representative run: `Python 3.12.x (main, …)` / `NumPy 2.x.y` / `Vector: [1 2 3], dot product with itself: 14`. The version numbers move; 14 does not — the arithmetic is checked below.

Three lines, three layer claims. sys.version proves the runtime layer is the one you think it is. import numpy proves the library layer exists in that interpreter. The dot product proves the library actually computes, not just imports. Work the number honestly: np.dot(a, a) = 1·1 + 2·2 + 3·3 = 1 + 4 + 9 = 14. A second example with two different vectors: for b = [4, 5, 6], a · b = 1·4 + 2·5 + 3·6 = 4 + 10 + 18 = 32. The rule is one line of algebra — multiply matching entries, add the products — and it is the same operation a neural network’s first layer performs millions of times.

If the snippet prints 14, you have simultaneously proved layer 3 (the right Python), layer 4 (NumPy importable) and the arithmetic. If it raises ModuleNotFoundError, you have one question left: which interpreter ran? That is what the preflight checks on your behalf, one line per probe, in chapter 06.

Watch where the environment is. Activation is per-shell, not per-machine: a new terminal starts unactivated. The fix is one line — source .venv/bin/activate (macOS, Linux) or .venv\Scripts\activate (Windows) — and the symptom of forgetting it is exactly the wrong-interpreter error from chapter 01.

Worked check — three verifiers, two version floors

The lesson ships three verifiers and they do not all draw the line in the same place. Reading them is a good habit to build now, because “Python 3.11+” is a rule with a specific owner:

verify.py (the preflight) if sys.version_info < (3, 11): FAIL → "need Python 3.11+" → corrective: uv python install 3.12 verify.ts (TypeScript port) major > 3 or (major == 3 and minor >= 10) main.rs (Rust port) (major, minor) >= (3, 10) so: 3.10.13 → FAIL in the preflight, PASS in both ports 3.11.0 → PASS everywhere 3.9.6 → FAIL everywhere, with the preflight's own Fix line

The lesson’s text and the phase prerequisites both say Python 3.11+, and those are the numbers to follow — the preflight is the authority for whether a route may start. The two ports are looser by one minor version, which is a useful reminder about probes in general: a check is only as strict as the code that runs it, so read the check, not the summary.

The install race below turns that claim into arithmetic you can move with two sliders: pip re-walks the dependency graph once per conflict while uv walks it once, and the package-install pass is identical for both — which is why resolution, not disk I/O, is where the order of magnitude lives.

The install race (a simulation)

Not a benchmark — a two-parameter model of the source’s claim that uv is 10–100× faster than pip: pip walks the whole dependency graph once per conflict, uv walks it once, and the package-install pass is identical for both. Change the numbers and check the arithmetic by hand.

SIMULATION — not a benchmark pip 5 passes × 1.40 s + 48 × 0.02 s = 7.00 s + 0.96 s = 7.96 s uv 1 pass × 0.14 s + 48 × 0.02 s = 0.14 s + 0.96 s = 1.10 s ratio 7.24× (source's claim: 10–100×) this model's range at 8 conflicts (the slider's package counts) 8 packages = 42.53× 64 packages = 9.77× so it spans ≈9.8–42.5× depending on package count: the top of that range is inside the source's 10–100× band, but the model never reaches the band's 100× top.

The honest reading: resolution is where the order of magnitude lives. With zero conflicts the two installers look almost the same — the same bytes land on the same disk — and every conflict pip has to re-resolve multiplies the gap.

Quick check

You activate `.venv`, install NumPy, and it imports. The next morning you open a new terminal and `import numpy` fails. What most likely happened?

NODE, RUST, AND OPTIONAL JULIA

Three more runtimes, three proof commands.
Install them when a route asks.

Only Python is needed to start the beginner route. Node, Rust and Julia arrive later — but each has one owner tool, one install command and one version check, so nothing needs to be improvised mid-lesson.

Node.js with fnm. TypeScript lessons — agent frameworks, MCP servers, web apps — run on Node. The source uses fnm (Fast Node Manager) to install and pin it, then installs pnpm as the package manager: the same shape as uv for Python, one level down. The source’s version floor is Node 20+, and the command installs 22.

layer 2 + 3 · Node with fnm, then pnpmbash
curl -fsSL https://fnm.vercel.app/install | bash
fnm install 22
fnm use 22

npm install -g pnpm

node -e "console.log('Node', process.version)"
Verbatim from the source. If the installer stops with a Rosetta 2 message on Apple Silicon, do not keep retrying it — that is a layer-1 problem with a two-line fix, and chapter 05 is where the diagnosis lives.

Rust with rustup. Rust appears in the performance-critical lessons: inference engines, systems work. rustup is its owner and cargo is the package manager it installs (the name comes from “cargo” as in freight: it fetches, builds and ships crates). Two proof commands, both cheap.

layer 2 + 3 · Rust with rustupbash
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh

rustc --version
cargo --version
Set PATH by restarting the shell after the installer runs — rustup edits your shell profile.

Julia, optional. The source keeps Julia for math-heavy lessons “where Julia shines”, and installs it through juliaup — which is the same pattern a fourth time: one owner tool, one runtime, one proof line.

layer 2 + 3 · Julia via juliaup (optional)bash
curl -fsSL https://install.julialang.org | sh

julia -e 'println("Julia ", VERSION)'
Optional in every route the preflight knows: `julia` is never a required probe.

What each language is for. The source’s own table maps runtimes to phases — and it is worth reading as a statement about the course, not just about tools:

LanguageUsed inPackage manager
PythonPhases 1–12 (machine learning, deep learning, natural language processing, vision, audio, large language models)uv
TypeScriptPhases 13–17 (tools, agents, swarms, infra)pnpm
RustPhases 12, 15–17 (performance-critical systems)cargo
JuliaPhase 1 (math foundations)Pkg

One consequence is visible in the preflight’s route table: every route’s first runnable command is Python except one, and only one route out of seven requires Node at all. Installing the full toolchain before lesson one is therefore optional work; the source’s advice is to install later tools when a lesson asks for them rather than blocking the first lesson on the whole stack.

Worked check — counting the route table

The preflight ships seven routes and a fixed set of probes. Read the table as arithmetic and the whole “do I need Node yet?” question answers itself:

routes 7 distinct probes 11 required probes per route 2 · 3 · 2 · 2 · 2 · 4 · 2 (17 requirement slots) probes required by all 7 python, git probes required by 1 route node, npx (agent-skills only) numpy (ml-foundations only) probes required by no route matplotlib, jupyter, torch, gpu, cargo, julia Next: lines that are python3 6 of 7 (the 7th opens a markdown file) so: a learner on the beginner route needs python + git (2 of the 11 probes) and everything else is reported LATER — never FAIL.

Two numbers are worth carrying forward. First, 2 of the 11 probes: the beginner route’s required set is two probes, so a fresh machine is closer to ready than a wall of warnings makes it feel. Second, 6 of 7: nearly every route opens with a python3 phases/… command, which is why Python and uv get the detailed treatment in this lesson and the other three runtimes get one paragraph each.

GPU, MPS, AND THE PLATFORM TRAPS

Two one-line checks say what your GPU is doing.
A third says which machine you are really on.

AI libraries talk to a graphics processing unit (GPU) through a backend: NVIDIA’s CUDA on Linux and Windows, Apple’s MPS (Metal Performance Shaders) on Apple Silicon. PyTorch exposes one boolean for each — and the expected answers are not the same everywhere.

The check is two lines and works on every platform. The source asks for both booleans every time, because “no CUDA” means different things depending on where you are:

layer 4 · the GPU verification snippetpython
import torch
print(f"CUDA available: {torch.cuda.is_available()}")           # False on macOS — expected
print(f"MPS available:  {torch.backends.mps.is_available()}")   # True on Apple Silicon
if torch.cuda.is_available():
    print(f"GPU: {torch.cuda.get_device_name(0)}")
Verbatim from the source, including the comments. A representative Mac run prints `CUDA available: False` and `MPS available: True`; an NVIDIA run prints `CUDA available: True` and the device name from get_device_name(0).

PyTorch gets to a GPU two ways, and the install command differs accordingly. On NVIDIA hardware, the CUDA build comes from a version-specific index; on a Mac, the plain build already contains MPS:

PlatformInstall commandExpected verdict
NVIDIA, Linux/Windowsuv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124CUDA True · MPS False
macOS, Apple Siliconuv pip install torch torchvision torchaudioCUDA False · MPS True
CPU onlythe plain buildboth False — and most lessons still run

No GPU? No problem. That is the source’s own answer: most lessons work on CPU, and for training-heavy ones the recommendation is Google Colab or a cloud GPU rather than a hardware purchase. The booleans are diagnostics, not a grade.

layer 4 · the NVIDIA path, then the Mac pathbash
# NVIDIA (Linux / Windows): confirm the driver first
nvidia-smi

# install the CUDA build
uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124

# macOS / Apple Silicon: no CUDA, no index URL — the plain build has MPS
uv pip install torch torchvision torchaudio
The `--index-url .../cu124` form is Linux/Windows only. On a Mac it does not produce a broken CUDA install; it produces a resolver error, because no macOS wheel exists on that index. The `cu124` tag tracks a CUDA release, so treat the source's line as the form to copy and check PyTorch's install selector for the tag that matches your driver.
Worked check — why a cu124 wheel cannot be found on macOS

A Python wheel file name carries the platform it was compiled for. The shape is standardised: name-version-python-abi-platform.whl, where the last field is the platform tag. The tags you meet in AI installs are:

manylinux_2_28_x86_64 Linux, x86_64 win_amd64 Windows, x86_64 macosx_11_0_arm64 macOS, Apple Silicon macosx_10_9_x86_64 macOS, Intel CUDA wheels on the cu124 index are built with Linux/Windows tags only. On macOS, pip/uv asks that index for a macOS tag, finds none, and reports a resolver error of the form: "Could not find a version that satisfies the requirement torch" There is no macosx+cu124 wheel to fix — it does not exist. On Apple Silicon the accelerator is MPS, and it ships in the plain macOS build.

The general lesson is worth more than the specifics: an install command encodes three facts at once — the package, the version, and the platform. When one of them is wrong for your machine, the failure arrives as a resolver message, which reads like the package is missing when the platform is what does not match.

The Apple Silicon trap: two installers, two architectures. Here is the failure the source documents for M1–M4 machines, with its exact message:

the error, and the two-line fixbash
# what you see when you run the fnm installer
Error: Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)

# what is actually happening
arch        # prints i386  → this shell is an x86_64 process, translated
brew --prefix # prints /opt/homebrew → Homebrew is a native arm64 build

# the fix: run this one command as a native arm64 process
arch -arm64 brew install fnm
echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc
source ~/.zshrc
Verbatim from the source, in order. The install then continues with `fnm install 22`. The `eval "$(fnm env --use-on-cd)"` line is what makes `node` follow the directory you are in — fnm works through a shell hook, not a fixed binary.

Rosetta 2 is the translation layer that lets an Apple Silicon Mac run Intel (x86_64) software. A terminal that was opened under it is a translated process: arch reports i386 — a macOS label for the x86 process, not a claim about a 32-bit CPU. That shell is fine on its own; the trouble starts when it meets an arm64 toolchain, because Homebrew refuses to mix architectures and stops rather than silently installing Intel copies of everything.

arch -arm64 … is the surgical fix: it re-executes a single command as a native arm64 process, which is enough to let the native Homebrew install fnm. For the durable fix, turn off “Open using Rosetta” in the terminal application’s Get Info panel so every future shell starts native — and use sysctl -n sysctl.proc_translated (1 = translated, 0 = native) as the tie-breaker when arch and uname -m seem to disagree.

Rosetta architecture detector

Two commands decide the whole diagnosis: what arch prints for the shell you are in, and which prefix Homebrew lives at. Pick a terminal state and read the verdict, the fix, and what to check next.

SHELL ARCHITECTURE → BRIDGE COMMAND → HOMEBREW PREFIXTHIS SHELLarch → i386translated x86_64 processTHE BRIDGEarch -arm64 brewone command, run nativeHOMEBREW/opt/homebrewarm64 binariesVERDICTTHE SOURCE'S ERROR · fix with arch -arm64Rosetta 2 is translating your terminal
WHAT IS HAPPENING

Homebrew is the native arm64 build (that is what `/opt/homebrew` means), but the shell that ran the installer is an x86_64 process: `arch` prints `i386` even on an M-series chip. The installer sees a mismatch and stops rather than mixing architectures.

THE FIX, IN ORDER
  1. arch -arm64 brew install fnm
  2. echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc
  3. source ~/.zshrc
  4. fnm install 22

Next check · `arch -arm64` re-executes the single command as a native arm64 process, which is why the install works even while the shell stays translated. For a durable fix, turn off “Open using Rosetta” in the terminal app's Get Info panel (or run the arm64 build of your terminal) so every later command starts native.

arch i386 brew --prefix /opt/homebrew shell translated: an x86_64 process homebrew native arm64 prefix verdict THE SOURCE'S ERROR · fix with arch -arm64 why the two commands disagree arch reports the architecture of THIS PROCESS uname -m reports the architecture of this process too brew --prefix reads where Homebrew was installed sysctl -n sysctl.proc_translated 1 = translated, 0 = native (the tie-breaker) installer rule --index-url .../cuXXX → Linux/Windows wheels only macOS → plain build, MPS is the accelerator

arch printing i386 is a macOS quirk: it labels the translated x86_64 process, not a 32-bit CPU. The chip is still Apple Silicon — and the mismatch with an arm64 Homebrew is exactly what makes the fnm installer stop.

VERIFY YOUR ROUTE

Ask the preflight, not the internet.
One route, one result line.

The lesson ships a script that checks only what the selected route needs to start, prints one corrective command per failure, and ends with the exact first thing to run. It is the smallest possible answer to “is my machine ready?”

Run it from the repository root — the directory that contains README.md and phases/. The source provides seven routes, and the one you pick decides which probes are required now and which are simply later:

the seven routes, verbatimbash
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route beginner
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route ml-foundations
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route llm-engineering
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route agents
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route mcp
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route agent-skills
python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route certification
Without `--route` the script defaults to `beginner`, which is the full beginner sequence.

What a passing run looks like. The source prints the first runnable lesson exactly like this:

the source's pass block, quotedtext
Ready to start Beginner course.
Next: python3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py
Those two lines are the source's own example. Expanded below is the script's full transcript format, with the same PASS lines the preflight prints on a healthy machine.
the same run, in the script's full output formattext
=== AI Engineering from Scratch: Environment Check ===

Route: Beginner course (`--route beginner`)

  [PASS] Python 3.11+ (required now)
         Python 3.12.7 at /Users/you/.venv/bin/python3
  [PASS] Git (required now)
         git version 2.43.0 at /usr/bin/git

Later checks skipped: 9 tools are not needed to start. Add `--show-later` when you want to inspect them.

Result: 2/2 required checks passed
Ready to start Beginner course.
Next: python3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py

# and when one required probe fails, that block changes to:
  [FAIL] Git (required now)
         'git' was not found on PATH
         Fix: Run `xcode-select --install`, then `git --version`.
...
Result: 1/2 required checks passed
Not ready yet. Run each Fix command above, then repeat this preflight.
Paths and versions are examples from one machine — the preflight prints whatever it actually detected. The labels, indentation and Result lines are the script's own format.

Which probes each route requires. Two probes are universal; the rest are route-specific. This table is the script’s route table, read as prose:

RouteRequired nowNext: command
beginnerpython · gitpython3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py
ml-foundationspython · git · numpythe same vectors.py
llm-engineeringpython · gitpython3 phases/11-llm-engineering/01-prompt-engineering/code/prompt_engineering.py
agentspython · gitpython3 phases/14-agent-engineering/01-the-agent-loop/code/main.py
mcppython · gitpython3 phases/13-tools-and-protocols/06-mcp-fundamentals/code/main.py
agent-skillspython · git · node · npxpython3 phases/13-tools-and-protocols/22-skills-and-agent-sdks/code/main.py
certificationpython · gitopen certifications/claude/GETTING_STARTED.md and choose a track

LATER is not FAIL. By default the script skips tools that the chosen route does not need yet and says how many it skipped — nine of them on the beginner route: node, npx, numpy, matplotlib, jupyter, torch, the accelerator backend, cargo and julia. Add --show-later when you want to inspect them; a missing later tool never changes the result line. Two routes add manual checks as well: agent-skills and certification, because no script can prove that an AI host has discovered a skill or that your chosen skill scope is writable.

The philosophy is in the last section of the source. “Install later tools when a lesson asks for them instead of blocking your first lesson on the whole stack.” The preflight is designed around that sentence: one clear answer instead of a wall of warnings, and a definition of “ready” that means ready for this route, not ready for everything.

Ship it. The artifact this lesson produces is the verification script itself — something anyone can run on their own machine. The source also points at outputs/prompt-env-check.md, a prompt that helps an AI assistant diagnose environment problems; that is the same triage pattern you practised in chapter 01, with a model in the loop.

Route preflight simulator

Pick one of the seven routes and break as many required probes as you like. The panel reproduces what verify.py --route … prints: a PASS or FAIL per required probe, the exact Fix: line for each failure, the skipped later checks, and — only when every required check passes — the Next: command that starts the route.

TERMINAL · BEGINNER COURSE · --ROUTE beginner
=== AI Engineering from Scratch: Environment Check ===

Route: Beginner course (`--route beginner`)

  [PASS] Python 3.11+ (required now)
         Python 3.12.7 at /Users/you/.venv/bin/python3
  [PASS] Git (required now)
         git version 2.43.0 at /usr/bin/git

Later checks skipped: 9 tools are not needed to start. Add `--show-later` when you want to inspect them.

Result: 2/2 required checks passed
Ready to start Beginner course.
Next: python3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py

verify.py adapts the Git fix to the platform: macOS runs `xcode-select --install`, Windows runs `winget install --id Git.Git -e`, Linux runs `sudo apt-get update && sudo apt-get install -y git`. The labs quote the macOS line.

required probes for this route — click one to break it

route Beginner course (--route beginner) routes available 7 · 17 required checks across all of them required now python, git optional/later node, npx, numpy, matplotlib, jupyter, torch, gpu, cargo, julia manual checks none Result: 2/2 required checks passed Ready to start the route. Next: python3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py

A failing required probe prints the detected path or import error and one corrective command. Add --show-later when you want the same preflight to look at optional tools — a missing later tool never changes the result line.

Quick check

The preflight prints `[LATER] Julia` on the ml-foundations route. Do you need to install Julia before starting?

CHECK YOURSELF

Six questions.
Then the terms worth keeping.

Answer before you look. The four-layer question and the preflight question are the two that decide whether you can debug an environment or only rebuild it.

0 / 6 answered · 0 correct

01Why do AI projects need a separate virtual environment?

02What does CUDA provide for AI workloads?

03In the four-layer environment stack, which layer must be installed first?

04What is the purpose of uv in a Python AI project?

05How do you verify that PyTorch can access your GPU?

06The preflight prints `[FAIL] Python 3.11+` and, further down, `[LATER] Julia`. Which of the two blocks the route?

Key terms, demystified

Click a card to swap the lazy description for what it actually means.

Exercises from the lesson

Four problems with exact commands — run the preflight and fix what it finds, build a course environment and install PyTorch, write hello world in all four languages, and diagnose the Rosetta state from two terminal readings. Try first; a worked answer is one click away.

  1. Run the verification script for the beginner route and fix whatever it reports. Then run it again with --show-later and explain why the second run's extra lines do not change the result.
    Show one worked answer

    From the repository root — the directory that contains `README.md` and `phases/` — run `python3 phases/00-setup-and-tooling/01-dev-environment/code/verify.py --route beginner`. Read it as a loop of three lines per probe: status, evidence, and (on failure) a fix. Worked example: a Mac with an old system Python reports `[FAIL] Python 3.11+ (required now)`, then `found Python 3.9.6 at /usr/bin/python3; need Python 3.11+`, then `Fix: Install it with \`uv python install 3.12\`, activate that environment, and rerun with \`python3\``. The corrective loop is exactly three commands: `uv python install 3.12`, `uv venv`, `source .venv/bin/activate` — then rerun the preflight, which should now print `[PASS] Python 3.11+ (required now)` with the interpreter path inside `.venv`, and close with `Result: 2/2 required checks passed` plus `Ready to start Beginner course.` and `Next: python3 phases/01-math-foundations/01-linear-algebra-intuition/code/vectors.py`. Fix failures in that loop, one probe at a time, and rerun after each fix: the script is cheap and it is the same check a teammate or CI job can run. Now add `--show-later`: the beginner route's nine optional probes are listed with their own PASS/LATER status, but the closing line is still `Result: 2/2 required checks passed`, because n counts required probes only. That is the design — later tools are visible without being blocking. Two honest notes. First, the preflight requires Python 3.11+ (`sys.version_info < (3, 11)`), while the lesson's TypeScript and Rust ports accept 3.10+; when they disagree, the preflight is the authority for starting a route. Second, the Git fix line is platform-specific: macOS runs `xcode-select --install`, Windows `winget install --id Git.Git -e`, Linux `sudo apt-get install -y git`.

  2. Create a Python virtual environment for this course and install PyTorch in it. Verify the accelerator backend and explain what each of the two booleans means on your machine.
    Show one worked answer

    Install a runtime, make the box, activate it, then install: `uv python install 3.12`, `uv venv`, `source .venv/bin/activate` (Windows: `.venv\Scripts\activate`), `uv pip install torch torchvision torchaudio`. Then prove it with the source's snippet: `python3 -c "import torch; print('CUDA', torch.cuda.is_available()); print('MPS', torch.backends.mps.is_available())"`. Expected readings: on an NVIDIA Linux/Windows machine `CUDA True` and MPS `False`, with `torch.cuda.get_device_name(0)` naming the card — and the CUDA build must come from the versioned index, e.g. `uv pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cu124`. On Apple Silicon the expected pair is `CUDA False`, `MPS True`; that False is not a failure, it is the platform fact that no CUDA wheel exists for macOS — which is why the plain build (no `--index-url`) is the correct command there. On a CPU-only machine both print False, and the source's own answer is that most lessons still work; training-heavy ones go to Colab or a cloud GPU. Two diagnostics for when it goes wrong instead. If the import fails, check *which* interpreter ran: `python3 -c "import sys; print(sys.executable)"` must end in `.venv/bin/python3`. If MPS is False on an M-series Mac, the usual cause is an install into the wrong environment (or an Intel build of PyTorch); reinstall with the plain command inside the activated environment. Budget honestly: the PyTorch wheels are large downloads — expect minutes, not seconds, and much longer on a slow link — and nothing in this lesson depends on a GPU being present.

  3. Write hello world in all four languages of the course and run each one. For any language that fails, name the layer the failure came from.
    Show one worked answer

    Python (layers 3 and 4, already proven by the NumPy check): `python3 -c "print('hello from python')"` → `hello from python`. Node (layers 2 and 3): `node -e "console.log('hello from node', process.version)"` → `hello from node v22.x.y` (the patch version is whatever fnm installed — the source's own Step 3 uses this exact one-liner). Rust (layers 2 and 3, plus a compiler step Python and Node do not have): write `hello.rs` containing `fn main() { println!("hello from rust"); }`, then `rustc hello.rs -o hello && ./hello` → `hello from rust`; the source's own build line has the same shape — `rustc --edition 2021 code/main.rs -o /tmp/lesson_dev_env && /tmp/lesson_dev_env`. Julia (optional): `julia -e 'println("hello from Julia ", VERSION)'`. The point of the drill is the failure taxonomy, not the greeting. `command not found: node` is a layer-2/1 problem (fnm missing, or PATH not reloaded) — fix it with `fnm install 22 && fnm use 22` in a fresh shell. `error[E0425]: cannot find function …` from rustc is a layer-3 success and a code problem: the runtime ran and is telling you about your program. A version that differs from these examples (`v22.11.0` rather than `v22.4.1`, Julia 1.11 rather than 1.10) is also a success — the runtime answered, which is the whole claim being tested.

  4. A learner on an M2 MacBook reports three readings: `arch` prints i386, `brew --prefix` prints /opt/homebrew, and the fnm installer stops with “Cannot install under Rosetta 2 in ARM default prefix (/opt/homebrew)”. Explain what is running where, give the exact fix in order, and say what would have happened if they had added --index-url https://download.pytorch.org/whl/cu124 instead.
    Show one worked answer

    Nothing here says the Mac is old or the CPU is 32-bit. `arch` reports the architecture of the *process* — here an x86_64 process running under Rosetta 2, which macOS labels `i386` — while `brew --prefix` reports where Homebrew is installed; `/opt/homebrew` is the arm64 prefix (the Intel prefix is `/usr/local`). So the shell is translated and the toolchain is native, and the installer refuses to mix architectures rather than quietly installing Intel copies of everything. The fix, in the source's order: `arch -arm64 brew install fnm` — re-executes that single command as a native arm64 process, so the native Homebrew installs the native fnm; then `echo 'eval "$(fnm env --use-on-cd)"' >> ~/.zshrc`; then `source ~/.zshrc`; then the original command succeeds: `fnm install 22 && fnm use 22`. The tie-breaker checks are `uname -m` (arm64 on Apple Silicon) and `sysctl -n sysctl.proc_translated` (1 while translated, 0 once native); for the durable fix, turn off “Open using Rosetta” in the terminal app's Get Info panel. Adding `--index-url https://download.pytorch.org/whl/cu124` would make things strictly worse: that index carries CUDA wheels built for Linux and Windows, so the resolver would report that no version satisfies the requirement. No macOS CUDA wheel exists to find, because macOS has no CUDA — the accelerator on Apple Silicon is MPS, and it ships in the plain build (`uv pip install torch torchvision torchaudio`).

Terms this lesson borrows from later lessons (or outside)

You do not need to master these here. Each one gets a proper treatment in its own lesson; the one-line meaning is enough to keep reading. Orange dotted underlines in the prose point back to this list.

  • dot productThe operation behind the lesson's verify line: `np.dot([1,2,3], [1,2,3])` multiplies matching entries and adds them — 1·1 + 2·2 + 3·3 = 14. It is also the core of every neural network layer you will build; the algebra gets its own lesson in Phase 1, Lesson 01 (Linear Algebra Intuition), whose `vectors.py` is the beginner route's first runnable file.
  • tensorThe n-dimensional array a library like NumPy or PyTorch computes on. This lesson only asks it to hold three integers; shapes, dtypes and broadcasting get their own lesson in Phase 1, Lesson 12 (Tensor Operations), and autograd arrives in Phase 1, Lesson 05 (Chain Rule & Automatic Differentiation) and Phase 3, Lesson 03 (Backpropagation from Scratch).
  • prompt engineeringThe first lesson the llm-engineering route's `Next:` line points at: `phases/11-llm-engineering/01-prompt-engineering/code/prompt_engineering.py` (Phase 11, Lesson 01). You can start it the moment the preflight passes for that route.
  • MCP (Model Context Protocol)The reason the mcp route's preflight exists in this lesson's route table at all: an MCP server is a process an AI host launches — usually a Node program, which is why Node/npx are probes. Full treatment in Phase 13, Lesson 06 (MCP Fundamentals).
  • agent skillA portable instructions-plus-files package an AI host discovers and loads. The agent-skills route is the only one whose required set includes Node and npx, and the only one with manual checks — no script can prove your host has found a skill. Phase 13, Lesson 22 (Agent Skills: Portable Contract and Runtime Boundary).
KEEP GOING

A picture is a start.
Practice is the rest.

This lesson is a port of an open course. Everything here traces back to it — and the next step is running the code yourself.

Original lessonDev EnvironmentAI Engineering from Scratch · Phase 00, Lesson 01 — the source text, its five-question quiz, and the three verifiers it ships: `verify.py` (the route-aware preflight this page quotes), `verify.ts` and `main.rs`. The four-layer stack, the uv/fnm/rustup/juliaup commands, the numpy and PyTorch verification snippets, the Rosetta 2 error and its fix, the `--index-url .../cu124` rule, the seven routes and the pass block are all from here.Official docsuv — an extremely fast Python package and project managerAstral · installs the tool, installs Python versions, creates virtual environments and installs packages (`uv python install`, `uv venv`, `uv pip …`). The standalone installer line in this lesson is the one from the source: `curl -LsSf https://astral.sh/uv/install.sh | sh`.Official docsfnm — Fast Node ManagerThe Node version manager the source uses: `fnm install 22`, `fnm use 22`, and the shell hook `eval "$(fnm env --use-on-cd)"` that makes a directory's Node version follow you. Its installer is the one that stops with the Rosetta 2 message on an Apple Silicon machine running a translated shell.Official docsrustup — the Rust toolchain installerThe one-line installer the source pipes into sh, plus the two proof commands this lesson quotes: `rustc --version` and `cargo --version`. rustup is also the layer-2 owner for Rust, in the same family as uv for Python and fnm for Node.Official notesPyTorch — MPS backend and CUDA semanticsThe two backend checks this lesson verifies: `torch.backends.mps.is_available()` (Apple Silicon's Metal backend, the expected True on a Mac) and `torch.cuda.is_available()` / `torch.cuda.get_device_name(0)` (NVIDIA). Together they explain why the plain macOS build is correct and a `cu124` index URL is not.

Lesson text adapted from AI Engineering from Scratch (Phase 00, Lesson 01) and its route-aware `verify.py`, with `verify.ts` and `main.rs` as cross-checks. Everything the source states is kept as-is: the four-layer stack and the bottom-up rule, the uv/fnm/rustup/juliaup install commands, the numpy and PyTorch verification snippets with their expected `False`/`True` readings, the Rosetta 2 error and its `arch -arm64 brew install fnm` fix, the `--index-url .../cu124` wheel rule, the seven routes with their required/optional probes, the pass block, and the “install later tools when a lesson asks” philosophy. Original to this page: the five labs (the stack assembler, the install race, the route preflight simulator, the Rosetta architecture detector and the import-error triage console), the second worked dot product (`a · b = 32`), the three-verifier version-floor check, the route-table arithmetic, the install-race model with its named parameters and its honest ≈10–43× range depending on package count, the wheel platform-tag explanation, the “install up, debug down” memory hook, the four-state Rosetta table and the sixth quiz question. The preflight simulator reproduces the script’s transcript format faithfully; the paths and version strings it shows are representatives, since a real run prints whatever it detects on your machine.