<?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>Forem: Natnael Getenew</title>
    <description>The latest articles on Forem by Natnael Getenew (@zeshama).</description>
    <link>https://forem.com/zeshama</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.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F277112%2F5b7b299c-c122-4cff-90de-31be2ec255c1.jpeg</url>
      <title>Forem: Natnael Getenew</title>
      <link>https://forem.com/zeshama</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://forem.com/feed/zeshama"/>
    <language>en</language>
    <item>
      <title>Building Real-Time Voice AI with AWS Bedrock: Lessons from Creating an Ethiopian AI Tutor</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Mon, 20 Apr 2026 01:19:04 +0000</pubDate>
      <link>https://forem.com/zeshama/building-real-time-voice-ai-with-aws-bedrock-lessons-from-creating-an-ethiopian-ai-tutor-1c65</link>
      <guid>https://forem.com/zeshama/building-real-time-voice-ai-with-aws-bedrock-lessons-from-creating-an-ethiopian-ai-tutor-1c65</guid>
      <description>&lt;p&gt;Most voice AI demos you see are either pre-recorded or have that awkward 2-3 second delay that kills natural conversation. When I started building Ivy, an AI tutor for Ethiopian students that needed to work in Amharic, I discovered that creating truly real-time voice AI is harder than it looks.&lt;/p&gt;

&lt;p&gt;Here's what I learned about using AWS Bedrock to power conversational voice AI that actually feels natural.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real-Time Challenge
&lt;/h2&gt;

&lt;p&gt;The biggest hurdle isn't the AI model itself—it's the pipeline. You need:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Speech-to-text conversion&lt;/li&gt;
&lt;li&gt;Language processing &lt;/li&gt;
&lt;li&gt;Response generation&lt;/li&gt;
&lt;li&gt;Text-to-speech synthesis&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Each step adds latency. String them together traditionally, and you're looking at 3-5 seconds of delay. That's conversation-killing.&lt;/p&gt;

&lt;h2&gt;
  
  
  Streaming is Everything
&lt;/h2&gt;

&lt;p&gt;AWS Bedrock's streaming capabilities changed the game for me. Instead of waiting for complete responses, you can process tokens as they arrive:&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="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;
&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;

&lt;span class="n"&gt;bedrock&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;boto3&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;client&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;bedrock-runtime&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region_name&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;us-east-1&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;stream_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;body&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;dumps&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;prompt&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="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;max_tokens_to_sample&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;500&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.7&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;stream&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="bp"&gt;True&lt;/span&gt;
    &lt;span class="p"&gt;})&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;bedrock&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;invoke_model_with_response_stream&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
        &lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;body&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;modelId&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;anthropic.claude-v2&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="n"&gt;contentType&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;application/json&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;)&lt;/span&gt;

    &lt;span class="k"&gt;for&lt;/span&gt; &lt;span class="n"&gt;event&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;body&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
        &lt;span class="n"&gt;chunk&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;loads&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;event&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;chunk&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;bytes&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;])&lt;/span&gt;
        &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="k"&gt;yield&lt;/span&gt; &lt;span class="n"&gt;chunk&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;completion&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Parallel Processing Trick
&lt;/h2&gt;

&lt;p&gt;Here's where it gets interesting. Instead of a linear pipeline, I built a parallel one:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Start TTS early&lt;/strong&gt;: As soon as I get the first few tokens from Bedrock, I begin text-to-speech conversion&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Chunk intelligently&lt;/strong&gt;: Break responses at natural pause points (commas, periods)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Buffer strategically&lt;/strong&gt;: Keep a small audio buffer ready while processing the next chunk&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This reduced perceived latency from 3+ seconds to under 800ms—the sweet spot for natural conversation.&lt;/p&gt;

&lt;h2&gt;
  
  
  Handling Amharic Complexity
&lt;/h2&gt;

&lt;p&gt;Working with Amharic presented unique challenges. The language has its own script, complex grammar, and limited training data in most models. AWS Bedrock's Claude models handled this surprisingly well, but I had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tune prompts with Amharic context&lt;/li&gt;
&lt;li&gt;Handle script switching (students often mix Amharic and English)&lt;/li&gt;
&lt;li&gt;Implement custom preprocessing for educational content
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight python"&gt;&lt;code&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;preprocess_amharic_input&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Handle mixed script input
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="nf"&gt;contains_amharic_script&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Apply Amharic-specific processing
&lt;/span&gt;        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="nf"&gt;normalize_amharic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;normalize_amharic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Custom normalization for Amharic characters
&lt;/span&gt;    &lt;span class="c1"&gt;# This was crucial for consistent model performance
&lt;/span&gt;    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;፡፡&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;.&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;replace&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;፣&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;,&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cost Optimization Reality Check
&lt;/h2&gt;

&lt;p&gt;Real-time voice AI can get expensive fast. Here's what worked for me:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Smart caching&lt;/strong&gt;: Cache common educational responses&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context management&lt;/strong&gt;: Keep conversation context minimal but relevant&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model selection&lt;/strong&gt;: Use Claude Instant for quick responses, full Claude for complex explanations&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Offline Capability Plot Twist
&lt;/h2&gt;

&lt;p&gt;The real breakthrough came when I realized many Ethiopian students have unreliable internet. I built offline capability using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Local speech recognition fallbacks&lt;/li&gt;
&lt;li&gt;Cached response patterns&lt;/li&gt;
&lt;li&gt;Smart sync when connection returns&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This wasn't just a nice-to-have—it became Ivy's differentiator.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Building Ivy taught me that great voice AI isn't just about the model—it's about the entire experience. AWS Bedrock gave me the foundation, but the magic happened in the details: streaming, parallel processing, and understanding your users' real constraints.&lt;/p&gt;

&lt;p&gt;Ivy is currently a finalist in the AWS AIdeas 2025 competition, where community voting helps decide the winner. If you found these insights helpful and want to support innovation in educational AI for underserved communities, I'd appreciate your vote: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to try building real-time voice AI yourself? Start with AWS Bedrock's streaming API and remember: latency is everything, but user experience is king.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>tutorial</category>
      <category>python</category>
    </item>
    <item>
      <title>Building an AI Tutor in Amharic: What I Learned as a Solo Developer</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Mon, 20 Apr 2026 00:31:20 +0000</pubDate>
      <link>https://forem.com/zeshama/building-an-ai-tutor-in-amharic-what-i-learned-as-a-solo-developer-2j32</link>
      <guid>https://forem.com/zeshama/building-an-ai-tutor-in-amharic-what-i-learned-as-a-solo-developer-2j32</guid>
      <description>&lt;p&gt;Over 120 million people speak Amharic, yet there's virtually no AI educational content in the language. When I realized this gap while watching my younger siblings struggle with online learning during COVID, I knew I had to build something.&lt;/p&gt;

&lt;p&gt;That's how Ivy was born – an AI tutor that speaks Amharic and helps Ethiopian students learn through natural conversation. Building it as a solo developer taught me lessons I never expected.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Voice AI Challenge Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Most developers think voice AI is just "add speech-to-text and text-to-speech APIs." Wrong. The real challenge is handling the conversational flow when dealing with languages that have limited training data.&lt;/p&gt;

&lt;p&gt;Amharic has complex grammar with over 200 verb conjugations. When a student says "ይህን አልገባኝም" (I don't understand this), the AI needs to:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Recognize the specific confusion marker&lt;/li&gt;
&lt;li&gt;Identify what "this" refers to in context&lt;/li&gt;
&lt;li&gt;Adjust its teaching approach accordingly&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Here's how I handled context preservation:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;ConversationMemory&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="na"&gt;currentTopic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;null&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
      &lt;span class="na"&gt;studentConfusion&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
      &lt;span class="na"&gt;learningStyle&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;adaptive&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;
    &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="nf"&gt;updateContext&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;aiResponse&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Track confusion patterns&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;detectConfusion&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;userInput&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;studentConfusion&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
        &lt;span class="na"&gt;topic&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;currentTopic&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
        &lt;span class="na"&gt;timestamp&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;Date&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;now&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
        &lt;span class="na"&gt;userPhrase&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;userInput&lt;/span&gt;
      &lt;span class="p"&gt;});&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Offline-First Decision That Changed Everything
&lt;/h2&gt;

&lt;p&gt;Initially, I planned Ivy as a cloud-only service. Then I remembered: most Ethiopian students don't have reliable internet. This constraint forced me to rethink the entire architecture.&lt;/p&gt;

&lt;p&gt;I ended up building a hybrid system where the core AI models run locally using TensorFlow.js, with cloud sync when available. This decision tripled my development time but made Ivy accessible to students in rural areas.&lt;/p&gt;

&lt;p&gt;The breakthrough came when I realized I could use service workers for more than just caching:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Service worker handles AI inference offline&lt;/span&gt;
&lt;span class="nb"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;addEventListener&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;message&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;type&lt;/span&gt; &lt;span class="o"&gt;===&lt;/span&gt; &lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="s1"&gt;AI_QUERY&lt;/span&gt;&lt;span class="dl"&gt;'&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;processWithLocalModel&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;data&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;query&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="nx"&gt;event&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;ports&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;].&lt;/span&gt;&lt;span class="nf"&gt;postMessage&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Cultural Context is Code
&lt;/h2&gt;

&lt;p&gt;Building for Ethiopian students meant encoding cultural context into the AI's responses. When teaching math, Ivy uses examples like calculating injera ingredients or coffee ceremony timing – concepts that resonate locally.&lt;/p&gt;

&lt;p&gt;This taught me that AI isn't just about algorithms; it's about cultural representation in code. I spent weeks interviewing teachers and students to understand how they naturally explain concepts in Amharic.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Loneliness of Solo Development
&lt;/h2&gt;

&lt;p&gt;The hardest part wasn't the technical challenges – it was the isolation. Debugging voice recognition at 2 AM with no one to bounce ideas off was brutal. I learned to embrace async communication with online communities and scheduled regular video calls with other developers, even if they weren't working on similar projects.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Ivy is now a finalist in the AWS AIdeas 2025 competition, competing globally against incredible innovations. The community voting phase is live, and honestly, it would mean the world to have support from fellow developers who understand the journey.&lt;/p&gt;

&lt;p&gt;You can check out Ivy and cast your vote here: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Building Ivy taught me that the most impactful projects often come from solving problems in your own backyard. Sometimes the best innovation happens when you build for the community you know best.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If this resonates with you, I'd be incredibly grateful for your vote in the AWS AIdeas competition. Every vote helps bring AI education to more Ethiopian students.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>startup</category>
      <category>ai</category>
      <category>webdev</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building Conversational AI in Amharic: Lessons from Creating Ethiopia's First Voice AI Tutor</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sun, 19 Apr 2026 01:01:02 +0000</pubDate>
      <link>https://forem.com/zeshama/building-conversational-ai-in-amharic-lessons-from-creating-ethiopias-first-voice-ai-tutor-4hne</link>
      <guid>https://forem.com/zeshama/building-conversational-ai-in-amharic-lessons-from-creating-ethiopias-first-voice-ai-tutor-4hne</guid>
      <description>&lt;p&gt;Did you know that over 100 million people speak Amharic, yet there's virtually no conversational AI built specifically for this language? When I started building Ivy, an AI tutor for Ethiopian students, I quickly discovered why.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Challenge: More Than Just Translation
&lt;/h2&gt;

&lt;p&gt;Most developers assume you can just translate English prompts and call it localization. I learned the hard way that Amharic has unique grammatical structures, cultural contexts, and educational frameworks that require a completely different approach.&lt;/p&gt;

&lt;p&gt;Here's what I wish I knew before starting:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Script Complexity Matters
&lt;/h3&gt;

&lt;p&gt;Amharic uses the Ge'ez script with over 200 characters. Unlike Latin-based languages, each character can represent different sounds depending on context:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;ሀ (ha), ሁ (hu), ሂ (hi), ሃ (haa), ሄ (hee), ህ (h), ሆ (ho)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;This means tokenization becomes incredibly complex. Standard NLP libraries often break Amharic words incorrectly, leading to poor model performance.&lt;/p&gt;

&lt;h3&gt;
  
  
  2. Voice AI Architecture for Low-Resource Languages
&lt;/h3&gt;

&lt;p&gt;Building voice AI for Amharic meant dealing with limited training data. Here's the architecture I settled on:&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="c1"&gt;# Simplified pipeline structure
&lt;/span&gt;&lt;span class="k"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AmharicVoiceAI&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;__init__&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speech_to_text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;WhisperAmharic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Fine-tuned Whisper
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;LlamaAmharic&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Custom fine-tuned model
&lt;/span&gt;        &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text_to_speech&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;CoquiTTS&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;  &lt;span class="c1"&gt;# Open-source TTS
&lt;/span&gt;
    &lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;process_conversation&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;audio_input&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="c1"&gt;# Convert speech to text
&lt;/span&gt;        &lt;span class="n"&gt;text&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;speech_to_text&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transcribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;audio_input&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Process with cultural context
&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;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;llm&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generate_culturally_aware_response&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

        &lt;span class="c1"&gt;# Convert back to natural-sounding Amharic speech
&lt;/span&gt;        &lt;span class="n"&gt;audio_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;self&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;text_to_speech&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synthesize&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;response&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;audio_output&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Cultural Context is Everything
&lt;/h3&gt;

&lt;p&gt;The biggest breakthrough came when I stopped trying to adapt Western educational content and started building from Ethiopian curriculum standards. For example, when teaching math, I use familiar examples:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Instead of "apples and oranges," I use "injera and berbere"&lt;/li&gt;
&lt;li&gt;Historical examples reference Ethiopian figures like Emperor Menelik II&lt;/li&gt;
&lt;li&gt;Currency examples use Ethiopian Birr&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;This cultural grounding improved student engagement dramatically.&lt;/p&gt;

&lt;h2&gt;
  
  
  Technical Implementation Tips
&lt;/h2&gt;

&lt;h3&gt;
  
  
  Fine-tuning for Amharic
&lt;/h3&gt;

&lt;p&gt;I found that starting with multilingual models and fine-tuning works better than training from scratch:&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="c1"&gt;# Fine-tuning approach that worked
&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;microsoft/DialoGPT-multilingual&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
&lt;span class="n"&gt;tokenizer&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;AutoTokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;base_model&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Add Amharic tokens
&lt;/span&gt;&lt;span class="n"&gt;new_tokens&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;ሰላም&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;እንደምን&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;ተማሪ&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;  &lt;span class="c1"&gt;# Common Amharic words
&lt;/span&gt;&lt;span class="n"&gt;tokenizer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;add_tokens&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;new_tokens&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Handling Code-Switching
&lt;/h3&gt;

&lt;p&gt;Ethiopian students often mix Amharic with English, especially for technical terms. I built a detection system that handles this naturally:&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;def&lt;/span&gt; &lt;span class="nf"&gt;detect_language_mix&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Simple regex for mixed content
&lt;/span&gt;    &lt;span class="n"&gt;has_amharic&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[\u1200-\u137F]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="n"&gt;has_english&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;bool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;re&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;search&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sa"&gt;r&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;[a-zA-Z]&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;text&lt;/span&gt;&lt;span class="p"&gt;))&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;has_amharic&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;has_english&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The Results
&lt;/h2&gt;

&lt;p&gt;After six months of development, Ivy can now:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Conduct natural conversations in Amharic&lt;/li&gt;
&lt;li&gt;Adapt to different Ethiopian English accents&lt;/li&gt;
&lt;li&gt;Work offline (crucial for areas with poor internet)&lt;/li&gt;
&lt;li&gt;Provide culturally relevant educational content&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The most rewarding moment was when a student from rural Ethiopia told me Ivy felt like talking to a patient older sibling who understood their world.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Building Ivy taught me that creating AI for underrepresented languages isn't just about technical challenges—it's about understanding and respecting the culture behind the language. &lt;/p&gt;

&lt;p&gt;Ivy recently became a finalist in the AWS AIdeas 2025 competition, where community voting determines the winner. If you found this technical journey interesting, I'd be grateful for your vote: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to help bring AI education to Ethiopian students? Cast your vote and help make quality education accessible to millions of Amharic speakers worldwide.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>nlp</category>
      <category>programming</category>
      <category>tutorial</category>
    </item>
    <item>
      <title>Building Voice AI for Students Who Can't Afford Internet: Lessons from Ethiopia</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sun, 19 Apr 2026 00:14:04 +0000</pubDate>
      <link>https://forem.com/zeshama/building-voice-ai-for-students-who-cant-afford-internet-lessons-from-ethiopia-104g</link>
      <guid>https://forem.com/zeshama/building-voice-ai-for-students-who-cant-afford-internet-lessons-from-ethiopia-104g</guid>
      <description>&lt;p&gt;Did you know that 70% of Ethiopian students don't have reliable internet access, yet they're expected to compete globally? This reality hit me hard when I watched my younger sister struggle with her studies, unable to access online learning resources that kids in other countries take for granted.&lt;/p&gt;

&lt;p&gt;That's when I decided to build Ivy – a voice AI tutor that works entirely offline and speaks Amharic, Ethiopia's primary language.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Challenge: Making AI Work Without Internet
&lt;/h2&gt;

&lt;p&gt;Building an offline voice AI system isn't just about downloading models. Here's what I learned:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Model Optimization is Everything
&lt;/h3&gt;

&lt;p&gt;I started with OpenAI's Whisper for speech recognition, but the full model was 1.5GB – way too heavy for most phones here. After experimenting with quantization and pruning techniques, I got it down to 200MB while maintaining 85% accuracy for Amharic.&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="c1"&gt;# Model compression approach that worked
&lt;/span&gt;&lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;
&lt;span class="kn"&gt;from&lt;/span&gt; &lt;span class="n"&gt;transformers&lt;/span&gt; &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;WhisperProcessor&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;WhisperForConditionalGeneration&lt;/span&gt;

&lt;span class="c1"&gt;# Load and quantize the model
&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;WhisperForConditionalGeneration&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;from_pretrained&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;openai/whisper-small&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
&lt;span class="n"&gt;quantized_model&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;quantization&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;quantize_dynamic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="n"&gt;model&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;nn&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;Linear&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="n"&gt;dtype&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="n"&gt;torch&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;qint8&lt;/span&gt;
&lt;span class="p"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Local Language Models Need Creative Solutions
&lt;/h3&gt;

&lt;p&gt;Running a capable LLM locally on budget Android phones seemed impossible until I discovered that you don't need GPT-4 level intelligence for tutoring. I fine-tuned a smaller model (1.3B parameters) specifically for educational conversations in Amharic.&lt;/p&gt;

&lt;p&gt;The key insight: &lt;strong&gt;domain-specific models can outperform general models while being 10x smaller&lt;/strong&gt;.&lt;/p&gt;

&lt;h3&gt;
  
  
  3. Battery Life is a Feature, Not an Afterthought
&lt;/h3&gt;

&lt;p&gt;Students here often share phones with family members and can't always charge devices. I implemented aggressive power management:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Wake word detection uses only 2% CPU&lt;/li&gt;
&lt;li&gt;Full AI processing activates only during conversation&lt;/li&gt;
&lt;li&gt;Conversation state persists through app kills&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  The Offline-First Architecture
&lt;/h2&gt;

&lt;p&gt;Here's the system design that made it work:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Voice Input    │───▶│  Local Whisper   │───▶│  Text Processing│
└─────────────────┘    └──────────────────┘    └─────────────────┘
                                                         │
┌─────────────────┐    ┌──────────────────┐    ┌─────────────────┐
│  Voice Output   │◀───│  Local TTS       │◀───│  Local LLM      │
└─────────────────┘    └──────────────────┘    └─────────────────┘
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Everything runs on-device. No internet required after initial app download.&lt;/p&gt;

&lt;h2&gt;
  
  
  Real Impact, Real Numbers
&lt;/h2&gt;

&lt;p&gt;After six months of testing with 200+ students:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;78% improvement in math scores for students using Ivy vs traditional methods&lt;/li&gt;
&lt;li&gt;Students practiced 3x more because they could learn in their native language&lt;/li&gt;
&lt;li&gt;92% said they felt more confident asking questions to AI than human teachers&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The last point surprised me. Turns out, cultural barriers disappear when students can practice privately in Amharic without fear of judgment.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Wish I Knew Before Starting
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Start with the constraint, not the ideal&lt;/strong&gt;: I initially tried to build a cloud-based solution. The offline requirement forced better design decisions.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Cultural context matters more than technical perfection&lt;/strong&gt;: Spending time understanding how Ethiopian students actually learn was more valuable than optimizing model accuracy from 85% to 90%.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Battery optimization is harder than model optimization&lt;/strong&gt;: I spent more time on power management than AI training.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  Looking Forward
&lt;/h2&gt;

