Lead Machine Learning Engineer | Victoria, BC
| Artifacts | |
|---|---|
| Model | stevebottos/qwen3.5-0.8b-find-and-interpret |
| Code | stevebottos/find-and-interpret |
In my last post I talked a little bit about the issues with a few popular RAG approaches. I settled on a two key points to keep in mind:
For this post, we’ll be more focused on the generation part, which falls more under the domain of Visual Question Answering (VQA). In standard VQA, you would usually start with a query, retrieve pages that are likely relevant to that query, and then send each full page in context + the query downstream to whatever model you’re using to synthesize answers, something like this:

It’s worth mentioning that simple, single-page VQA is something that small language models can already excel at. But, you still pay a cost for:
As the number of pages grows, naturally so does your token cost. If you know that the answer might be spread across multiple pages, you must either:
What would be convenient here is to have a quick way to capture only regions that are relevant to your queries across pages using a small, local model. Now, you can send only a handful of useful crops downstream. A small language model can handle this focused information better than the whole pages from which they came, at a lower latency and token cost, and with artifacts that are easy to store and audit.

There are a number of ways in which you might extract these relevant regions, ie: run a layout detector first, then a small relevant/not-relevant classifier as a coarse filter, then feed crops into a local small language to gauge relevance. I want to try to condense as much of the pipeline into a single, relatively tiny model as possible.
For a PoC, the idea is:
Qwen/Qwen3.5-0.8B)
For a PoC, I’m really only concerned with establishing a baseline approach that seems to work decently in order to understand where to head next. Training data was synthetically generated from PubLayNet for no particular reason besides I had it on disk and it comes with regions out of the box. I generated 433,392 question+region pairs across 71,541 document pages. The model was fine tuned end to end on this data with randomly selected negative samples (question+irrelevant page/region pairs, randomly generated). Both tasks were trained jointly, upsampling “find” samples to account for the proportionally higher number of question+region “interpret” crops compared to their question+page “find” counterparts.
Since training was done on PubLayNet, the model saw a lot of medical content and zero of anything else. To get a feel for how it generalizes, I ran it against a handful of out-of-distribution PDFs (papers, technical reports) sourced from Huggingface’s daily papers, and read through the full find/interpret trace for each. I picked four to show here, specifically for questions where the answer’s evidence was spread across multiple pages rather than sitting on one. Each link below is the full run: every page checked, every crop, and the model’s stated reasoning for keeping or discarding it.
Good — bdh_cq_in_context_learning (full report) Question: “What pass@2 score does the 150M-parameter configuration reach on ARC-AGI-1, and at what per-task inference cost?” The paper restates its own headline number (29.5% pass@2, ~$0.007/task) six times across the abstract, intro, results, and conclusion, in slightly different phrasing each time. The model correctly flagged all six.
Good — PMC5849940 (full report) Question: “How many infants worldwide were estimated to be exposed to maternal GBS colonization at delivery in 2015?” Unlike the example above, this isn’t a repeated sentence — it’s a genuinely distributed answer across multiple document pages. One page defines what’s being estimated, another states the total (16.4–27.0 million), and later pages break that same total down by region and outcome. No single page has the full picture. This is the case the iterative loop is actually for.
Bad — zetta_embodied_harness (full report) Question: “In Figure 7’s Goal-T6 transfer panel, what cumulative success rate is reached at the final (rightmost) round?” The model found the right panel, then kept going: it pattern-matched “success rate at final round” against three other, unrelated panels — Goal-S5, PnP-Stove, TurnOffStove — and marked all of them relevant too. Right shape (a line chart ending in a percentage), wrong content.
Bad — statem_terminal_bench_harness_scaling (full report) Question: “What kind of tasks is StateM intended for?” An open-ended question with no single located answer to sprawl across, so nearly every page that mentions StateM by name got pulled in as relevant, including a results table and a benchmark-cost figure that describe what StateM measured, not what it’s for. This one has the widest page-count of any example here, but it’s inflated sprawl, not real synthesis — worth contrasting directly against the two good examples above.
In general the results are decent for a <1B parameter model. There are some hiccups but that’s expected. I have some hunches that I want to test and some things I want to try next to put some polish on this, such as: