Lectures about AI are cheap. Labs that test AI are rare.
If you teach a bootcamp, skip the "here's how LLMs work" slide deck and run a model evaluation lab instead. Students learn more in one afternoon of watching a model fail than in a week of theory. And the whole thing runs on a free server with a free model tier — no GPU budget, no cloud credit card, no excuses.
There's a recurring argument in the DEV feed lately: AI writes the code, humans review it, and almost nobody has a method for testing the reviewer. This lab is that method, compressed into a bootcamp assignment. It forces students to measure instead of opine.
Why a lab, not a lecture
Most AI assignments in bootcamps grade essays about AI, not work done with AI. Students write "I used an assistant and it was great" and get full marks. That teaches nothing.
A real assignment needs four things:
- A reproducible setup
- Clear checkpoints with definitions of done
- A stretch goal for the fast students
- A rubric that rewards honesty over green checkmarks
Here's the lab I put together for a zero-budget cohort. Steal it.
What you need (all free)
Three things:
- A server. MonkeyCode's free server option removes the "where do I run this" question entirely — the harness is a small Python script, not a GPU cluster. MonkeyCode is open source, so students can read the client code instead of trusting a README. (Disclosure: This article was prepared as part of MonkeyCode's product outreach.)
- Model access. MonkeyCode's free tier currently includes a 10M-token allowance. That's true as of August 2026. Verify it before your cohort starts, because free tiers move.
- A sandbox. One small git repo per task, each with tests that must pass.
Wait — a free server for a whole bootcamp? Yes. The harness is deliberately small. If it can't run on a free tier, the lab is too complicated, not the server too weak.
The lab layout
ai-eval-lab/
├── tasks/
│ ├── 01_fizzbuzz.md
│ ├── 02_refactor.md
│ └── 03_flaky.md
├── sandbox/
│ ├── 01_fizzbuzz/
│ ├── 02_refactor/
│ └── 03_flaky/
├── solve.py
├── run_eval.py
└── results/
The idea is simple. Each file in tasks/ is a prompt. solve.py sends that prompt to a model and prints a patch. run_eval.py applies the patch to a clean copy of the sandbox and runs the tests.
The model is a black box on purpose. Students wire solve.py to whatever endpoint they configured, and the harness never cares.
solve.py (student-written)
#!/usr/bin/env python3
"""Read a prompt from stdin, print a patch to stdout."""
import sys
prompt = sys.stdin.read()
# TODO: wire this to the model endpoint you configured.
# Keep the payload small. Print only the patch.
print("# your patch here")
run_eval.py (the harness)
#!/usr/bin/env python3
"""Tiny eval harness. Model-agnostic on purpose."""
import json
import subprocess
import sys
import time
from pathlib import Path
TASKS = sorted(Path("tasks").glob("*.md"))
SANDBOX = Path("sandbox")
RESULTS = Path("results")
def run_task(task: Path) -> dict:
prompt = task.read_text()
started = time.monotonic()
patch = subprocess.run(
[sys.executable, "solve.py"],
input=prompt,
capture_output=True,
text=True,
timeout=180,
).stdout
elapsed = time.monotonic() - started
work = SANDBOX / task.stem
subprocess.run(["git", "checkout", "."], cwd=work, check=True)
(work / "patch.diff").write_text(patch)
applied = (
subprocess.run(["git", "apply", "patch.diff"], cwd=work, capture_output=True).returncode
== 0
)
tests = subprocess.run(["pytest", "-q"], cwd=work, capture_output=True, text=True)
return {
"task": task.name,
"elapsed_s": round(elapsed, 1),
"applied": applied,
"tests_passed": tests.returncode == 0,
"test_output": tests.stdout[-500:],
}
if __name__ == "__main__":
RESULTS.mkdir(exist_ok=True)
results = [run_task(t) for t in TASKS]
(RESULTS / "latest.json").write_text(json.dumps(results, indent=2))
for r in results:
status = "PASS" if r["tests_passed"] else "FAIL"
print(f"{r['task']}: applied={r['applied']} tests={status} ({r['elapsed_s']}s)")
That's the whole harness. Under 60 lines, no dependencies beyond Python, git, and pytest.
Checkpoint 1: Setup (20 minutes)
Definition of done: python3 run_eval.py runs without crashing and writes results/latest.json.
Two rules:
- No hardcoded keys.
solve.pyshould read the API key from an environment variable. Secrets in a repo are an automatic fail. - Each sandbox task must be a clean git repo. The harness resets it with
git checkout .before every run.
If a student can't reach this checkpoint, they learn more from debugging the environment than from any lecture I could give.
Checkpoint 2: Baseline (30 minutes)
Run all three tasks. Record pass/fail, elapsed time, and the tail of the test output.
Then the real question: can you explain one anomaly?
If every task passed instantly, you didn't look hard enough. The flaky test exists precisely so a naive "all green" report is suspicious.
Checkpoint 3: Failure injection (the fun one)
This is where the lab earns its keep. After the baseline, I break the sandbox in three silent ways:
- I add a flaky test that passes about 50% of the time.
- I remove a dependency from
requirements.txt. - I make the prompt ambiguous — "fix the bug" without saying which bug.
Students must catch the fake pass. The model will happily report "all tests pass" while the sandbox is lying. That's the reviewer moment. Everyone talks about testing the reviewer; this is the assignment that actually does it.
Checkpoint 4: The report
One page. Numbers, not vibes.
- A results table (task, pass/fail, time, tokens if you tracked them)
- One graph. A bar chart of pass/fail per task is enough.
- One honest limitation. "We only tested three tasks" beats "the model is great" every time.
Stretch goals
Finished early? Pick one:
- Add a fourth task from a real open-source issue (check the license first).
- Track token usage per task and compute cost-per-pass. The free tier makes the dollar cost zero, but token discipline still matters.
- Run the same tasks against two different models and write a decision table.
- Turn the harness into a GitHub Action that comments on PRs with the eval result.
The grading rubric
| Criterion | Points | What passing looks like |
|---|---|---|
| Setup | 20 | Harness runs from a clean shell; no secrets in the repo |
| Baseline | 20 | Results recorded; student explains one anomaly |
| Failure injection | 25 | Student catches the fake pass and documents the root cause |
| Report | 20 | One page, one graph, one honest limitation |
| Stretch | 15 | One stretch goal, done properly |
Notice what's missing: "the model passed everything." That is not a criterion.
A student who reports a broken test and explains why it broke gets more points than one who reports all green. That's what makes the rubric fair — it grades the engineer, not the model.
Limitations and who should skip this
Honest limitations:
- This measures one narrow thing: can a model produce a patch that passes a given test suite? It says nothing about code quality, security, or maintainability.
- Three tasks is a demo, not a benchmark. Don't publish the numbers as a model comparison.
- Free tiers are a moving target. The 10M-token allowance and the free server are real as of August 2026, but re-verify before every cohort.
Skip this lab if:
- Your students can't be trusted with a sandbox yet. If they'll paste API keys into a public repo, fix that habit first.
- You need statistically meaningful model comparisons. Use a real eval framework with hundreds of tasks.
- You're looking for a sales pitch. This lab is useful precisely because it treats the model as an unknown that needs testing — not as a miracle.
The takeaway
So here's my question: what's in your bootcamp's AI module — a slide deck or a lab?
If it's a slide deck, steal this one. Three tasks, one harness, one rubric. Grab a free server, wire up the model, and see what breaks. The model will fail in front of your students, and that failure will teach them more than any demo.
That's the whole point. If you run it, I'd love to hear what the flaky test caught.
Top comments (0)