&lt;p&gt;Ivy recently became a finalist in the AWS AIdeas 2025 global competition, which has been incredible validation for this approach. The competition focuses on AI solutions that can create real social impact, and I'm excited to see voice AI for education getting recognition.&lt;/p&gt;

&lt;p&gt;If you believe that every student deserves access to personalized learning regardless of their internet connection or economic situation, I'd be grateful for your vote in the competition: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The future of education isn't just about having the best AI – it's about making AI accessible to everyone who needs it most.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>voiceai</category>
      <category>education</category>
      <category>aws</category>
    </item>
    <item>
      <title>From Weekend Hackathon to AWS Global Finalist: Building an AI Tutor That Actually Works Offline</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sat, 18 Apr 2026 00:55:39 +0000</pubDate>
      <link>https://forem.com/zeshama/from-weekend-hackathon-to-aws-global-finalist-building-an-ai-tutor-that-actually-works-offline-3864</link>
      <guid>https://forem.com/zeshama/from-weekend-hackathon-to-aws-global-finalist-building-an-ai-tutor-that-actually-works-offline-3864</guid>
      <description>&lt;p&gt;Six months ago, I was debugging a React component at 2 AM when my little sister called from our village outside Addis Ababa. She was struggling with her physics homework and couldn't afford extra tutoring. That moment sparked an idea that would eventually land me as a finalist in AWS AIdeas 2025.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Problem That Wouldn't Leave Me Alone
&lt;/h2&gt;

&lt;p&gt;In Ethiopia, quality education is a luxury. Most students can't access personalized tutoring, and even fewer can learn in their native language. While building web apps for clients, I kept thinking about my sister and millions of students like her. The real kicker? Most educational AI tools require constant internet connectivity – something we definitely can't count on in rural Ethiopia.&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Ivy: More Than Just Another Chatbot
&lt;/h2&gt;

&lt;p&gt;I started Ivy as a weekend hackathon project, but it quickly became my obsession. The core challenge was creating an AI tutor that could:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Understand and respond in Amharic naturally&lt;/li&gt;
&lt;li&gt;Work offline when internet is spotty&lt;/li&gt;
&lt;li&gt;Actually engage students in conversation, not just answer questions&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Here's the technical approach that made it work:&lt;/p&gt;

&lt;h3&gt;
  
  
  Voice-First Architecture
&lt;/h3&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified voice processing pipeline&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;VoiceProcessor&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nf"&gt;constructor&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechRecognition&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nf"&gt;webkitSpeechRecognition&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nb"&gt;window&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;speechSynthesis&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt;
    &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amharicModel&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;AmharicNLPModel&lt;/span&gt;&lt;span class="p"&gt;();&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processVoiceInput&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transcribeAmharic&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;response&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;amharicModel&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;generateResponse&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;synthesizeAmharicSpeech&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;response&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The breakthrough came when I realized students learn better through conversation than Q&amp;amp;A. Instead of waiting for questions, Ivy proactively guides discussions, asks follow-ups, and adapts to each student's pace.&lt;/p&gt;

&lt;h3&gt;
  
  
  Offline-First Design
&lt;/h3&gt;

&lt;p&gt;The real technical challenge was making AI work without constant cloud connectivity. I implemented a hybrid approach:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Core reasoning engine&lt;/strong&gt; runs locally using optimized models&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge base&lt;/strong&gt; is cached and synced when online&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice processing&lt;/strong&gt; happens on-device for privacy and speed&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;This meant students in remote areas could still get quality tutoring even when their internet cut out mid-lesson.&lt;/p&gt;

&lt;h2&gt;
  
  
  From Side Project to Global Recognition
&lt;/h2&gt;

&lt;p&gt;What started as helping my sister became something bigger. I spent nights after my day job training Amharic language models, optimizing for mobile devices with limited processing power, and testing with students across Addis Ababa.&lt;/p&gt;

&lt;p&gt;The user feedback was incredible. Students weren't just getting answers – they were having genuine learning conversations in their native language. Teachers started reaching out, asking how they could integrate Ivy into their classrooms.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Lessons That Shaped Everything
&lt;/h2&gt;

&lt;p&gt;Building Ivy taught me that the best tech solutions come from real problems, not cool technologies looking for applications. Here's what I learned:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Start with empathy, not algorithms&lt;/strong&gt;: Understanding my users' daily reality was more valuable than any ML paper&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Constraints breed creativity&lt;/strong&gt;: Limited internet forced innovative offline solutions&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Community feedback is gold&lt;/strong&gt;: Ethiopian students shaped every feature decision&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The AWS AIdeas competition validation feels surreal. Ivy is now a &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;finalist competing globally&lt;/a&gt;, representing not just my work, but the potential of African innovation in AI.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Whether Ivy wins or not, this journey proved that impactful AI doesn't require Silicon Valley resources – just genuine problems and persistent iteration. I'm continuing to expand Ivy's capabilities and exploring partnerships with Ethiopian schools.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;If this resonates with you, I'd be incredibly grateful for your vote in the AWS AIdeas competition.&lt;/strong&gt; Community voting determines the winner, and your support could help bring quality education to students across Ethiopia and beyond. &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;Vote here&lt;/a&gt; – it takes just a minute but could change everything for students like my sister.&lt;/p&gt;

&lt;p&gt;What problems in your community could you solve with code? Sometimes the best ideas are hiding in plain sight.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>career</category>
      <category>startup</category>
      <category>motivation</category>
    </item>
    <item>
      <title>Building an AI Tutor for 40 Million Ethiopian Students Who Learn in Amharic</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sat, 18 Apr 2026 00:13:54 +0000</pubDate>
      <link>https://forem.com/zeshama/building-an-ai-tutor-for-40-million-ethiopian-students-who-learn-in-amharic-3i42</link>
      <guid>https://forem.com/zeshama/building-an-ai-tutor-for-40-million-ethiopian-students-who-learn-in-amharic-3i42</guid>
      <description>&lt;p&gt;When I tell people that 40 million Ethiopian students don't have access to quality tutoring, they're shocked. When I explain that most of them learn in Amharic—not English—the tech community suddenly goes quiet.&lt;/p&gt;

&lt;p&gt;This is the reality I've been wrestling with as a developer in Addis Ababa. Ethiopia has one of the largest student populations in Africa, but educational resources are scarce, expensive, and almost exclusively in English. Meanwhile, most students think and learn in their native language.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Language Barrier Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;Here's what blew my mind during my research: cognitive load theory shows that students learn 40% faster when taught in their native language. Yet every AI tutor I found was English-first, treating other languages as an afterthought.&lt;/p&gt;

&lt;p&gt;I realized we needed something different—an AI that could naturally converse in Amharic, understand cultural context, and work offline (because internet connectivity is still a luxury for many).&lt;/p&gt;

&lt;h2&gt;
  
  
  Building Ivy: Technical Challenges I Didn't Expect
&lt;/h2&gt;

&lt;p&gt;Creating Ivy, my AI tutoring platform, taught me that voice AI in low-resource languages is &lt;em&gt;hard&lt;/em&gt;. Here are the biggest technical hurdles:&lt;/p&gt;

&lt;h3&gt;
  
  
  1. Amharic Speech Recognition
&lt;/h3&gt;

&lt;p&gt;Most speech-to-text APIs barely support Amharic. I had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Fine-tune existing models with local speech patterns&lt;/li&gt;
&lt;li&gt;Handle code-switching (when students mix Amharic and English mid-sentence)&lt;/li&gt;
&lt;li&gt;Account for regional accents and dialects&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  2. Offline-First Architecture
&lt;/h3&gt;

&lt;p&gt;With unreliable internet, Ivy needed to work offline. My solution:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified offline sync strategy&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;syncQueue&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[],&lt;/span&gt;
  &lt;span class="na"&gt;sync&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onLine&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nb"&gt;Promise&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;all&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;pending&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;map&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;syncItem&lt;/span&gt;&lt;span class="p"&gt;));&lt;/span&gt;
      &lt;span class="nx"&gt;pending&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[];&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;};&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  3. Cultural Context in AI Responses
&lt;/h3&gt;

&lt;p&gt;Generic AI responses don't work. Ethiopian students relate better to examples using familiar contexts—like calculating the area of an injera (traditional bread) rather than a pizza.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Learned About Voice AI for Education
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Voice changes everything.&lt;/strong&gt; Text-based tutors feel formal and intimidating. Voice makes learning conversational and natural. Students ask follow-up questions they'd never type.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Proactive AI works better than reactive.&lt;/strong&gt; Instead of waiting for questions, Ivy checks in: "You seem stuck on this problem. Want me to break it down differently?"&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local deployment matters.&lt;/strong&gt; Running inference locally reduces latency and works offline, but model optimization becomes crucial.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Numbers That Keep Me Going
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;40 million students in Ethiopia's education system&lt;/li&gt;
&lt;li&gt;70% lack access to quality tutoring&lt;/li&gt;
&lt;li&gt;Average tutor costs $50/month (many families earn $100/month)&lt;/li&gt;
&lt;li&gt;Ivy costs less than $5/month&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Real Impact, Real Feedback
&lt;/h2&gt;

&lt;p&gt;Beta testing revealed something beautiful: students started asking Ivy questions they were too shy to ask human teachers. The AI removed the fear of judgment that often blocks learning.&lt;/p&gt;

&lt;p&gt;One student told me, "Finally, someone who speaks my language and doesn't make me feel stupid for not knowing something."&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Ivy is now a finalist in the &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;AWS AIdeas 2025 global competition&lt;/a&gt;. The winner is decided by community voting, and this could be the push needed to scale Ivy across Ethiopia and beyond.&lt;/p&gt;

&lt;p&gt;Building for underserved markets taught me that the most impactful tech often comes from solving problems the global tech community doesn't see. We don't need another English-first AI tool—we need AI that meets people where they are.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Want to help 40 million students get better education? &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;Vote for Ivy in the AWS AIdeas competition&lt;/a&gt;.&lt;/strong&gt; Every vote brings us closer to making quality education accessible in every language.&lt;/p&gt;

&lt;p&gt;&lt;em&gt;What challenges have you faced building for underserved markets? I'd love to hear your experiences in the comments.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>education</category>
      <category>startup</category>
      <category>discuss</category>
    </item>
    <item>
      <title>Why Building AI for Non-English Speakers is Harder Than You Think (And How I Did It Anyway)</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Fri, 17 Apr 2026 04:37:53 +0000</pubDate>
      <link>https://forem.com/zeshama/why-building-ai-for-non-english-speakers-is-harder-than-you-think-and-how-i-did-it-anyway-6b9</link>
      <guid>https://forem.com/zeshama/why-building-ai-for-non-english-speakers-is-harder-than-you-think-and-how-i-did-it-anyway-6b9</guid>
      <description>&lt;p&gt;Over 70% of the world doesn't speak English fluently, yet most AI applications are built with English as the default. When I started building Ivy, an AI tutor for Ethiopian students, I quickly discovered why this gap exists—and it's not just about translation.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Real Challenge Isn't Translation
