<?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: Dibyendu Chatterjee</title>
    <description>The latest articles on DEV Community by Dibyendu Chatterjee (@dibyendu_chatterjee_409d4).</description>
    <link>https://dev.to/dibyendu_chatterjee_409d4</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%2F3764190%2Fbe1f7d38-1fac-4a36-854e-2bc7efc52251.jpeg</url>
      <title>DEV Community: Dibyendu Chatterjee</title>
      <link>https://dev.to/dibyendu_chatterjee_409d4</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/dibyendu_chatterjee_409d4"/>
    <language>en</language>
    <item>
      <title>I Built an Altar That Works With the Wifi Off</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Thu, 29 Oct 2026 15:28:52 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/i-built-an-altar-that-works-with-the-wifi-off-1m2e</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/i-built-an-altar-that-works-with-the-wifi-off-1m2e</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;On Día de los Muertos, families build an ofrenda: an altar with marigolds, candles, the food someone loved, the song that was theirs, a photograph. The idea is that the dead stay close as long as someone keeps telling their story.&lt;/p&gt;

&lt;p&gt;The stories are the fragile part. They sit in a few heads and a few chat threads. And the obvious tool for turning scattered memories into writing, a cloud AI, asks you to paste your grandmother's last recipe into a company's server.&lt;/p&gt;

&lt;p&gt;So I built ofrenda, a command line tool with zero dependencies. You answer eight questions about someone you miss: name, who they were to you, a food, a song, a scent, something they always said, one story you never want to lose, and optionally a photo.&lt;/p&gt;

&lt;p&gt;A model running on your own laptop writes a short remembrance from your answers. Then the tool builds one HTML file: a papel picado banner that sways, three flickering candles, marigold petals drifting down, your remembrance typing itself out line by line, and an offerings tier for the food, song, flower, and phrase.&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo
&lt;/h2&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/dibyendu-coder" rel="noopener noreferrer"&gt;
        dibyendu-coder
      &lt;/a&gt; / &lt;a href="https://github.com/dibyendu-coder/Ofrenda-CLI" rel="noopener noreferrer"&gt;
        Ofrenda-CLI
      &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&gt;
&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🕯️ ofrenda&lt;/h1&gt;
&lt;/div&gt;
&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;A ghost story where the ghost is someone you love.&lt;/h3&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Answer seven questions about a person you miss. A language model running on your own laptop writes their remembrance. You get a candlelit altar page you can keep forever.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;No cloud. No account. No API key. No upload. Nothing leaves your machine.&lt;/p&gt;
&lt;p&gt;&lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/6f2a53bbd6cc36d6d6bcfb82334ef3dc2354bdbdbe123029cab6cb755cf2dd63/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d25453225383925413531382d336338373361"&gt;&lt;img src="https://camo.githubusercontent.com/6f2a53bbd6cc36d6d6bcfb82334ef3dc2354bdbdbe123029cab6cb755cf2dd63/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6e6f64652d25453225383925413531382d336338373361" alt="Node"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/f6ec087dd2507c00a28b0d4ca1c2e8085170b4569b1261cc6759bb64c9bb3e84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646570656e64656e636965732d302d663539653062"&gt;&lt;img src="https://camo.githubusercontent.com/f6ec087dd2507c00a28b0d4ca1c2e8085170b4569b1261cc6759bb64c9bb3e84/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f646570656e64656e636965732d302d663539653062" alt="Dependencies"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/41b4e1551c3b570ab21a9df6d7ad6e2f213672ce332ad6d0f1bf486130b99a7e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e732d6f66666c696e652d313230393163"&gt;&lt;img src="https://camo.githubusercontent.com/41b4e1551c3b570ab21a9df6d7ad6e2f213672ce332ad6d0f1bf486130b99a7e/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f72756e732d6f66666c696e652d313230393163" alt="Runs offline"&gt;&lt;/a&gt; &lt;a rel="noopener noreferrer nofollow" href="https://camo.githubusercontent.com/b8cadaa967891081f8f165695470689986c028821dd8a040132f6e661795dc0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c7565"&gt;&lt;img src="https://camo.githubusercontent.com/b8cadaa967891081f8f165695470689986c028821dd8a040132f6e661795dc0d/68747470733a2f2f696d672e736869656c64732e696f2f62616467652f6c6963656e73652d4d49542d626c7565" alt="License"&gt;&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;&lt;em&gt;Built for the Hacktoberfest Open-Source AI Challenge: Ghost in the Machine&lt;/em&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;The idea&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;On Día de los Muertos, families build an &lt;strong&gt;ofrenda&lt;/strong&gt;: an altar with the food, the song, the flowers, and the stories of people who have died. The ritual says someone stays present as long as someone tells their story.&lt;/p&gt;
&lt;p&gt;The stories are the fragile part. They live in a few heads and a few chat threads. If you hand them to a cloud AI to help you write them down, you upload your grandmother's last recipe to a company's server.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;ofrenda&lt;/code&gt; keeps…&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/dibyendu-coder/Ofrenda-CLI" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;Model: gemma3:4b, an open-weight model, served by Ollama on localhost. One environment variable swaps it (OFRENDA_MODEL=llama3.2).&lt;br&gt;
Code: one Node file, no dependencies. It asks the questions, calls Ollama's /api/chat, and renders the altar as a single HTML file with the photo embedded as base64.&lt;br&gt;
Prompts: two. One writes the remembrance, one runs the echo. Both are built around the same rule: use only what the person told you, and leave gaps empty.&lt;br&gt;
Built with: Antigravity&lt;/p&gt;

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

&lt;p&gt;I could not have built this honestly with a closed API.&lt;/p&gt;

&lt;p&gt;It ran with the wifi off. I turned the wifi off, ran the whole thing, and it worked. The only time the tool needs internet is the one-time model download.&lt;/p&gt;

&lt;p&gt;[SCREENSHOT: terminal with wifi-off icon visible]&lt;/p&gt;

&lt;p&gt;Nothing about a dead person left my machine. Answers, photo, chat, and output all stay in a folder on disk. That folder is gitignored so one careless git add . cannot publish a family's private stories.&lt;/p&gt;

&lt;p&gt;Nothing can be switched off under it. The output is a plain HTML file with the photo inside it. No framework, no hosted viewer, no account. A memorial that depends on a company keeping an API alive is not much of a memorial. A file you can open in thirty years is.&lt;/p&gt;

&lt;p&gt;I could change how it behaves. I swapped models with one variable, and I rewrote the echo's rules until it refused what it should refuse. With a hosted model, someone else's policy sits between me and that prompt.&lt;/p&gt;

&lt;p&gt;It cost nothing to run. I rewrote prompts dozens of times with no metered calls.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

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

&lt;p&gt;Gemma&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>SlothWatch: I Made a Cloud Browser and a Local Gemma Do My Boring Web Checks</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Sat, 24 Oct 2026 21:25:12 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/slothwatch-i-made-a-cloud-browser-and-a-local-gemma-do-my-boring-web-checks-28nf</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/slothwatch-i-made-a-cloud-browser-and-a-local-gemma-do-my-boring-web-checks-28nf</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I keep re-checking the same web pages. A portal status. A deadline. A price. An announcements page. It takes two minutes each time and I forget half the time anyway.&lt;/p&gt;

&lt;p&gt;SlothWatch is a lazy web-chore watcher. You tell it once, in plain English, what you care about on a page:&lt;/p&gt;

&lt;p&gt;slothwatch add &lt;a href="https://example.com/page" rel="noopener noreferrer"&gt;https://example.com/page&lt;/a&gt; --care "tell me if the deadline changes or results are announced"&lt;/p&gt;

&lt;p&gt;After that:&lt;/p&gt;

&lt;p&gt;A cloud browser (Browserbase, driven by Stagehand) opens the page on a schedule and pulls the text.&lt;br&gt;
If the page is identical to last time, SlothWatch stops. No model call at all.&lt;br&gt;
If something changed, a local Gemma model running on my laptop through Ollama reads only the change and decides if it matters to me.&lt;br&gt;
If it matters, my phone gets a two-line push through ntfy. If not, silence.&lt;/p&gt;

&lt;p&gt;Silence is the feature. A sloth does not check things twice.&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&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/dibyendu-coder" rel="noopener noreferrer"&gt;
        dibyendu-coder
      &lt;/a&gt; / &lt;a href="https://github.com/dibyendu-coder/slothwatch" rel="noopener noreferrer"&gt;
        slothwatch
      &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;SlothWatch 🦥&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Built during Hacktoberfest Open-Source AI Challenge Week 3 (Oct 19-25, 2026)&lt;/strong&gt;&lt;br&gt;
&lt;em&gt;Note: This project was conceived, designed, and built entirely inside the challenge window.&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Submission Deadline: October 25, 2026, 11:59 PM PDT (October 26, 12:29 PM IST).&lt;/em&gt;&lt;br&gt;
&lt;em&gt;Landing Page:&lt;/em&gt; &lt;a href="https://slothwatch.tech" rel="nofollow noopener noreferrer"&gt;slothwatch.tech&lt;/a&gt; (or &lt;code&gt;docs/&lt;/code&gt; on GitHub Pages)&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;SlothWatch is a lazy web-chore watcher. You describe in plain English what you care about on a web page. A cloud browser (&lt;a href="https://browserbase.com" rel="nofollow noopener noreferrer"&gt;Browserbase&lt;/a&gt; + &lt;a href="https://stagehand.dev" rel="nofollow noopener noreferrer"&gt;Stagehand&lt;/a&gt;) visits the page on an automated schedule. Visible text is extracted deterministically and hashed. If the page is unchanged, SlothWatch exits immediately—consuming &lt;strong&gt;0 LLM calls, 0 tokens, and 0 API dollars&lt;/strong&gt;.&lt;/p&gt;
&lt;p&gt;When real text changes occur, a local open-weight model (&lt;a href="https://ollama.com" rel="nofollow noopener noreferrer"&gt;Gemma&lt;/a&gt; via Ollama) evaluates the compact diff to determine if it satisfies your intent. Your phone receives a push notification via &lt;a href="https://ntfy.sh" rel="nofollow noopener noreferrer"&gt;ntfy&lt;/a&gt; only when it genuinely matters—filtering out timestamps, visitor counters, ads, and cookie…&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/dibyendu-coder/slothwatch" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;Install:&lt;/p&gt;

