<?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: Bipul Chamoli</title>
    <description>The latest articles on DEV Community by Bipul Chamoli (@bipul_chamoli_09b5f0cc719).</description>
    <link>https://dev.to/bipul_chamoli_09b5f0cc719</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%2F4156842%2F52493be1-d68f-495a-ac53-ad4fc2ce65d9.png</url>
      <title>DEV Community: Bipul Chamoli</title>
      <link>https://dev.to/bipul_chamoli_09b5f0cc719</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/bipul_chamoli_09b5f0cc719"/>
    <language>en</language>
    <item>
      <title>Prep Buddy: an AI interview coach for my classmate that runs on his laptop with the Wi-Fi off</title>
      <dc:creator>Bipul Chamoli</dc:creator>
      <pubDate>Fri, 02 Oct 2026 22:21:45 +0000</pubDate>
      <link>https://dev.to/bipul_chamoli_09b5f0cc719/prep-buddy-an-ai-interview-coach-for-my-classmate-that-runs-on-his-laptop-with-the-wi-fi-off-426b</link>
      <guid>https://dev.to/bipul_chamoli_09b5f0cc719/prep-buddy-an-ai-interview-coach-for-my-classmate-that-runs-on-his-laptop-with-the-wi-fi-off-426b</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;&lt;strong&gt;Aman&lt;/strong&gt; is my classmate. We're both in final-year B.Tech, and like everyone in our batch he's preparing for campus placements: SDE-1 roles, which means technical rounds on DSA, DBMS, Operating Systems, Networks and OOP, plus an HR round.&lt;/p&gt;

&lt;p&gt;When I asked him how his prep was going, he wasn't sure. He was putting in the hours, but he couldn't tell which topics were actually weak. Nobody was asking him questions and telling him what he'd missed, so he kept revising the topics he was already comfortable with.&lt;/p&gt;

&lt;p&gt;The usual answers didn't fit him either:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;He practises late, around 11 PM&lt;/strong&gt;, when nobody is free to give him a mock interview.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The hostel Wi-Fi is unreliable&lt;/strong&gt;, and paid mock-interview platforms need a stable connection (and money).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Interview answers are personal.&lt;/strong&gt; A "tell me about yourself" answer shouldn't have to go to someone else's server.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;So I built &lt;strong&gt;Prep Buddy&lt;/strong&gt;: a mock-interview coach that runs &lt;strong&gt;entirely on his laptop&lt;/strong&gt;. It asks him a placement question, grades his typed answer against the points a real interviewer listens for, tells him what he missed, and &lt;strong&gt;remembers his weak topics&lt;/strong&gt; so the next question goes after them.&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;₹0 to run · no API keys · no account · works with Wi-Fi off · his answers never leave his laptop&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;A practice session works like this:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Prep Buddy asks a question from a bank of &lt;strong&gt;56 questions across 7 topics&lt;/strong&gt; (DSA, CS fundamentals, DBMS, OS, CN, OOP, HR), reworded the way a friendly interviewer would ask it.&lt;/li&gt;
&lt;li&gt;Aman types an answer and gets a &lt;strong&gt;score out of 10&lt;/strong&gt;, what went well, what was missing, an outline of a strong answer and the follow-up question an interviewer would likely ask next.&lt;/li&gt;
&lt;li&gt;If he missed something, the &lt;strong&gt;next question is picked to target that gap&lt;/strong&gt;.&lt;/li&gt;
&lt;li&gt;At the end, he gets a summary with his strongest topic, the one to work on next and &lt;strong&gt;3 concrete next steps&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;After a few sessions, the dashboard answers the question he couldn't: &lt;em&gt;where should I spend my next ten minutes?&lt;/em&gt;&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%2F2jv6b1n5xcqygvme614i.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%2F2jv6b1n5xcqygvme614i.png" alt="Prep Buddy dashboard showing average score, per-topic progress and a suggested topic to practise next" width="800" height="775"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The Wi-Fi is &lt;strong&gt;off&lt;/strong&gt; in this recording. Every question, grade and summary comes from Gemma running locally on my Apple M2 laptop with 8 GB of RAM.&lt;/p&gt;

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/TgRr7m_U_FE" width="710" height="399"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;There's no hosted link, on purpose. Hosting Prep Buddy on a server would break the one promise it makes: your answers stay on your laptop. The repo below runs on any 8 GB+ machine.&lt;/p&gt;

