<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Livansh Malhotra</title>
    <description>The latest articles on DEV Community by Livansh Malhotra (@livansh_malhotra).</description>
    <link>https://dev.to/livansh_malhotra</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4155474%2F55ad32ae-547f-4fbd-bc95-6c078b54aae6.png</url>
      <title>DEV Community: Livansh Malhotra</title>
      <link>https://dev.to/livansh_malhotra</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/livansh_malhotra"/>
    <language>en</language>
    <item>
      <title>I Built My Friend a Study Buddy That Reads Her Notes and Never Uploads Them</title>
      <dc:creator>Livansh Malhotra</dc:creator>
      <pubDate>Sun, 04 Oct 2026 18:55:49 +0000</pubDate>
      <link>https://dev.to/livansh_malhotra/i-built-my-friend-a-study-buddy-that-reads-her-notes-and-never-uploads-them-4a7o</link>
      <guid>https://dev.to/livansh_malhotra/i-built-my-friend-a-study-buddy-that-reads-her-notes-and-never-uploads-them-4a7o</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  What I Built
&lt;/h2&gt;

&lt;p&gt;I built this for Kanishka, who is preparing for Engineering 1st sem exams. She has a problem every serious student knows: hundreds of pages of lecture slides, scanned handwritten notes and typed summaries, scattered across folders, none of it searchable. Before every revision session she spends the first hour just &lt;em&gt;finding&lt;/em&gt; things.&lt;/p&gt;

&lt;p&gt;The obvious fix is to upload everything to a chatbot. She won't, and I agree with her. Her notes hold her draft answers, her mnemonics and her margin scribbles about what she doesn't understand. That is a record of how she thinks, and she shouldn't have to hand it to a server she has never heard of.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zc1f0uiwxw38krc6tco.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F2zc1f0uiwxw38krc6tco.png" alt=" " width="800" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;StudyBuddy&lt;/strong&gt; is a local-first study assistant:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Drop a PDF into a folder&lt;/strong&gt;, and about a minute later it's searchable.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ask a question in plain English&lt;/strong&gt; and get a short, cited answer pointing at the exact page of &lt;em&gt;her own&lt;/em&gt; notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Take quizzes&lt;/strong&gt; (MCQ) generated from those same notes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;See her weak spots&lt;/strong&gt;, with topics ranked by how often she gets them wrong.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;It runs on a laptop. After the one-time model download it works with the Wi-Fi off, including on her commute.&lt;/p&gt;




&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;


&lt;div class="crayons-card c-embed text-styles text-styles--secondary"&gt;
    &lt;div class="c-embed__content"&gt;
      &lt;div class="c-embed__body"&gt;
        &lt;h2 class="fs-xl lh-tight"&gt;
          &lt;a href="https://drive.google.com/file/d/1YBFgXPnGcMYXTPvZrxzWmpFqQgIdUfEZ/view" rel="noopener noreferrer" class="c-link"&gt;
            studybuddy_demo.mp4 - Google Drive
          &lt;/a&gt;
        &lt;/h2&gt;
        &lt;div class="color-secondary fs-s flex items-center"&gt;
            &lt;img alt="favicon" class="c-embed__favicon m-0 mr-2 radius-0" src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fssl.gstatic.com%2Fdocs%2Fdoclist%2Fimages%2Fdrive_favicon_2026_32dp.png" width="32" height="32"&gt;
          drive.google.com
        &lt;/div&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The recording shows:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A new PDF dropped into &lt;code&gt;/uploads&lt;/code&gt;, with its status flipping from &lt;code&gt;pending&lt;/code&gt; to &lt;code&gt;ready&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;em&gt;"What is a pointer?"&lt;/em&gt; answered with two cited bullets instead of a page of text&lt;/li&gt;
&lt;li&gt;A repeated question answered instantly from cache and counted as an LLM call skipped&lt;/li&gt;
&lt;li&gt;A quiz, a wrong answer, and that topic moving up the weak-topics list&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Her real notes never leave her machine.&lt;/p&gt;