&lt;p&gt;npm install -g github:[[your-username/your-repo]]&lt;br&gt;
slothwatch init&lt;br&gt;
slothwatch doctor&lt;/p&gt;

&lt;p&gt;You need Node 20+, Ollama with a pulled model, a Browserbase API key, and an ntfy topic.&lt;/p&gt;

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

&lt;p&gt;Stack: TypeScript, Stagehand and Browserbase for browser sessions, Ollama running [[Gemma tag, for example gemma4:e2b]] for judgment, SQLite for state, ntfy for push. Built on a laptop with an RTX 2050 and about 15 GB RAM. No closed model API anywhere in the path.&lt;/p&gt;

&lt;p&gt;The design rule that made it work: split the job. My first instinct was to let the model drive the browser. Small local models are not reliable at that. So the browser does the deterministic work (navigate, wait, extract text) and the model only reasons over plain text it is handed. [[One or two sentences from NOTES.md on what actually failed when you tried the other way, if you tried it. If you did not, delete this sentence.]]&lt;/p&gt;

&lt;p&gt;Laziness, enforced in code:&lt;/p&gt;

&lt;p&gt;Hash check first. Page text is normalized and hashed. Same hash as last run means no model call. [[X of Y runs skipped the model entirely, from stats.]]&lt;br&gt;
Recipe replay. The first run of a watch can include steps like dismissing a banner or clicking a tab. Those steps are recorded, and later runs replay them without asking a model to rediscover anything. [[First run took X s with Y model calls, replay took X s with 0, from stats.]]&lt;br&gt;
Strict judge output. Gemma must return JSON with matters, a two-line summary, and a reason, validated with zod. Invalid output is retried once, then recorded as a failed judgment. It is never silently treated as "nothing changed".&lt;br&gt;
Read-only. It never submits forms, buys anything, or posts for you.&lt;/p&gt;

&lt;p&gt;Benchmark. I wrote [[N]] labelled test cases (real changes, noise like timestamps and cookie banners, and a few tricky ones) and ran them through [[model names]]:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Model&lt;/th&gt;
&lt;th&gt;Accuracy&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;False Positives (Noise)&lt;/th&gt;
&lt;th&gt;False Negatives (Missed)&lt;/th&gt;
&lt;th&gt;Invalid JSON / Retried&lt;/th&gt;
&lt;th&gt;Avg Latency&lt;/th&gt;
&lt;th&gt;Avg Tokens&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gemma4:e2b&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;95.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;21/22&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 / 0&lt;/td&gt;
&lt;td&gt;5.31s&lt;/td&gt;
&lt;td&gt;696&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;gemma4:e4b&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;95.5%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;21/22&lt;/td&gt;
&lt;td&gt;1&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 / 0&lt;/td&gt;
&lt;td&gt;6.35s&lt;/td&gt;
&lt;td&gt;550&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;llama3.2:3b&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;90.9%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;20/22&lt;/td&gt;
&lt;td&gt;2&lt;/td&gt;
&lt;td&gt;0&lt;/td&gt;
&lt;td&gt;0 / 0&lt;/td&gt;
&lt;td&gt;1.81s&lt;/td&gt;
&lt;td&gt;527&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;Private by default. Page content, including pages behind a login, is judged by a model on my own machine. It never goes to a closed API.&lt;br&gt;
Costs nothing per check. After setup, each judgment is free. A closed API would turn "check this page every hour" into a bill that grows with every watch.&lt;br&gt;
Swap the brain. The model is one config line. That is how the benchmark above was possible: same cases, different open models, real numbers.&lt;br&gt;
Honest limits. Small open models were [[describe the real weakness from your benchmark]]. Splitting the job let me keep the model local anyway. A bigger closed model might have judged better, but it would have cost the privacy and the zero bill, which were the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  My Agent Session
&lt;/h2&gt;

&lt;h2&gt;
  
  
  The Lazy Payoff
&lt;/h2&gt;

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

&lt;p&gt;Best Use of Browserbase: Stagehand sessions fetch and render pages, and recipes turn a one-off run into a repeatable workflow.&lt;br&gt;
Best Use of Gemma: Gemma runs locally through Ollama as the judge and was benchmarked against other open models.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>PayRadar: a late-payment radar for freelancers that never uploads an invoice</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Sun, 18 Oct 2026 21:27:17 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/payradar-a-late-payment-radar-for-freelancers-that-never-uploads-an-invoice-p6g</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/payradar-a-late-payment-radar-for-freelancers-that-never-uploads-an-invoice-p6g</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Freelancers rarely go broke from a lack of work. They go broke in week 7, when three invoices that were "Net 30" are still unpaid and rent is due.&lt;/p&gt;

&lt;p&gt;PayRadar is a local-first tool that tells a freelancer three things:&lt;/p&gt;

&lt;p&gt;Which open invoices will probably be paid late, and by how many days.&lt;br&gt;
Whether the next 8 weeks of expected cash-in cover their monthly costs, with gap weeks flagged.&lt;br&gt;
What to write to each client. It drafts a reminder whose tone matches the risk: a friendly nudge, a clear and polite note, or a firm one. The freelancer reviews it and copies it. Nothing is ever sent automatically.&lt;/p&gt;

&lt;p&gt;You drop in invoice PDFs or photos. A local vision model reads them, a tabular foundation model learns each client's payment habits from the freelancer's own history, and everything stays in a SQLite file on the laptop.&lt;/p&gt;

&lt;p&gt;Who would pay for it? The target is a solo freelancer or a tiny agency billing a handful of clients, especially one whose contracts or NDAs make uploading client invoices to a cloud SaaS uncomfortable. I think $19 to $29 a month is plausible, but that is a hypothesis. I have not tested it with a paying customer, so I am not claiming demand. The demo on the synthetic data below shows the product works. It does not show anyone wants to buy it yet.&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 flex items-center justify-between"&gt;
        &lt;a href="https://payradar-640a.onrender.com/" rel="noopener noreferrer" class="c-link fw-bold flex items-center"&gt;
          &lt;span class="mr-2"&gt;payradar-640a.onrender.com&lt;/span&gt;
          

        &lt;/a&gt;
      &lt;/div&gt;
    &lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The hosted copy runs in a lightweight demo mode with synthetic data and precomputed results. The free server has no GPU and no room for PyTorch, so it cannot run Gemma or TabPFN live. The real app runs entirely on your own machine. The first load can take about a minute because the free tier sleeps.&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/dibyendu-coder" rel="noopener noreferrer"&gt;
        dibyendu-coder
      &lt;/a&gt; / &lt;a href="https://github.com/dibyendu-coder/PayRadar" rel="noopener noreferrer"&gt;
        PayRadar
      &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;PayRadar 📡&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;Local-first late-payment radar for freelancers.&lt;/strong&gt;&lt;br&gt;
Drop in invoices, and it tells you which ones will be paid late, whether you will have a cash gap in the next 8 weeks, and drafts the right chaser email for each one.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;Built Oct 12-18, 2026 during Hacktoberfest Week 2 (Side Hustle Challenge).&lt;/p&gt;

&lt;div class="markdown-heading"&gt;
&lt;h3 class="heading-element"&gt;🏆 Hackathon Window Note&lt;/h3&gt;
&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Event&lt;/strong&gt;: Hacktoberfest Open-Source AI Challenge Week 2 (Side Hustle)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Challenge Window&lt;/strong&gt;: October 12 – October 18, 2026&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Window Statement&lt;/strong&gt;: This repository was created and completely built within the challenge window (Oct 12-18, 2026).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Post-Deadline Commits&lt;/strong&gt;
&lt;em&gt;None.&lt;/em&gt; (Any commit made after October 18, 2026 23:59 UTC will be documented here with justification).&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="markdown-alert markdown-alert-note"&gt;
&lt;p class="markdown-alert-title"&gt;Note&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;SYNTHETIC DATA NOTICE&lt;/strong&gt;: All benchmark tables, model evaluation figures, demo documents, and client histories in this repository and application are &lt;strong&gt;100% synthetically generated&lt;/strong&gt; for demonstration and benchmarking purposes. No real personal, business, or financial customer data was…&lt;/p&gt;
&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dibyendu-coder/PayRadar" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;p&gt;Open pieces doing the real work&lt;/p&gt;

&lt;p&gt;Gemma 4 (gemma4:e2b) through Ollama reads invoice images and returns JSON constrained to a schema (client, amount, issue date, due date, terms). The same model drafts the chaser emails.&lt;br&gt;
TabPFN-3.5, run locally with the tabpfn package, predicts two things per open invoice: the probability of being paid more than 7 days late, and the expected days late.&lt;br&gt;
scikit-learn supplies the baselines I measured against.&lt;br&gt;
Streamlit + SQLite + PyMuPDF for the app, the store, and PDF-to-image conversion.&lt;/p&gt;

&lt;p&gt;The pipeline&lt;/p&gt;

&lt;p&gt;PDF or image goes to an image of at most 1024px.&lt;br&gt;
Gemma extracts the fields. The user reviews and edits them in a form, then confirms.&lt;br&gt;
Features are built per invoice: log amount, payment terms, issue month and weekday, client, project type, and the client's history (how many earlier invoices, their mean lateness, and how this amount compares to their median).&lt;br&gt;
TabPFN scores every open invoice. Scores are cached in SQLite so the UI never waits on the model.&lt;br&gt;
Expected pay date is the due date plus predicted days late. I sum expected cash-in per week for 8 weeks and compare it to monthly costs.&lt;/p&gt;

&lt;p&gt;The hardware constraint shaped the design. I built this on a laptop with an RTX 2050 (4 GB of VRAM) and 15.6 GB of RAM. So Gemma never runs inside a page request. Extraction and drafting are explicit button presses with a timeout and a template fallback, and results are saved. The default model is the small e2b, switchable with one environment variable.&lt;/p&gt;

&lt;p&gt;Measured results (all on synthetic data)&lt;/p&gt;

&lt;p&gt;The demo data is generated from a seed: about 400 invoices across 25 clients with deliberately different habits (always late, pays at month end, big invoices slower, one very reliable client). I split by time, not randomly, and client-history features only ever see invoices issued earlier. A test fails if a feature for invoice N can see invoice N or anything later.&lt;/p&gt;

&lt;p&gt;Extraction on 20 rendered invoices with known answers, using gemma4:e2b: all four fields (client, amount, issue date, due date) correct on 20 of 20, at about 19 seconds per document. These are clean synthetic documents. Real scans will do worse.&lt;/p&gt;

&lt;p&gt;Prediction, averaged over 3 expanding time windows (mean ± std):&lt;/p&gt;

&lt;p&gt;Model   ROC-AUC Brier   Delay MAE (days)&lt;br&gt;
Per-client mean 0.855 ± 0.035  0.128 ± 0.025  8.02 ± 0.32&lt;br&gt;
HistGradientBoosting    0.863 ± 0.062  0.144 ± 0.058  8.66 ± 1.20&lt;br&gt;
TabPFN-3.5-Fast 0.854 ± 0.055  0.122 ± 0.027  7.36 ± 0.97&lt;br&gt;
TabPFN-3.5  0.844 ± 0.020  0.124 ± 0.025  7.28 ± 0.92&lt;/p&gt;

&lt;p&gt;I want to be straight about what this says. TabPFN had the lowest delay error and a slightly better Brier score, and it needed no tuning. But the gaps are inside one standard deviation, and its ROC-AUC was the lowest of the four. On this data, where each client has a strong habit, a simple per-client average is a tough baseline. So the honest claim is "comparable accuracy with no feature tuning, and a modest edge on predicting how many days late", not "TabPFN wins". Full tables are in results/benchmark.md.&lt;/p&gt;

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

&lt;p&gt;The product's whole pitch is that invoices are confidential. A client list with amounts and due dates is exactly what an NDA tells you not to upload. A closed vision API would have meant sending every invoice to someone else's server. With an open-weight model on Ollama, the document never leaves the laptop, and the privacy claim is something I can show in the code (the only network call in the extraction path is to localhost).&lt;/p&gt;

&lt;p&gt;Cost works the same way. There is no per-document price. At about 19 seconds per invoice on a 4 GB GPU laptop, a freelancer with a few dozen invoices a month pays nothing per page, so a low monthly price can still leave a margin.&lt;/p&gt;

&lt;p&gt;Open also meant I could adapt it to the hardware I actually had: swapping e2b for e4b is one variable, and if the TabPFN weights are unavailable the app falls back to scikit-learn instead of failing.&lt;/p&gt;

&lt;p&gt;A caveat I would rather say out loud: the TabPFN-3.5 weights are released for research and evaluation. Selling PayRadar as a commercial product would need a commercial license from Prior Labs. That is why the app has a fully permissive fallback path (gradient boosting and per-client averages) that works commercially. So the open story is strong for Gemma and for the architecture, and more conditional for TabPFN. A closed API would have been better at one thing: extraction from messy, low-quality photos. I have not tested that against real scans, so I do not claim a win there.&lt;/p&gt;

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

&lt;p&gt;Best Use of Gemma: Gemma 4 (e2b) runs locally through Ollama for vision extraction and email drafting.&lt;br&gt;
Best Use of TabPFN: TabPFN-3.5, run locally, predicts late-payment risk and days late inside a cash-flow forecasting pipeline, benchmarked against baselines on time-split data.&lt;br&gt;
Best Use of Render: the hosted demo runs on Render in a lightweight mode.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Fieldnotes: an offline Gemma field guide that turns a park walk into a quest</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Mon, 12 Oct 2026 06:49:39 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/fieldnotes-an-offline-gemma-field-guide-that-turns-a-park-walk-into-a-quest-43oe</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/fieldnotes-an-offline-gemma-field-guide-that-turns-a-park-walk-into-a-quest-43oe</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;Fieldnotes is an offline field-guide that turns a short walk into a&lt;br&gt;
quest. You pick a park and how many minutes you have, and a local&lt;br&gt;
open-weight model writes three tasks grounded in real OpenStreetMap&lt;br&gt;
features and species recorded nearby on iNaturalist. You photograph&lt;br&gt;
what you find, a local vision model checks each photo, and the app&lt;br&gt;
writes a short field-journal entry. It's for anyone who wants a reason&lt;br&gt;
to put the phone away and look around a park. [I built the area pack for Eco Park, Kolkata.]&lt;/p&gt;
&lt;h2&gt;
  
  
  Demo
&lt;/h2&gt;

&lt;p&gt;Screenshots and a screen recording coming soon. The README has the full setup.&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/dibyendu-coder" rel="noopener noreferrer"&gt;
        dibyendu-coder
      &lt;/a&gt; / &lt;a href="https://github.com/dibyendu-coder/Fieldnotes" rel="noopener noreferrer"&gt;
        Fieldnotes
      &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;🌿 Fieldnotes — Touch Grass Challenge&lt;/h1&gt;
&lt;/div&gt;
&lt;blockquote&gt;
&lt;p&gt;An offline-first outdoor nature quest assistant and field journal powered by local open-weight AI (&lt;code&gt;gemma4:e2b&lt;/code&gt;). Fieldnotes plans short walking quests tailored to your local park, verifies your photo observations using on-device vision, and writes a calm, reflective field journal — completely on-device with Wi-Fi switched off.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;📸 Screenshots&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;&lt;em&gt;(Replace with field walkthrough recordings or UI screenshots)&lt;/em&gt;&lt;/p&gt;
&lt;div class="snippet-clipboard-content notranslate position-relative overflow-auto"&gt;
&lt;pre class="notranslate"&gt;&lt;code&gt;+-----------------------------------------------------------------------+
|  🌿 Fieldnotes                 [Offline: runs locally]  [DEMO DATA]    |
|  Quiet, observation-first nature guide powered by local AI.            |
+-----------------------------------------------------------------------+
|  1. PLAN YOUR WALK                                                    |
|     Area Pack: [ demo [DEMO DATA]          v ]                        |
|     Time Available: [ 20 Minutes (Standard Walk) v ]                  |
|     [ Generate Quest ]                                                |
+-----------------------------------------------------------------------+
|  ACTIVE QUEST: Coastal Observation Walk                               |
|  Intro: Let us take a brief survey of the immediate coastal           |
|         environment and the unique life it harbors.                   |
|                                                                       |&lt;/code&gt;&lt;/pre&gt;…&lt;/div&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/dibyendu-coder/Fieldnotes" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


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

&lt;ul&gt;
&lt;li&gt;Model: Gemma 4 (e2b) running locally through Ollama, for both quest
writing and photo checking.&lt;/li&gt;
&lt;li&gt;Data: a one-time "area pack" download from OpenStreetMap and
iNaturalist (open data). After that, the app makes no network calls.&lt;/li&gt;
&lt;li&gt;Sunset and daylight are computed locally.&lt;/li&gt;
&lt;li&gt;Stack: Python/FastAPI backend, plain HTML page. &lt;/li&gt;
&lt;li&gt;Benchmarks on my laptop (RTX 2050, Windows, Ollama): gemma4:e2b ran
at about 35 tokens/s for text and 42 tokens/s for vision. gemma4:e4b
was about 9 tokens/s and froze my laptop while loading, so I
switched to e2b.&lt;/li&gt;
&lt;li&gt;Offline test: the smoke test passed with Wi-Fi switched off.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;ul&gt;
&lt;li&gt;It works where trails have no signal, because the model runs on the
laptop. A closed API can't follow you there.&lt;/li&gt;
&lt;li&gt;My location and photos never leave the device. Coordinates are only
sent to the public data services once, when I download the area pack.&lt;/li&gt;
&lt;li&gt;The data is open (OpenStreetMap, iNaturalist), and the model can be
swapped or fine-tuned for a region.&lt;/li&gt;
&lt;li&gt;Tradeoff, honestly: the small model is sometimes generic or unsure
about photos. &lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Best Use of Gemma (Gemma 4 e2b running locally).&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>You See the Swags. You Don’t See the Rejections.</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Tue, 01 Sep 2026 12:34:28 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/you-see-the-swags-you-dont-see-the-rejections-5bgl</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/you-see-the-swags-you-dont-see-the-rejections-5bgl</guid>
      <description>&lt;p&gt;If you look at my LinkedIn profile, you might think I have figured things out.&lt;/p&gt;

&lt;p&gt;You’ll see opportunities.&lt;/p&gt;

&lt;p&gt;Programs.&lt;/p&gt;

&lt;p&gt;Hackathons.&lt;/p&gt;

&lt;p&gt;Certificates.&lt;/p&gt;

&lt;p&gt;Rewards.&lt;/p&gt;

&lt;p&gt;Swags.&lt;/p&gt;

&lt;p&gt;Achievements.&lt;/p&gt;

&lt;p&gt;You’ll see posts where I’m excited because I got selected for something. You’ll see pictures of packages that arrived at my door. You’ll see another opportunity, another badge, another small win.&lt;/p&gt;

&lt;p&gt;And honestly, I’m grateful for every single one of them.&lt;/p&gt;

&lt;p&gt;But there’s a part of the story that rarely gets posted.&lt;/p&gt;

&lt;p&gt;The part where I applied and didn’t get selected.&lt;/p&gt;

&lt;p&gt;Again.&lt;/p&gt;

&lt;p&gt;And again.&lt;/p&gt;

&lt;p&gt;And again.&lt;/p&gt;

&lt;p&gt;There was a time when my inbox felt like a collection of rejection emails.&lt;/p&gt;

&lt;p&gt;“Unfortunately…”&lt;/p&gt;

&lt;p&gt;“We regret to inform you…”&lt;/p&gt;

&lt;p&gt;“Your application was not selected…”&lt;/p&gt;

&lt;p&gt;“Due to the high number of applications…”&lt;/p&gt;

&lt;p&gt;You get used to reading those words.&lt;/p&gt;

&lt;p&gt;Or at least, you think you do.&lt;/p&gt;

&lt;p&gt;The truth is, rejection still hurts.&lt;/p&gt;

&lt;p&gt;Especially when you’ve spent hours filling out an application.&lt;/p&gt;

&lt;p&gt;Writing answers.&lt;/p&gt;

&lt;p&gt;Building something.&lt;/p&gt;

&lt;p&gt;Recording a video.&lt;/p&gt;

&lt;p&gt;Making a resume.&lt;/p&gt;

&lt;p&gt;Trying to explain why you deserve a chance.&lt;/p&gt;

&lt;p&gt;And then you wait.&lt;/p&gt;

&lt;p&gt;Sometimes for days.&lt;/p&gt;

&lt;p&gt;Sometimes for weeks.&lt;/p&gt;

&lt;p&gt;Only to receive a polite email telling you that you weren’t selected.&lt;/p&gt;

&lt;p&gt;Nobody sees that part.&lt;/p&gt;

&lt;p&gt;Nobody sees the applications that went nowhere.&lt;/p&gt;

&lt;p&gt;Nobody sees the nights spent working on something that never got noticed.&lt;/p&gt;

&lt;p&gt;Nobody sees the projects that didn't win.&lt;/p&gt;

&lt;p&gt;Nobody sees the hackathons where I walked away without a prize.&lt;/p&gt;

&lt;p&gt;Nobody sees the opportunities I really wanted but didn't get.&lt;/p&gt;

&lt;p&gt;People usually see the result.&lt;/p&gt;

&lt;p&gt;They rarely see the attempts.&lt;/p&gt;

&lt;p&gt;And I think that's one of the biggest illusions social media creates.&lt;/p&gt;

&lt;p&gt;We compare our &lt;strong&gt;behind the scenes&lt;/strong&gt; with someone else's &lt;strong&gt;highlight reel&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;We see someone getting selected and think, “They are so lucky.”&lt;/p&gt;

&lt;p&gt;We see someone winning a hackathon and think, “They are naturally talented.”&lt;/p&gt;

&lt;p&gt;We see someone receiving a package full of goodies and think, “Everything is working out for them.”&lt;/p&gt;

&lt;p&gt;But we don't see the hundreds of applications behind that one selection.&lt;/p&gt;

&lt;p&gt;We don't see the dozens of failures behind that one success.&lt;/p&gt;

&lt;p&gt;I have applied to opportunities knowing there was a very high chance I wouldn't get selected.&lt;/p&gt;

&lt;p&gt;I still applied.&lt;/p&gt;

&lt;p&gt;Because what if?&lt;/p&gt;

&lt;p&gt;What if this is the one?&lt;/p&gt;

&lt;p&gt;What if this application changes something?&lt;/p&gt;

&lt;p&gt;What if I learn something while applying?&lt;/p&gt;

&lt;p&gt;What if I meet someone?&lt;/p&gt;

&lt;p&gt;What if I get selected?&lt;/p&gt;

&lt;p&gt;Sometimes the answer is no.&lt;/p&gt;

&lt;p&gt;But occasionally, the answer is yes.&lt;/p&gt;

&lt;p&gt;And those few yeses make the previous no's feel a little different.&lt;/p&gt;

&lt;p&gt;The swags that arrive at my home aren't just swags to me.&lt;/p&gt;

&lt;p&gt;They are reminders.&lt;/p&gt;

&lt;p&gt;Reminders of an application I almost didn't submit.&lt;/p&gt;

&lt;p&gt;A project I almost gave up on.&lt;/p&gt;

&lt;p&gt;A late night.&lt;/p&gt;

&lt;p&gt;A failed attempt.&lt;/p&gt;

&lt;p&gt;A rejection that came before the acceptance.&lt;/p&gt;

&lt;p&gt;So when you see me posting about another opportunity or another reward, please don't assume that everything has been easy.&lt;/p&gt;

&lt;p&gt;It hasn't.&lt;/p&gt;

&lt;p&gt;I have had my fair share of self-doubt.&lt;/p&gt;

&lt;p&gt;I've questioned whether I'm good enough.&lt;/p&gt;

&lt;p&gt;I've wondered why someone else got selected while I didn't.&lt;/p&gt;

&lt;p&gt;I've looked at other students moving faster than me and wondered if I'm falling behind.&lt;/p&gt;

&lt;p&gt;But I'm still applying.&lt;/p&gt;

&lt;p&gt;Still building.&lt;/p&gt;

&lt;p&gt;Still learning.&lt;/p&gt;

&lt;p&gt;Still trying.&lt;/p&gt;

&lt;p&gt;And maybe that's the real achievement.&lt;/p&gt;

&lt;p&gt;Not the certificate.&lt;/p&gt;

&lt;p&gt;Not the swag.&lt;/p&gt;

&lt;p&gt;Not the badge.&lt;/p&gt;

&lt;p&gt;Not even the selection.&lt;/p&gt;

&lt;p&gt;It's being able to open an inbox full of rejection emails and still send the next application.&lt;/p&gt;

&lt;p&gt;Because behind every visible achievement, there is usually an invisible pile of attempts.&lt;/p&gt;

&lt;p&gt;And mine is pretty big.&lt;/p&gt;

&lt;p&gt;So yes, you can celebrate the wins with me.&lt;/p&gt;

&lt;p&gt;But remember that they didn't happen overnight.&lt;/p&gt;

&lt;p&gt;You are just seeing the part of the journey that finally worked.&lt;/p&gt;

&lt;p&gt;You didn't see all the times it didn't.&lt;/p&gt;

&lt;p&gt;And honestly?&lt;/p&gt;

&lt;p&gt;I'm learning to be proud of those attempts too.&lt;br&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%2F08uw1vvr40nw9ee55v1d.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%2F08uw1vvr40nw9ee55v1d.png" alt=" " width="800" height="1200"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>I Built a Voice-First Learning Companion in 10 Days</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Sat, 15 Aug 2026 09:53:52 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/i-built-a-voice-first-learning-companion-in-10-days-1hoe</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/i-built-a-voice-first-learning-companion-in-10-days-1hoe</guid>
      <description>&lt;p&gt;Building an AI voice agent sounded simple at first.&lt;/p&gt;

&lt;p&gt;Give it a voice. Connect it to an LLM. Let users talk to it.&lt;/p&gt;

&lt;p&gt;But as I progressed through the 10 Days of Voice Agents – Voice for Bharat Edition, I realized that a useful voice agent needs much more than the ability to listen and speak.&lt;/p&gt;

&lt;p&gt;It needs to understand the user.&lt;/p&gt;

&lt;p&gt;It needs to remember context.&lt;/p&gt;

&lt;p&gt;It needs to know when to use external tools.&lt;/p&gt;

&lt;p&gt;It should know when to continue helping and when a human should take over.&lt;/p&gt;

&lt;p&gt;And most importantly, it should be able to measure whether it is actually helping the user achieve something.&lt;/p&gt;

&lt;p&gt;Over the last 10 days, I built a voice-first Learning &amp;amp; Literacy agent focused on helping learners practice spoken English in a more natural and supportive way.&lt;/p&gt;

&lt;p&gt;This is the story of what I built, the problems I faced, and what I learned while building it.&lt;/p&gt;

&lt;p&gt;Why I Chose the Learning &amp;amp; Literacy Track&lt;/p&gt;

&lt;p&gt;Many learners understand English but hesitate when it comes to speaking.&lt;/p&gt;

&lt;p&gt;Sometimes the problem is not a lack of knowledge.&lt;/p&gt;

&lt;p&gt;It is the fear of making mistakes.&lt;/p&gt;

&lt;p&gt;People may worry about pronunciation, grammar, or simply saying something wrong in front of others.&lt;/p&gt;

&lt;p&gt;I wanted to build something that gives learners a space to practice without feeling judged.&lt;/p&gt;

&lt;p&gt;That became the main idea behind my project.&lt;/p&gt;

&lt;p&gt;A voice-based learning companion that can:&lt;/p&gt;

&lt;p&gt;Talk with learners naturally&lt;br&gt;
Help them practice spoken English&lt;br&gt;
Give exercises&lt;br&gt;
Evaluate responses&lt;br&gt;
Provide supportive feedback&lt;br&gt;
Remember previous interactions&lt;br&gt;
Support Hindi-English code-mixed conversations&lt;br&gt;
Reach out for practice sessions&lt;br&gt;
Know when a human teacher should step in&lt;br&gt;
Track whether learning sessions are successful&lt;br&gt;
Hand off specialised requests to another agent when needed&lt;/p&gt;

&lt;p&gt;Instead of building just another chatbot with a voice attached to it, I wanted to explore what happens when a voice agent starts behaving more like a complete learning system.&lt;/p&gt;

&lt;p&gt;Why Voice?&lt;/p&gt;

&lt;p&gt;For spoken-English practice, typing is not enough.&lt;/p&gt;

&lt;p&gt;A learner needs to actually speak.&lt;/p&gt;

&lt;p&gt;Voice makes the interaction more natural.&lt;/p&gt;

&lt;p&gt;The learner can respond as they would in a normal conversation instead of typing carefully constructed sentences.&lt;/p&gt;

&lt;p&gt;It also makes the experience more accessible.&lt;/p&gt;

&lt;p&gt;A learner can simply speak:&lt;/p&gt;

&lt;p&gt;"Mujhe English practice karni hai."&lt;/p&gt;

&lt;p&gt;And continue the conversation naturally.&lt;/p&gt;

&lt;p&gt;This was especially important for my project because I wanted to support Hindi-English code-mixed conversations.&lt;/p&gt;

&lt;p&gt;In India, many people naturally switch between languages while speaking.&lt;/p&gt;

&lt;p&gt;Forcing users to speak in only one language can make an interaction feel unnatural.&lt;/p&gt;

