Skip to content
All phases

Phase 01

Foundations: Python for AI Engineering

Goal
Learn Python from the ground up — scoped ruthlessly to what AI engineering actually uses — plus the engineering substrate every later phase leans on: typing, async, streaming, a little SQL, and containers.
You build
`wrangle`, a typed data-cleaning CLI (warm-up), then `mockstream`, a streaming "fake LLM" web service — typed, tested, Dockerized.
Effort
~30–50 hours if Python is new territory; a fraction of that as a refresher. Either way, don't skip the projects.

Part A — the twelve topics

Read the lesson, then make the tests pass. Everything runs in this browser.

  1. 00Python basics
    0/9
  2. 01Core mechanics
    0/7
  3. 02Functions, properly
    0/6
  4. 03Comprehensions & iteration
    0/6
  5. 04Errors
    0/6
  6. 05Files & data
    0/6
  7. 06Modules & structure
    0/5
  8. 07Just enough OOP
    0/6
  9. 08Generators & iterators
    0/7
  10. 09Decorators & context managers
    0/6
  11. 10Type hints
    0/8
  12. 11A little SQL
    0/12

Why this phase exists

AI engineering is 80% ordinary software engineering under unusual constraints: everything is async and streamed, everything is untyped JSON until you force types onto it, and everything talks to slow, expensive, flaky external APIs. This phase teaches exactly the Python that serves those constraints — and skips the rest of the language’s folklore. The filter for every topic below: will a later phase use this? If not, it’s out.

Part A — Python, from the start (the AI engineering subset)

Work through these in order, writing small scripts at every step. Type-hint everything from day one — in this field, schemas are the job.

[!TIP] Part A is built out in 01-foundations/part-a/ — a lesson and a set of failing tests for each bullet below. Read the lesson, fill in the drills, run uv run pytest phases/01-foundations/part-a until it’s green.

  • Core mechanics: variables, numbers, strings and f-strings, truthiness; list, dict, set, tuple and when each fits; slicing; control flow. Dicts get special attention — every API payload you’ll ever touch is one.
  • Functions, properly: positional vs keyword args, defaults (and the mutable-default trap), *args/**kwargs, functions as values, closures. Provider SDKs and frameworks assume fluency here.
  • Comprehensions & iteration: list/dict comprehensions, enumerate, zip, sorting with key=.
  • Errors: try/except/finally, raising your own exceptions, exception chaining. API-calling code is mostly error handling; get comfortable early.
  • Files & data: pathlib, reading/writing text, and json deeply — JSON is the substrate of everything LLM. csv in passing.
  • Modules & structure: imports, packages, the src/ layout, entry points.
  • Just enough OOP: classes, @dataclass, methods, properties, minimal inheritance; prefer Protocol (structural typing) over class hierarchies. You’ll use many classes and write few.
  • Generators & iterators: yield, generator functions, lazy iteration. This is literally how token streaming works — Part B depends on it.
  • Decorators & context managers: at read-and-use level (@app.get(...), with open(...), @pytest.fixture) — FastAPI and pytest are built from them. Write one trivial decorator and one context manager to demystify the syntax, then move on.
  • Type hints: every signature typed; Optional/unions, Literal, TypedDict, generics as they come up.
  • A little SQL: SELECT, JOIN, GROUP BY, indexes at a practical level. From Phase 4 onward your app data, vectors, and full-text search all live in PostgreSQL.

Explicitly skipped: metaclasses, multiple-inheritance puzzles, threading/multiprocessing internals (asyncio covers this roadmap’s concurrency), NumPy/pandas, notebooks-as-a-lifestyle, classical ML (sklearn), statistics. If a tutorial starts with a gradient, close the tab.

Part B — the engineering substrate

  • Modern Python tooling: uv for packages/environments/Python versions (it has effectively replaced pip + venv + pyenv + poetry), ruff for linting + formatting, pyright for type checking, pytest for tests. Set these up once as your default project template.
  • Pydantic v2: models, validators, model_json_schema(), serialization. This is the load-bearing library of Python AI engineering — it defines your API contracts, your LLM output schemas, and your tool signatures.
  • asyncio: async/await, gather, semaphores for concurrency limits, async generators. LLM apps are I/O-bound; concurrency is how you make 100 extraction calls take 30 seconds instead of 30 minutes.
  • FastAPI: routes, dependency injection, Pydantic request/response models, background tasks, StreamingResponse.
  • Streaming over HTTP: what Server-Sent Events (SSE) are and how to produce/consume them. Every chat UI you’ll ever build streams tokens this way.
  • Docker: write a Dockerfile for a Python service, multi-stage builds, compose for app + database. Competence, not expertise.
  • Git hygiene: branches, meaningful commits, PR-shaped changes — your later projects are portfolio pieces.

Warm-up project: wrangle

A CLI that turns messy JSON into clean, typed data — pure Part A muscle, zero web code.

  1. Feed it a real messy export: browser bookmarks, a social/streaming data export, or any public JSON dataset.
  2. Parse → validate into Pydantic models → normalize (dates, URLs) and dedupe → emit clean JSON plus a terminal summary report (counts, top categories, rejects with reasons).
  3. Typed end-to-end, pytest coverage for the ugly edge cases, ruff clean, proper src/ layout, installable entry point (uv run wrangle ...).

If Python is fresh, expect this to take a few evenings. That’s the point — it exercises every Part A topic in one artifact.

Main project: mockstream

A FastAPI service that pretends to be an LLM API — so you learn the transport layer with zero API cost, and get a reusable test double for later phases.

  1. POST /v1/chat accepts {messages: [...], stream: bool} validated by Pydantic; rejects malformed input with useful errors.
  2. Non-streaming mode returns a canned completion after an artificial delay.
  3. Streaming mode returns SSE, emitting word-by-word chunks with realistic pacing, terminated by a [DONE] event (mirror the shape of a real provider’s streaming format).
  4. A httpx-based async client script consumes the stream and prints tokens as they arrive; run 20 concurrent requests bounded by a semaphore.
  5. Typed end-to-end (pyright clean), tested (pytest + httpx test client, including a streaming test), linted (ruff), Dockerized (compose file runs it).

Stretch: add a failure mode (random 429s with Retry-After) and make your client handle it with exponential backoff — you’ll reuse this exact logic against real providers in Phase 2.

Resources

Exit criteria

8 criteria

Honest self-assessment. Tick one when you could do it unaided, not when you have read about it.