&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;GitHub Repository:&lt;/strong&gt; &lt;a href="https://github.com/livanshmalhotra/StudyBuddy" rel="noopener noreferrer"&gt;StudyBuddy&lt;/a&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  How I Built It
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;drop file → parse → chunk → embed → pgvector
question  → cache → hybrid search → rerank → gate → (LLM only if needed)
quiz      → cached questions → code-graded MCQs → weak-topic ranking
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyja17ttdyj5ner9mwh2.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fbyja17ttdyj5ner9mwh2.png" alt=" " width="799" height="383"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The principle: the LLM is the last resort.&lt;/strong&gt; A small local model is slow and sometimes unreliable, so every step that &lt;em&gt;can&lt;/em&gt; work without it does. Ingestion has no LLM. Search has no LLM. Repeat questions hit a semantic cache. MCQs are graded in plain code. The model only runs when a question genuinely needs synthesis.&lt;/p&gt;

&lt;h3&gt;
  
  
  The open stack
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;LLM&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Gemma&lt;/strong&gt; via Ollama&lt;/td&gt;
&lt;td&gt;Answers, quiz generation, free-text grading&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Embeddings&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;BGE-M3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Multilingual, self-hosted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Reranker&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;bge-reranker-v2-m3&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Open-weight, CPU-friendly, calibrated scores&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Database&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;PostgreSQL + pgvector + full-text search&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hybrid retrieval in one place&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Durable ingestion&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;Temporal&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;A 600-page scan that fails at page 400 resumes instead of restarting&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend / UI&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;FastAPI&lt;/strong&gt;, &lt;strong&gt;React + Vite&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;No lock-in&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Tracing&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[Sentry, if your DSN is live]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Every request tagged &lt;code&gt;llm_used&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  Drop-a-file ingestion
&lt;/h3&gt;

&lt;p&gt;A watcher monitors the uploads folder. Each file is hashed, so duplicates are skipped and an edited file is re-ingested alone. Nothing is retrained, because adding a document to a RAG system is just an index update.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that taught me the most
&lt;/h3&gt;

&lt;p&gt;My first version answered &lt;em&gt;"what is a pointer?"&lt;/em&gt; with this:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;C++ - Quick Notes Page 1 of 2 C++ Syntax, memory, OOP, STL, templates and modern C++ 1. Program Structure &amp;amp; Basics #include  using namespace std; int main() { int x = 5; ... 2. Pointers, References &amp;amp; Memory * Pointer stores an address... 3. Classes &amp;amp; OOP class Animal { protected: ...&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;The answer was in there, buried in a wall of unrelated text. Four problems were stacking up:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;The gate returned whole chunks.&lt;/strong&gt; A "high-confidence" hit was shown verbatim.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunks were page-sized, not idea-sized.&lt;/strong&gt; A hard 400-token split cut across topics, so Pointers shared a chunk with OOP.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Flat text extraction destroyed structure.&lt;/strong&gt; Headings and bullets collapsed into one run-on string.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Rank-based scores aren't relevance scores.&lt;/strong&gt; Reciprocal rank fusion only knows ordering, so a weak match could still look confident.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The fix was &lt;strong&gt;small-to-big retrieval&lt;/strong&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Parse PDFs into markdown so headings and bullets survive.&lt;/li&gt;
&lt;li&gt;Split by heading, then index at &lt;em&gt;bullet level&lt;/em&gt;, with each bullet prefixed by its heading.&lt;/li&gt;
&lt;li&gt;Add a local cross-encoder reranker whose scores are calibrated enough to set real thresholds.&lt;/li&gt;
&lt;li&gt;Rebuild the gate on three outcomes: &lt;strong&gt;NOT_FOUND&lt;/strong&gt;, &lt;strong&gt;EXTRACT&lt;/strong&gt; (show the best 1-3 bullets, no LLM) or &lt;strong&gt;SYNTHESIZE&lt;/strong&gt; (Gemma over a tiny, clean context).&lt;/li&gt;
&lt;li&gt;Never show a parent section as the answer. It only appears in a collapsed "Source" panel.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flr5menzu70fmeyi2qz6r.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Flr5menzu70fmeyi2qz6r.png" alt=" " width="799" height="379"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;The same question now returns:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;• Pointer stores an address: &lt;code&gt;int* p = &amp;amp;x;&lt;/code&gt; and &lt;code&gt;*p&lt;/code&gt; dereferences it. [C++ Quick Notes, p.1]&lt;br&gt;
• Prefer smart pointers over raw new/delete to avoid leaks. [C++ Quick Notes, p.1]&lt;/p&gt;
&lt;/blockquote&gt;