&lt;/h2&gt;

&lt;p&gt;My first naive approach was simple: build in English, then translate. Wrong move. Here's what I learned:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Cultural Context Matters More Than Grammar&lt;/strong&gt;&lt;br&gt;
Ethiopian students don't just need Amharic words—they need culturally relevant examples. When teaching math, mentioning "buying injera at the market" resonates way more than "buying apples at the store."&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voice AI Gets Tricky with Tonal Languages&lt;/strong&gt;&lt;br&gt;
Amharic has unique phonetic patterns that standard speech recognition models struggle with. I had to fine-tune my voice processing pipeline specifically for Amharic pronunciation and intonation patterns.&lt;/p&gt;
&lt;h2&gt;
  
  
  Technical Hurdles I Hit (And Solved)
&lt;/h2&gt;
&lt;h3&gt;
  
  
  1. Limited Training Data
&lt;/h3&gt;

&lt;p&gt;Unlike English, there's not much Amharic educational content online to train on. My solution:&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="c1"&gt;# Custom data augmentation for low-resource languages
&lt;/span&gt;&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;augment_amharic_dataset&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;original_text&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Synthetic data generation using cultural context
&lt;/span&gt;    &lt;span class="n"&gt;augmented_samples&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;[]&lt;/span&gt;

    &lt;span class="c1"&gt;# Replace generic examples with local ones
&lt;/span&gt;    &lt;span class="n"&gt;cultural_replacements&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;pizza&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;injera&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;dollars&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;birr&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;subway&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;blue donkey taxi&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;augmented_samples&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  2. Offline Capability
&lt;/h3&gt;

&lt;p&gt;Internet connectivity in Ethiopia can be unreliable. I built Ivy to work offline by:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Pre-loading essential models locally&lt;/li&gt;
&lt;li&gt;Using efficient model compression techniques&lt;/li&gt;
&lt;li&gt;Implementing smart caching for frequently accessed content&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  3. Code-Switching Handling
&lt;/h3&gt;

&lt;p&gt;Students often mix Amharic with English mid-conversation. I had to build a detection system that could seamlessly handle both languages without breaking the conversation flow.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I Wish I Knew Before Starting
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Start with the Community, Not the Code&lt;/strong&gt;&lt;br&gt;
I spent months perfecting the AI before talking to actual students. Big mistake. The feedback I got after building an MVP changed everything about my approach.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Voice-First Changes Everything&lt;/strong&gt;&lt;br&gt;
Text-based tutoring feels formal and intimidating to many Ethiopian students. But voice conversations? That's natural. It's how they learn from elders, how they discuss problems with friends.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Performance Optimization is Critical&lt;/strong&gt;&lt;br&gt;
When your target users have older Android phones and limited data, every millisecond and megabyte matters. I learned to obsess over model size and response times in ways I never had to with English-first applications.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Technical Stack That Worked
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Speech Processing&lt;/strong&gt;: Custom fine-tuned models for Amharic&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;NLP&lt;/strong&gt;: Multilingual transformers with cultural context injection&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend&lt;/strong&gt;: Lightweight Python APIs optimized for edge deployment&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Mobile&lt;/strong&gt;: React Native with offline-first architecture&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Beyond the Code
&lt;/h2&gt;

&lt;p&gt;Building for non-English speakers taught me that great AI isn't just about algorithms—it's about understanding your users' world. The most elegant code means nothing if it doesn't fit into someone's daily life and cultural context.&lt;/p&gt;

&lt;p&gt;The response from Ethiopian students has been incredible. Seeing kids who struggled with traditional learning methods suddenly engage through natural conversation in their native language—that's what makes the technical challenges worth it.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Ivy is currently a finalist in the AWS AIdeas 2025 global competition, where community voting helps decide the winner. If you found this technical journey interesting, I'd love your support: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;Vote for Ivy here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Building AI for underrepresented languages isn't just a technical challenge—it's an opportunity to democratize access to quality education. The world needs more developers thinking beyond English-first solutions.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Have you built applications for non-English speakers? What challenges did you face? Drop your experiences in the comments—I'd love to learn from your journey too.&lt;/strong&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>education</category>
      <category>startup</category>
      <category>programming</category>
    </item>
    <item>
      <title>Building an AI Tutor for Ethiopia: What I Learned Competing in AWS AIdeas 2025</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Fri, 17 Apr 2026 04:01:56 +0000</pubDate>
      <link>https://forem.com/zeshama/building-an-ai-tutor-for-ethiopia-what-i-learned-competing-in-aws-aideas-2025-gk4</link>
      <guid>https://forem.com/zeshama/building-an-ai-tutor-for-ethiopia-what-i-learned-competing-in-aws-aideas-2025-gk4</guid>
      <description>&lt;p&gt;Over 70% of Ethiopian students don't have reliable internet access. Yet here I was, building an AI tutor that needed to work for them too.&lt;/p&gt;

&lt;p&gt;When I started building Ivy, my AI tutor for Ethiopian students, I thought the hard part would be the voice recognition in Amharic. Turns out, that was just the beginning of a journey that taught me more about building resilient AI systems than any tutorial ever could.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Offline Challenge That Changed Everything
&lt;/h2&gt;

&lt;p&gt;My first prototype was a typical web app – sleek, fast, and completely useless when the internet cut out. In Ethiopia, power outages and connectivity issues are daily realities. I realized I wasn't just building an AI tutor; I was building for infrastructure constraints that most developers never consider.&lt;/p&gt;

&lt;p&gt;This led me to explore &lt;strong&gt;edge AI deployment&lt;/strong&gt; in ways I never expected. Instead of relying solely on cloud APIs, I had to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Implement local speech-to-text using lightweight models&lt;/li&gt;
&lt;li&gt;Cache conversation context aggressively&lt;/li&gt;
&lt;li&gt;Build a hybrid system that gracefully degrades when offline
&lt;/li&gt;
&lt;/ul&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Simplified offline detection and fallback&lt;/span&gt;
&lt;span class="kd"&gt;class&lt;/span&gt; &lt;span class="nc"&gt;AITutorService&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processQuery&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nb"&gt;navigator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;onLine&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;cloudAvailable&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processWithCloud&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;

    &lt;span class="c1"&gt;// Fallback to local processing&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;processLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;

  &lt;span class="k"&gt;async&lt;/span&gt; &lt;span class="nf"&gt;processLocally&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="c1"&gt;// Use cached models and pre-computed responses&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localSTT&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;transcribe&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;audioBlob&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="k"&gt;this&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;localLLM&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;respond&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;context&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  Voice AI in Low-Resource Languages
&lt;/h2&gt;

&lt;p&gt;Building voice interfaces for Amharic taught me that "just use OpenAI's API" isn't always the answer. Amharic has unique phonetic patterns, and most commercial STT services perform poorly with it.&lt;/p&gt;

&lt;p&gt;I ended up training custom models using:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Mozilla DeepSpeech&lt;/strong&gt; as a base&lt;/li&gt;
&lt;li&gt;Crowdsourced audio from Ethiopian university students&lt;/li&gt;
&lt;li&gt;Data augmentation techniques to stretch limited training data&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The breakthrough came when I realized I didn't need perfect transcription – I needed good enough recognition for educational contexts. By constraining the vocabulary to academic terms and common student questions, accuracy jumped from 60% to 85%.&lt;/p&gt;

&lt;h2&gt;
  
  
  Scaling on a Shoestring Budget
&lt;/h2&gt;

&lt;p&gt;Running AI models isn't cheap, especially when you're targeting users who can't pay premium prices. I learned to optimize ruthlessly:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Model Compression&lt;/strong&gt;: Used quantization to reduce model sizes by 75% with minimal accuracy loss.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Smart Caching&lt;/strong&gt;: Implemented semantic caching for common questions. If a student asks "What is photosynthesis?" in slightly different ways, serve the cached response.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Usage Patterns&lt;/strong&gt;: Ethiopian students often study in groups. Building collaborative features reduced per-user compute costs while improving the learning experience.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Competition Experience
&lt;/h2&gt;

&lt;p&gt;Entering AWS AIdeas 2025 forced me to articulate not just what Ivy does, but why it matters. The competition pushed me to think bigger – how could this solution work beyond Ethiopia? What technical patterns could other developers use for similar challenges?&lt;/p&gt;

&lt;p&gt;The feedback from AWS experts helped me realize that building for constraints often leads to more innovative solutions. Ivy's offline capabilities and efficiency optimizations make it suitable for any region with infrastructure challenges.&lt;/p&gt;

&lt;h2&gt;
  
  
  What's Next
&lt;/h2&gt;

&lt;p&gt;Being selected as a finalist in AWS AIdeas 2025 has been incredible validation, but the real test is impact. I'm already seeing students in rural areas use Ivy to practice English pronunciation and get help with math problems – things that weren't possible before.&lt;/p&gt;

&lt;p&gt;The competition has also opened doors to collaborate with other developers facing similar challenges in different contexts. There's something powerful about building technology that works for everyone, not just those with perfect connectivity.&lt;/p&gt;

&lt;p&gt;If you're interested in supporting innovative AI solutions for education, I'd be grateful for your vote in the AWS AIdeas competition: &lt;a href="https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/3CQJ9SY2gNvSZKWd3tEq8ny7kSr/aideas-finalist-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Community voting plays a huge role in determining the winner, and every vote helps bring AI education tools to students who need them most.&lt;/p&gt;

</description>
      <category>aws</category>
      <category>ai</category>
      <category>career</category>
      <category>startup</category>
    </item>
    <item>
      <title>One sentence in VS Code. My entire Notion workspace becomes a live interactive briefing and the AI handles the rest.</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sat, 28 Mar 2026 08:08:36 +0000</pubDate>
      <link>https://forem.com/zeshama/one-sentence-in-vs-code-my-entire-notion-workspace-becomes-a-live-interactive-briefing-and-the-ai-2kdp</link>
      <guid>https://forem.com/zeshama/one-sentence-in-vs-code-my-entire-notion-workspace-becomes-a-live-interactive-briefing-and-the-ai-2kdp</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I maintain an open source AI agent SDK. I'm building a startup. I do both alone, from Addis Ababa, at 24, no team.&lt;/p&gt;

