work / hypatia

Hypatia

MBPrep's content engine: raw exam material in, a solved and blind-verified question bank draft out, on Cloudflare.

Years
2026
Status
live

What it is

The first version was a general agent running the whole pipeline in prose. It guessed at duplicates, misread which exam a folder belonged to, and lost runs without a word. Hypatia replaced it with a rule: code decides, models do typed jobs, a judge makes the human calls.

Read more

Code decides what to process, what is a duplicate (MinHash over character shingles, with LSH) and where a file belongs. Models only do typed work, extract, reword, solve, verify, through 18 skill packs, and every output is checked against one of 21 JSON-Schema contracts before it is kept. A judge model makes the calls a person would, with the thresholds applied in code.

Every write to D1 or R2 goes through one writer that records who decided it and the row before, so any decision can be undone. Each intake has a budget held in a Durable Object that pauses the run at its cap, and a ModelGate Durable Object gives Z.ai five concurrent slots: a call waits up to two minutes for one, then falls back to DeepSeek.

Banks adopted from the old laptop pipeline came out byte-identical to the laptop's exports, measured on 4 October 2026.

Stack

  • TypeScript
  • Cloudflare Workers
  • Cloudflare Workflows
  • Cloudflare Queues
  • Durable Objects
  • D1
  • R2
  • Vectorize
  • Cloudflare Containers
  • AI Gateway
  • Z.ai GLM
  • DeepSeek
  • Python

Counted

  • 18skill packs
    apps/hypatia skill registry
  • 21JSON-Schema contracts
    apps/hypatia contracts
  • 5Z.ai concurrent slots
    ModelGate Durable Object
  • 13D1 migrations
    apps/hypatia/migrations

How it works

The system as a machine on a bench. Hover or use the arrow keys to drive it.

Document cards, one per file the intake foundZ.ai slots in use (5 at most); a full gate sends the call to DeepSeekRejects: a duplicate paper, or a card the ledger's undo took backHypatia, MBPrep's content engine on Cloudflare: files are hashed into content-addressed R2, extracted in a container, deduplicated by MinHash over 5-character shingles (32 LSH bands, same paper at Jaccard 0.85), routed to one of four programmes, worked by 18 skill packs checked against 21 JSON-Schema contracts, sent through a 5-slot Z.ai gate that overflows to DeepSeek (measured at about 77 times the cost per call), held under a per-intake budget that pauses at its cap, judged against a 0.9 threshold applied in code, and written by one writer that logs a before-image so every write can be undone.

The path through it

  1. Register and fetchEvery file name is checked against the register before anything downloads; bytes are hashed into content-addressed R2.
  2. ExtractA Python container (LibreOffice, PyMuPDF, Tesseract, no internet) behind a queue reads every page, scans included.
  3. Deduplicate and routeMinHash + LSH over character shingles and an FTS5 page index; folder consensus decides the programme and subject.
  4. 18 skill packsRead, extract, map, reword, picture-check, solve, verify-blind, adjudicate. Each output must match its JSON-Schema contract or it is retried.
  5. Model gate and budgetZ.ai gets 5 concurrent slots; a call waits up to 120 s for one, then falls back to DeepSeek. A per-intake Budget Durable Object pauses at its cap ($5 by default).
  6. Judge, write, draftAct above 0.9 or below 0.1, in code; one writer that logs the actor and the before-image, near-duplicates in Vectorize, then a bank draft for MBPrep.