Back to projects
ReCall-nx

ReCall-nx

Review 320 hours of lectures in 16, then prove the recall actually stuck.

31 August 2026

Python · TypeScript · MIT

Why I built it

Video-Distiller ended with a vector database and a promise: the next project would consume it. This is that project.

The industry now asks for too many technologies at once, and nobody holds all of them at full sharpness at the same time. But knowledge you once worked with isn't gone. I spent years in C++; it's in there, it's just slow to reach. The wrong fix is to pay for it twice. I don't need to re-learn 320 hours of material; I need to recall it, and recall should cost a fraction of the time I already invested.

I tried paying twice anyway. I rewatched the courses, more than once. It didn't work: 160 hours at 1× is a full-time month, the pace is set by the lecturer rather than by what I'd forgotten, and by the end I'd lost the beginning again. The database from Video-Distiller solved access: I could ask about any lecture and get a grounded, timestamped answer in seconds. But asking requires already knowing what you've forgotten, and after seven years away, the gaps are exactly the part you can't see.

So the app does three things in order. It walks me back through the material, compressed. It answers questions when I have them. And it asks me questions, to check whether the review actually worked.

That gives it its name and its one control. Review at : your review budget is the original hours divided by n. Two courses of 160 hours each at the default 10× is a 16-hour review. Push n higher and it shows fewer, denser concepts and paces you faster; pull it down and you get full notes and code. Then it tests you. Review fast, then prove it stuck.

What it does

ReCall-nx is a thin FastAPI backend over the knowledge base, with a React front end. Almost everything hard was already computed by the pipeline; the app's job is to expose it at the right cost and in the right order.

The API is split by what each call costs:

  • Free, precomputed on disk: /collections and /concepts, the review material itself.
  • One embedding: /search, hybrid retrieval (dense vectors + BM25, fused with RRF) over the notes.
  • Embedding + chat: /ask, RAG answering only from the notes, every answer cited as [collection/video @ hh:mm:ss].
  • On-demand LLM: /quiz/question generates a recall, multiple-choice, or code question from a concept; /quiz/grade marks your answer against the notes.

On my machine, three collections are live: C (6,531 concepts), C++ (6,529), and an AWS ML certification course (832), 13,892 concepts in total, all searchable, reviewable, and quizzable. C and C++ were distilled from video by Video-Distiller (project post); the AWS course came from a PDF, through a local text-distiller sibling. You can distill your own videos with the Video-Distiller repo and point ReCall-nx at the result.

The front end follows the order you'd actually use it in. First, a source board: pick which courses are in scope for this session.

Source board: choosing which courses to review

Then the n× control, which shows what your chosen speed buys: budget in hours, depth tier, and how many concepts survive the cut.

n× control: speed slider with budget, depth tier and concepts kept

From there, two modes that mirror the two directions of the conversation. Ask recall is you asking: a cited answer, self-graded.

Ask recall: a question answered from the notes with a timestamp citation

Quiz me is the app asking: it generates a question, you answer from memory, it grades you and hands back the timestamp so you can go check.

Quiz me: a generated question, my answer, and the LLM-judge grade with citation

The n× model

This is where the project stopped being "mostly UI." Compressing a course by a factor of n means deciding what to drop, and the obvious signal for that turned out to be useless.

Importance. My first instinct was dependency in-degree: a concept that many other concepts build on must matter. On this corpus, 99% of concepts tied at zero. Lecture notes don't form a dense dependency graph; they form a sequence. What actually separates foundational material from a passing mention is how many distinct lectures teach it. A concept taught in three or more lectures is foundational. One that appears once, in passing, is the long tail. Importance became rule-based on that count, and it held up across all three courses.

Depth tiers. Every concept renders at one of four depths, Deep, Standard, Skim, or Flash, chosen by n. Raising n does two things at once: it raises the importance floor, so the long tail drops first, and it shortens the per-card pacing timer so the whole review fits the budget. The two levers together are what make the hours arithmetic honest rather than aspirational.

Review order. A walkthrough in course order first, because the original sequence encodes prerequisites the graph didn't capture, then a priority pass over everything above the floor.

The course that broke my assumptions

The C and C++ collections came from video. The AWS ML course came from a slide deck: a PDF, no audio, no timestamps. It was the only document-sourced collection, and it surfaced three bugs that were video-shaped without my having noticed:

  1. "Dropout" was deleted as course admin. A filter that strips enrolment reminders and housekeeping was tuned on lecture transcripts, where "drop" means leaving the course. In an ML deck it's a regularisation technique. The filter removed it.
  2. 724 concepts sank to the bottom. Importance had a code-presence term, on the reasonable theory that a concept the lecturer demonstrated in code matters more. Slide decks have almost no code. Nearly the entire course was scored as unimportant.
  3. Slide 582 displayed as 00:09:42. The citation formatter assumed every source location was a timestamp.

None of these were hard to fix. All of them were invisible until the second kind of source arrived. The model is now genuinely course-type-agnostic, and I'd rather tell that story than "it worked first time."

It grades its own homework

The first version of /quiz/grade used keyword matching against the reference note. A correct answer, phrased in my own words, scored 17%. Switching to an LLM judge that reads the note and the answer together, and is explicitly instructed to credit paraphrases, took the same answer to 75%. Keyword matching was measuring vocabulary recall; the judge measures concept recall, which is the thing I actually wanted to know.

The grader stays grounded: it marks against the distilled notes, not against the model's own knowledge, and every question carries the citation it was generated from. If the grade looks wrong, the source is one click away.

The decisions I'd make again

The knowledge base lives in neither repo. The KB is the interface between the pipeline and the app, so it shouldn't sit in either project's tree. It moved to a neutral root (kb/, synthesis/, media/) behind one environment variable. I proved the boundary by deleting the pipeline project from disk entirely and watching /collections, the slide viewer, and a live /ask keep working. What remains is a library dependency, the same way the app depends on fastapi, not a dependency on the other project.

Query embeddings must match ingest. text-embedding-3-small, 1536 dimensions, on both sides. Any drift here fails silently: search returns results, they're just wrong. It's pinned in one place and documented in the prep contract.

One store, shared. Embedded Qdrant holds an exclusive lock, so the backend opens the store once and every endpoint borrows it. Obvious in retrospect; the first crash taught it.

Mock-first front end. The React app was built against mock data before the backend existed, and it still runs standalone with mocks by default. That made UI iteration free, and it means the public repo runs on a clean clone without a knowledge base attached.

Bring your own KB. The public repo is de-branded: it documents the Qdrant layout and the precompute contract, links the public Video-Distiller as one way to produce a compatible KB, and assumes nothing about what's in yours.

What's next

The remaining coupling is a small library dependency for hybrid search and RAG, which will collapse to qdrant-client plus an OpenAI client so the app has no lineage to the pipeline at all.

After that, a change of domain, and a lighter one. The next project still solves a personal problem, but this time it's not only mine, it's mostly my loved ones': Turkish–English translation for the way people actually talk, idiom, tone, and the things my mother says that no dictionary will help my partner with. Working title: Chicken Translate 🐔. Same pattern as before, the project first, then the post.

If you point ReCall-nx at a KB from a different domain, or run the n× model over something that isn't a lecture course, I'd like to hear where the assumptions break next. The repo is MIT, and issues are open.