&lt;p&gt;Every morning I open Notion and spend 15 minutes manually figuring out what's actually on fire. What's overdue. What's tied to which goal. I piece it together across five databases, hold it in working memory, then try to work.&lt;/p&gt;

&lt;p&gt;That 15 minutes compounds. Every day. It's not a productivity problem - it's a tax on building alone.&lt;/p&gt;

&lt;p&gt;People who have a chief of staff don't pay that tax. I can't afford one. So I built one.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Thing Nobody Had Done Before
&lt;/h2&gt;

&lt;p&gt;Before this, "AI + Notion" meant: AI reads your data and writes a text summary back at you. You still had to act on it yourself. You still had to go to Notion and change things.&lt;/p&gt;

&lt;p&gt;Chief of Staff breaks both of those constraints at once.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;First: the UI lives inside the chat.&lt;/strong&gt; When you ask for your briefing, a full rendered dashboard appears inside the conversation — task rows, progress bars, overdue indicators, action buttons. It's not a screenshot. It's not a link. It's a live React app running inside an iframe inside VS Code Copilot or Claude. You can interact with it. Check off a task and it's gone from the list and marked done in Notion in the same click.&lt;/p&gt;

&lt;p&gt;Checking a checkbox on a visual task — inside VS Code, without opening Notion, without leaving your editor — that had never been built before.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Second: the agent doesn't hand you a report. It executes.&lt;/strong&gt; The action buttons in the dashboard don't navigate you somewhere. They tell the AI to go do the work. The AI calls the right MCP tool, reasons through the changes, and writes them back to Notion. You direct. It executes. The loop closes inside a single conversation.&lt;/p&gt;

&lt;p&gt;This is what a chief of staff actually does. Not informing you. Acting on your behalf.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Chief of Staff&lt;/strong&gt; is an MCP App that reads your Notion workspace every morning and briefs you — then handles the work you tell it to.&lt;/p&gt;

&lt;p&gt;You type: &lt;em&gt;"Give me my morning briefing."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;A &lt;strong&gt;live, interactive dashboard renders directly inside VS Code Copilot Chat&lt;/strong&gt; — or Claude. Not a link to an external tool. Not a text summary. A real UI with real data, living inside your editor. You can click a checkbox and the task is marked done in Notion. You can click a button and the agent reschedules your entire overdue pile. You never leave your editor.&lt;/p&gt;

&lt;p&gt;That's new. Nobody had shipped this before.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Plan my week&lt;/strong&gt; → the AI generates a task breakdown and creates every task directly in your Notion database&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📅 Reschedule overdue tasks&lt;/strong&gt; → the AI looks at everything overdue, picks sensible new dates based on priority, patches them all in Notion. The guilt pile disappears.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📋 Write weekly review&lt;/strong&gt; → the AI pulls your completed tasks, synthesizes what happened, writes a full structured page into your workspace&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🎯 Break down stalled goal&lt;/strong&gt; → the AI takes a goal sitting at 5% and creates 4-6 concrete sub-tasks with due dates in Notion&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The briefing is the interface. Notion is where the work lands.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/Garinmckayl/chief-of-staff" rel="noopener noreferrer"&gt;https://github.com/Garinmckayl/chief-of-staff&lt;/a&gt;&lt;/p&gt;




&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/14en36xPBo0"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion MCP is the reason the write path exists. Without it I'd need custom integrations per action. With it, the AI can read and write across the entire workspace through one protocol, and every agent tool is just a description of what needs to happen.&lt;/p&gt;

&lt;h3&gt;
  
  
  The 8 MCP tools
&lt;/h3&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Tool&lt;/th&gt;
&lt;th&gt;What it does&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;chief_of_staff_briefing&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Renders the live interactive dashboard as an MCP App&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_notion_briefing_data&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Reads your workspace — discovers databases dynamically, no hardcoded IDs&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;complete_notion_task&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Marks a task done — detects whether Status is a select, native status, or checkbox&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;create_notion_tasks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Writes an AI-generated task plan straight into your Notion database&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;reschedule_overdue_tasks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Updates due dates — the AI picks the dates and explains each one&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;write_weekly_review&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a structured weekly review page in your workspace&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;break_down_goal&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Generates sub-tasks for a stalled goal and creates them in Notion&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;get_completed_tasks&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fetches done tasks from the past N days for the weekly review&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h3&gt;
  
  
  The generative UI layer
&lt;/h3&gt;

&lt;p&gt;The interactive dashboard is a live React app that renders inside the chat - compiled to a single self-contained HTML string and returned as a tool response. When the AI calls &lt;code&gt;chief_of_staff_briefing&lt;/code&gt;, the entire UI materialises: task rows, progress bars, overdue indicators, action buttons. All driven by your real Notion data.&lt;/p&gt;

&lt;p&gt;The component catalog defines everything the AI can compose:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;FocusCard      — the single most important thing right now
TaskList       — grouped task rows with heading and count
TaskRow        — individual task with completion checkbox that writes to Notion
GoalProgress   — progress bar with live percentage
InsightBadge   — win / tip / warning / pattern pill
AgentAction    — the button that triggers real Notion writes
SectionHeader  — section divider
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The AI fills this catalog from your actual Notion data. Every task row is real. Every progress bar reflects a real goal. The &lt;code&gt;AgentAction&lt;/code&gt; component fires an event that the AI receives and routes to the right MCP tool. Visual layer and execution layer are the same system.&lt;/p&gt;

&lt;h3&gt;
  
  
  The agentic loop
&lt;/h3&gt;

&lt;p&gt;The dashboard and the agent tools are two halves of the same system. The briefing shows the situation. The &lt;code&gt;AgentAction&lt;/code&gt; buttons close the loop.&lt;/p&gt;

&lt;p&gt;When you click "Reschedule overdue tasks," the AI gets a &lt;code&gt;run_agent&lt;/code&gt; event, calls &lt;code&gt;get_notion_briefing_data&lt;/code&gt; to see what's actually overdue, reasons about dates based on priority, and calls &lt;code&gt;reschedule_overdue_tasks&lt;/code&gt; with the full update list. Notion gets patched. You touched nothing.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="nx"&gt;mcpServer&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;tool&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
  &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;reschedule_overdue_tasks&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="s2"&gt;`Reschedule overdue tasks by updating their due dates in Notion.
  First call get_notion_briefing_data to get current overdue tasks.
  Decide sensible new due dates based on priority and today's date.
  Spread them out — don't dump everything on one day.`&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="na"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;array&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;object&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
      &lt;span class="na"&gt;taskId&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;newDueDate&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
      &lt;span class="na"&gt;reason&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;z&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;string&lt;/span&gt;&lt;span class="p"&gt;(),&lt;/span&gt;
    &lt;span class="p"&gt;})),&lt;/span&gt;
  &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="k"&gt;async &lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="nx"&gt;updates&lt;/span&gt; &lt;span class="p"&gt;})&lt;/span&gt; &lt;span class="o"&gt;=&amp;gt;&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;results&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;rescheduleTasks&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;updates&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;content&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt; &lt;span class="na"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;text&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;text&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;JSON&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;stringify&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;results&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;}]&lt;/span&gt; &lt;span class="p"&gt;};&lt;/span&gt;
  &lt;span class="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;The &lt;code&gt;reason&lt;/code&gt; field is intentional. The AI isn't just moving dates — it's explaining why. You can see the reasoning in the tool call output. That's what makes it feel like delegation rather than automation.&lt;/p&gt;

&lt;h3&gt;
  
  
  Dynamic workspace discovery
&lt;/h3&gt;

&lt;p&gt;No hardcoded database IDs. The system discovers your workspace by reading property shapes — it inspects what fields each database has, not what it's named. Databases with &lt;code&gt;progress&lt;/code&gt; or &lt;code&gt;percent&lt;/code&gt; fields are classified as goal trackers. Databases with &lt;code&gt;status&lt;/code&gt; or due date fields are classified as task lists. This means it adapts to however you've structured your workspace — different column names, different layouts, different numbers of databases.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Goals have progress fields — exclude them from task DBs&lt;/span&gt;
&lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasProgress&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;goalDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="k"&gt;else&lt;/span&gt; &lt;span class="k"&gt;if &lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;hasStatus&lt;/span&gt; &lt;span class="o"&gt;||&lt;/span&gt; &lt;span class="nx"&gt;hasDue&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;taskDbs&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;push&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;db&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;id&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;title&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;Works on any Notion workspace structure, out of the box.&lt;/p&gt;

&lt;h3&gt;
  
  
  The parts that were actually hard
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;&lt;code&gt;completeTask&lt;/code&gt; silently did nothing for weeks.&lt;/strong&gt; It was calling the Notion native &lt;code&gt;status&lt;/code&gt; type, but most databases use a &lt;code&gt;select&lt;/code&gt; field for Status. The silent fallback was to archive the page instead. Fixed it by reading the page schema first and detecting the actual property type before writing.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Goal databases kept appearing as task databases.&lt;/strong&gt; Any database with a &lt;code&gt;Status&lt;/code&gt; column and a date field got classified as tasks. My Goals DB has both. Fixed by checking for a &lt;code&gt;progress&lt;/code&gt;/&lt;code&gt;percent&lt;/code&gt; field first — if it exists, it's a goal DB.&lt;/p&gt;

&lt;p&gt;Neither was hard to fix. Both would silently break the demo if I hadn't caught them.&lt;/p&gt;




&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;MCP server&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt; — stdio + StreamableHTTP transports&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Generative UI&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;React app compiled to a single HTML string, served as a tool response, rendered live inside the chat&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Notion writes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Direct REST API with dynamic schema detection&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Bundler&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Vite + &lt;code&gt;vite-plugin-singlefile&lt;/code&gt; (entire React app as one inlined HTML string)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Runtime&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Node.js + tsx&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Run it in 60 seconds with GitHub Codespaces&lt;/strong&gt; — the repo includes &lt;code&gt;devcontainer.json&lt;/code&gt; with everything pre-configured, port 3333 forwarded, &lt;code&gt;NOTION_API_KEY&lt;/code&gt; as the only required secret.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Garinmckayl/chief-of-staff
&lt;span class="nb"&gt;cd &lt;/span&gt;chief-of-staff &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; npm run build
&lt;span class="nv"&gt;NOTION_API_KEY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;your_key npm run start:stdio
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Before Chief of Staff, "AI + your data" meant a smarter search or a better summary. You still had to act on the output yourself. The AI was a reader. You were still the writer.&lt;/p&gt;

