A hands-on learning platform that takes engineers from transformer internals to shipping production AI systems.
109 deeply-researched lessons across 12 categories β RAG, agents, evals, fine-tuning, prompting β wired together with instant search, AI audio narration, an interactive knowledge graph, a RAG tutor, and per-learner mastery analytics.
π Quick Start Β Β·Β β¨ Features Β Β·Β π§± Stack Β Β·Β π Architecture Β Β·Β π Dev
109 lessons Β Β·Β 12 categories Β Β·Β 33 D1 tables Β Β·Β 75 API routes Β Β·Β 10 course evaluators
| Feature | What it does | |
|---|---|---|
| π | 109 lessons, 12 categories | 8 ordered phases plus appendices β from transformer internals β RAG β agents β evals β production, prerequisite-ordered. |
| π | Instant search | Cmd+K lexical search over data/content/sections.json β one row per lesson section, no round-trip. |
| π§ | Audio narration | Per-lesson TTS built by the Python kg.audio pipeline (DashScope Qwen3-TTS) β Cloudflare R2, with per-user resume positions in D1. |
| πΈοΈ | Knowledge graph | Concepts linked by prerequisite / builds_on / related edges, rendered as an explorable graph. |
| π§ | Qdrant mind map | /mind-map β a drillable React Flow mind map of everything in the kg-corpus vector store (lesson sections, code, docs), with clickable nodes and edges. |
| π€ | AI tutor chat | RAG chat grounded in lesson content, retrieved from Qdrant by the Python rag_service and cited back to the source lesson. |
| π | Mastery analytics | Bayesian Knowledge Tracing (lib/bkt.ts) plus FSRS scheduling weights, per learner per lesson. |
| βοΈ | Self-authoring | kg.gen_article β a multi-pass Python writer (research β outline β draft β review β revise) that generates new lessons behind a quality gate. |
| π | Course reviewer | 10 expert evaluators score & rank external AI courses concurrently. |
Prerequisites: Node 22.x Β· pnpm Β· a Cloudflare account (D1 + R2)
pnpm install
cp .env.example .env.local # fill in the keys (see Environment below)
pnpm db:push # sync schema to D1 over the HTTP API
pnpm seed # seed lessons from content/*.md
pnpm dev # β http://localhost:3002 πThe Next.js app is the whole frontend. The AI tutor proxies to the Python
rag_service (LLAMAINDEX_SERVICE_URL) β unset it and the tutor degrades to a
soft offline notice rather than erroring. All content generation (articles,
prep, flashcards, audio) runs offline as Python kg.* commands under uv β
see AI generation.
| Layer | Technology |
|---|---|
| Framework | Next.js 16 (App Router, webpack build) |
| Database | Cloudflare D1 (SQLite) via Drizzle ORM + d1-http |
| UI | shadcn/ui (new-york) Β· Radix primitives Β· Tailwind Β· lucide icons |
| AI / LLM | DeepSeek, reached through a Cloudflare AI Gateway worker |
| AI backend | Python β roadmap-kg (LlamaIndex PropertyGraphIndex, Neo4j Aura, FastEmbed) and services/llamaindex (rag_service, FastAPI on Render) |
| Vectors | Qdrant (kg-corpus) with FastEmbed bge-small embeddings |
| Storage | Cloudflare R2 (audio) Β· D1 (relational + playback state) |
| Workers | ai-gateway (LLM proxy + cache) Β· edge-tasks Β· ext-api |
| Deployment | Vercel, via scripts/deploy.sh (prebuilt, submodule-safe) |
graph TD
Browser --> Next["Next.js on Vercel<br/>pages Β· API routes Β· server actions"]
Next --> Content["data/content/*.json<br/>lessons Β· sections Β· search index"]
Next --> D1[("Cloudflare D1<br/>auth Β· applications Β· progress")]
Next --> R2["Cloudflare R2<br/>audio files"]
Next -->|"LLAMAINDEX_SERVICE_URL<br/>+ shared secret"| Rag["Python rag_service<br/>FastAPI on Render"]
Rag --> Qdrant[("Qdrant<br/>kg-corpus vectors")]
Rag --> Gateway["Cloudflare AI Gateway"]
Gateway --> DeepSeek["DeepSeek API"]
Request paths: lesson pages read committed JSON under data/content/, so
the reading experience needs no database. Search is lexical over the same
sections.json. The tutor POSTs the conversation to the Python rag_service,
which does Qdrant retrieval and calls DeepSeek through the AI Gateway, returning
the answer plus the excerpts it was grounded on. D1 holds anything per-user
(auth, applications, audio positions, mastery); R2 holds the audio.
app/ Next.js App Router (lessons, phase hubs, applications,
agents-lab, mind-map, api/* β 75 route handlers)
components/ React components (search, audio-player, toc, phase-hub, β¦)
content/ Markdown lesson files (109)
data/ Generated corpus β content JSON, topics, audio, app-prep
lib/ content loaders, D1 queries, r2.ts, auth, bkt.ts, actions
src/db/ Drizzle schema (33 tables) + D1 client
roadmap-kg/ Python `kg.*` β content/audio/KG pipelines, ~540 Makefile
targets drive them (LlamaIndex, Neo4j, Qdrant, FastEmbed)
services/llamaindex/ Python: the FastAPI + LlamaIndex service. rag_service/agents_lab/
holds the runnable autonomous-agent patterns (ReAct, Reflexion,
Plan-and-Execute, LATS, ToT, supervisor, debate, ReWOO,
LLMCompiler, self-refine), served at POST /agents/{pattern}
workers/ Cloudflare Workers β ai-gateway, edge-tasks, ext-api
chrome-extension/ MV3 "Job Assistant" extension (own build + README)
blog/ Docusaurus site + the `press` Python pipeline
scripts/ seed, scrape, generators, deploy.sh
drizzle-d1/ D1 migrations (drizzle/ is legacy Postgres, kept for history)
specs/ Product/research specs
services/llamaindex/rag_service/agents_lab/ is a hands-on, paper-grounded companion to
the agent lessons β each canonical pattern as a runnable LlamaIndex Workflow control loop.
It lives inside the RAG service so the patterns share its LLM factory (DeepSeek via the
Cloudflare AI Gateway), shared-secret auth, and deploy. DeepSeek is the only paid API;
orchestration (LlamaIndex Workflows), embeddings (FastEmbed) and web search (DuckDuckGo)
are open source.
Run a pattern through the app's proxy β no local Python needed:
curl -sX POST https://ai-engineer-roadmap.xyz/api/agents-lab/run \
-H 'content-type: application/json' \
-d '{"pattern": "react", "task": "What is 17 * 23, minus 100?"}'Patterns: react, tool-use, reflexion, plan-execute, lats, supervisor, tot,
self-refine, rewoo, llm-compiler, debate (GET /agents/patterns lists them).
Step through pre-recorded runs at /agents-lab, or read the lessons: ReAct, Reflexion, Plan-and-Execute, LATS, Supervisor.
pnpm dev # start on :3002
pnpm typecheck # tsc --noEmit
pnpm build # production build
pnpm deploy # scripts/deploy.sh β Vercel (prebuilt)
pnpm db:push / db:studio # sync schema to D1 / open Drizzle Studio
pnpm seed / seed:courses # seed lessons / course catalog
pnpm scrape:udemy # scrape AI/ML Udemy topics β external_courses
pnpm generate <slug> # generate a lesson (TypeScript driver)
pnpm generate:dry <slug> # preview without saving
pnpm generate:batch # generate all missing lessons
pnpm review:courses # batch-review unreviewed courses
pnpm backend:test # pytest over roadmap-kgThe Python content, audio and grounding lanes are driven by the Makefile
(~540 targets). There is no help target β list the documented ones with:
grep -E '^[a-z0-9-]+:.*##' MakefileThere is no long-running backend server in this repo. The tutor calls the
separately-deployed rag_service; everything else runs offline as one-shot
Python commands that write data/ and Cloudflare D1. The :rust suffixes below
are legacy names β the Rust implementations were ported to Python and the
scripts now shell out to uv run python -m kg.*:
pnpm generate:rust -- --slug <slug> # kg.gen_article β article generation
pnpm prep:loop -- --slug <slug> # kg.gen_app_prep_loop β prep β validate β D1
pnpm prep:rust / prep:owner:rust # kg.gen_app_prep / _owner (artifact only)
pnpm prep:memorize -- --slug <slug> # kg.flashcard_gen β flashcards
pnpm review:courses [--dry-run] # kg.review_courses β data/courses.db
pnpm audio:meta <args> # kg.audio_meta β markdown β AudioMeta JSON
pnpm export:content # kg.export_content β data/content/*.jsonprep:loop reads the job description from data/app-prep/<slug>.json,
regenerates interviewQuestions (4 ## sections) and techStack, then
strictly validates before touching anything: β₯200-char questions, β₯4
sections, non-empty techStack whose every category β CATEGORIES, valid
relevance. Only then does it UPDATE the D1 applications row (via kg.d1).
It exits non-zero and leaves D1 untouched on a bad artifact. --no-db
regenerates and validates only.
.env.example is the source of truth β copy it to .env.local and fill in.
The minimum to boot:
PORT=3002
NEON_AUTH_BASE_URL= # hosted Neon Auth (Better Auth); unset β owner routes fail closed
NEON_AUTH_COOKIE_SECRET= # session-cookie signing secret
AUTH_DEV_OWNER=1 # dev-only: treat local requests as the owner
DEEPSEEK_API_KEY= # LLM access
DEEPSEEK_BASE_URL= # Cloudflare AI Gateway compat endpoint
LLAMAINDEX_SERVICE_URL= # Python rag_service (tutor); unset β tutor offline
LLAMAINDEX_SHARED_SECRET=
CLOUDFLARE_ACCOUNT_ID= CLOUDFLARE_AUDIO_D1_ID= CLOUDFLARE_D1=
R2_ACCOUNT_ID= R2_ACCESS_KEY_ID= R2_SECRET_ACCESS_KEY= R2_BUCKET_NAME=
NEXT_PUBLIC_R2_DOMAIN= # audio CDN domain
DASHSCOPE_API_KEY= # Qwen3-TTS, only for audio generation runsFrom zero to production AI engineer β one lesson at a time.