&lt;p&gt;The badge in the top-right corner says &lt;strong&gt;AI ready&lt;/strong&gt; only when the database is up, Ollama is running and both models are downloaded. If something is missing, it shows the exact command to fix it (for example &lt;code&gt;ollama pull gemma3:4b&lt;/code&gt;) instead of an error page.&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%2Fef2dkz7fyresnuyvk9hm.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%2Fef2dkz7fyresnuyvk9hm.png" alt="End-of-session summary: a 9.1 out of 10 score, DBMS as the strongest topic, Operating Systems to work on next, and three next steps" width="799" height="562"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;Machine: Apple M2, 8 GB&lt;br&gt;&lt;br&gt;
Chat model: Gemma 3 4B&lt;br&gt;&lt;br&gt;
Question bank: 56 questions across 7 topics&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Cold&lt;/th&gt;
&lt;th&gt;Warm average&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Next question&lt;/td&gt;
&lt;td&gt;41.15 s&lt;/td&gt;
&lt;td&gt;20.37 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Grade answer&lt;/td&gt;
&lt;td&gt;25.50 s&lt;/td&gt;
&lt;td&gt;14.81 s&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Session summary&lt;/td&gt;
&lt;td&gt;23.81 s&lt;/td&gt;
&lt;td&gt;15.08 s&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the golden-answer test, all 5 questions passed the required&lt;br&gt;
Excellent &amp;gt; Average &amp;gt; Wrong ordering.&lt;/p&gt;

&lt;p&gt;The prompt-injection test scored 2/10 and was classified as weak.&lt;/p&gt;
&lt;h2&gt;
  
  
  Code
