免費 AI 智能體堆疊的兩類故障:一堂歸因實作課
Free Breaks in Two Places: A Bootcamp Lab on Attributing Agent Failures
Charlie Xu 設計了一堂 90 分鐘的配對實作課,讓學員用兩項探測分辨免費 AI 智能體堆疊中的主機端與模型端故障。課程以 MonkeyCode 的免費模型及伺服器選項作實驗環境,結合 TCP 連線探測、/models 請求、故障歸因表及四個檢查點。探測只涵蓋可達性和驗證,不評估輸出質素、負載下延遲或成本;作者披露文章是 MonkeyCode 產品推廣的一部分。
Free Breaks in Two Places: A Bootcamp Lab on Attributing Agent Failures
A student's agent run dies on a free stack. They spend forty minutes rewriting prompts. The actual cause? The box went to sleep. Or the reverse — they restart the server three times to fix an expired key.
That's the whole lab in one sentence: on a free stack, "it broke" is two different bugs wearing the same shirt.
This is the lab I hand out for a 90-minute pair session. It teaches failure attribution before it teaches anything about prompts, and it works because attribution is cheap to practice and expensive to get wrong.
What the lab is, and what it isn't
- It is a drill: given a broken run, name the layer before you touch a line of code.
- It is two probes, one table, four checkpoints, and a rubric you can grade in ten minutes.
- It isn't a benchmark. We measure nothing about model quality here.
- It isn't a vendor review. Free tiers change; the skill of telling layers apart does not.
Two budgets, two clocks
Here's the framing that makes the lab click. A free agent stack usually gives you two independent things, each with its own way of running out:
- A metered allowance — you consume work, and the work has a ceiling.
- A hosted runtime — a machine exists for you, and machines have a lifetime.
They fail differently. One fails as a refusal, the other as an absence. And the fix for a refusal (slow down, check credentials) is not the fix for an absence (wake it, reconnect).
So — have you ever restarted a server to fix a bad API key? Congratulations, you've debugged the wrong clock.
For the lab substrate I use MonkeyCode, an open-source project whose operator-supplied brief to me describes free model access plus a free server option. That combination is exactly what the drill needs: two failure surfaces, no invoice. The brief also cites a free token allowance — treat any such number as perishable. Date-stamp it on your handout and re-check the current terms before class, because nothing dates a syllabus faster than a quota printed in September.
Disclosure: This article was prepared as part of MonkeyCode's product outreach.
Artifact 1 — the two-probe script
One script, two probes, one verdict. Standard library only, so nobody spends the lab fighting pip. This version is provided as a lab artifact and was not executed for this post — you run it, you paste the JSON into your log.
#!/usr/bin/env python3
# lab_probe.py — one run, two probes, one verdict.
import json, os, socket, time, urllib.request
BASE = os.environ["PROBE_BASE_URL"].rstrip("/") # model endpoint
KEY = os.environ.get("PROBE_API_KEY", "")
HOST = os.environ["PROBE_HOST"] # the free server
def run(label, fn):
t0 = time.monotonic()
try:
detail = fn()
return {"probe": label, "ok": True, "detail": detail,
"seconds": round(time.monotonic() - t0, 2)}
except Exception as exc: # lab code: we want every failure
return {"probe": label, "ok": False,
"detail": f"{type(exc).__name__}: {exc}"[:240],
"seconds": round(time.monotonic() - t0, 2)}
def host_probe():
port = int(os.environ.get("PROBE_PORT", "443"))
with socket.create_connection((HOST, port), timeout=5):
return "tcp-handshake-ok"
def model_probe():
req = urllib.request.Request(BASE + "/models")
if KEY:
req.add_header("Authorization", f"Bearer {KEY}")
with urllib.request.urlopen(req, timeout=20) as resp:
return f"http-{resp.status}"
def verdict(host, model):
if host["ok"] and model["ok"]:
return "both-up: the bug is yours, not the stack's"
if not host["ok"] and not model["ok"]:
return "both-down: network or credentials before anything else"
if not host["ok"]:
return "host-side: asleep, restarted, or gone"
return "model-side: allowance, key, or endpoint — not the box"
if __name__ == "__main__":
h, m = run("host", host_probe), run("model", model_probe)
print(json.dumps({"host": h, "model": m, "verdict": verdict(h, m)}, indent=2))
A warning to students: seconds is a hint, not a diagnosis. A slow model probe and a dead host probe can both take ten seconds. Classify on the ok flags first; use timing only to separate "slow" from "refused".
Artifact 2 — the attribution table
After the probes, the pairs fill this in from real failures. Empty cells are the point — a blank cell is a bug in their observation, not in the stack.
| Symptom | Host-side tell | Model-side tell | Cheapest next test |
|---|---|---|---|
| Run stalls with no output | probe times out, TCP refused | probe returns 4xx quickly | re-run lab_probe.py
|
| Sudden auth error | stop... nothing host-side explains it | 401/403 right after key change | re-check the key only |
| Worked yesterday, dead today | lifetime ended softly | allowance consumed | both probes, in order |
| Long silence, then success | cold start | throttling | log seconds twice |
| Fine locally, broken in the runtime | env vars missing there | endpoint not reachable | print env, never the key |
The four checkpoints
-
Green baseline (10 min). Both probes
ok: true, pasted into the log with a timestamp. No baseline, no lab. -
Break it on purpose (20 min). Point
PROBE_BASE_URLat a closed port → verdict must say host. Use a deliberately wrong key → verdict must say model. If the script can't tell sabotage apart, the script is wrong, not the stack. - Collect ten real failures (40 min). Every row gets probes, timestamp, and the layer you claim. Guessing is allowed; unrecorded guessing is not.
- Cold handoff (20 min). Swap logs with another pair. They predict the layer from the log alone. Your notes are the product now.
Stretch goals
- Add one retry-with-backoff, but only for the layer you can name. If it helps both, you've added jitter, not understanding.
- Count how many past failures in your own project were misattributed. That number is usually embarrassing and always useful.
- Swap in a different free host or model endpoint and confirm the table survives the swap.
The grading rubric
| Criterion | Points | What full marks looks like |
|---|---|---|
| Baseline exists and is timestamped | 4 | two ok: true probes, reproducible |
| Sabotage tests pass | 4 | verdict flips correctly both ways |
| Ten logged failures | 5 | probes + claimed layer + outcome per row |
| Handoff predicts correctly | 4 | partner's prediction matches your log ≥ 7/10 |
| Honesty about unknowns | 3 | cells left blank rather than invented |
| Total | 20 |
Notice what earns no points: a beautiful prompt. The rubric grades observation, and observation is what transfers to the next stack.
Limitations, and who should skip this
- The probe tests reachability and auth. It says nothing about output quality, latency under load, or cost.
- Classification is heuristic. Two-layer failures exist, and the script will pick one.
- Free-tier terms move. Any number in your handout needs a date and a link.
Skip this lab if your team already runs structured tracing with per-layer error budgets — you have this instrument, just bigger. Skip it too if your project is a single script you run once; attribution only pays off when you debug repeatedly.
Run it before you teach it
Set the three environment variables, run the script once against a live setup, then break it on purpose. If your own verdict surprises you, that surprise is the first slide of your class.
If you want to run checkpoints 1–3 without a purchase order, the free models and free server option in MonkeyCode are enough scaffolding for a first session — just verify the current terms the morning you print the handout.
來源:DEV Community · dev.to