<?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: Abhisek Roy</title>
    <description>The latest articles on DEV Community by Abhisek Roy (@abhisekroy169).</description>
    <link>https://dev.to/abhisekroy169</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fthepracticaldev.s3.amazonaws.com%2Fi%2F99mvlsfu5tfj9m7ku25d.png</url>
      <title>DEV Community: Abhisek Roy</title>
      <link>https://dev.to/abhisekroy169</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/abhisekroy169"/>
    <language>en</language>
    <item>
      <title>My Parents Kept Asking "Is This SMS a Scam?" So I Built Her an Offline AI That Explains Every Message</title>
      <dc:creator>Abhisek Roy</dc:creator>
      <pubDate>Sat, 03 Oct 2026 04:24:00 +0000</pubDate>
      <link>https://dev.to/abhisekroy169/my-parents-kept-asking-is-this-sms-a-scam-so-i-built-her-an-offline-ai-that-explains-every-5067</link>
      <guid>https://dev.to/abhisekroy169/my-parents-kept-asking-is-this-sms-a-scam-so-i-built-her-an-offline-ai-that-explains-every-5067</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;Every few days my phone buzzes with a screenshot from &lt;strong&gt;my mom&lt;/strong&gt;. It's always an SMS, and it's always followed by the same question: &lt;em&gt;"What does this mean? Should I do something?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Sometimes it's harmless: a subscription renewal, an EMI reminder, a recharge confirmation. Sometimes it's a scam: &lt;em&gt;"Your KYC has expired, your account will be blocked today, click this link."&lt;/em&gt; To her, they look exactly the same. Bank messages are full of short forms like &lt;em&gt;A/c XX1234&lt;/em&gt;, &lt;em&gt;NEFT&lt;/em&gt;, &lt;em&gt;Avl Bal&lt;/em&gt;, and &lt;em&gt;debited&lt;/em&gt;, and scammers copy that style on purpose.&lt;/p&gt;

&lt;p&gt;I'm not always free to answer right away. And the worst time for her to be confused is when a &lt;strong&gt;scammer is creating urgency&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Offline SMS Buddy&lt;/strong&gt;: a small app on her laptop where she pastes any confusing SMS and presses one big button. It answers three questions in plain language:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🧠 &lt;strong&gt;What does this SMS mean?&lt;/strong&gt;
&lt;/li&gt;
&lt;li&gt;⚠️ &lt;strong&gt;What should I do?&lt;/strong&gt; (often: "nothing, this is just information")&lt;/li&gt;
&lt;li&gt;🛑 &lt;strong&gt;What should I be careful about?&lt;/strong&gt;
&lt;/li&gt;
&lt;/ul&gt;

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

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;SMS:&lt;/strong&gt; Your account will be debited ₹499 on 5 October for your recurring subscription.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline SMS Buddy:&lt;/strong&gt; ₹499 will be automatically taken from your account on 5 October for a subscription. You don't need to do anything if you know about this subscription. If you don't recognise it, call your bank using the number on your card.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;And when a message looks like a scam, the app shows a clear red warning: don't click the link, don't share any OTP, and call the bank directly.&lt;/p&gt;

&lt;p&gt;The most important part: &lt;strong&gt;her SMS never leaves her computer.&lt;/strong&gt; No cloud, no account, and it even works with the &lt;strong&gt;Wi-Fi turned off&lt;/strong&gt;.&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/abhisek247767/offline-sms-buddy/tree/main/demo" rel="noopener noreferrer"&gt;Demo Video&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%2Fjujvhavfxkbbgnzbjdfc.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%2Fjujvhavfxkbbgnzbjdfc.png" alt=" " width="800" height="420"&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%2F7i6lsneyvduwzsh7a4e0.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%2F7i6lsneyvduwzsh7a4e0.png" alt=" " width="800" height="481"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;a href="https://github.com/abhisek247767/offline-sms-buddy" rel="noopener noreferrer"&gt;offline-sms-buddy&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The stack is entirely open source and runs locally:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/library/qwen2.5" rel="noopener noreferrer"&gt;Qwen 2.5&lt;/a&gt;&lt;/strong&gt;, an open-weight LLM. I use the 3B version so it runs comfortably on an ordinary laptop with 8 GB RAM.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;&lt;/strong&gt;, which runs the model locally and serves it at &lt;code&gt;localhost:11434&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://streamlit.io" rel="noopener noreferrer"&gt;Streamlit&lt;/a&gt;&lt;/strong&gt; for the UI.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Python + uv&lt;/strong&gt; for packaging.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  1. Structured output instead of free text
&lt;/h3&gt;

&lt;p&gt;I didn't want to parse a paragraph and guess where "what to do" starts. Ollama lets you pass a JSON schema, so I define the answer shape with Pydantic and the model must fill in exactly those three fields:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;SmsExplanation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;BaseModel&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;meaning&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;what_to_do&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;
    &lt;span class="n"&gt;be_careful&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;

&lt;span class="n"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;chat&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;MODEL&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
    &lt;span class="n"&gt;messages&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;[{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;role&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;user&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;content&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;PROMPT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;format&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sms&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;sms&lt;/span&gt;&lt;span class="p"&gt;)}],&lt;/span&gt;
    &lt;span class="nb"&gt;format&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;SmsExplanation&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;model_json_schema&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="n"&gt;options&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;temperature&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mf"&gt;0.2&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;Each field goes straight into its own section on the page. The low temperature (0.2) keeps the model close to what the SMS actually says instead of getting creative.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. A prompt written for safety, not cleverness
&lt;/h3&gt;

&lt;p&gt;The prompt tells the model to use short sentences and avoid banking jargon. It also sets some strict rules: don't invent information that isn't in the SMS, and never suggest sharing an OTP. If a message asks her to click a link, update KYC, or threatens to block her account, the model must tell her to verify with the official bank or company first.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. A safety net that doesn't depend on the AI
&lt;/h3&gt;

&lt;p&gt;Small models are good, but they aren't perfect, and with scams a single miss matters. So the app also runs a plain regex check. If an SMS contains a link, a shortened URL, "KYC" or "blocked", the red warning appears &lt;strong&gt;no matter what the model says&lt;/strong&gt;. If it mentions an OTP, there's always a "never share this code" reminder. The AI explains, and the simple code makes sure the most important warning is never skipped.&lt;/p&gt;

&lt;h3&gt;
  
  
  4. Designed for my mom, not for developers
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;Text is about 21px, and the button is huge and full width&lt;/li&gt;
&lt;li&gt;There's one text box and one button, nothing else to figure out&lt;/li&gt;
&lt;li&gt;Errors are in plain language: &lt;em&gt;"The AI helper is not running. Please start Ollama and try again."&lt;/em&gt; instead of a Python stack trace&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  5. Privacy that's actually true
&lt;/h3&gt;

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

&lt;p&gt;For this project, open-source AI isn't just a nice extra. &lt;strong&gt;Without it, the idea doesn't work.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Her SMS messages are some of the most private data she has.&lt;/strong&gt;&lt;br&gt;
They contain bank balances, partial account numbers, OTPs, loan EMIs, and delivery addresses. Sending all of that to a cloud API I don't control (with its own logging, retention and terms of service) would create a new privacy risk inside a tool meant to protect her. With an open-weight model running through Ollama, the message goes from the text box to &lt;code&gt;localhost&lt;/code&gt; and back. That's the whole journey.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. It works without internet.&lt;/strong&gt;&lt;br&gt;
Once the model is downloaded, the app works fully offline. I tested it with the Wi-Fi switched off. That matters for a tool she should be able to trust at any moment, not only when the connection is good.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. It costs nothing to run.&lt;/strong&gt;&lt;br&gt;
There are no API keys, no per-request billing, and no subscription that runs out. She can paste a hundred messages a day and it costs ₹0. A closed API would mean I'd have to manage billing for my mom's laptop forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. I can swap or tune the model freely.&lt;/strong&gt;&lt;br&gt;
The model name is a single constant in &lt;code&gt;llm.py&lt;/code&gt;. If a better small open model comes out next month, I run &lt;code&gt;ollama pull&lt;/code&gt; and change one line. With a closed API, the provider decides when the model changes, and that can change the app's behaviour without warning.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;5. Nobody can take it away.&lt;/strong&gt;&lt;br&gt;
If an API gets deprecated, a price changes, or an account gets locked, the app keeps working. The model file sits on her disk.&lt;/p&gt;

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

&lt;ol&gt;
&lt;li&gt;Featured categories&lt;/li&gt;
&lt;li&gt;Partner categories&lt;/li&gt;
&lt;/ol&gt;

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