06

Always-Generative Bytes

A build retrospective on the adaptive knowledge graph behind Amal, a kids' Arabic-learning app used by 95,000+ families. • Day 6 fills in the last engine stage: generating the actual lesson from a concept on demand, instead of hand-authoring every lesson in advance.

From a concept to a playable lesson

Day 5 handed us a per-child subject tree whose nodes are still just concept references — "the sound /b/", "count to 5". A child cannot play a concept reference; they need an actual lesson: something to look at, something to answer, something to say out loud. In Amal a single playable lesson is called a byte, and the component that turns a concept into a byte is GraphByteGenerator (which wraps an existing lesson-builder called TCBG, the ConceptByteGenerator).

The defining choice here is in the word generator. Rather than storing a finished, hand-made lesson for every concept, the byte is generated from the concept on demand — assembled at the moment the child is about to play it, from the concept's data and assets. This is the "always-generative bytes" idea, and it is what makes the one-graph model pay off.

The diagram shows the last hop of the engine: a concept goes in, GraphByteGenerator runs, and a playable byte comes out — created at play time, not pulled from a shelf of pre-written lessons.

   ONE CONCEPT  ->  ONE GENERATED BYTE (on demand)

   concept: sound /b/
        │  GraphByteGenerator
        ▼
   ┌───────────────────────────────┐
   │  BYTE  (a playable lesson)     │
   │   built now, from the concept  │
   │   + its assets (Day 2)         │
   └───────────────────────────────┘

A byte is made of bits

A byte is not one screen; it is a short sequence of smaller steps called bits. A bit is a single interaction — show something, ask something, have the child say something. Stringing a few bits together into one byte is what takes a child from "seeing" a concept to "using" it in the space of a minute or two.

The reason to have several bits per byte is pedagogical: really learning a concept means meeting it in more than one way. You see it, you are tested on recognizing it, you produce it yourself. A byte walks the child through that arc in order, and — crucially for the next few days — only some of those bits are scored (they judge a right/wrong answer), while others are just exposure.

The diagram shows the shape of an Arabic byte: an expose bit that simply shows the concept, a scored recognize bit that tests whether the child can pick it out, and a speak bit that has the child produce it. That "only the scored bit judges" detail looks minor now; Day 9 shows it is the hinge the entire adaptivity signal turns on.

   ONE BYTE = A SEQUENCE OF BITS

   ┌────────┐   ┌────────────────────┐   ┌────────┐
   │ expose │──►│ recognize (SCORED) │──►│ speak  │
   └────────┘   └────────────────────┘   └────────┘
     show it       test recall            produce it

   only the SCORED bit judges right/wrong; the others build exposure

The bit recipe differs by domain

Different subjects need different practice shapes, so GraphByteGenerator uses a different bit recipe per domain — the ordered list of bit types that make up a byte for that kind of concept. The recipe is a small piece of configuration, not a hand-written lesson, so one recipe generates bytes for every concept in its domain.

Two real recipes from Amal, side by side:

DomainBits per byteThe recipe (in order)
Arabic3expose → recognize (scored, e.g. letter_recognize) → speak
Math4fragment → select-sum (scored, e.g. addition_select_sum) → word-build → speak

Reading the table: an Arabic byte is a three-bit arc (show, scored recognize, speak); a math byte is a four-bit arc (break the problem into a fragment, scored pick-the-sum, build the word for the number, speak it). In both, exactly one bit is the scored one that judges correctness — the rest are exposure and production. Change a recipe once and every byte in that domain changes; that is the leverage of generating instead of authoring.

Why generate instead of hand-author

Generating bytes is more up-front engineering than just writing lessons, so it has to earn its place. It does, for four reasons that a hand-authored deck cannot match:

  • Marginal cost near zero. Once a domain has a recipe, adding a new concept to the graph automatically gets a byte — no one writes a new lesson. A curriculum of thousands of concepts does not need thousands of hand-built lessons; it needs a handful of recipes.
  • Consistency for free. Every Arabic byte follows the same expose → recognize → speak arc because they all come from one recipe. Hand-authored lessons drift in quality and structure; generated ones cannot.
  • Freshness instead of a fixed deck. Because a byte is built at play time, it can vary its specifics (which distractors, which example word) rather than replaying an identical frozen card, keeping practice from going stale.
  • It is the join that makes the graph pay off. The generator is what connects the graph (Day 2) and the overlay-driven tree (Days 4–5) to something a child can actually do. Without it, "one graph" would still need one hand-made lesson per node — and the whole reuse argument (Day 7) collapses.

The diagram completes the pipeline begun on Day 4: from the graph and a child's overlay, through the frontier and the tree, to a generated byte the child plays — every stage pure or configuration-driven, nothing hand-authored per concept. Day 7 steps back to the payoff this design unlocks: the same graph feeding not just the app, but every surface Amal ships.

   THE ENGINE, END TO END

   graph + overlay
     └► LearnerFrontier ─► GraphTreeBuilder ─► GraphByteGenerator ─► byte
        (ready set)         (per-child tree)     (bits, on demand)   (play!)

Key takeaways

  • A byte is a single playable lesson; GraphByteGenerator (wrapping the TCBG ConceptByteGenerator) builds it from a concept on demand rather than pulling a pre-authored lesson off a shelf.
  • A byte is a short sequence of bits (single interactions) that walk a child from seeing a concept to producing it; only some bits are scored (judge right/wrong) while others build exposure.
  • The bit sequence is set by a per-domain recipe — Arabic: expose → scored recognize → speak (3 bits); Math: fragment → scored select-sum → word-build → speak (4 bits) — so one recipe generates bytes for every concept in the domain.
  • Generating beats hand-authoring because the marginal cost of a new concept's lesson is ~0, structure stays consistent, practice stays fresh, and it is the join that makes the one-graph model actually reach the child.
  • With this stage the engine is complete end-to-end: graph + overlay → frontier → tree → generated byte, with nothing hand-authored per concept.

Checklist

  • [ ] I can explain what a byte is and what "generated on demand" means versus a pre-authored lesson.
  • [ ] I can explain what a bit is and why a byte strings several bits together.
  • [ ] I can give the Arabic and Math recipes and point out which single bit in each is the scored one.
  • [ ] I can give at least three reasons to generate bytes instead of hand-authoring lessons.
  • [ ] I can trace the full engine pipeline from graph + overlay to a playable byte.