<?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: Hridya Jain</title>
    <description>The latest articles on DEV Community by Hridya Jain (@blackbeaneagles).</description>
    <link>https://dev.to/blackbeaneagles</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%2F4159981%2Fd1e5d853-4b74-4136-bff2-4d9efc2145bb.jpg</url>
      <title>DEV Community: Hridya Jain</title>
      <link>https://dev.to/blackbeaneagles</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/blackbeaneagles"/>
    <language>en</language>
    <item>
      <title>I built my friend a study ghost that runs with the Wi-Fi off</title>
      <dc:creator>Hridya Jain</dc:creator>
      <pubDate>Sun, 04 Oct 2026 18:36:07 +0000</pubDate>
      <link>https://dev.to/blackbeaneagles/i-built-my-friend-a-study-ghost-that-runs-with-the-wi-fi-off-561e</link>
      <guid>https://dev.to/blackbeaneagles/i-built-my-friend-a-study-ghost-that-runs-with-the-wi-fi-off-561e</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;Three days before Anushka's Microbiology exam, the problem wasn't that she wasn't studying. She was studying constantly, and still failing, because she was re-reading the chapters she already knew. Nobody had ever told her which topics she was actually weak at. You can't see that from the inside.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Ghost Tutor&lt;/strong&gt;: a study ghost that lives in a notebook on a desk, reads &lt;em&gt;her&lt;/em&gt; notes, and quizzes her until it finds the cracks.&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%2Fwhx6shz7qejdqfltm7r7.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%2Fwhx6shz7qejdqfltm7r7.png" alt="CoverPage of GhostTutor" width="800" height="539"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;She pastes in her notes. The ghost splits them into topics, writes multiple-choice questions grounded in her own material, and tracks which topics she keeps getting wrong. It always serves her weakest topic first. It marks her wrong answers in red pen, bleeds ink into the page, throws a crumpled ball of paper onto the desk, and stamps her report card like a tired teacher: &lt;code&gt;READY&lt;/code&gt;, &lt;code&gt;NEEDS WORK&lt;/code&gt;, or &lt;code&gt;SEE ME AFTER CLASS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It also roasts her, if she asks it to. She picks the ghost's mood by its face.&lt;/p&gt;

&lt;p&gt;The important part: &lt;strong&gt;it runs entirely on a laptop.&lt;/strong&gt; No account, no API key, no internet. Her notes never leave her machine.&lt;/p&gt;

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

&lt;p&gt;Visit the live app: &lt;strong&gt;&lt;a href="https://ghosttutors.onrender.com/" rel="noopener noreferrer"&gt;https://ghosttutors.onrender.com/&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Paste some sample notes in the Notes tab, then go to Study and answer a question. The ghost writes it live from your notes. Turn off your Wi-Fi and ask another question in the Ask tab — it still works. That's the money shot.&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/BlackBeanEagles" rel="noopener noreferrer"&gt;
        BlackBeanEagles
      &lt;/a&gt; / &lt;a href="https://github.com/BlackBeanEagles/GhostTutor" rel="noopener noreferrer"&gt;
        GhostTutor
      &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;👻 Ghost Tutor&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;A study ghost that reads a friend's notes, finds the cracks, and haunts them until the exam.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built on open-weight &lt;strong&gt;Gemma&lt;/strong&gt;. Runs fully on a laptop with zero API keys. Every cloud integration switches on with one env var and falls back gracefully when it isn't set.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo →&lt;/strong&gt; &lt;a href="https://ghosttutors.onrender.com" rel="nofollow noopener noreferrer"&gt;ghosttutors.onrender.com&lt;/a&gt;&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;How it works&lt;/h2&gt;
&lt;/div&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;&lt;pre class="notranslate"&gt;&lt;code&gt;Your notes
    │
    ▼
[ Gemma splits into topics ]
    │
    ├──▶ Study tab  ──▶ MCQ written from your notes ──▶ answer ──▶ mastery score updated
    │                        ↑ weakest topic first (Beta + spaced repetition)
    │
    ├──▶ Ask tab    ──▶ Mastra agent picks a tool ──▶ search_notes / get_progress /
    │                        remember / find_web_practice ──▶ grounded answer
    │
    ├──▶ Report tab ──▶ TabPFN reads 7 features per attempt ──▶ predicted exam score
    │
    └──▶ Plan tab   ──▶ day-by-day schedule, weakest topics first, ends in mock test
&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;At 9 pm each night, a…&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/BlackBeanEagles/GhostTutor" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Clone it and you are two commands from a working tutor:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm start
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;With nothing else installed it still works — it cuts fill-in-the-blank questions straight out of your notes and labels them &lt;code&gt;offline · from your notes&lt;/code&gt;, so it never pretends to be smarter than it is. Add Ollama and the ghost starts writing real questions.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Gemma 3 4B&lt;/strong&gt; (open weights, via &lt;strong&gt;Ollama&lt;/strong&gt;) is the brain. It splits notes into topics, writes the questions, reacts in character, and explains the answers. Embeddings come from &lt;code&gt;nomic-embed-text&lt;/code&gt;, also local.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Mastra&lt;/strong&gt; runs the "Ask the Ghost" agent, with four tools over the student's own data: &lt;code&gt;search_notes&lt;/code&gt;, &lt;code&gt;get_progress&lt;/code&gt;, &lt;code&gt;remember&lt;/code&gt;, and &lt;code&gt;find_web_practice&lt;/code&gt;. Ask &lt;em&gt;"which topic am I weakest at?"&lt;/em&gt; and it calls &lt;code&gt;get_progress&lt;/code&gt;; ask &lt;em&gt;"explain Mitochondria"&lt;/em&gt; and it calls &lt;code&gt;search_notes&lt;/code&gt;. It picks correctly on its own — here it is in the server log, deciding:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[agent] tool get_progress()
[agent] tool search_notes("Mitochondrial structure")
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;ElevenLabs&lt;/strong&gt; gives the ghost a real voice and listens to spoken answers.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;MongoDB Atlas&lt;/strong&gt; stores the student profile, long-term agent memory, quiz history, and generated questions — so the ghost remembers everything across sessions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Tiger Data&lt;/strong&gt; runs pgvector + full-text hybrid search (RRF) over notes and a Timescale hypertable of attempts, so recall is fast and grounded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;SerpApi&lt;/strong&gt; brings fresh web practice questions for topics Anushka is weak at, so studying never goes stale.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Sentry&lt;/strong&gt; traces every LLM call, tool invocation, and partner integration, so I can see exactly what the ghost did and how long it took.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Render&lt;/strong&gt; hosts the live app and Temporal worker, so Anushka can use it from anywhere.&lt;/p&gt;

&lt;h3&gt;
  
  
  The bug that justifies the whole project
&lt;/h3&gt;

&lt;p&gt;The first real question Gemma wrote was good:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;Which organelle is responsible for cellular respiration?&lt;/em&gt;&lt;br&gt;
Options: Ribosome / &lt;strong&gt;Mitochondrion&lt;/strong&gt; / Lysosome / Golgi Apparatus&lt;br&gt;
&lt;code&gt;"answer": 2&lt;/code&gt; ← Lysosome&lt;br&gt;
&lt;code&gt;"explanation": "The mitochondrion is the powerhouse of the cell… it produces energy through cellular respiration."&lt;/code&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Read it twice. The model's own explanation says Mitochondrion. Its answer index says Lysosome. It did this on &lt;strong&gt;three runs out of three.&lt;/strong&gt; A 4B model can reason about the content and still not count to three.&lt;/p&gt;

&lt;p&gt;If I had shipped that, Anushka would have been taught the wrong answer, in her own notes' words, with a confident explanation attached. That is worse than no tutor at all.&lt;/p&gt;

&lt;p&gt;The fix was to stop asking the model to do the thing it is bad at. Now it returns the correct answer &lt;em&gt;as text&lt;/em&gt; plus three wrong ones, and my code shuffles them and computes the index:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt;&lt;span class="nx"&gt;wrong&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;sort&lt;/span&gt;&lt;span class="p"&gt;(()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="nb"&gt;Math&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;random&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;-&lt;/span&gt; &lt;span class="mf"&gt;0.5&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
&lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;answer&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;options&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;indexOf&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;correct&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Then it checks the correct answer actually appears in the student's notes, and regenerates if it doesn't. Two tests lock it down — one runs the shuffle 20 times and asserts the index always lands on the right option.&lt;/p&gt;

&lt;p&gt;Gemma also returned 8 options because it copied my prompt's placeholder letters, asked the same question three times in a row, and leaked &lt;code&gt;**&lt;/code&gt; markdown into the ghost's dialogue. All fixed, all found by running it.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;I could see the model fail.&lt;/strong&gt; That is the entire argument. A closed API would have handed me the same confidently wrong JSON, and I would have had no way to watch it happen three times, read the raw output, and conclude &lt;em&gt;this model cannot pick an index.&lt;/em&gt; I could run it a hundred times for free while I figured that out. Metering that debugging loop would have shortened it, and I'd have shipped the bug.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I swapped the model mid-build, in one line.&lt;/strong&gt; Gemma 3 can't call tools in Ollama, so the Mastra agent couldn't use its tools. I pulled Gemma 4, set &lt;code&gt;AGENT_MODEL=gemma4:e2b-it-q4_K_M&lt;/code&gt;, and the agent started calling &lt;code&gt;get_progress&lt;/code&gt; on its own. Questions still run on Gemma 3, because it's faster. Two models, each doing what it's good at, picked by me, swappable by anyone who clones this.&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%2Fw8gpseb8qj6zpzbwn47l.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%2Fw8gpseb8qj6zpzbwn47l.png" alt="Question Section" width="800" height="527"&gt;&lt;/a&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%2F1o4u3b69iuorzctsr073.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%2F1o4u3b69iuorzctsr073.png" alt="Report Card" width="800" height="446"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing per question.&lt;/strong&gt; Someone cramming for an exam asks hundreds of questions a night. Any per-token price turns a study tool into a thing you ration. The ghost is free to pester Anushka forever, which is the only way a tutor works.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Her notes stay hers.&lt;/strong&gt; A student's notes are an unflattering picture of what she doesn't understand yet. Those shouldn't be sitting in someone's logs. On this build they never leave the laptop.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;And it works with the Wi-Fi off.&lt;/strong&gt; That's not a hypothetical where she lives. It is the difference between a study tool and a study tool &lt;em&gt;that was there the night before the exam&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Anushka Said
&lt;/h2&gt;

&lt;blockquote&gt;
&lt;p&gt;"This is actually really useful. The questions are hard and it remembers what I got wrong. Also the ghost roasting me is weirdly motivating."&lt;/p&gt;
&lt;/blockquote&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 3 4B writes every question and explanation locally; Gemma 4 E2B drives the tool-calling agent.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Mastra&lt;/strong&gt; — Agent orchestrates four tools (&lt;code&gt;search_notes&lt;/code&gt;, &lt;code&gt;get_progress&lt;/code&gt;, &lt;code&gt;remember&lt;/code&gt;, &lt;code&gt;find_web_practice&lt;/code&gt;) over the student's own data, picking correctly on its own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of MongoDB Atlas&lt;/strong&gt; — Stores student profile, long-term agent memory, quiz history, and generated questions across sessions.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Tiger Data&lt;/strong&gt; — Runs pgvector + full-text hybrid search (RRF) for note recall and Timescale hypertable for attempt time series.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of SerpApi&lt;/strong&gt; — Brings fresh web practice questions for weak topics so studying never goes stale.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of ElevenLabs&lt;/strong&gt; — Tutor voice (TTS) for questions and spoken answer input (STT).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Sentry Agent Tracing&lt;/strong&gt; — Traces every LLM call, tool invocation, and partner integration to show what the ghost did and how long it took.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Render&lt;/strong&gt; — Hosts the live app so the ghost works from anywhere.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of GitHub Copilot&lt;/strong&gt; — Copilot reviewed the PR and caught inconsistencies in the agent tool signatures.&lt;/li&gt;
&lt;/ul&gt;

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