&lt;p&gt;So one of my goals was to make the agent respond in a similar register when appropriate.&lt;br&gt;
The Core Idea&lt;/p&gt;

&lt;p&gt;The final project can be viewed as a learning journey.&lt;/p&gt;

&lt;p&gt;Learner speaks&lt;br&gt;
      ↓&lt;br&gt;
Voice agent understands the request&lt;br&gt;
      ↓&lt;br&gt;
Learner context and memory are considered&lt;br&gt;
      ↓&lt;br&gt;
Personalized learning interaction&lt;br&gt;
      ↓&lt;br&gt;
Exercise is provided&lt;br&gt;
      ↓&lt;br&gt;
Learner responds&lt;br&gt;
      ↓&lt;br&gt;
Answer is evaluated&lt;br&gt;
      ↓&lt;br&gt;
Supportive feedback&lt;br&gt;
      ↓&lt;br&gt;
Learning outcome is recorded&lt;br&gt;
      ↓&lt;br&gt;
If needed → Human help&lt;br&gt;
      ↓&lt;br&gt;
If specialist knowledge is needed → Agent handoff&lt;/p&gt;

&lt;p&gt;The goal was not to make every feature independent.&lt;/p&gt;

&lt;p&gt;I wanted the different parts of the project to work together.&lt;br&gt;
Building the Voice Agent&lt;/p&gt;

&lt;p&gt;The first step was creating the basic voice interaction.&lt;/p&gt;

&lt;p&gt;A typical voice agent involves several components working together:&lt;/p&gt;

&lt;p&gt;User speaks&lt;br&gt;
    ↓&lt;br&gt;
Speech-to-Text&lt;br&gt;
    ↓&lt;br&gt;
LLM / Agent Logic&lt;br&gt;
    ↓&lt;br&gt;
Memory + Tools + Agent Decisions&lt;br&gt;
    ↓&lt;br&gt;
Text Response&lt;br&gt;
    ↓&lt;br&gt;
Text-to-Speech&lt;br&gt;
    ↓&lt;br&gt;
User hears the response&lt;/p&gt;

&lt;p&gt;For the voice output, I used Murf Falcon, the fast text-to-speech API.&lt;/p&gt;

&lt;p&gt;The voice layer was important because the project was built around real conversations rather than text-based interactions.&lt;/p&gt;

&lt;p&gt;But getting an agent to speak is only the beginning.&lt;/p&gt;

&lt;p&gt;The next challenge was deciding how it should behave.&lt;/p&gt;

&lt;p&gt;Giving the Agent a Clear Personality and Guardrails&lt;/p&gt;

&lt;p&gt;For a Learning &amp;amp; Literacy agent, how the agent responds is just as important as what it responds with.&lt;/p&gt;

&lt;p&gt;A learner can make mistakes repeatedly.&lt;/p&gt;

&lt;p&gt;That is part of learning.&lt;/p&gt;

&lt;p&gt;So one of the important guardrails I implemented was:&lt;/p&gt;

&lt;p&gt;The agent should never shame a learner for giving a wrong answer.&lt;/p&gt;

&lt;p&gt;Instead of saying:&lt;/p&gt;

&lt;p&gt;"That's wrong."&lt;/p&gt;

&lt;p&gt;The agent should respond in a supportive way.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;"Good attempt. Let's look at this together."&lt;/p&gt;

&lt;p&gt;The goal was to make the interaction feel encouraging.&lt;/p&gt;

&lt;p&gt;Another important rule was that the agent should not make inappropriate assumptions about learners.&lt;/p&gt;

&lt;p&gt;It should focus on helping them practice and improve without making claims beyond its role.&lt;/p&gt;

&lt;p&gt;These instructions became the foundation for the rest of the project.&lt;/p&gt;

&lt;p&gt;Adding Memory and Personalization&lt;/p&gt;

&lt;p&gt;One of the biggest improvements came when I added persistent memory.&lt;/p&gt;

&lt;p&gt;A normal conversation can feel disconnected when the agent forgets everything after the session ends.&lt;/p&gt;

&lt;p&gt;If a learner practiced yesterday and comes back today, the experience should not always start from zero.&lt;/p&gt;

&lt;p&gt;I integrated a database to store useful learner context.&lt;/p&gt;

&lt;p&gt;This allowed the agent to remember information such as:&lt;/p&gt;

&lt;p&gt;Learner identity&lt;br&gt;
Preferences&lt;br&gt;
Relevant learning context&lt;br&gt;
Previous interactions&lt;/p&gt;

&lt;p&gt;The frontend was also adapted to reflect learner information and session state.&lt;/p&gt;

&lt;p&gt;This changed the experience from:&lt;/p&gt;

&lt;p&gt;"Hello, how can I help you?"&lt;/p&gt;

&lt;p&gt;every single time,&lt;/p&gt;

&lt;p&gt;to something more contextual and personalized.&lt;/p&gt;

&lt;p&gt;Memory made the agent feel less like a one-time interaction and more like a continuing learning companion.&lt;/p&gt;

&lt;p&gt;Teaching the Agent to Use Tools&lt;/p&gt;

&lt;p&gt;An AI agent cannot rely only on what the language model already knows.&lt;/p&gt;

&lt;p&gt;Sometimes it needs to fetch information or perform a specific action.&lt;/p&gt;

&lt;p&gt;For my Learning &amp;amp; Literacy track, I added tools that support the learning flow.&lt;/p&gt;

&lt;p&gt;The agent can:&lt;/p&gt;

&lt;p&gt;Fetch or select the next exercise&lt;br&gt;
Evaluate a learner's answer&lt;br&gt;
Use the result to provide feedback&lt;/p&gt;

&lt;p&gt;One lesson I learned here was that creating the function itself is not enough.&lt;/p&gt;

&lt;p&gt;The model needs to understand when it should use that function.&lt;/p&gt;

&lt;p&gt;A tool can work perfectly in the backend and still fail to be useful if the description is unclear.&lt;/p&gt;

&lt;p&gt;The tool description became an important part of the system.&lt;/p&gt;

&lt;p&gt;The agent needed to know:&lt;/p&gt;

&lt;p&gt;When to fetch an exercise&lt;br&gt;
When to evaluate an answer&lt;br&gt;
When not to call a tool unnecessarily&lt;/p&gt;

&lt;p&gt;This was one of the first moments where I understood that building AI agents is not only about writing functions.&lt;/p&gt;

&lt;p&gt;It is also about designing the decisions around those functions.&lt;/p&gt;

&lt;p&gt;Adding Outbound Practice Calls&lt;/p&gt;

&lt;p&gt;Most voice agents wait for the user to start the interaction.&lt;/p&gt;

&lt;p&gt;For Day 6, I explored the opposite approach.&lt;/p&gt;

&lt;p&gt;What if the agent could proactively reach out to the learner for practice?&lt;/p&gt;

&lt;p&gt;For a learning agent, the use case was simple:&lt;/p&gt;

&lt;p&gt;A learner chooses a practice time, and the agent can initiate a practice session.&lt;/p&gt;

&lt;p&gt;This introduced a new challenge.&lt;/p&gt;

&lt;p&gt;Outbound conversations need to begin differently from inbound conversations.&lt;/p&gt;

&lt;p&gt;The user did not initiate the interaction.&lt;/p&gt;

&lt;p&gt;So the agent needs to clearly communicate:&lt;/p&gt;

&lt;p&gt;Who is calling&lt;br&gt;
Why it is calling&lt;br&gt;
How the user can stop future calls&lt;/p&gt;

&lt;p&gt;While building this feature, I also faced practical challenges with telephony services, trial limitations, and account requirements.&lt;/p&gt;

&lt;p&gt;I initially explored different options before moving toward a SIP-based setup using Linphone for testing.&lt;/p&gt;

&lt;p&gt;This was one of the more challenging parts of the project because the idea was simple, but getting the communication setup working reliably required experimenting with different approaches.&lt;/p&gt;

&lt;p&gt;It reminded me that real-world AI products are not only about AI.&lt;/p&gt;

&lt;p&gt;Integration and infrastructure can sometimes take more time than the model logic itself.&lt;/p&gt;

&lt;p&gt;Teaching the Agent When to Ask for Human Help&lt;/p&gt;

&lt;p&gt;This was one of my favourite parts of the project.&lt;/p&gt;

&lt;p&gt;An AI agent should not try to solve every situation by itself.&lt;/p&gt;

&lt;p&gt;For my Learning &amp;amp; Literacy agent, I defined two situations where human support may be needed:&lt;/p&gt;

&lt;p&gt;The learner is genuinely frustrated or upset.&lt;br&gt;
The learner explicitly asks to speak with a teacher or human.&lt;/p&gt;

&lt;p&gt;However, I also needed to make sure that the agent did not escalate unnecessarily.&lt;/p&gt;

&lt;p&gt;A wrong answer is not a reason to involve a human.&lt;/p&gt;

&lt;p&gt;Making mistakes is part of learning.&lt;/p&gt;

&lt;p&gt;So the system needed to distinguish between:&lt;/p&gt;

&lt;p&gt;"I got this answer wrong."&lt;/p&gt;

&lt;p&gt;and&lt;/p&gt;

&lt;p&gt;"I'm really frustrated. I don't understand this anymore. Can I talk to a teacher?"&lt;/p&gt;

&lt;p&gt;When human help is needed, the agent does not immediately share information.&lt;/p&gt;

&lt;p&gt;It first asks for permission.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;p&gt;Learner needs help&lt;br&gt;
        ↓&lt;br&gt;
Agent detects the situation&lt;br&gt;
        ↓&lt;br&gt;
Agent explains what information can be shared&lt;br&gt;
        ↓&lt;br&gt;
Learner gives permission&lt;br&gt;
        ↓&lt;br&gt;
Human-help request is created&lt;br&gt;
        ↓&lt;br&gt;
Reference ID is generated&lt;br&gt;
        ↓&lt;br&gt;
Teacher or human can review the request&lt;/p&gt;

&lt;p&gt;The request contains only useful information, such as:&lt;/p&gt;