&lt;/h2&gt;


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/bipul724" rel="noopener noreferrer"&gt;
        bipul724
      &lt;/a&gt; / &lt;a href="https://github.com/bipul724/prep-buddy" rel="noopener noreferrer"&gt;
        prep-buddy
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;Prep Buddy&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;An offline, private mock-interview coach for campus placements, powered by Google Gemma running on your own laptop.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;Built for Aman for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01" rel="nofollow"&gt;DEV Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt;.&lt;/p&gt;
&lt;blockquote&gt;
&lt;p&gt;₹0 to run · no API keys · works with Wi-Fi off · your answers never leave your laptop&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;▶️ &lt;strong&gt;Demo video&lt;/strong&gt;: &lt;a href="https://youtu.be/TgRr7m_U_FE" rel="nofollow noopener noreferrer"&gt;youtu.be/TgRr7m_U_FE&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer" href="https://github.com/bipul724/prep-buddy/docs/screenshots/landing.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbipul724%2Fprep-buddy%2FHEAD%2Fdocs%2Fscreenshots%2Flanding.png" alt="Prep Buddy landing page"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;What it does&lt;/h2&gt;
&lt;/div&gt;
&lt;ol&gt;
&lt;li&gt;Asks you placement interview questions from a hand-written bank of &lt;strong&gt;56 questions across 7 topics&lt;/strong&gt; (DSA, CS fundamentals, DBMS, OS, CN, OOP, HR), in a friendly interviewer tone.&lt;/li&gt;
&lt;li&gt;Grades your typed answer: a score out of 10, strengths, gaps, an outline of a strong answer and a follow-up question.&lt;/li&gt;
&lt;li&gt;Remembers your &lt;strong&gt;weak topics&lt;/strong&gt; and picks the next question to target them (EmbeddingGemma + pgvector).&lt;/li&gt;
&lt;li&gt;Ends each session with a short summary and &lt;strong&gt;3 concrete next steps&lt;/strong&gt;.&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Dashboard&lt;/th&gt;
&lt;th&gt;Session summary&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/bipul724/prep-buddy/docs/screenshots/dashboard.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbipul724%2Fprep-buddy%2FHEAD%2Fdocs%2Fscreenshots%2Fdashboard.png" alt="Dashboard with average score, topic progress and recent sessions"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;td&gt;&lt;a rel="noopener noreferrer" href="https://github.com/bipul724/prep-buddy/docs/screenshots/summary.png"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fraw.githubusercontent.com%2Fbipul724%2Fprep-buddy%2FHEAD%2Fdocs%2Fscreenshots%2Fsummary.png" alt="Session summary with score, strongest and weakest topic and next steps"&gt;&lt;/a&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why local, open-source AI&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Every model call goes to &lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Ollama&lt;/a&gt;…&lt;/p&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/bipul724/prep-buddy" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The setup is three steps: start Ollama and pull the two models, run &lt;code&gt;docker compose up&lt;/code&gt; for the database, then &lt;code&gt;npm run dev&lt;/code&gt;. The README has the full commands. The repo also has the PRD, the architecture, the API reference, the AI design notes and the test results in &lt;code&gt;docs/&lt;/code&gt;. The code is MIT-licensed.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The open-source AI at the core:&lt;/strong&gt;&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;What it does in Prep Buddy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Gemma 3 4B&lt;/strong&gt; (open-weight, 3.3 GB) via &lt;strong&gt;Ollama&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Rewords questions, grades answers, writes the session summary&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;EmbeddingGemma&lt;/strong&gt; (622 MB, 768-dim vectors) via &lt;strong&gt;Ollama&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;Turns questions and "what you missed" into vectors&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;
&lt;strong&gt;Mastra&lt;/strong&gt; (open-source TypeScript agent framework)&lt;/td&gt;
&lt;td&gt;Three agents: &lt;strong&gt;interviewer&lt;/strong&gt;, &lt;strong&gt;evaluator&lt;/strong&gt;, &lt;strong&gt;coach&lt;/strong&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;PostgreSQL + pgvector&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Stores progress &lt;em&gt;and&lt;/em&gt; does the "find a question about this gap" search&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;The rest of the stack is Next.js 16, React 19, TypeScript, Tailwind, Prisma 7 and Zod.&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;      Browser (localhost:3000)
        | fetch JSON
        v
 Next.js API routes --&amp;gt; services: pick next question, grade, summarise
                              |                         |
                     Mastra agents                ollama JS client
          (interviewer, evaluator, coach)          (embeddings)
                              |                         |
                              v                         v
                  Ollama on localhost:11434: gemma3:4b + embeddinggemma

 PostgreSQL 17 + pgvector (Docker, 127.0.0.1): profiles, attempts, topic stats, question vectors
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;Every arrow stays on the laptop. There is no cloud service in this diagram.&lt;/p&gt;

&lt;p&gt;Here are the decisions that made a &lt;strong&gt;4B model&lt;/strong&gt; good enough to trust with someone's interview prep.&lt;/p&gt;

&lt;h3&gt;
  
  
  1. One job and one JSON schema per call, with no tool calling
&lt;/h3&gt;

&lt;p&gt;Small models get unreliable when you ask them to do several things at once, and Gemma 3 isn't listed as tool-capable on Ollama. So each agent does exactly one thing and must return JSON that matches a Zod schema. Mastra passes the schema to Ollama as its &lt;code&gt;format&lt;/code&gt; field, so the &lt;strong&gt;decoding itself is constrained&lt;/strong&gt; to valid JSON. Then I validate it again with Zod anyway:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;res&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;agent&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate&lt;/span&gt;&lt;span class="p"&gt;([{&lt;/span&gt; &lt;span class="na"&gt;role&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;user&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;prompt&lt;/span&gt; &lt;span class="p"&gt;}],&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;structuredOutput&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;errorStrategy&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;strict&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;providerOptions&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;ollama&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;options&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;num_predict&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;800&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;abortSignal&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;AbortSignal&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;timeout&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="mi"&gt;60&lt;/span&gt;&lt;span class="nx"&gt;_000&lt;/span&gt;&lt;span class="p"&gt;),&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nx"&gt;schema&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;parse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;res&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;object&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt; &lt;span class="c1"&gt;// never show half-broken JSON to the user&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A failed call is retried once, and then the user sees a clear error. If Ollama isn't running, the API returns &lt;code&gt;503 MODEL_UNAVAILABLE&lt;/code&gt; with the command to start it, so the UI never just crashes.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Facts come from the question bank, not the model
&lt;/h3&gt;