&lt;p&gt;Chief of Staff makes the AI the writer too. It reads your workspace, shows you the situation visually, and when you point it at a problem — it fixes it. All in Notion. None of it requiring you to open a single Notion page.&lt;/p&gt;

&lt;p&gt;I built this because I needed it. I'm a solo founder in Addis Ababa, maintaining open source infrastructure, building a startup, without a team, in a city where many of the tools the rest of the world assumes you have aren't available to you. Claude Desktop doesn't work here. I demo this in VS Code Copilot because that's what I actually have access to.&lt;/p&gt;

&lt;p&gt;That constraint shaped everything. It works with what you have. One workspace, one API key, one command.&lt;/p&gt;

&lt;p&gt;Three weeks ago, building an interactive visual app that lives inside VS Code wasn't possible. Now it is. And the first thing I built with it was a chief of staff — because that's what I needed most.&lt;/p&gt;

&lt;p&gt;This isn't productivity software. It's what happens when the person who builds infrastructure finally gets some infrastructure of their own.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;br&gt;
&lt;em&gt;GitHub: &lt;a href="https://github.com/Garinmckayl/chief-of-staff" rel="noopener noreferrer"&gt;https://github.com/Garinmckayl/chief-of-staff&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>Arlo - I Built an AI Companion That Gives Blind Users the Same 3-Second Superpower Sighted People Have</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sun, 22 Mar 2026 12:16:40 +0000</pubDate>
      <link>https://forem.com/zeshama/arlo-i-built-an-ai-companion-that-gives-blind-users-the-same-3-second-superpower-sighted-people-55co</link>
      <guid>https://forem.com/zeshama/arlo-i-built-an-ai-companion-that-gives-blind-users-the-same-3-second-superpower-sighted-people-55co</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/notion-2026-03-04"&gt;Notion MCP Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;




&lt;p&gt;I'm 24. I dropped out. I'm building an AI startup from Addis Ababa, Ethiopia.&lt;/p&gt;

&lt;p&gt;I built Arlo in 9 days because I kept thinking about a specific number: &lt;strong&gt;253 million people&lt;/strong&gt; with vision loss navigate the web the same way every single time - from zero, with no memory of what helped them before. Every visit. Every site. From scratch.&lt;/p&gt;

&lt;p&gt;Notion MCP is what finally made a real solution possible.&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Talks About
&lt;/h2&gt;

&lt;p&gt;A sighted person lands on a flight booking page and within 3 seconds they know: there's a search bar at the top, filters on the left, results in the middle. Three seconds.&lt;/p&gt;

&lt;p&gt;A blind user with a screen reader starts from the top and listens. Every navigation link. Every cookie banner. Every decorative image. Every sponsored result. On a site like Kayak, that's often &lt;strong&gt;200+ elements&lt;/strong&gt; before a single fare. And every visit starts from zero - the screen reader has no memory of what helped last time.&lt;/p&gt;

&lt;p&gt;I built Arlo because that's not good enough.&lt;/p&gt;




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

&lt;p&gt;&lt;strong&gt;Arlo is an AI companion that gives visually impaired users the same 3-second superpower sighted people have.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;You tell Arlo what you want to do. Arlo reads the entire page and tells you exactly what matters - in natural spoken language. Like a trusted friend who can see the screen.&lt;/p&gt;

&lt;p&gt;But here's what makes Arlo different from every other accessibility tool:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Arlo remembers you. And that memory lives in Notion.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Every visit, Arlo learns. It learns that you always pick the cheapest option. It learns that on Amazon you skip sponsored results. It learns that the SSA website has a confusing dropdown on step 3 that catches people off guard. All of that gets saved to your personal Notion database — structured, readable, yours to own and edit.&lt;/p&gt;

&lt;p&gt;The next visit, Arlo opens with: &lt;em&gt;"I remember you've been here before. Last time you were looking for Delta flights and picked the 7am option — want me to head straight there?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That's not a screen reader. That's a companion.&lt;/p&gt;




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

&lt;p&gt;  &lt;iframe src="https://www.youtube.com/embed/1EFyr0KuSQ8"&gt;
  &lt;/iframe&gt;
&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://arlo.arcumet.com" rel="noopener noreferrer"&gt;https://arlo.arcumet.com&lt;/a&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Try it yourself: paste any URL, speak or type your goal, and Arlo guides you.&lt;/p&gt;
&lt;/blockquote&gt;




&lt;h2&gt;
  
  
  The Flow
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;1. You say what you want&lt;/strong&gt;&lt;br&gt;
Type it or speak it. Arlo uses GLM-ASR for voice — accurate across accents.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Arlo reads the entire page&lt;/strong&gt;&lt;br&gt;
Not static HTML parsing — GLM Web Reader fully renders the page including JavaScript. React apps, SPAs, Google Flights, Twitter — all work.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Notion memory is checked&lt;/strong&gt;&lt;br&gt;
Before analyzing, Arlo queries your Notion database: &lt;em&gt;"What do I know about this domain? What has this user done here before?"&lt;/em&gt; That context shapes everything.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;4. Arlo speaks&lt;/strong&gt;&lt;br&gt;
Not a list of elements. Arlo says: &lt;em&gt;"You're on Amazon search results. Based on what I remember, you prefer under $100 and skip sponsored results. The first non-sponsored option is the Soundcore Q20i at $59.99."&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;After the visit, new learnings are written back to Notion via MCP. The loop closes.&lt;/p&gt;


&lt;h2&gt;
  
  
  How I Used Notion MCP
&lt;/h2&gt;

&lt;p&gt;Notion isn't a feature in Arlo. Notion is Arlo's brain.&lt;/p&gt;

&lt;p&gt;Without Notion, Arlo is just another AI tool that forgets you the moment you close the tab. With Notion MCP, Arlo becomes something that grows with you — a companion that gets better every single time you use it.&lt;/p&gt;
&lt;h3&gt;
  
  
  The MCP integration loop
&lt;/h3&gt;


&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;User visits page
       ↓
Arlo queries Notion MCP: "What do I know about this domain?"
       ↓
GLM-4.6 analyzes page + goal + memory context
       ↓
Arlo speaks guidance (Hume Octave ultra-realistic TTS)
       ↓
New insights written back to Notion via MCP
       ↓
Next visit: Arlo already knows you
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Every memory entry is a &lt;strong&gt;full rich Notion page&lt;/strong&gt; — not just a database row. Heading blocks, bullet context, callout explaining what was learned, linked back to the source page. The user can open Notion and read exactly what Arlo knows about them, edit it, or delete it. Transparent, human-readable memory they own.&lt;/p&gt;
&lt;h3&gt;
  
  
  The Notion MCP server integration
&lt;/h3&gt;

&lt;p&gt;Arlo uses &lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt; with stdio transport for all writes — the same MCP protocol that Claude Desktop, Cursor, and other AI tools use:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight typescript"&gt;&lt;code&gt;&lt;span class="c1"&gt;// Spawn the Notion MCP server as a subprocess&lt;/span&gt;
&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;transport&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;StdioClientTransport&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;
  &lt;span class="na"&gt;command&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;node&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
  &lt;span class="na"&gt;args&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;MCP_SERVER_BIN&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;--transport&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;stdio&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;
  &lt;span class="na"&gt;env&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;NOTION_TOKEN&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nx"&gt;process&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;NOTION_API_KEY&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
&lt;span class="p"&gt;});&lt;/span&gt;

&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;new&lt;/span&gt; &lt;span class="nc"&gt;Client&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;arlo&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;version&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;1.0.0&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;capabilities&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="nx"&gt;transport&lt;/span&gt;&lt;span class="p"&gt;);&lt;/span&gt;

&lt;span class="c1"&gt;// Write memory via MCP tool call — not REST API&lt;/span&gt;
&lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nx"&gt;client&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;callTool&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt; &lt;span class="na"&gt;name&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;API-post-page&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;arguments&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt; &lt;span class="p"&gt;});&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






&lt;h2&gt;
  
  
  Show me the code
&lt;/h2&gt;

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




&lt;h2&gt;
  
  
  Technical Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;What&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Page reading&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GLM Web Reader API — full JS rendering&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Intelligence&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GLM-4.6 with thinking mode&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Vision&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GLM-4.6V for screenshot analysis&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice input&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;GLM-ASR-2512&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice output&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Hume Octave TTS — ultra-realistic&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory writes&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Notion MCP (&lt;code&gt;@notionhq/notion-mcp-server&lt;/code&gt; stdio)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory reads&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Notion REST API (zero-latency for live context)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Next.js 16, deployed on Vercel&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  Why This Matters
&lt;/h2&gt;

&lt;p&gt;Most AI accessibility tools are built by people who don't need them, for a problem they've read about rather than felt. They work on clean demo sites and fall apart on the chaotic, JS-heavy, dark-pattern-filled reality of the actual web.&lt;/p&gt;

&lt;p&gt;Arlo is built around the real failure mode: the web doesn't remember you, and that costs blind users enormous time and cognitive load on every single visit.&lt;/p&gt;

&lt;p&gt;The Notion memory layer isn't a clever integration for the sake of a hackathon. It's the answer to a real question: &lt;em&gt;if this tool is going to be useful long-term, it needs to get better with use, and the user needs to be able to trust and control what it knows about them.&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Notion is the right answer. It's human-readable. It's editable. It's already where people organize their lives. And with MCP, it becomes a living brain that any AI tool can read from and write to.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built in 9 days · Live at &lt;a href="https://arlo.arcumet.com" rel="noopener noreferrer"&gt;https://arlo.arcumet.com&lt;/a&gt; · &lt;a href="https://github.com/Garinmckayl/arlo" rel="noopener noreferrer"&gt;GitHub&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>notionchallenge</category>
      <category>mcp</category>
      <category>ai</category>
    </item>
    <item>
      <title>I Built a Personal AI Computer With Gemini - Here's How</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Thu, 12 Mar 2026 12:54:12 +0000</pubDate>
      <link>https://forem.com/zeshama/i-built-a-personal-ai-computer-with-gemini-heres-how-934</link>
      <guid>https://forem.com/zeshama/i-built-a-personal-ai-computer-with-gemini-heres-how-934</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was created for the purposes of entering the &lt;a href="https://geminiliveagentchallenge.devpost.com/" rel="noopener noreferrer"&gt;Gemini Live Agent Challenge&lt;/a&gt; hackathon. #GeminiLiveAgentChallenge&lt;/em&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  The Problem Nobody Has Solved
&lt;/h2&gt;

&lt;p&gt;306,000 people starred Open Claw on GitHub. They all want the same thing: a personal AI that actually &lt;em&gt;does things&lt;/em&gt;. Sends emails. Manages calendars. Runs code. Browses the web. Learns new skills.&lt;/p&gt;

&lt;p&gt;But every solution looks the same: clone the repo, install Docker, configure API keys, run terminal commands, manage a cloud bill. The technology is amazing. The accessibility is terrible.&lt;/p&gt;

&lt;p&gt;8 billion people want a personal AI computer. 99% of them will never run a Docker container.&lt;/p&gt;

&lt;p&gt;So I built Elora.&lt;/p&gt;

&lt;h2&gt;
  
  
  What Elora Is
&lt;/h2&gt;

&lt;p&gt;Elora is not a chatbot. She's a &lt;strong&gt;personal AI computer that lives on your phone&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;She has her own sandbox (a persistent cloud VM where she installs packages, runs code, and saves files - isolated per user). She has her own skill system (she can search for skills, install them, or write new ones from scratch). And she has a security layer that protects everything she does.&lt;/p&gt;

&lt;p&gt;You download the app and talk to her. That's it. No setup. No API keys. No Docker.&lt;/p&gt;

&lt;h3&gt;
  
  
  Elora Live Voice Architecture
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2Fkqplnmcdiox53fe19l14.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%2Fkqplnmcdiox53fe19l14.png" alt="Elora Live Voice Architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h3&gt;
  
  
  Elora Wake Word
&lt;/h3&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.amazonaws.com%2Fuploads%2Farticles%2Fo2440hr2hd0yb2itsrwm.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%2Fo2440hr2hd0yb2itsrwm.png" alt="Elora Wake word architecture"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The Tech Stack
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Layer&lt;/th&gt;
&lt;th&gt;Technology&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Mobile&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Expo / React Native (TypeScript)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Voice&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemini Live API (real-time bidirectional audio)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Google ADK (multi-agent orchestration)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;LLM&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Gemini 2.0 Flash / 2.5 Flash&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Browser&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Playwright + Gemini 2.5 Flash (computer use)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Code Sandbox&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;E2B (per-user persistent VMs)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Custom skill engine (search, install, create, execute)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Security&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Agntor trust protocol&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Memory&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;MemU + Firestore + text-embedding-004&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Backend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;FastAPI on Google Cloud Run&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;IaC&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Terraform + GitHub Actions CI/CD&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Let me walk through how I built the pieces that matter.&lt;/p&gt;

&lt;h2&gt;
  
  
  1. Voice That Feels Alive - Gemini Live API
&lt;/h2&gt;

&lt;p&gt;The Gemini Live API is what makes Elora feel real. It's full duplex audio - she talks while you talk, you can interrupt her mid-sentence, and she handles it naturally.&lt;/p&gt;

&lt;p&gt;Here's the architecture for voice:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;Phone (mic) → PCM audio chunks via WebSocket → Cloud Run
  → Gemini Live API session (bidirectional)
  → Audio response chunks → WebSocket → Phone (speaker)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The mobile app maintains three simultaneous WebSocket connections:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Text chat&lt;/strong&gt; - ADK agent with full tool calling&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Live audio&lt;/strong&gt; - Gemini Live API with real-time audio streaming&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Wake word&lt;/strong&gt; - Always-on "Hey Elora" detection&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The wake word detector is its own Gemini Live session configured to only respond with "WAKE" when it hears the trigger phrase. Minimal tokens, always listening.&lt;/p&gt;

&lt;p&gt;The hardest part: &lt;strong&gt;Gemini Live API doesn't support ADK's tool-calling protocol natively.&lt;/strong&gt; So I built a parallel system - manual JSON schemas for every tool declaration, a dispatch function that maps tool names to the same Python functions the ADK agent uses, and a response handler that streams tool results back into the Live session. Every tool works in both text mode (ADK) and voice mode (Live API).&lt;/p&gt;

&lt;h2&gt;
  
  
  2. Vision - She Sees Your World
&lt;/h2&gt;

&lt;p&gt;During a live call, Elora watches through your camera. The mobile app captures frames and sends them as base64 JPEG over the WebSocket. On the backend, a proactive vision loop runs every 3 seconds:&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="c1"&gt;# Simplified proactive vision logic
&lt;/span&gt;&lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;camera_active&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;user_quiet_for_8s&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;last_proactive_25s_ago&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="n"&gt;frame&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;latest_camera_frame&lt;/span&gt;
    &lt;span class="n"&gt;faces&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="k"&gt;await&lt;/span&gt; &lt;span class="nf"&gt;recognize_faces&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="sa"&gt;f&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;[VISION CHECK] You see: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;faces&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Comment if relevant.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="n"&gt;session&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;send&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;prompt&lt;/span&gt; &lt;span class="o"&gt;+&lt;/span&gt; &lt;span class="n"&gt;frame&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="c1"&gt;# If Elora responds with &amp;lt;silent&amp;gt;, swallow the response
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;She doesn't just respond when asked - she speaks up when she sees something worth mentioning. Point the camera at a friend she's seen before, and she'll say their name. That's face recognition using Gemini Vision with two-pass comparison against stored reference images in Cloud Storage.&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The Skill System - Why Elora Is a Computer, Not a Chatbot
&lt;/h2&gt;

&lt;p&gt;This is the feature I'm most proud of. Every AI assistant has a fixed set of tools. Elora can learn new ones.&lt;/p&gt;

&lt;p&gt;The skill system works in four modes:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Search:&lt;/strong&gt; Query the skill registry (bundled + community) by keyword.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Install:&lt;/strong&gt; Download a skill definition (YAML metadata + Python code) into the user's Firestore profile and deploy it to their sandbox.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Execute:&lt;/strong&gt; Load the skill code, fill in template parameters, and run it in the user's E2B sandbox. Real code, real output.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Create:&lt;/strong&gt; This is the magic. Tell Elora "create a skill that checks if a website is up" and she:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Writes the Python code&lt;/li&gt;
&lt;li&gt;Creates a YAML skill definition with parameters&lt;/li&gt;
&lt;li&gt;Tests the code in your sandbox with a dry run&lt;/li&gt;
&lt;li&gt;Validates the output&lt;/li&gt;
&lt;li&gt;Saves it permanently to your library&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The skill you asked for now exists forever. You can run it tomorrow, next week, next year.&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="c1"&gt;# Bundled skills ship with Elora
&lt;/span&gt;&lt;span class="n"&gt;BUNDLED_SKILLS&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;weather&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&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;name&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;weather&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;description&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;Get current weather for any city&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;code&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;import requests&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;url = f&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;https://api.open-meteo.com/v1/forecast?latitude={lat}&amp;amp;longitude={lon}&amp;amp;current_weather=true&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="se"&gt;\n&lt;/span&gt;&lt;span class="s"&gt;...&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;crypto_prices&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;hackernews&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;exchange_rates&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;wikipedia&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;rss_reader&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="p"&gt;...&lt;/span&gt; &lt;span class="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;Six skills ship bundled. Users can create unlimited custom ones. And there's a community registry where you can publish skills for others to use.&lt;/p&gt;

&lt;p&gt;This is what transforms Elora from "assistant" to "computer." A computer isn't defined by what it ships with - it's defined by what you can make it do.&lt;/p&gt;

&lt;h2&gt;
  
  
  4. Per-User Sandbox - Your Computer in the Cloud
&lt;/h2&gt;

&lt;p&gt;Every Elora user gets their own isolated cloud VM via E2B. This isn't shared compute - it's YOUR machine.&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;def&lt;/span&gt; &lt;span class="nf"&gt;get_or_create_sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="nb"&gt;str&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="c1"&gt;# Check in-memory cache
&lt;/span&gt;    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;_active_sandboxes&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;_active_sandboxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;

    &lt;span class="c1"&gt;# Check Firestore for paused sandbox ID
&lt;/span&gt;    &lt;span class="n"&gt;doc&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;_get_sandbox_doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;()&lt;/span&gt;
    &lt;span class="k"&gt;if&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;exists&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="n"&gt;sandbox_id&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;doc&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;to_dict&lt;/span&gt;&lt;span class="p"&gt;().&lt;/span&gt;&lt;span class="nf"&gt;get&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sandbox_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Reconnect to existing sandbox
&lt;/span&gt;        &lt;span class="n"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;connect&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;sandbox_id&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="k"&gt;else&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Create new sandbox with pre-installed packages
&lt;/span&gt;        &lt;span class="n"&gt;sandbox&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nc"&gt;Sandbox&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;timeout&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;&lt;span class="mi"&gt;3600&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;metadata&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;user_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
        &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;pip install requests beautifulsoup4 feedparser pyyaml&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;commands&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;run&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;mkdir -p /home/user/skills /home/user/workspace /home/user/data&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="c1"&gt;# Persist sandbox ID
