Skip to content

Question-bank pipeline: qbank/ (~2,500 questions, checked in, scales to 10k+) - #4

Merged
monikagadage merged 2 commits into
mainfrom
qbank-pipeline
Sep 4, 2026
Merged

Question-bank pipeline: qbank/ (~2,500 questions, checked in, scales to 10k+)#4
monikagadage merged 2 commits into
mainfrom
qbank-pipeline

Conversation

@monikagadage

Copy link
Copy Markdown
Owner

What

Replaces the one-off rag.ipynb scrape (single GitHub README → 1,715 questions, ~27 of the source's own labels, transcription noise and all) with qbank/, a real build pipeline. questions_db.json is now a checked-in build artifact (~2,500 questions, 10 canonical topics) so the app runs with no build step.

Three inputs, one shape {topic: [question]}

Input What it is
qbank/curated/*.txt ~1,700 questions hand-written for this repo, one per line, filed by the topic in the file name (OOP.txt, OOP.2.txtOOP)
qbank/sources.py 4 public GitHub lists (teamlead, learning-zone, Devinterview, sudheerj), each with a format-specific parser returning (question, category_hint)
qbank/taxonomy.py + --expand opt-in LLM generation per (topic, subtopic), cached & resumable — takes the bank past 10k

build.py orchestration

normalize (strip numbering/markdown/glued answers; reject headings & boilerplate) → _route to a canonical topic (source hint via CATEGORY_MAP, else the keyword classifier, else drop — unlabelled + unclassifiable is almost always noise, which is why the committed bank is ~2,500 not the ~3,300 raw) → global dedupe → bucket by topic.

  • dedup.py is blocked, not O(n²): exact pass on a punctuation-insensitive key, then bucket each question by its 3 rarest tokens and drop any whose token-set Jaccard with a kept one is ≥ 0.85. Question stop words (difference, between, vs, java, …) are stripped first so "difference between X and Y" and "X vs Y in Java" collide.
  • classify.py is an ordered-regex heuristic (most specific topic first), Java Core fallback, and build._route only trusts it when a rule actually fired.
  • expand.py takes any .invoke(str) -> .content object (fake in tests, ChatGroq in build.py); per-subtopic cache under qbank/.cache/expand/; a flaky call yields [] for that subtopic instead of aborting.
  • fetch.py caches downloads with a certifi TLS context (macOS system Python often can't verify GitHub otherwise).

Wiring

  • questions_db.json committed; removed from .gitignore; qbank/.cache/ ignored.
  • rag.ipynb → 2 cells that shell out to python -m qbank.build.
  • corpus.py docstring updated; no code change to the retrieval path.

Tests / CI

tests/test_qbank.py38 hermetic tests: normalize, classify, blocked-fuzzy dedup, all 4 source parsers, _route, expansion against a fake LLM (+ cache hit), and a full offline build.build() with fixture Markdown monkeypatched over qbank.fetch.fetch. Needs only pytest. 99 tests total.

Verify

uv run pytest tests/ → 99 passed. python -m qbank.build → 2,558 questions across 10 topics. python -m qbank.build --stats works.

Note

Licensing of the scraped repos is unclear (no LICENSE files) — documented in qbank/SOURCES.md, questions are used as a heavily-transformed seed with attribution, and the build still works with the scraped sources removed (curated + --expand only).

qbank/ builds questions_db.json (~2,500 questions, 10 topics, checked in)
from three inputs and normalizes / classifies / fuzzy-dedupes them:

- qbank/curated/*.txt  — ~1,700 questions hand-written for this repo,
  one per line, filed by the topic in the file name
- qbank/sources.py     — 4 public GitHub lists, each with a format parser
  returning (question, category_hint); hints map to canonical topics via
  CATEGORY_MAP, unlabelled/unclassifiable rows are dropped as noise
- qbank/taxonomy.py + qbank/expand.py — opt-in LLM expansion per
  (topic, subtopic), cached and resumable; `--expand` takes it to 10k+

build.py orchestrates: normalize -> route -> global dedupe -> bucket.
dedup.py is blocked (bucket by rarest tokens, then Jaccard >= 0.85) so it
stays ~linear. classify.py is an ordered-regex heuristic, labelled as such.
fetch.py caches downloads with a certifi TLS context.

- questions_db.json is now committed; rag.ipynb is a 2-cell wrapper over
  `python -m qbank.build`; corpus.py docstring updated
- tests/test_qbank.py: 38 hermetic tests (parsers, normalize, classify,
  dedup, expand with a fake LLM, full offline build with fixture Markdown)
- 99 tests total; README/DESIGN updated
@monikagadage
monikagadage merged commit 7f89843 into main Sep 4, 2026
2 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant