<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:dc="http://purl.org/dc/elements/1.1/">
  <channel>
    <title>DEV Community: Rawan Ehab</title>
    <description>The latest articles on DEV Community by Rawan Ehab (@rawan523).</description>
    <link>https://dev.to/rawan523</link>
    <image>
      <url>https://media2.dev.to/dynamic/image/width=90,height=90,fit=cover,gravity=auto,format=auto/https:%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Fuser%2Fprofile_image%2F4102627%2F1a9cff28-2ff6-4540-9e7b-b8a10b5a9216.jpg</url>
      <title>DEV Community: Rawan Ehab</title>
      <link>https://dev.to/rawan523</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/rawan523"/>
    <language>en</language>
    <item>
      <title>Building an Autonomous Taskmaster for Curly Hair Chemistry</title>
      <dc:creator>Rawan Ehab</dc:creator>
      <pubDate>Mon, 31 Aug 2026 19:06:33 +0000</pubDate>
      <link>https://dev.to/rawan523/building-an-autonomous-taskmaster-for-curly-hair-chemistry-h7c</link>
      <guid>https://dev.to/rawan523/building-an-autonomous-taskmaster-for-curly-hair-chemistry-h7c</guid>
      <description>&lt;p&gt;&lt;em&gt;This article was created for the purpose of entering the Google #AllThingsAgentic Hackathon (Taskmaster Category).&lt;/em&gt;&lt;/p&gt;




&lt;h3&gt;
  
  
  The Friction: Haircare is a Chemistry Experiment
&lt;/h3&gt;

&lt;p&gt;Anyone managing textured or curly hair knows that finding the right routine is rarely straightforward. It often turns into an accidental cosmetic chemistry experiment:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Put a high-glycerin product in your hair when tomorrow's dew point spikes above 70°F? You get extreme frizz.&lt;/li&gt;
&lt;li&gt;Use a heavy silicone without alternating with a clarifying sulfate? You build up an occlusive layer that blocks hydration.&lt;/li&gt;
&lt;li&gt;Layer high-concentration keratin treatments over already protein-sensitive strands? You end up with brittle, snapping hair (protein overload).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Humans aren't built to memorize the chemical interactions of dozens of cosmetic compounds across multiple bottles, let alone reconcile those ingredients against tomorrow's local UV index and humidity forecast.&lt;/p&gt;

&lt;p&gt;I wanted to fix this—not with another static chatbot where you paste text and read advice, but with an &lt;strong&gt;autonomous background Taskmaster&lt;/strong&gt; that actually does the heavy lifting for you.&lt;/p&gt;




&lt;h3&gt;
  
  
  What is Curl Chemist?
&lt;/h3&gt;

&lt;p&gt;&lt;strong&gt;Curl Chemist&lt;/strong&gt; is an autonomous AI agent built to manage the entire lifecycle of a personalized haircare routine without manual intervention.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;[ Snap Label Photo ] ──&amp;gt; [ Cloud Storage ] ──&amp;gt; [ Pub/Sub ]
                                                      │
                                                      ▼
[ Google Calendar ] &amp;lt;── [ Nightly Scheduler ] &amp;lt;── [ Cloud Run + Gemini 3.5 ]
  (Auto-booked)          (Weather + Inventory)     (N×N Conflict Engine)

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Scan &amp;amp; Analyze:&lt;/strong&gt; Snap a photo of any product label. Cloud Storage fires an event through Pub/Sub, triggering &lt;strong&gt;Gemini 3.5 Flash&lt;/strong&gt; to extract the chemical formulation and cross-reference it against your existing digital shelf (detecting silicone/sulfate mismatches, moisture-protein balance issues, etc.).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Autonomous Daily Scheduling:&lt;/strong&gt; Every night at 9:00 PM, a Cloud Scheduler cron job wakes up the Taskmaster orchestrator. It fetches the next day's local weather forecast, calculates humidity/dew point risks, builds the optimal product combination, and &lt;strong&gt;autonomously books a time block on your Google Calendar&lt;/strong&gt; if a deep treatment or wash day is needed.&lt;/li&gt;
&lt;/ol&gt;




&lt;h3&gt;
  
  
  Architectural Highlights &amp;amp; Engineering Deep-Dive
&lt;/h3&gt;

&lt;p&gt;Building a system that operates asynchronously in the background requires failure tolerance, context boundaries, and data security.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;┌──────────────────────────────────────────────────────────┐
│                   Incoming Request                       │
└────────────────────────────┬─────────────────────────────┘
                             │
                             ▼
              ┌─────────────────────────────┐
              │  Agent Gateway (Gemma 2 9B) │ ──[ Off-Topic / Malicious ]──&amp;gt; [ Drop / 400 ]
              └──────────────┬──────────────┘
                             │ (Valid Haircare Intent)
                             ▼
              ┌─────────────────────────────┐
              │ Cloud DLP (PII Redaction)   │
              └──────────────┬──────────────┘
                             │
                             ▼
              ┌─────────────────────────────┐
              │   Orchestrator (Cloud Run)  │
              │   Self-Healing ReAct Loop   │ &amp;lt;──&amp;gt; [ Gemini 3.5 Flash ]
              └──────────────┬──────────────┘
                             │
            ┌────────────────┴────────────────┐
            ▼                                 ▼
   [ Firestore Memory ]             [ Google Calendar API ]

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h4&gt;
  
  
  1. Self-Healing ReAct Loop
&lt;/h4&gt;

&lt;p&gt;Background pipelines fail when an LLM produces a malformed tool call or bad JSON argument. Instead of letting the server crash, I built a custom &lt;strong&gt;self-healing ReAct loop&lt;/strong&gt; using the Google GenAI SDK:&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;# Conceptual execution loop
&lt;/span&gt;&lt;span class="k"&gt;while&lt;/span&gt; &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;&amp;lt;&lt;/span&gt; &lt;span class="n"&gt;MAX_RETRIES&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="k"&gt;try&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;agent_orchestrator&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;step&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;current_context&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;response&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;has_tool_call&lt;/span&gt;&lt;span class="p"&gt;():&lt;/span&gt;
            &lt;span class="n"&gt;tool_output&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;execute_tool&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="n"&gt;tool_call&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
            &lt;span class="n"&gt;current_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_tool_result&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;tool_output&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="k"&gt;break&lt;/span&gt;
    &lt;span class="k"&gt;except&lt;/span&gt; &lt;span class="n"&gt;ToolExecutionError&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
        &lt;span class="c1"&gt;# Feed the literal traceback directly back into the LLM context
&lt;/span&gt;        &lt;span class="n"&gt;current_context&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append_error&lt;/span&gt;&lt;span class="p"&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;Tool execution failed: &lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="nf"&gt;str&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;e&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;. Please adjust your arguments and retry.&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
        &lt;span class="p"&gt;)&lt;/span&gt;
        &lt;span class="n"&gt;retries&lt;/span&gt; &lt;span class="o"&gt;+=&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt;

&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;If a tool call fails, the Python backend catches the traceback and appends the exact error message back into the model's execution context. The agent inspects its own mistake, adjusts its arguments, and recovers gracefully without human intervention.&lt;/p&gt;

&lt;h4&gt;
  
  
  2. Zero-Trust Gateway with Gemma
&lt;/h4&gt;

&lt;p&gt;To ensure efficient resource usage and guard against junk or off-topic prompts reaching Gemini 3.5 Flash, I implemented an Agent Gateway powered by &lt;code&gt;gemma-2-9b-it&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Gemma acts as an ultra-fast, local-intent classifier that &lt;strong&gt;fails closed&lt;/strong&gt;. If an incoming request falls outside cosmetic chemistry or routine scheduling, the gateway intercepts and drops the request before any heavy background processing occurs.&lt;/p&gt;

&lt;h4&gt;
  
  
  3. Model Armor &amp;amp; Inline Privacy (Cloud DLP)
&lt;/h4&gt;

&lt;p&gt;To maintain enterprise privacy standards, all reasoning traces and raw inputs are passed through &lt;strong&gt;Google Cloud DLP&lt;/strong&gt; (&lt;code&gt;dlp_client.deidentify_content&lt;/code&gt;) prior to logging in Firestore. Any potential PII (such as personal contact details or location strings) is redacted inline, ensuring the long-term memory bank stays clean and compliant.&lt;/p&gt;




&lt;h3&gt;
  
  
  Tech Stack
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;AI &amp;amp; Agent Core:&lt;/strong&gt; Gemini 3.5 Flash, Gemma (&lt;code&gt;gemma-2-9b-it&lt;/code&gt;), Google GenAI SDK&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Compute &amp;amp; Services:&lt;/strong&gt; Google Cloud Run, Cloud Scheduler, Cloud Storage, Cloud Pub/Sub, Cloud DLP&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Database &amp;amp; Memory:&lt;/strong&gt; Google Cloud Firestore&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Backend:&lt;/strong&gt; Python, FastAPI, Uvicorn&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Integrations:&lt;/strong&gt; Google Calendar API, Open-Meteo API&lt;/li&gt;
&lt;/ul&gt;




&lt;h3&gt;
  
  
  Key Learnings
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Error traces make great prompts:&lt;/strong&gt; Giving an agent its own runtime errors turns brittle API pipelines into resilient workflows.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Decouple triage from heavy reasoning:&lt;/strong&gt; Using a smaller model like Gemma as a gatekeeper significantly reduces latency and keeps the primary agent focused on domain-specific execution.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action beats conversation:&lt;/strong&gt; Moving away from standard chat interfaces toward scheduled, event-driven autonomous tasks makes AI genuinely useful for recurring real-world friction.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwr7vq47379c543qv978l.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fwr7vq47379c543qv978l.png" alt="architecture" width="800" height="534"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr4ijr6gzkqb4mmc07eig.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fr4ijr6gzkqb4mmc07eig.png" alt="signup hair type chooser" width="635" height="860"&gt;&lt;/a&gt;&lt;br&gt;
&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywb6nm9c2hbj88mjiyzq.png" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fywb6nm9c2hbj88mjiyzq.png" alt="add product feature" width="800" height="384"&gt;&lt;/a&gt;&lt;/p&gt;

</description>
      <category>googlecloud</category>
      <category>gemini</category>
      <category>ai</category>
      <category>allthingsagentic</category>
    </item>
  </channel>
</rss>