&lt;h3&gt;
  
  
  Did it actually get better?
&lt;/h3&gt;

&lt;p&gt;I wrote &lt;strong&gt;[N]&lt;/strong&gt; question and expected-answer pairs from her real notes and ran them before and after.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;&lt;/th&gt;
&lt;th&gt;Before&lt;/th&gt;
&lt;th&gt;After&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Avg. characters shown&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answer contains the fact&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Contains unrelated text&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Answered without the LLM&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]%&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;p50 latency (no LLM / LLM)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]s / [ ]s&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;[ ]s / [ ]s&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;[One honest sentence on any number that disappointed you.]&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  Why Does Open Innovation Matter?
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Her notes stay hers.&lt;/strong&gt; With a closed API, every page of her notes is a request to someone else's server. With open-weight models and a local database, the whole system runs on a laptop with the network unplugged. That is not a feature you can bolt onto a closed model, however good its privacy policy is.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero cost per question.&lt;/strong&gt; In exam season she may ask thousands of questions. Free local inference means she never rations curiosity because of a bill.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could fix the retrieval because I owned it.&lt;/strong&gt; The pointer-dump bug lived in chunking, scoring and the gate. Every layer was readable code I could change. With a black-box RAG API, the best I could have done is tweak a prompt and hope.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swappable models.&lt;/strong&gt; The model is one line in &lt;code&gt;.env&lt;/code&gt;. I started on &lt;code&gt;gemma:2b&lt;/code&gt; and moved to &lt;code&gt;gemma3:4b&lt;/code&gt; for grounded answering. &lt;strong&gt;[Say what actually changed.]&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Where a closed model would have been better:&lt;/strong&gt; a frontier model writes more fluent explanations and handles messy questions more gracefully than a 2B or 4B model on a laptop CPU. Mine is also slower, about &lt;strong&gt;[ ]s&lt;/strong&gt; per synthesized answer. I accepted that because most queries never reach the model, and because the alternative was her notes leaving the device.&lt;/p&gt;







&lt;h2&gt;
  
  
  The Hand-over
&lt;/h2&gt;

&lt;p&gt;Her first search was "What is a pointer?". She thought she knew almost every topix well but had scored 1/3 on the quiz. After ten minutes she said: "this will help me for sure, but its ofcourse not your brain", (actually it is my own idea).&lt;/p&gt;




&lt;h2&gt;
  
  
  Prize Categories
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Uses local Gemma 2b for answer synthesis, quiz generation, and grading.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of TabPFN&lt;/strong&gt;: Uses TabPFN tabular model to predict student weak topics from quiz attempt history.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Temporal&lt;/strong&gt;: Durable ingestion pipeline (&lt;code&gt;IngestWorkflow&lt;/code&gt;) handling background parsing, chunking, and embedding.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tiger Data&lt;/strong&gt;: PostgreSQL &lt;code&gt;pgvector&lt;/code&gt; hybrid search combining vector embeddings (BGE-M3) + BM25 keyword search with RRF.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Sentry Agent Tracing&lt;/strong&gt;: Sentry SDK integration monitoring RAG pipeline latency ($p50$/$p95$) and confidence gate metrics.&lt;/li&gt;
&lt;/ol&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
    </item>
  </channel>
</rss>
