<?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: Monika Sadlok</title>
    <description>The latest articles on DEV Community by Monika Sadlok (@msadlok).</description>
    <link>https://dev.to/msadlok</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%2F1292231%2F143b746f-1a8c-41a3-97e2-3b3257bfedae.png</url>
      <title>DEV Community: Monika Sadlok</title>
      <link>https://dev.to/msadlok</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/msadlok"/>
    <language>en</language>
    <item>
      <title>I Built a 25-Agent Polish Parliament That Drafts Bills With Real Legal Citations</title>
      <dc:creator>Monika Sadlok</dc:creator>
      <pubDate>Sat, 30 May 2026 21:59:57 +0000</pubDate>
      <link>https://dev.to/msadlok/i-built-a-25-agent-polish-parliament-that-drafts-bills-with-real-legal-citations-45h7</link>
      <guid>https://dev.to/msadlok/i-built-a-25-agent-polish-parliament-that-drafts-bills-with-real-legal-citations-45h7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hermes-agent-2026-05-15"&gt;Hermes Agent Challenge&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;TL;DR&lt;/strong&gt; — Type a one-line bill topic. Twenty-five Hermes agents (1 Speaker, 19 ministries, 5 parties) run a full Polish legislative session in 2 minutes. Vote tally, social impact, party tweets — and a side-by-side &lt;strong&gt;"current law vs proposed amendment"&lt;/strong&gt; with every clause cited to a real statute. Built on &lt;code&gt;delegate_task&lt;/code&gt; for parallel ministry consultation.&lt;/p&gt;
&lt;/blockquote&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%2Frvg21x66976lqdejahlm.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%2Frvg21x66976lqdejahlm.png" alt="Virtual Parliament — live chamber view with the four-day work week bill passing 248:212, AI thought process panel, vote tally and ministry analyses"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;Live:&lt;/strong&gt; &lt;a href="https://web-production-53027.up.railway.app/" rel="noopener noreferrer"&gt;https://web-production-53027.up.railway.app/&lt;/a&gt;&lt;br&gt;
🎥 &lt;strong&gt;Walkthrough:&lt;/strong&gt; &lt;a href="https://www.loom.com/share/b0201270f67745baaa2945c931058b8c" rel="noopener noreferrer"&gt;https://www.loom.com/share/b0201270f67745baaa2945c931058b8c&lt;/a&gt;&lt;br&gt;
📦 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/monsad/ai-politics" rel="noopener noreferrer"&gt;https://github.com/monsad/ai-politics&lt;/a&gt; (MIT)&lt;/p&gt;


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

&lt;p&gt;Watch a politician debate a new tax law on TV. They argue whether it's fair, whether it'll work, whether the other side is lying. &lt;strong&gt;Nobody ever shows you the diff&lt;/strong&gt; — &lt;em&gt;which paragraph of which statute&lt;/em&gt; actually changes, and from what to what. The conversation is theatre on top of an invisible legal document.&lt;/p&gt;

&lt;p&gt;So I built the theatre AND the legal document.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Virtual Parliament&lt;/strong&gt; is a multi-agent simulation of the Polish Sejm. You type something like &lt;em&gt;"four-day work week"&lt;/em&gt; or &lt;em&gt;"flat income tax"&lt;/em&gt;, and 25 Hermes agents run a full legislative session:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;🎯 Marszałek (Speaker) — the orchestrator.&lt;/strong&gt; Classifies the topic. Picks 2–3 ministries via &lt;code&gt;delegate_task&lt;/code&gt; &lt;em&gt;in parallel&lt;/em&gt;. Reads their findings. Routes the bill to a party debate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🏛️ 19 ministry experts&lt;/strong&gt; — Finance, Climate, Labour &amp;amp; Social Policy, Justice, … Each returns a structured analysis: &lt;em&gt;legal finding · budget impact · top 3 risks · recommendation&lt;/em&gt;. Every claim cites a real statute via PageIndex RAG.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;🗳️ 5 party agents&lt;/strong&gt; — CR, NC, AC, Liberty Front, SD. Each one carries the real party's seat count (157, 194, 65, 18, 26 — totalling 460), policy positions and rhetorical style. First reading. Second reading with rebuttals.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📊 Vote&lt;/strong&gt; — weighted by seats. &amp;gt;230 passes.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;📜 Draft bill&lt;/strong&gt; — produced with explicit &lt;em&gt;"Article 129 §1 of the Labour Code **is amended to read&lt;/em&gt;&lt;em&gt;…"&lt;/em&gt; diffs against current law.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The frontend surfaces the diff as a &lt;strong&gt;Current law vs proposed change&lt;/strong&gt; panel. Left column: what's in force today, quoted from the statute. Right column: what the AI just proposed. A non-lawyer can finally see the actual edit.&lt;/p&gt;