&lt;p&gt;The 56 questions were written for this app, not copied from LeetCode or GFG, and each one has 2 to 6 &lt;strong&gt;key points&lt;/strong&gt; (for example, normalization → &lt;em&gt;reduces redundancy, avoids insert/update/delete anomalies, 1NF/2NF/3NF, trade-off: more joins&lt;/em&gt;). The model never invents a question. It only rewords one, and the evaluator grades against those key points as a rubric. That's how a 4B model can grade like an interviewer without making up what the "right" answer is.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Targeting weak spots with EmbeddingGemma and pgvector
&lt;/h3&gt;

&lt;p&gt;When the evaluator returns gaps like &lt;em&gt;"didn't mention update anomalies"&lt;/em&gt;, I embed those gaps and ask Postgres for the closest unasked question in the same topic:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight sql"&gt;&lt;code&gt;&lt;span class="k"&gt;SELECT&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;embedding&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;=&amp;gt;&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;gaps&lt;/span&gt;&lt;span class="p"&gt;::&lt;/span&gt;&lt;span class="n"&gt;vector&lt;/span&gt; &lt;span class="k"&gt;AS&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;FROM&lt;/span&gt; &lt;span class="nv"&gt;"Question"&lt;/span&gt;
&lt;span class="k"&gt;WHERE&lt;/span&gt; &lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;topic&lt;/span&gt; &lt;span class="k"&gt;AND&lt;/span&gt; &lt;span class="n"&gt;id&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&amp;gt;&lt;/span&gt; &lt;span class="k"&gt;ALL&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="n"&gt;alreadyAsked&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="k"&gt;ORDER&lt;/span&gt; &lt;span class="k"&gt;BY&lt;/span&gt; &lt;span class="n"&gt;distance&lt;/span&gt;
&lt;span class="k"&gt;LIMIT&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;On top of that, there's a plain difficulty ladder. A topic starts at EASY, moves to MEDIUM once his average is 7 or higher, and to HARD at 8.5. In "Auto" mode, each session starts with his weakest focus topic. The vectors and the progress data live in the same database, so I didn't need a separate vector store.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Not trusting the model with things code can do
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Arithmetic:&lt;/strong&gt; the overall session score is computed in code. The model's number is overwritten.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Consistency:&lt;/strong&gt; Gemma sometimes said "strong" next to a score of 5, so the UI derives the verdict from the score.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Double-clicks:&lt;/strong&gt; every submit sends an &lt;code&gt;Idempotency-Key&lt;/code&gt;. The attempt insert and the topic-average update run in one database transaction, so an answer can't be graded twice.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Every number here was measured on the laptop itself, not on a cloud GPU. To be honest about it: "next question" misses my 15-second target, because rewording costs an extra model call. Setting &lt;code&gt;REPHRASE_QUESTIONS=false&lt;/code&gt; skips it. On a laptop with less RAM, &lt;code&gt;CHAT_MODEL=gemma3:1b&lt;/code&gt; is a one-line switch.&lt;/p&gt;