&lt;p&gt;Who needs help&lt;br&gt;
What the learner is struggling with&lt;br&gt;
What the agent has already tried&lt;br&gt;
Urgency&lt;br&gt;
Preferred language or follow-up method&lt;/p&gt;

&lt;p&gt;The agent does not need to send the entire conversation.&lt;/p&gt;

&lt;p&gt;This feature made the project feel more realistic.&lt;/p&gt;

&lt;p&gt;Sometimes the best thing an AI system can do is recognize that another person is better suited to help.&lt;/p&gt;

&lt;p&gt;Measuring Whether Learning Actually Happened&lt;/p&gt;

&lt;p&gt;Building an agent is one thing.&lt;/p&gt;

&lt;p&gt;Knowing whether it is useful is another.&lt;/p&gt;

&lt;p&gt;For the Call Analytics Dashboard, I had to answer a simple question:&lt;/p&gt;

&lt;p&gt;What does a successful call mean for my agent?&lt;/p&gt;

&lt;p&gt;I decided that a successful learning session means:&lt;/p&gt;

&lt;p&gt;The learner completes at least one exercise and receives feedback.&lt;/p&gt;

&lt;p&gt;A failed session does not necessarily mean the system crashed.&lt;/p&gt;

&lt;p&gt;It simply means the learning objective was not completed.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;The learner ends the session early.&lt;br&gt;
The learner does not complete an exercise.&lt;br&gt;
The conversation stops before feedback is given.&lt;/p&gt;

&lt;p&gt;Every session is recorded with an outcome.&lt;/p&gt;

&lt;p&gt;The dashboard tracks:&lt;/p&gt;

&lt;p&gt;Total Calls&lt;br&gt;
Successful Calls&lt;br&gt;
Failed Calls&lt;/p&gt;

&lt;p&gt;The important part was that these numbers come from actual agent sessions.&lt;/p&gt;

&lt;p&gt;They are not hardcoded values.&lt;/p&gt;

&lt;p&gt;This changed the way I thought about AI agents.&lt;/p&gt;

&lt;p&gt;It is easy to measure activity.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;How many conversations happened?&lt;/p&gt;

&lt;p&gt;But activity does not automatically mean success.&lt;/p&gt;

&lt;p&gt;For a learning agent, a better question is:&lt;/p&gt;

&lt;p&gt;Did the learner actually complete something useful?&lt;/p&gt;

&lt;p&gt;That became the purpose of the analytics system.&lt;/p&gt;

&lt;p&gt;Moving from One Agent to Multiple Agents&lt;/p&gt;

&lt;p&gt;By Day 9, the main agent could already handle several tasks.&lt;/p&gt;

&lt;p&gt;But one agent should not try to become an expert at everything.&lt;/p&gt;

&lt;p&gt;For the Learning &amp;amp; Literacy track, I created a separate:&lt;/p&gt;

&lt;p&gt;Maths Practice Specialist&lt;/p&gt;

&lt;p&gt;The main Learning &amp;amp; Literacy agent continues to handle things such as:&lt;/p&gt;

&lt;p&gt;Spoken English practice&lt;br&gt;
English exercises&lt;br&gt;
Grammar support&lt;br&gt;
General learning conversations&lt;/p&gt;

&lt;p&gt;But when the learner asks for maths help, the conversation is handed off to the Maths Practice Specialist.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;Learner:&lt;/p&gt;

&lt;p&gt;"I don't understand percentages."&lt;/p&gt;

&lt;p&gt;The main agent responds:&lt;/p&gt;

&lt;p&gt;"I'll connect you to our Maths Practice Specialist."&lt;/p&gt;

&lt;p&gt;The specialist then continues the conversation.&lt;/p&gt;

&lt;p&gt;The important part is that the learner does not have to repeat the entire problem.&lt;/p&gt;

&lt;p&gt;Relevant context is passed during the handoff.&lt;/p&gt;

&lt;p&gt;The flow looks like this:&lt;/p&gt;

&lt;p&gt;Learner asks for help&lt;br&gt;
        ↓&lt;br&gt;
Main Learning Agent&lt;br&gt;
        ↓&lt;br&gt;
Does this require Maths expertise?&lt;br&gt;
        ↓&lt;br&gt;
      YES&lt;br&gt;
        ↓&lt;br&gt;
Agent announces handoff&lt;br&gt;
        ↓&lt;br&gt;
Maths Practice Specialist&lt;br&gt;
        ↓&lt;br&gt;
Continues with existing context&lt;/p&gt;

&lt;p&gt;The specialist can:&lt;/p&gt;

&lt;p&gt;Explain concepts step by step&lt;br&gt;
Give practice questions&lt;br&gt;
Provide hints&lt;br&gt;
Evaluate answers&lt;br&gt;
Give supportive feedback&lt;/p&gt;

&lt;p&gt;The same language style can also continue after the handoff.&lt;/p&gt;

&lt;p&gt;For example:&lt;/p&gt;

&lt;p&gt;"Mujhe fractions samajh nahi aa raha."&lt;/p&gt;

&lt;p&gt;The specialist can continue naturally in a Hindi-English code-mixed style.&lt;/p&gt;

&lt;p&gt;This was my first deeper experience with a multi-agent workflow.&lt;/p&gt;

&lt;p&gt;The biggest lesson was simple:&lt;/p&gt;

&lt;p&gt;Instead of making one agent responsible for everything, it can be better to give different agents clear and focused responsibilities.&lt;/p&gt;

&lt;p&gt;The Overall Architecture&lt;/p&gt;

&lt;p&gt;The final system can be represented like this:&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                    ┌───────────────────┐
                    │      Learner      │
                    │   Voice / Browser │
                    └─────────┬─────────┘
                              │
                              ▼
                    ┌───────────────────┐
                    │ Speech-to-Text    │
                    └─────────┬─────────┘
                              │
                              ▼
             ┌─────────────────────────────┐
             │ Main Learning &amp;amp; Literacy    │
             │ Agent                       │
             │                             │
             │ • English Practice          │
             │ • Guardrails                │
             │ • Memory                    │
             │ • Learning Flow             │
             └───────┬──────────┬──────────┘
                     │          │
                     │          │
              ┌──────▼───┐  ┌──▼────────────────┐
              │ Learning │  │ Maths Practice    │
              │ Tools    │  │ Specialist Agent  │
              └──────┬───┘  └───────────────────┘
                     │
                     ▼
             ┌─────────────────┐
             │ Memory / SQLite │
             └────────┬────────┘
                      │
         ┌────────────┼─────────────┐
         │            │             │
         ▼            ▼             ▼
  Human Help     Call Analytics   Outbound
   Escalation      Dashboard       Calling

                      │
                      ▼
                ┌─────────────┐
                │ Murf Falcon │
                │     TTS     │
                └─────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

&lt;p&gt;The Difficult Parts I Faced&lt;/p&gt;

&lt;p&gt;Not everything worked perfectly on the first attempt.&lt;/p&gt;

&lt;p&gt;And honestly, that was probably one of the biggest learning experiences from this challenge.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Telephony Setup&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;I initially explored different telephony services for outbound calling.&lt;/p&gt;

&lt;p&gt;Some required upgrades before allowing access to certain features.&lt;/p&gt;

&lt;p&gt;Others had trial limitations or different onboarding requirements.&lt;/p&gt;

&lt;p&gt;I had to experiment with different approaches instead of assuming that the first service would work perfectly.&lt;/p&gt;

&lt;p&gt;Eventually, I explored a SIP-based setup using Linphone for the outbound calling workflow.&lt;/p&gt;

&lt;p&gt;The lesson was:&lt;/p&gt;

&lt;p&gt;Always have an alternative approach when a third-party integration becomes a blocker.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tool Calling&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Creating a function was relatively straightforward.&lt;/p&gt;

&lt;p&gt;Making sure the agent calls it at the correct time was harder.&lt;/p&gt;

&lt;p&gt;If the tool description is too vague, the agent might:&lt;/p&gt;

&lt;p&gt;Call it unnecessarily&lt;br&gt;
Never call it&lt;br&gt;
Call it at the wrong point in the conversation&lt;/p&gt;

&lt;p&gt;I learned that tool descriptions are part of the agent's reasoning interface.&lt;/p&gt;

&lt;p&gt;The description needs to clearly explain:&lt;/p&gt;

&lt;p&gt;What the tool does&lt;br&gt;
When it should be used&lt;br&gt;
When it should not be used&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Human Escalation&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The difficult part was defining when the agent should stop.&lt;/p&gt;

&lt;p&gt;A learner making mistakes does not mean the system failed.&lt;/p&gt;

&lt;p&gt;The agent should continue encouraging and teaching.&lt;/p&gt;

&lt;p&gt;But genuine frustration or an explicit request for a teacher is different.&lt;/p&gt;

&lt;p&gt;Finding this boundary helped me understand the importance of defining clear conditions for AI systems.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Defining Success&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;At first, it would have been easy to mark every completed conversation as successful.&lt;/p&gt;

&lt;p&gt;But that would not tell me much.&lt;/p&gt;

&lt;p&gt;A learner could talk for five minutes and still not complete a single exercise.&lt;/p&gt;

&lt;p&gt;So I defined success around the actual learning outcome.&lt;/p&gt;

&lt;p&gt;The learner needs to:&lt;/p&gt;

&lt;p&gt;Complete at least one exercise.&lt;br&gt;
Have the answer evaluated.&lt;br&gt;
Receive feedback.&lt;/p&gt;

&lt;p&gt;Only then is the session marked as successful.&lt;/p&gt;

&lt;p&gt;This made the analytics more meaningful.&lt;/p&gt;

&lt;p&gt;What I Learned About Building Voice Agents&lt;/p&gt;

&lt;p&gt;After working on this project for 10 days, I learned that a voice agent is much more than:&lt;/p&gt;

&lt;p&gt;Speech in → AI → Speech out.&lt;/p&gt;

&lt;p&gt;A useful agent needs a complete system around it.&lt;/p&gt;