&lt;p&gt;This is the part of legislation that's normally invisible. &lt;strong&gt;The whole point is to make it visible.&lt;/strong&gt;&lt;/p&gt;


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

&lt;p&gt;🎥 &lt;strong&gt;Video walkthrough (~2 min):&lt;/strong&gt; &lt;a href="https://www.loom.com/share/b0201270f67745baaa2945c931058b8c" rel="noopener noreferrer"&gt;https://www.loom.com/share/b0201270f67745baaa2945c931058b8c&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;🌐 &lt;strong&gt;Live URL:&lt;/strong&gt; &lt;a href="https://web-production-53027.up.railway.app/" rel="noopener noreferrer"&gt;https://web-production-53027.up.railway.app/&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;Two buttons:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;▶ Open session&lt;/strong&gt; — fresh AI-generated debate, ~2–3 minutes. Costs ~$0.04 in &lt;code&gt;google/gemini-3.1-flash-lite&lt;/code&gt; calls.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;⚡ Demo&lt;/strong&gt; — instant replay of a cached transcript, ~25 s. Useful when you just want to see the UI without burning credits.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Try &lt;strong&gt;"four-day work week"&lt;/strong&gt; (Demo) — it's the demo fixture I ship in the Docker image. SD and Liberty Front vote on opposite sides (pro-labour vs free-market), which is the political-coherence check I built into the acceptance tests.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Local one-command run:&lt;/strong&gt;&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/monsad/ai-politics &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; &lt;span class="nb"&gt;cd &lt;/span&gt;ai-politics &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; make setup &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; parliament &lt;span class="s2"&gt;"four-day work week"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;






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

&lt;p&gt;📦 &lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/monsad/ai-politics" rel="noopener noreferrer"&gt;https://github.com/monsad/ai-politics&lt;/a&gt; (MIT)&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;skills/                         # 25 Hermes Agent skills, validated by skills-ref
  marszalek-sejmu/              # the orchestrator — owns the bill-drafting template
  ministry-finansow/            # 19 ministry experts (Finance, Health, Climate, ...)
  ...
  party-cr/                     # 5 party agents (CR, NC, AC, Liberty Front, SD)
parliament/
  session.py                    # subprocess launcher around `hermes chat -s &amp;lt;skill&amp;gt;`
  transcript_parser.py          # splits orchestrator stdout into per-speaker utterances
  citation_validator.py         # every [node:...] must resolve back to a real statute
  api.py                        # FastAPI: POST /sessions, polling SSE /stream/{id}
  cli.py                        # `parliament "&amp;lt;topic&amp;gt;"` (typer)