&lt;h2&gt;
  
  
  Things that went wrong (and what I did about them)
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The model got stuck in a loop.&lt;/strong&gt; Sometimes Gemma repeated &lt;code&gt;0} 0} 0}…&lt;/code&gt; until the context filled up, and a request hung for minutes. I tried capping &lt;code&gt;maxOutputTokens&lt;/code&gt;, and it did nothing. Reading the provider's source showed why: that setting is sent as a field Ollama's &lt;code&gt;/api/chat&lt;/code&gt; ignores. Ollama's own &lt;code&gt;num_predict&lt;/code&gt; works, so I used that, plus a 60-second timeout.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The interviewer answered its own questions.&lt;/strong&gt; When asked to reword a question, the model would sometimes answer it instead. Now a reworded question is only used if it still ends with &lt;code&gt;?&lt;/code&gt;. Otherwise Prep Buddy shows the original wording.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Prompt injection.&lt;/strong&gt; Answers are wrapped in &lt;code&gt;&amp;lt;answer&amp;gt;&lt;/code&gt; tags and treated as data. While tuning, I found that Gemma 3 4B followed injected text &lt;em&gt;more&lt;/em&gt; when my rules came after the answer, so the answer now goes last. I also had to tell it to penalise answers that use the right words but state the facts wrong.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;For Aman, it's the difference between a tool he can use and one he can't.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;What Aman needs&lt;/th&gt;
&lt;th&gt;With Gemma on his laptop&lt;/th&gt;
&lt;th&gt;With a typical closed AI API&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Practise at 11 PM on hostel Wi-Fi&lt;/td&gt;
&lt;td&gt;Works fully offline once the models are downloaded&lt;/td&gt;
&lt;td&gt;Needs a working connection for every answer&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Practise every night as a student&lt;/td&gt;
&lt;td&gt;₹0 per answer, so 50 questions cost nothing&lt;/td&gt;
&lt;td&gt;Paid per token, or a subscription&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Keep his answers private&lt;/td&gt;
&lt;td&gt;Nothing leaves the laptop: no account, no API key&lt;/td&gt;
&lt;td&gt;Every answer goes to a third-party server&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Run on &lt;em&gt;his&lt;/em&gt; machine&lt;/td&gt;
&lt;td&gt;Swap &lt;code&gt;gemma3:4b&lt;/code&gt; ↔ &lt;code&gt;gemma3:1b&lt;/code&gt; with one env var&lt;/td&gt;
&lt;td&gt;Whatever model the vendor serves&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;There are two less obvious ways open mattered.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could read the code when things broke.&lt;/strong&gt; Both of my worst bugs (the token loop and the ignored output cap) were solved by reading the source of the Ollama provider and checking what was actually being sent. With a closed API, all I'd have seen was a request that took four minutes.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The privacy promise shaped the product.&lt;/strong&gt; I planned voice input, then dropped it. The browser's speech recognition in Chrome sends audio to a server, and that would have broken the one promise this app makes.&lt;/p&gt;

&lt;p&gt;To be fair to closed models: a large hosted model would give richer feedback, and it would give it faster. But Aman can't use that one in the hostel at 11 PM, for free, without sending his answers to someone else's server. &lt;strong&gt;A 4B model he can use every night beats a better model he won't.&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Handing it to Aman
&lt;/h2&gt;

&lt;p&gt;I gave it to Aman and sat with him while he did a practice session. His verdict:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;"What I liked about Prep Buddy was that after answering the questions, I was provided with instant feedback and it helped me to identify my weak areas. It helped me to know about my strengths and weaknesses prior to the interview."&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Finding his weak areas before the interview is the part I built it for. He was already putting in the hours. What he didn't have was someone telling him &lt;em&gt;where&lt;/em&gt; to put them, and now his laptop does that, with the Wi-Fi off.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt;: Gemma &lt;em&gt;is&lt;/em&gt; the product. Gemma 3 4B runs all three agents (rewording, grading against a rubric, coaching), and EmbeddingGemma powers the weak-spot targeting. Both run locally through Ollama, on an 8 GB laptop.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra&lt;/strong&gt;: the interviewer, evaluator and coach are Mastra agents. Mastra's &lt;code&gt;structuredOutput&lt;/code&gt; with Zod schemas is how every model call returns validated, schema-constrained JSON from a local model.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;&lt;em&gt;If you're preparing for placements too, clone it, add your own questions to &lt;code&gt;data/questions.json&lt;/code&gt;, and practise without needing Wi-Fi.&lt;/em&gt;&lt;/p&gt;

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