&lt;p&gt;Here are some of my biggest takeaways.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Voice is only one layer&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The voice may be the first thing the user notices.&lt;/p&gt;

&lt;p&gt;But memory, tools, guardrails, decisions, and context determine whether the interaction is actually useful.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Memory changes the experience&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Remembering previous interactions makes the agent feel more continuous.&lt;/p&gt;

&lt;p&gt;The conversation no longer needs to restart from zero every time.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Tools need good descriptions&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;A working function is not enough.&lt;/p&gt;

&lt;p&gt;The agent needs clear instructions about when and why it should use it.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;AI should know its limits&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Human escalation was an important reminder that an AI agent does not need to solve everything.&lt;/p&gt;

&lt;p&gt;Knowing when to ask for help can be just as important as answering correctly.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Measure outcomes, not just conversations&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Counting calls is easy.&lt;/p&gt;

&lt;p&gt;Understanding whether users achieved something meaningful is more valuable.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;One agent does not need to do everything&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Specialist agents can have smaller, clearer responsibilities.&lt;/p&gt;

&lt;p&gt;The main agent can focus on coordinating the experience and hand off specific tasks when needed.&lt;br&gt;
How to Build and Run This Project &lt;br&gt;
GitHub Repository&lt;/p&gt;

&lt;p&gt;Repository: &lt;a href="https://github.com/dibyendu-coder/murf-livekit-starter" rel="noopener noreferrer"&gt;https://github.com/dibyendu-coder/murf-livekit-starter&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Prerequisites&lt;/p&gt;

&lt;p&gt;1.Python (v3.9 or higher)&lt;br&gt;
2.Node.js (v18 or higher) and npm&lt;br&gt;
3.LiveKit Cloud / Server credentials or local LiveKit Server instance&lt;br&gt;
4.API Keys:&lt;br&gt;
 LiveKit API Key &amp;amp; Secret&lt;br&gt;
 Murf AI API Key&lt;br&gt;
 LLM Provider API Key (e.g., OpenAI / Gemini API key used in your agent        backend)&lt;/p&gt;

&lt;p&gt;Installation&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Obtain the Project Files&lt;br&gt;
Clone the repository or download the project files into a local folder.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Configure Environment Variables (.env.local)&lt;br&gt;
Create a .env.local file inside the murf-livekit-starter root (or backend/frontend folders as required by your setup) containing:&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;env&lt;/p&gt;

&lt;h1&gt;
  
  
  LiveKit Configuration
&lt;/h1&gt;

&lt;p&gt;LIVEKIT_URL=wss://your-livekit-domain.livekit.cloud&lt;br&gt;
LIVEKIT_API_KEY=your_livekit_api_key&lt;br&gt;
LIVEKIT_API_SECRET=your_livekit_api_secret&lt;/p&gt;

&lt;h1&gt;
  
  
  Next.js Public URL
&lt;/h1&gt;

&lt;p&gt;NEXT_PUBLIC_LIVEKIT_URL=wss://your-livekit-domain.livekit.cloud&lt;/p&gt;

&lt;h1&gt;
  
  
  Murf &amp;amp; AI Keys
&lt;/h1&gt;

&lt;p&gt;MURF_API_KEY=your_murf_api_key&lt;br&gt;
OPENAI_API_KEY=your_openai_api_key # (or relevant LLM key)&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Backend Setup
Open a terminal and navigate to the backend folder:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;bash&lt;br&gt;
cd murf-livekit-starter/backend&lt;br&gt;
Create and activate a Virtual Environment:&lt;/p&gt;

&lt;p&gt;Windows (PowerShell):&lt;br&gt;
powershell&lt;br&gt;
python -m venv .venv&lt;br&gt;
..venv\Scripts\Activate.ps1&lt;br&gt;
macOS / Linux:&lt;br&gt;
bash&lt;br&gt;
python3 -m venv .venv&lt;br&gt;
source .venv/bin/activate&lt;br&gt;
Install Dependencies:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
pip install -r requirements.txt&lt;br&gt;
Start the Agent Backend Worker:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
python agent.py dev&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Frontend Setup
Open a second terminal window and navigate to the frontend folder:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;bash&lt;br&gt;
cd murf-livekit-starter/frontend&lt;br&gt;
Install Node Dependencies:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
npm install&lt;br&gt;
Start the Next.js Development Server:&lt;/p&gt;

&lt;p&gt;bash&lt;br&gt;
npm run dev&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Launch the Application
Open your browser and go to &lt;a href="http://localhost:3000" rel="noopener noreferrer"&gt;http://localhost:3000&lt;/a&gt;.
Click Connect / Start Call to initiate a voice session with your LiveKit + Murf AI voice agent.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Environment Variables&lt;/p&gt;

&lt;h1&gt;
  
  
  LiveKit Configuration
&lt;/h1&gt;

&lt;p&gt;LIVEKIT_URL=wss://your-livekit-domain.livekit.cloud&lt;br&gt;
LIVEKIT_API_KEY=your_livekit_api_key&lt;br&gt;
LIVEKIT_API_SECRET=your_livekit_api_secret&lt;/p&gt;

&lt;h1&gt;
  
  
  Next.js Public URL
&lt;/h1&gt;

&lt;p&gt;NEXT_PUBLIC_LIVEKIT_URL=wss://your-livekit-domain.livekit.cloud&lt;/p&gt;

&lt;h1&gt;
  
  
  Murf &amp;amp; AI Keys
&lt;/h1&gt;

&lt;p&gt;MURF_API_KEY=your_murf_api_key&lt;br&gt;
OPENAI_API_KEY=your_openai_api_key  or GEMINI_API_KEY=your_gemini_api_key&lt;br&gt;
Also make sure .env is included in .gitignore.&lt;br&gt;
How to Run the Project&lt;br&gt;
Activate the livekit server from the root folder :&lt;br&gt;
.\livekit-server.exe --dev&lt;br&gt;
Activate the backend : uv run python src/agent.py dev&lt;br&gt;
Activate the  frontend : pnpm dev&lt;br&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%2Fiv7ox37mm1jlcktv3bd8.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%2Fiv7ox37mm1jlcktv3bd8.png" alt=" " width="799" height="357"&gt;&lt;/a&gt;&lt;br&gt;
What I Built in 10 Days&lt;/p&gt;

&lt;p&gt;Looking back, the project started as a simple idea:&lt;/p&gt;

&lt;p&gt;Build a voice agent for learning.&lt;/p&gt;

&lt;p&gt;By the end of the challenge, the project had grown into a system that could:&lt;/p&gt;

&lt;p&gt;Communicate through voice&lt;br&gt;
Follow learning and safety guardrails&lt;br&gt;
Support Hindi-English code-mixed conversations&lt;br&gt;
Remember returning learners&lt;br&gt;
Personalize interactions&lt;br&gt;
Fetch and provide learning exercises&lt;br&gt;
Evaluate answers&lt;br&gt;
Give supportive feedback&lt;br&gt;
Make outbound practice calls&lt;br&gt;
Ask for permission before escalating to a human&lt;br&gt;
Create structured human-help requests&lt;br&gt;
Track successful and failed learning sessions&lt;br&gt;
Display analytics from real session data&lt;br&gt;
Hand conversations to a specialist agent&lt;/p&gt;

&lt;p&gt;The project changed significantly from the original idea.&lt;/p&gt;

&lt;p&gt;And that is probably the biggest thing I enjoyed about this challenge.&lt;/p&gt;

&lt;p&gt;Every day added a new question.&lt;/p&gt;

&lt;p&gt;What should the agent remember?&lt;/p&gt;

&lt;p&gt;When should it use a tool?&lt;/p&gt;

&lt;p&gt;How should it contact the user?&lt;/p&gt;

&lt;p&gt;When should it stop?&lt;/p&gt;

&lt;p&gt;How do we know whether it is actually helping?&lt;/p&gt;

&lt;p&gt;When should another agent take over?&lt;/p&gt;

&lt;p&gt;Answering those questions slowly turned a simple voice assistant into something that feels much closer to a real product.&lt;/p&gt;

&lt;p&gt;Final Thoughts&lt;/p&gt;

&lt;p&gt;Ten days ago, I started with the goal of building a voice agent.&lt;/p&gt;

&lt;p&gt;I thought the biggest challenge would be getting the AI to listen and respond.&lt;/p&gt;

&lt;p&gt;It was not.&lt;/p&gt;

&lt;p&gt;The bigger challenge was deciding how the agent should behave once the conversation starts.&lt;/p&gt;

&lt;p&gt;What should it remember?&lt;/p&gt;

&lt;p&gt;What should it do with that information?&lt;/p&gt;

&lt;p&gt;When should it use a tool?&lt;/p&gt;

&lt;p&gt;When should it continue helping?&lt;/p&gt;

&lt;p&gt;When should it ask for human support?&lt;/p&gt;

&lt;p&gt;And how do we know if the interaction was actually successful?&lt;/p&gt;

&lt;p&gt;By the end of this journey, I realized that building an AI agent is not just about making it more capable.&lt;/p&gt;

&lt;p&gt;It is also about giving it boundaries.&lt;/p&gt;

&lt;p&gt;Knowing what it should do.&lt;/p&gt;

&lt;p&gt;Knowing what it should not do.&lt;/p&gt;

&lt;p&gt;And knowing when it should step aside.&lt;/p&gt;

&lt;p&gt;This project is still a work in progress, but the last 10 days gave me a much better understanding of how voice agents can move beyond simple conversations.&lt;/p&gt;

&lt;p&gt;I started by building a voice agent.&lt;/p&gt;

&lt;p&gt;I ended up building the foundation of a voice-first learning companion that can remember learners, personalize practice, use tools, proactively initiate conversations, involve humans when needed, measure outcomes, and work with specialist agents.&lt;/p&gt;

&lt;p&gt;And this is only the beginning.&lt;/p&gt;

&lt;p&gt;Built as part of the 10 Days of Voice Agents – Voice for Bharat Edition using Murf Falcon.&lt;/p&gt;

