The cheapest, highest-leverage pattern in the course. Classify each incoming request, then send it down the right path: a rules handler, a small model, or a big model, with a fallback. No agent required.
What you'll build
A single handle(request) function that decides how much machine a request deserves. Trivial and known cases never touch a model. Easy language goes to a small model. Only genuinely hard requests reach the expensive one. Everything is wrapped in code you can test.
Like a receptionist who answers simple questions herself, sends routine ones to a junior, and only books the senior partner for the hard cases. Nobody pays partner rates to ask where the toilets are.
The shape
flowchart TB
IN["request"] --> R{"classify
rule or tiny model"}
R -- "known / trivial" --> RULES["Rules handler
no model call"]
R -- "easy language" --> SMALL["Small model"]
R -- "hard / ambiguous" --> BIG["Big model"]
SMALL --> V["validate + return"]
BIG --> V
RULES --> V
V -- "bad output" --> FB["Fallback: escalate or safe default"]
classDef code fill:#D6F5E3,stroke:#1F2937,color:#0E1726;
classDef big fill:#EEE6FF,stroke:#1F2937,color:#0E1726;
class RULES,V,FB code; class BIG big;
Starter code
Language-agnostic; TypeScript shown. The router itself is plain code, the only model calls are inside smallModel and bigModel.
import OpenAI from "openai";
const client = new OpenAI();
// classify with a cheap signal first: rules, then a tiny model if needed
function classify(request) {
if (matchesFaq(request)) return "trivial"; // exact / keyword rules, free
if (isShortAndSimple(request)) return "easy"; // heuristic, or a tiny classifier model
return "hard";
}
async function smallModel(request) { // TODO: cheap, fast model
const res = await client.chat.completions.create({
model: "gpt-4o-mini",
messages: [{ role: "user", content: request }],
});
return res.choices[0].message.content;
}
async function bigModel(request) { // TODO: capable, pricey model
const res = await client.chat.completions.create({
model: "gpt-4o",
messages: [{ role: "user", content: request }],
});
return res.choices[0].message.content;
}
async function handle(request) {
const route = classify(request);
let answer;
if (route === "trivial") {
answer = faqLookup(request); // TODO: no model call
} else if (route === "easy") {
answer = await smallModel(request); // cheap, fast model
} else {
answer = await bigModel(request); // capable, pricey model
}
if (!isValid(answer)) { // schema / sanity check in code
return fallback(request, route); // escalate or safe default
}
return { answer, route, cost: costOf(route) };
}
Do this, in order
You're done when
Go further
Wrapping up
Hand in your code and one line of output: the route-and-cost split over your test requests. This is slice one of the system you will build all six weeks.
handle() + the cost-split number. A repo link or a gist.Frame it as the highest-ROI thing they will build all course: "routing alone halves most teams' bill, and it is just an if-statement in front of two model calls." Insist the classifier uses rules first, the classic mistake is calling the big model just to decide which model to call.
Push on validation and fallback, that is the boundary lesson from S2 arriving early. If a student's router crashes on a weird input, that is the teachable moment: the model is a flaky dependency, wrap it. Ask everyone to post their cost split in the chat, the spread across the room makes the point better than any slide.