Engineering · AI
The Proof on Page 12: From File Upload to Grounded Answer
One question about an uploaded chapter, followed from the upload to an answer that cites the file.
A student drags a 42-page PDF of Chapter 7 into the chat and types: "Explain the proof on page 12." (The file and the question are synthetic.) The first version of Cereby's file chat put every byte of the file into the prompt.
One chapter could fill the model's whole context window (the most text it can read at once), so the file was cut off at a fixed token count, and the answer leaned on whatever sat near the cut rather than on the page the student asked about. On large PDFs a question cost $1.50 to $2.00 in tokens, and students with large files burned through their coin balance in two or three questions.
This post follows that question through the file pipeline, the path that by April turned an uploaded PDF, screenshot or pinned note into compressed, query-relevant context the model could cite: store the file, extract its text, compress it to a token budget, and ground the prompt in what is left. What made it tractable was treating parsing and compression as separate problems, each debugged on its own. A PDF that parses cleanly can still blow the budget, and a scanned page that needs OCR (optical character recognition, reading text out of an image) can fit in 400 tokens once extracted. Here is the same question on the path we landed on:
Selectable text can be nonsense
The upload checked the student's tier and coin balance and stored the file before anything read it (video and audio went to transcription instead). Storing first meant a file could be parsed again without a second upload, and that mattered more than we expected, because some PDFs carry selectable text that is garbage. A few academic publishers embed fonts that extract as valid Unicode and read as nonsense. Rather than guess, we let the student switch on "Use OCR for file parsing" and parse the same file again in place. The resource kept its ID; only its text changed:
OCR ran on the server with a vision model, which read the equations, multi-column pages and diagrams that Tesseract in the browser had failed on.
An attachment outranks the classifier
Next the message was routed: did the student want a quiz, notes, or an answer? Cereby's intent classifier (Cells Could Mean Three Subjects: Inside Cereby's Intent Classifier) normally decided with a model call. An attachment short-circuited it. A file-intent detector ran first and matched keyword patterns in priority order, so "quiz me on Chapter 7" built a quiz from the file. Our question matched nothing more specific and took the default, an answer from the file, with no model call to add latency. A pinned note, by contrast, was context rather than a routing signal: the full classifier ran and read the pin, except when one pinned assessment met wording like "how did I do", which went straight to performance analysis:
Eight thousand tokens, shared by every file
Compression is where the economics live. Every attached file shared a default budget of about 8,000 tokens, with a floor of about 400 per file so that none was starved: one file got all 8,000, and five got about 1,600 each. A file under its share passed through whole, so most screenshots and short notes never reached the compressor. A 42-page chapter is over, so it went down a ladder:
The page-level step, the first generation (The $1.50 Question: Hierarchical Context Compression in File Chat), cut roughly 92% of the tokens on a representative large textbook while keeping the pages the student asked about. The chunk-level step, current in April (Everything but the Answer: Query-Aware Compression for Oversized Pages), scored each chunk against the question with an embedding service (which turns text into vectors, so that passages with similar meanings land close together). It kept an answer in paragraph 47 from losing to a cut at paragraph 12. A pinned item that embedded a source, such as a quiz built from a textbook chapter, got the same treatment with a separate 6,000-token budget.
The prompt says where the answer must come from
The excerpts went into the student's message as a clearly delimited block, between a list of the attached files and the question, with strict grounding rules beside them. For our question:
Prompt for "Explain the proof on page 12"
files Chapter 7, 42 pages
content the compressed, query-relevant excerpts, fenced off from the rest
question Explain the proof on page 12
rules answer only from the attached file
cite specific pages and sections
say so when the file does not contain enough
Pinned materials reached the same prompt by a longer route: loaded from the database, their embedded sources compressed against the question, merged in alongside the session, memories and thread history.
The boring failures
Almost none of the incident time in this work came from the model. It came from small misalignments that never show up in a design doc.
The empty file block. When a parser returned no text (an encrypted PDF, an empty image), the model still got the prompt, with an empty file block, and answered from nothing. A student cannot tell a hallucinated answer from a wrong one. We made the pipeline drop such files, return a clear error, and call the model only when an attachment had readable text. It was one of the simplest fixes we shipped and one of the most important:
Two hundred vision calls. A 200-page scanned PDF with OCR on meant 200 vision calls, four pages at a time. We added per-file page limits tied to the student's tier, and logged pages sent, chunks selected and model tier, so a cost spike would show up in monitoring before it showed up on an invoice.
The compressor that chose the wrong chunks. Twice, a change to the chunking made the compressor pick irrelevant chunks. Token accounting caught both: a spike in tokens per file-chat message, out of line with the question, flagged each regression before a student reported it.
Rasterizing on someone else's machine. Turning PDF pages into images on the server behaved differently across serverless environments. That fragility is why OCR stayed opt-in rather than the default.
Before and after
| Before | After | |
|---|---|---|
| File chat cost | $1.50 to $2.00 a question on large PDFs | Roughly 92% fewer tokens on a representative textbook |
| Compression | Truncate at a fixed token count | Pages first, then chunks ranked against the question |
| OCR | Tesseract in the browser | A vision model on the server (Tesseract kept for light OCR in the editor) |
| Formats | PDF and images | Also .docx, .xlsx, .pptx, audio (transcribed) and video (transcript plus transcribed audio) |
| Student control | None | An OCR toggle, explicit coin costs, model tier selection |
| A file with no text | A hallucinated answer | A clear error, and no model call |
File chat went from a feature students avoided to a default study surface. Students started uploading whole course readers and asking follow-ups across sessions, which they had not done while answers cited the wrong page.
What this taught us
OCR is a product feature, not a fallback. Giving it a toggle, a cost and its own monitoring surfaced quality problems that a silent automatic fallback would have hidden.
A token budget is a product decision. The 8,000-token default, the per-file floor and the coin cost were product choices, and students noticed compression that was too aggressive before we did.
Design for the empty case. A missing check for readable text produces a hallucinated answer, and a student does not tell that apart from the model being wrong.