&lt;h1&gt;
  
  
  VoiceForBharat #MurfAI #MurfFalcon #AIVoiceAgents #VoiceAI #LearningAndLiteracy #AIAgents #MultiAgent #ConversationalAI #BuildInPublic #10DaysofAIVoiceAgents
&lt;/h1&gt;

</description>
    </item>
    <item>
      <title>The Silent Side of Hackathons: When Your Name Is Never Called</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Wed, 03 Jun 2026 17:26:17 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/the-silent-side-of-hackathons-when-your-name-is-never-called-2683</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/the-silent-side-of-hackathons-when-your-name-is-never-called-2683</guid>
      <description>&lt;p&gt;If you look at my social media profile, you might think my hackathon journey has been exciting. There are photos from events, team pictures, project demos, certificates, and countless posts about late-night coding sessions.&lt;/p&gt;

&lt;p&gt;What you won't see are the moments after the winner announcements.&lt;/p&gt;

&lt;p&gt;The moments when everyone gathers around the stage, waiting for the results.&lt;/p&gt;

&lt;p&gt;The moments when I sit quietly in the audience, listening to names that are never mine.&lt;/p&gt;

&lt;p&gt;I have participated in many hackathons. I have spent sleepless nights building projects, fixing bugs at 3 AM, creating presentations minutes before submission, and learning technologies that I had never touched before. Every time I join a new hackathon, I tell myself that maybe this time things will be different.&lt;/p&gt;

&lt;p&gt;Maybe this time my project will be good enough.&lt;/p&gt;

&lt;p&gt;Maybe this time my team will make it to the top.&lt;/p&gt;

&lt;p&gt;Maybe this time I will finally hear my name being called.&lt;/p&gt;

&lt;p&gt;But often, the results are the same.&lt;/p&gt;

&lt;p&gt;Someone else wins.&lt;/p&gt;

&lt;p&gt;I clap for them.&lt;/p&gt;

&lt;p&gt;I smile.&lt;/p&gt;

&lt;p&gt;And then I quietly leave the venue.&lt;/p&gt;

&lt;p&gt;At first, it was easy to accept. I was a beginner, and there was always something new to learn. But after participating in many hackathons, the feeling became heavier. Watching other teams win again and again started making me question myself.&lt;/p&gt;

&lt;p&gt;Am I not working hard enough?&lt;/p&gt;

&lt;p&gt;Am I not skilled enough?&lt;/p&gt;

&lt;p&gt;What am I doing wrong?&lt;/p&gt;

&lt;p&gt;These questions are difficult because there is rarely a clear answer.&lt;/p&gt;

&lt;p&gt;The truth is that hackathons are strange competitions. Sometimes the best technical solution does not win. Sometimes the team with the best presentation stands out. Sometimes the judges are looking for something different. Sometimes another team simply builds something extraordinary.&lt;/p&gt;

&lt;p&gt;And sometimes, despite doing everything right, you still don't win.&lt;/p&gt;

&lt;p&gt;That is probably the hardest lesson I have learned.&lt;/p&gt;

&lt;p&gt;I used to think that effort always guarantees results. Hackathons taught me that effort guarantees growth, not necessarily trophies.&lt;/p&gt;

&lt;p&gt;Every project I built taught me something valuable. I learned how to work under pressure. I learned how to communicate ideas. I learned how to collaborate with teammates. I learned how to present in front of judges. Most importantly, I learned how to keep going after disappointment.&lt;/p&gt;

&lt;p&gt;People often celebrate the winners, and they deserve that recognition. But there are hundreds of participants who return home without medals, prize money, or certificates of achievement. Their stories are rarely told.&lt;/p&gt;

&lt;p&gt;I am one of them.&lt;/p&gt;

&lt;p&gt;I know what it feels like to refresh the results page again and again.&lt;/p&gt;

&lt;p&gt;I know what it feels like to believe your project has a chance.&lt;/p&gt;

&lt;p&gt;I know what it feels like to hear the final winner announcement and realize your journey ends there.&lt;/p&gt;

&lt;p&gt;It hurts.&lt;/p&gt;

&lt;p&gt;Not because someone else won.&lt;/p&gt;

&lt;p&gt;But because of the dreams you secretly attached to that project.&lt;/p&gt;

&lt;p&gt;The dreams of proving yourself.&lt;/p&gt;

&lt;p&gt;The dreams of finally succeeding.&lt;/p&gt;

&lt;p&gt;The dreams of showing everyone that your hard work was worth it.&lt;/p&gt;

&lt;p&gt;Yet despite all of this, I keep coming back.&lt;/p&gt;

&lt;p&gt;Not because I enjoy losing.&lt;/p&gt;

&lt;p&gt;Not because failure feels good.&lt;/p&gt;

&lt;p&gt;But because every hackathon gives me another opportunity to improve.&lt;/p&gt;

&lt;p&gt;Every loss teaches me something that a victory never could.&lt;/p&gt;

&lt;p&gt;Every rejected project reveals a weakness I can work on.&lt;/p&gt;

&lt;p&gt;Every unsuccessful attempt makes me more experienced than I was before.&lt;/p&gt;

&lt;p&gt;One day, maybe my name will finally be called on that stage.&lt;/p&gt;

&lt;p&gt;Maybe I will stand where the winners stand today.&lt;/p&gt;

&lt;p&gt;Maybe all these failures will become part of a success story.&lt;/p&gt;

&lt;p&gt;But even if that day takes longer than expected, I refuse to stop trying.&lt;/p&gt;

&lt;p&gt;Because hackathons were never just about winning.&lt;/p&gt;

&lt;p&gt;They were about becoming the kind of person who keeps building even when nobody is watching.&lt;/p&gt;

&lt;p&gt;And until the day my name is announced, I will continue doing exactly that.&lt;/p&gt;

&lt;p&gt;Building.&lt;/p&gt;

&lt;p&gt;Learning.&lt;/p&gt;

&lt;p&gt;Failing.&lt;/p&gt;

&lt;p&gt;Improving.&lt;/p&gt;

&lt;p&gt;And showing up again.&lt;/p&gt;

&lt;p&gt;Because sometimes the strongest participants in a hackathon are not the ones holding the trophies.&lt;/p&gt;

&lt;p&gt;They are the ones sitting quietly in the audience, carrying their disappointment home, and still deciding to come back for the next challenge.&lt;br&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.amazonaws.com%2Fuploads%2Farticles%2Fkfbovhnfxcvvmweidzll.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.amazonaws.com%2Fuploads%2Farticles%2Fkfbovhnfxcvvmweidzll.png" alt=" " width="800" height="533"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
    </item>
    <item>
      <title>Building BackBench — An Emotionally Adaptive AI Learning Platform</title>
      <dc:creator>Dibyendu Chatterjee</dc:creator>
      <pubDate>Tue, 19 May 2026 21:22:52 +0000</pubDate>
      <link>https://dev.to/dibyendu_chatterjee_409d4/building-backbench-an-emotionally-adaptive-ai-learning-platform-3bhd</link>
      <guid>https://dev.to/dibyendu_chatterjee_409d4/building-backbench-an-emotionally-adaptive-ai-learning-platform-3bhd</guid>
      <description>&lt;p&gt;During this hackathon I kind of wanted to build something that felt different from the usual AI chatbots, quiz makers, and productivity tools. I mean not just another screen that delivers content or spits out answers right away. Instead of focusing on content only, I wanted to tackle a more real and human issue in education—silent student struggle, you know.&lt;/p&gt;

&lt;p&gt;A lot of students don’t exactly “stop learning” they just… hesitate. They mess up again and again , lose confidence, avoid asking doubts where everyone can see, and slowly drift out of the whole learning vibe. Most platforms never catch that emotional part. They can track marks, timelines, “progress”, but they don’t really see when a student is quietly giving up.&lt;/p&gt;

&lt;p&gt;That’s how BackBench happened. It’s an emotionally adaptive AI-powered learning platform made to support students in real time when they struggle silently , without turning it into a big performance.&lt;/p&gt;

&lt;p&gt;The main idea behind BackBench is honestly pretty plain:&lt;/p&gt;

&lt;p&gt;“Learning should adapt to the student, not the other way around.” &lt;/p&gt;

&lt;p&gt;So the platform looks for patterns like repeated wrong answers, hesitation, inactivity, quick guessing, and focus drops. Once those signals show up, BackBench adjusts the learning experience automatically with things like Rescue Mode, adaptive AI explanations, emotional learning analytics, and anonymous doubt rooms.&lt;/p&gt;

&lt;p&gt;One of the coolest pieces I built was Rescue Mode. When the system notices that a student is struggling repeatedly, the interface turns into a more calm, more supportive space. The AI simplifies the concepts, it changes teaching styles, and it gives guided explanations in a way that feels less overwhelming. The point isn’t to “correct” them harder, or make them feel judged. It’s more like helping them get confidence back, step by step, while learning.&lt;/p&gt;

&lt;p&gt;Another feature I really enjoyed was the Anonymous Doubt Rooms. A ton of students avoid questions publicly because they worry about embarrassment or being judged. These rooms let students collaborate anonymously in real time, so they get a safe emotional space, while still getting AI-assisted help when they need it.&lt;/p&gt;

&lt;p&gt;I built the whole project using MeDo.dev. One thing I learned here is that modern development isn’t just about writing code by hand. It’s also product thinking, user experience design, and figuring out the actual problem you’re solving. I used MeDo not only as a code generation helper, but kind of as a creative partner to sharpen the product direction, the interaction flows, the adaptive systems, and the emotional UX of the platform , step by step.&lt;/p&gt;

&lt;p&gt;This hackathon taught me that the best projects aren’t always the ones with the most technical complexity. Sometimes the strongest ideas come from understanding human problems deeply, and then building experiences that actually feel meaningful.&lt;/p&gt;

&lt;p&gt;BackBench isn’t just another AI tutor. It’s an attempt to make education more emotionally aware, more supportive, and more human-centered.&lt;/p&gt;

&lt;p&gt;And yeah, this is only the beginning.&lt;/p&gt;

</description>
      <category>builtwithmedo</category>
    </item>
  </channel>
</rss>