web/                            # Next.js 16 static export, served by FastAPI
deploy/                         # Dockerfile entrypoint + Hermes config + demo fixture
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  My Tech Stack
&lt;/h3&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;Tech&lt;/th&gt;
&lt;th&gt;Notes&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Agent framework&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;hermes-agent 0.14.0&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;the load-bearing piece — &lt;code&gt;pip install hermes-agent==0.14.0&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Skills spec&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Anthropic Agent Skills + &lt;code&gt;skills-ref@0.1.5&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;25 skills, lowercase-hyphen, validated in CI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;RAG&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;PageIndex Cloud&lt;/strong&gt; via MCP&lt;/td&gt;
&lt;td&gt;vectorless retrieval over Polish Constitution + ~50 statutes; every citation traces to a real document&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Models&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;code&gt;google/gemini-3.1-flash-lite&lt;/code&gt; via OpenRouter&lt;/td&gt;
&lt;td&gt;~$0.04 per full session, fast enough for live demo&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Orchestrator&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Python 3.11 + FastAPI + uvicorn&lt;/td&gt;
&lt;td&gt;subprocess launcher around &lt;code&gt;hermes chat&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Stream&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;sse-starlette + polling SQLite&lt;/td&gt;
&lt;td&gt;per-speaker utterances pushed as &lt;code&gt;event: utterance&lt;/code&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Frontend&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Next.js 16 (App Router, static export) + Tailwind&lt;/td&gt;
&lt;td&gt;served from &lt;code&gt;/app/*&lt;/code&gt; by the same FastAPI&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;Deploy&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;Railway (single Docker container)&lt;/td&gt;
&lt;td&gt;public HTTPS, ~$5/month&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;




&lt;h2&gt;
  
  
  How I Used Hermes Agent
&lt;/h2&gt;

&lt;p&gt;There's one Hermes property the whole project is built on:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;&lt;code&gt;delegate_task&lt;/code&gt; lets a parent skill fan out to N child skills in parallel as a single tool call.&lt;/strong&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Without that, this project isn't tractable. With it, the entire 25-agent pipeline is &lt;strong&gt;24 LLM calls in a tight DAG&lt;/strong&gt;, runs in 2 minutes, and the orchestrator never has to manage thread pools or async gathers itself.&lt;/p&gt;

&lt;p&gt;Here's the shape:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;                  ┌─────────────────────────────┐
                  │  marszalek-sejmu (skill)    │
                  │  Topic → ministry selection │
                  └──────────────┬──────────────┘
                                 │ delegate_task(tasks=[...])  ← Hermes batch mode
                ┌────────────────┼────────────────┐
                ▼                ▼                ▼
       ┌────────────────┐ ┌────────────────┐ ┌────────────────┐
       │ ministry-      │ │ ministry-      │ │ ministry-      │
       │ finansow       │ │ klimatu        │ │ rodziny-pracy  │
       └────────┬───────┘ └────────┬───────┘ └────────┬───────┘
                │  PageIndex RAG   │   (cite real statutes)
                └────────┬─────────┴─────────┬───────┘
                         ▼                   ▼
                  Synthesized findings → Marszałek
                         │
                         ▼   5 × party debate, ×2 readings
                  ┌──────────────┐
                  │ CR  NC  AC  │
                  │ LF  SD       │
                  └──────┬───────┘
                         ▼
                  Seat-weighted vote → Draft bill
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h3&gt;
  
  
  Why &lt;code&gt;delegate_task&lt;/code&gt; was the right primitive
&lt;/h3&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ministries are independent.&lt;/strong&gt; Finance doesn't need to know what Climate said before doing its own analysis. They both run on the same input bill topic, return findings, get merged by the orchestrator. Classic embarrassingly parallel.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hermes already handles the thread pool.&lt;/strong&gt; Batch mode uses &lt;code&gt;ThreadPoolExecutor&lt;/code&gt; to spawn &lt;code&gt;AIAgent&lt;/code&gt; children. I don't have to mix asyncio with hermes-agent's threaded subagents — a known foot-gun if you roll your own.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context isolation is free.&lt;/strong&gt; Each ministry gets its own skill prompt with its own toolsets (&lt;code&gt;pageindex-rag&lt;/code&gt;). The Marszałek doesn't pollute their context.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Approval and audit are centralized.&lt;/strong&gt; When PageIndex is called from a ministry, it goes through Hermes' tool registry. I get the audit trail for free.&lt;/li&gt;
&lt;/ul&gt;

&lt;h3&gt;
  
  
  The other Hermes pieces that mattered
&lt;/h3&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Skills as the unit of expertise.&lt;/strong&gt; Every agent is one &lt;code&gt;SKILL.md&lt;/code&gt;. The Marszałek has the bill-drafting template (&lt;code&gt;assets/bill-draft-template.md&lt;/code&gt;). The parties have their actual policy positions. None of this fits in one big system prompt — but as 25 separate skills, it's maintainable. I can rewrite SD's economic stance without touching Liberty Front.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;MCP toolsets for retrieval.&lt;/strong&gt; Every skill that cites Polish law declares &lt;code&gt;toolsets: ["pageindex-rag"]&lt;/code&gt; and gets retrieval for free. Zero Python integration code. The PageIndex MCP server is one config-yaml entry.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Subprocess as the integration surface.&lt;/strong&gt; Hermes is a CLI first. The cleanest way to embed it in FastAPI is &lt;code&gt;subprocess.Popen(["hermes", "chat", "-s", skill, "-q", topic, "-Q", "--accept-hooks", "--yolo"])&lt;/code&gt;. My &lt;code&gt;session.py&lt;/code&gt; is essentially that subprocess launcher plus a stdout parser that splits the result into per-speaker utterances for SSE streaming.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;&lt;strong&gt;Bake-time config for the container.&lt;/strong&gt; For Railway, the Dockerfile copies &lt;code&gt;hermes-config.yaml&lt;/code&gt; to &lt;code&gt;/root/.hermes/config.yaml&lt;/code&gt; and &lt;code&gt;skills/*&lt;/code&gt; to &lt;code&gt;/root/.hermes/skills/&lt;/code&gt;. An entrypoint script materializes &lt;code&gt;OPENROUTER_API_KEY&lt;/code&gt; into &lt;code&gt;~/.hermes/.env&lt;/code&gt; at boot. Crucially: &lt;strong&gt;&lt;code&gt;disabled_toolsets: [browser, computer-use, voice, terminal-modal]&lt;/code&gt;&lt;/strong&gt; — otherwise Hermes hangs at startup looking for a Chromium binary that isn't in &lt;code&gt;python:3.11-slim&lt;/code&gt;. I only found that via a &lt;code&gt;/diag&lt;/code&gt; endpoint I added to introspect the running container.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

&lt;h3&gt;
  
  
  What this combination unlocks
&lt;/h3&gt;

&lt;p&gt;If I had to write the parallel fan-out + tool registry + skill loader by hand, I'd still be debugging deadlocks instead of arguing with my own bill drafts.&lt;/p&gt;

&lt;p&gt;Hermes let me spend my time on &lt;strong&gt;the simulation design&lt;/strong&gt; (how does a Marszałek pick ministries? what does each party's house style sound like? how do you parse "Article 129 §1 is amended to read…" out of free-form markdown?) and &lt;strong&gt;the legal-diff UX&lt;/strong&gt; (the Current law vs proposed change panel) — not on the orchestration framework.&lt;/p&gt;

&lt;p&gt;That's the right division of labour for a 5-day contest project, and frankly for most agent projects.&lt;/p&gt;




&lt;h2&gt;
  
  
  What surprised me
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Hermes models matter less than you'd think.&lt;/strong&gt; Most of the quality comes from the skill prompts. Swapping &lt;code&gt;gemini-flash-lite&lt;/code&gt; ↔ &lt;code&gt;llama-3.3-70b&lt;/code&gt; changes vocabulary, barely changes the structure of the debate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The frontend is where civic value lives.&lt;/strong&gt; The pipeline produces a 40 KB markdown blob. Useless to a non-lawyer. The UI panel showing &lt;em&gt;"&lt;code&gt;Czas pracy nie może przekraczać 8 godzin na dobę i przeciętnie 40 godzin…&lt;/code&gt;"&lt;/em&gt; on the left and the proposed &lt;em&gt;"…32 godzin w przeciętnie czterodniowym…"&lt;/em&gt; on the right is what makes this a tool instead of a transcript.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free OpenRouter tiers are rate-limited into uselessness during contest week.&lt;/strong&gt; Plan for $5 of paid model credit, or bake a demo fixture into the image. I shipped both.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;🇵🇱 Built in Warsaw. MIT-licensed. Educational simulation only — no real Members of Parliament are represented, no hate speech is produced, and a disclaimer is emitted at the top and bottom of every session.&lt;/p&gt;

</description>
      <category>hermesagentchallenge</category>
      <category>devchallenge</category>
      <category>agents</category>
    </item>
    <item>
      <title>MedScan Assistant — AI medication label reader for seniors, powered by Gemma 4</title>
      <dc:creator>Monika Sadlok</dc:creator>
      <pubDate>Thu, 14 May 2026 16:47:08 +0000</pubDate>
      <link>https://dev.to/msadlok/medscan-assistant-ai-medication-label-reader-for-seniors-powered-by-gemma-4-3kc6</link>
      <guid>https://dev.to/msadlok/medscan-assistant-ai-medication-label-reader-for-seniors-powered-by-gemma-4-3kc6</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/google-gemma-2026-05-06"&gt;Gemma 4 Challenge: Build with Gemma 4&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;MedScan Assistant helps elderly and visually impaired people understand their medication labels. Half of all medication errors happen because patients misread or misunderstand the label — especially people with low vision or cognitive decline.&lt;/p&gt;

&lt;p&gt;Point your phone at any medication label → Gemma 4 reads and interprets it → the app reads the result aloud in plain language.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Two markets supported:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;🇺🇸 US Market — FDA Drug Facts panels, OTC/Rx labels&lt;/li&gt;
&lt;li&gt;🇵🇱 Polish Market — EU-format labels, Polish TTS&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;Key features:&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;📷 Photo scan or manual text input&lt;/li&gt;
&lt;li&gt;🔊 Automatic voice readout (Web Speech API, language-matched)&lt;/li&gt;
&lt;li&gt;📅 Color-coded expiry status (green / amber / red)&lt;/li&gt;
&lt;li&gt;⚠️ Warnings and drug interactions&lt;/li&gt;
&lt;li&gt;🏥 "See a doctor if..." — symptoms requiring medical attention&lt;/li&gt;
&lt;li&gt;♿ WCAG 2.1 AA accessible (large buttons, aria-live, keyboard nav)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;strong&gt;GitHub:&lt;/strong&gt; &lt;a href="https://github.com/monsad/medscan-assistant" rel="noopener noreferrer"&gt;https://github.com/monsad/medscan-assistant&lt;/a&gt;&lt;br&gt;
&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://monsad.github.io/medscan-assistant" rel="noopener noreferrer"&gt;https://monsad.github.io/medscan-assistant&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;
  
  
  How I Used Gemma 4
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Model: &lt;code&gt;google/gemma-4-31b-it:free&lt;/code&gt; via OpenRouter&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;I chose Gemma 4 31B Dense for three specific reasons:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;1. Native multimodal input&lt;/strong&gt;&lt;br&gt;
Gemma 4 processes label photos directly — no separate OCR step. Medication labels have irregular layouts, rotated text, and small fonts. The model understands the full visual context, not just extracted characters.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;2. Structured medical reasoning&lt;/strong&gt;&lt;br&gt;
The model converts complex pharmaceutical terminology into plain-language JSON. &lt;code&gt;Acidum acetylsalicylicum 500mg&lt;/code&gt; becomes "Aspirin — pain reliever and fever reducer." I prompt it to return a strict JSON schema with fields for &lt;code&gt;brand_name&lt;/code&gt;, &lt;code&gt;directions&lt;/code&gt;, &lt;code&gt;expiry_status&lt;/code&gt;, &lt;code&gt;warnings&lt;/code&gt;, &lt;code&gt;see_doctor&lt;/code&gt; (symptoms requiring a doctor visit), and &lt;code&gt;voice_text&lt;/code&gt; for TTS readout.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;3. Multilingual in a single model&lt;/strong&gt;&lt;br&gt;
The same model handles both FDA Drug Facts panels (English) and Polish EU-format labels — switching via market-aware system prompts. No translation APIs, no separate models.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Why 31B Dense over E4B:&lt;/strong&gt;&lt;br&gt;
For a patient safety application, accuracy matters more than latency. The 31B Dense model gives noticeably better results on complex dosing instructions, drug interaction identification, and expiry date parsing across formats (&lt;code&gt;EXP 04/26&lt;/code&gt; vs &lt;code&gt;04/2026&lt;/code&gt; vs &lt;code&gt;APR 2026&lt;/code&gt;).&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight javascript"&gt;&lt;code&gt;&lt;span class="kd"&gt;const&lt;/span&gt; &lt;span class="nx"&gt;MARKETS&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;
  &lt;span class="na"&gt;us&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;You are a medication assistant for Americans...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="na"&gt;ttsLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;en-US&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt; &lt;span class="p"&gt;},&lt;/span&gt;
  &lt;span class="na"&gt;pl&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="na"&gt;systemPrompt&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;Jesteś aptecznym asystentem dla seniorów...&lt;/span&gt;&lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;   &lt;span class="na"&gt;ttsLang&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="dl"&gt;"&lt;/span&gt;&lt;span class="s2"&gt;pl-PL&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="c1"&gt;// One model. Two prompts. Two languages.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



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

&lt;p&gt;The hardest part was making Gemma 4 reliable enough for medical use. I use &lt;code&gt;temperature: 0.2&lt;/code&gt; and a strict JSON schema. The model occasionally wraps JSON in backticks, so &lt;code&gt;parseGemmaResponse()&lt;/code&gt; strips those automatically.&lt;/p&gt;

&lt;p&gt;Adding a dedicated &lt;code&gt;see_doctor&lt;/code&gt; field — symptoms requiring medical attention — was the most impactful UX improvement. Users shouldn't have to parse warning text to figure out when something is serious. Gemma 4 identifies these situations reliably when explicitly prompted.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Test suite:&lt;/strong&gt; 49 unit tests, zero npm dependencies:&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/monsad/medscan-assistant
&lt;span class="nb"&gt;cd &lt;/span&gt;medscan-assistant
node tests/app.test.js
&lt;span class="c"&gt;# ✓ 49/49 passing&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



</description>
      <category>devchallenge</category>
      <category>gemma4</category>
      <category>gemma4challenge</category>
      <category>a11y</category>
    </item>
  </channel>
</rss>