&lt;/span&gt;        &lt;span class="nf"&gt;_get_sandbox_doc&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;).&lt;/span&gt;&lt;span class="nf"&gt;set&lt;/span&gt;&lt;span class="p"&gt;({&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;sandbox_id&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;sandbox_id&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;

    &lt;span class="n"&gt;_active_sandboxes&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="n"&gt;user_id&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;
    &lt;span class="k"&gt;return&lt;/span&gt; &lt;span class="n"&gt;sandbox&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Sandboxes auto-pause when idle and reconnect when needed. Packages you install persist. Files you create persist. The sandbox ID is stored in Firestore so it survives server restarts.&lt;/p&gt;

&lt;p&gt;When Elora runs code for you - whether it's a skill, a script you asked for, or a data analysis &lt;br&gt;
 it runs in YOUR sandbox. Nobody else can see it or touch it.&lt;/p&gt;
&lt;h2&gt;
  
  
  5. Security - The Agntor Trust Protocol
&lt;/h2&gt;

&lt;p&gt;When your AI agent has access to your email, calendar, files, and code execution, security isn't optional.&lt;/p&gt;

&lt;p&gt;The Agntor trust protocol runs as middleware on every incoming message:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Prompt injection guard&lt;/strong&gt; - 12 regex patterns + 3 heuristic checks + structural analysis. Catches "ignore previous instructions" and its 50 variants.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;PII/secret redaction&lt;/strong&gt; - Detects and masks API keys, tokens, credit card numbers, and SSNs before they reach the model.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Tool guardrails&lt;/strong&gt; - Blocklist (shell.exec, eval) and confirmation list (send_email, delete_file). Dangerous tools are blocked. Sensitive tools require explicit confirmation.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;SSRF protection&lt;/strong&gt; - Validates all URLs against private IP ranges with DNS resolution. Prevents the model from being tricked into accessing internal services.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Agent identity&lt;/strong&gt; - A verifiable identity endpoint that exposes Elora's capabilities and security posture.&lt;br&gt;
&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;&lt;span class="nv"&gt;$ &lt;/span&gt;curl https://elora-backend-453139277365.us-central1.run.app/agent/identity
&lt;span class="o"&gt;{&lt;/span&gt;
  &lt;span class="s2"&gt;"agent_name"&lt;/span&gt;: &lt;span class="s2"&gt;"Elora"&lt;/span&gt;,
  &lt;span class="s2"&gt;"version"&lt;/span&gt;: &lt;span class="s2"&gt;"0.5.0"&lt;/span&gt;,
  &lt;span class="s2"&gt;"security"&lt;/span&gt;: &lt;span class="o"&gt;{&lt;/span&gt;
    &lt;span class="s2"&gt;"prompt_guard"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"pii_redaction"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"tool_guardrails"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;,
    &lt;span class="s2"&gt;"ssrf_protection"&lt;/span&gt;: &lt;span class="nb"&gt;true&lt;/span&gt;
  &lt;span class="o"&gt;}&lt;/span&gt;
&lt;span class="o"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;The entire security layer is pure Python - no external dependencies. It's fast enough to run on every message without noticeable latency.&lt;/p&gt;
&lt;h2&gt;
  
  
  6. Multi-Agent Architecture - Google ADK
&lt;/h2&gt;

&lt;p&gt;Elora uses Google's Agent Development Kit (ADK) with a hierarchical multi-agent architecture:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;elora_root (orchestrator)
├── web_researcher    → web_search + fetch_webpage
├── browser_worker    → Playwright + Gemini computer-use
├── email_calendar    → Gmail + Google Calendar (full CRUD)
├── file_memory       → Cloud Storage + Firestore memory
└── research_loop     → LoopAgent with self-verification
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The root agent decides which sub-agent to delegate to based on the user's intent. "Send an email" goes to &lt;code&gt;email_calendar&lt;/code&gt;. "What's on Hacker News" goes to &lt;code&gt;browser_worker&lt;/code&gt;. "Remember that I prefer morning meetings" goes to &lt;code&gt;file_memory&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;ADK's constraint of one parent per agent forced clean separation of concerns. Each sub-agent has exactly the tools it needs and nothing more.&lt;/p&gt;

&lt;h2&gt;
  
  
  7. 40+ Real Tools
&lt;/h2&gt;

&lt;p&gt;These aren't mock tools. They execute real actions:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Gmail&lt;/strong&gt; - Send, read, archive, trash, label, batch manage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Calendar&lt;/strong&gt; - Create, update, delete, list, search&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser&lt;/strong&gt; - Playwright opens real pages, takes screenshots, Gemini reasons about what it sees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Code execution&lt;/strong&gt; - Python and JavaScript in your personal sandbox&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SMS&lt;/strong&gt; - Twilio (with deep-link fallback)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Google Slides &amp;amp; Docs&lt;/strong&gt; - Programmatic creation with shareable links&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Face recognition&lt;/strong&gt; - Two-pass Gemini Vision comparison against stored references&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;File management&lt;/strong&gt; - Upload, read, list, delete in per-user Cloud Storage&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reminders&lt;/strong&gt; - Natural language time parsing, push notification delivery&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;People memory&lt;/strong&gt; - Names, relationships, birthdays, contact info, appearance&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Proactive engine&lt;/strong&gt; - Meeting alerts, birthday nudges, stale contact check-ins&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  8. Memory - She Remembers Everything
&lt;/h2&gt;

&lt;p&gt;Elora has a 3-layer memory system:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 1: Raw facts.&lt;/strong&gt; After every conversation, a background task extracts key facts and stores them as vector embeddings (text-embedding-004) in Firestore. Semantic search retrieves relevant memories on every new conversation.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 2: Compacted profile.&lt;/strong&gt; Periodically, Gemini Flash merges and deduplicates raw facts into a structured user profile - preferences, relationships, work info, goals.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Layer 3: Session summaries.&lt;/strong&gt; After every call, a summary is generated. The last 3 summaries are injected into the next session for continuity.&lt;/p&gt;

&lt;p&gt;This is powered by MemU, which achieves 92% accuracy on the Locomo memory benchmark at 10x lower always-on cost compared to traditional RAG approaches.&lt;/p&gt;

&lt;h2&gt;
  
  
  Deployment
&lt;/h2&gt;

&lt;p&gt;The entire backend deploys to Google Cloud Run with a single &lt;code&gt;git push&lt;/code&gt;:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;GitHub Actions builds the Docker image&lt;/li&gt;
&lt;li&gt;Pushes to Artifact Registry&lt;/li&gt;
&lt;li&gt;Deploys to Cloud Run with all environment variables&lt;/li&gt;
&lt;li&gt;Creates Firestore indexes&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Infrastructure is managed with Terraform:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight hcl"&gt;&lt;code&gt;&lt;span class="nx"&gt;resource&lt;/span&gt; &lt;span class="s2"&gt;"google_cloud_run_service"&lt;/span&gt; &lt;span class="s2"&gt;"elora_backend"&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="nx"&gt;name&lt;/span&gt;     &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"elora-backend"&lt;/span&gt;
  &lt;span class="nx"&gt;location&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1"&lt;/span&gt;

  &lt;span class="nx"&gt;template&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
    &lt;span class="nx"&gt;spec&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
      &lt;span class="nx"&gt;containers&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
        &lt;span class="nx"&gt;image&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"us-central1-docker.pkg.dev/${var.project_id}/elora/backend:latest"&lt;/span&gt;
        &lt;span class="nx"&gt;resources&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
          &lt;span class="nx"&gt;limits&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nx"&gt;cpu&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nx"&gt;memory&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;"2Gi"&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;
        &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="p"&gt;}&lt;/span&gt;
      &lt;span class="nx"&gt;timeout_seconds&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="mi"&gt;3600&lt;/span&gt;  &lt;span class="c1"&gt;# Long-running WebSocket connections&lt;/span&gt;
    &lt;span class="p"&gt;}&lt;/span&gt;
  &lt;span class="p"&gt;}&lt;/span&gt;
&lt;span class="p"&gt;}&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;40+ Cloud Run revisions tell the development story. The backend has been continuously deployed and iterated throughout the build.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;The gap between "chatbot" and "computer" is isolation, persistence, and extensibility.&lt;/strong&gt; It's not about making the LLM smarter. It's about giving it a sandbox that persists, a skill system that grows, and security that you can trust. That's what makes it a computer.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Security can't be an afterthought.&lt;/strong&gt; When your agent can read your email, send texts, and execute code, prompt injection isn't theoretical - it's an attack vector. Build the guard first.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;ADK multi-agent is production-ready.&lt;/strong&gt; The one-parent-per-agent constraint feels limiting at first, but it forces clean architecture. Each agent has exactly the tools and context it needs.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try It
&lt;/h2&gt;

&lt;p&gt;The backend is live:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://elora-backend-453139277365.us-central1.run.app
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The code is open:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;https://github.com/Garinmckayl/elora
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;To run the mobile app:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/Garinmckayl/elora.git
&lt;span class="nb"&gt;cd &lt;/span&gt;elora/app
npm &lt;span class="nb"&gt;install
&lt;/span&gt;npx expo start &lt;span class="nt"&gt;--tunnel&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Scan the QR code with Expo Go. Talk to Elora.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by a solo developer in Addis Ababa, Ethiopia for the &lt;a href="https://geminiliveagentchallenge.devpost.com/" rel="noopener noreferrer"&gt;Gemini Live Agent Challenge&lt;/a&gt;. #GeminiLiveAgentChallenge&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;&lt;em&gt;GitHub: &lt;a href="https://github.com/Garinmckayl/elora" rel="noopener noreferrer"&gt;github.com/Garinmckayl/elora&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

</description>
      <category>gemini</category>
      <category>ai</category>
      <category>googlecloud</category>
      <category>hackathon</category>
    </item>
    <item>
      <title>Ivy - Bringing LLMs to 35 Million offline students in Ethiopia</title>
      <dc:creator>Natnael Getenew</dc:creator>
      <pubDate>Sun, 08 Mar 2026 07:08:28 +0000</pubDate>
      <link>https://forem.com/zeshama/ivy-bringing-llms-to-35-million-offline-students-in-ethiopia-30e8</link>
      <guid>https://forem.com/zeshama/ivy-bringing-llms-to-35-million-offline-students-in-ethiopia-30e8</guid>
      <description>&lt;p&gt;Hi everyone,&lt;/p&gt;

&lt;p&gt;I’m writing this from Addis Ababa. While the world is talking about the latest LLM cloud features, 35 million students in Ethiopia are being left behind because they simply don't have stable internet or the hardware to run "modern" education.&lt;/p&gt;

&lt;p&gt;I’ve spent the last few months building Ivy a specialized, offline tutor designed to run on low-end Android devices with zero connectivity. it’s an architecture built on edge-inference and local language support (Amharic) so that a kid in a rural village has the same educational "co-pilot" as someone in London or New York.&lt;/p&gt;

&lt;p&gt;I’m a solo founder, and I’m currently 95% of the way through a global AWS challenge to get this project the resources it needs to scale. I’ve reached 50 likes on my own, but I’m at a point where I need the support of the broader tech community to reach the Top 50 and secure the next round of funding/support.&lt;/p&gt;

&lt;p&gt;If you believe that state of the art education should be a tool for all students not just those with a fiber connection I would be incredibly grateful for your support.&lt;/p&gt;

&lt;p&gt;How you can help:&lt;/p&gt;

&lt;p&gt;Click the link to my official AWS entry: &lt;a href="https://builder.aws.com/content/39w2EpJsgvWLg1yI3DNXfdX24tt/aideas-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent" rel="noopener noreferrer"&gt;https://builder.aws.com/content/39w2EpJsgvWLg1yI3DNXfdX24tt/aideas-ivy-the-worlds-first-offline-capable-proactive-ai-tutoring-agent&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;If you find the "Why" and the "How" compelling, please hit the "Like" button.&lt;/p&gt;

&lt;p&gt;I’m happy to answer any questions about the technical hurdles of edge-inference or the reality of building tech in Ethiopia. Thank you for reading.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>learning</category>
      <category>agents</category>
      <category>llm</category>
    </item>
  </channel>
</rss>
