<?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: Jeffrey Turov</title>
    <description>The latest articles on DEV Community by Jeffrey Turov (@jeffreyturov).</description>
    <link>https://dev.to/jeffreyturov</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%2F4050944%2Fabfb8306-248c-4a94-8471-d954f41366c3.png</url>
      <title>DEV Community: Jeffrey Turov</title>
      <link>https://dev.to/jeffreyturov</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/jeffreyturov"/>
    <language>en</language>
    <item>
      <title>Memoria: Keeping Family Voices Alive with Local Whisper + Gemma</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Tue, 27 Oct 2026 08:17:49 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/memoria-keeping-family-voices-alive-with-local-whisper-gemma-eap</link>
      <guid>https://dev.to/jeffreyturov/memoria-keeping-family-voices-alive-with-local-whisper-gemma-eap</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week4-2026-10-26"&gt;Hacktoberfest Open-Source AI Challenge Week 4: Ghost in the Machine&lt;/a&gt;.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Memoria&lt;/strong&gt; is a local oral-history archivist. You give it a family voice memo. It returns a structured keepsake containing:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;the complete transcript with timestamps;&lt;/li&gt;
&lt;li&gt;the people, places, and time references explicitly mentioned;&lt;/li&gt;
&lt;li&gt;themes and a short summary;&lt;/li&gt;
&lt;li&gt;verbatim quotes checked against the transcript;&lt;/li&gt;
&lt;li&gt;three thoughtful questions to ask during the next conversation.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The result is exported as JSON and Markdown. There is also a small web interface with a playable demo recording.&lt;/p&gt;

&lt;p&gt;I built it for the &lt;strong&gt;Ghost in the Machine&lt;/strong&gt; theme as a way to honor the past without surrendering it to a cloud service. A recording of a grandparent is not ordinary prompt data. It can contain names, addresses, health details, and stories that a family never intended to publish.&lt;/p&gt;

&lt;p&gt;Memoria runs both speech recognition and language understanding locally. The recording and transcript never leave the machine for inference.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://actors-pendant-packard-outlet.trycloudflare.com" rel="noopener noreferrer"&gt;Open the live Memoria demo&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Press &lt;strong&gt;“Archive this memory”&lt;/strong&gt; to run the complete pipeline on local CPU models. Please allow about a minute.&lt;/p&gt;

&lt;p&gt;The bundled 58-second story is &lt;strong&gt;synthetic and fictional&lt;/strong&gt;, narrated with TTS specifically so that no private family recording is exposed. The processing is not mocked: the button runs Whisper and Gemma against that audio. The demo tunnel is temporary; the repository contains everything needed to run it locally.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;&lt;a href="https://github.com/jeffreyturov-dev/memoria-local" rel="noopener noreferrer"&gt;github.com/jeffreyturov-dev/memoria-local&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The repository includes the CLI, FastAPI demo, synthetic demo audio, generated example output, tests, and setup instructions.&lt;/p&gt;

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

&lt;p&gt;The pipeline is deliberately small and inspectable:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;voice memo
    │
    ▼
faster-whisper / Whisper small (CPU int8)
    │  transcript + segment timestamps
    ▼
Gemma 3 4B IT QAT through llama.cpp
    │  strict JSON memory card
    ▼
deterministic quote validation
    │
    ├── JSON archive
    ├── Markdown keepsake
    └── local web result
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Whisper handles the voice. Gemma receives only the resulting transcript and must produce eight fields: title, summary, people, places, time references, themes, quotes, and follow-up questions. Temperature is zero, missing evidence becomes an empty array, and the prompt explicitly forbids adding facts.&lt;/p&gt;

&lt;p&gt;A small model can still produce a quotation that sounds plausible but does not occur in the recording. For an archive, that is unacceptable. I therefore added a deterministic guard after generation:&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;verify_quotes&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;normalized_transcript&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;normalize_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;transcript&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;
    &lt;span class="n"&gt;verified&lt;/span&gt; &lt;span class="o"&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;quote&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;card&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;quotes&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="nf"&gt;isinstance&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&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="ow"&gt;and&lt;/span&gt; &lt;span class="nf"&gt;normalize_words&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;normalized_transcript&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
            &lt;span class="n"&gt;verified&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;append&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;quote&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;strip&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;verified&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Only whitespace and letter case are normalized. If the passage is absent, Memoria drops it rather than presenting it as the speaker's words. The original transcript always remains beside the generated card, so a family can review the source rather than trusting the summary blindly.&lt;/p&gt;

&lt;p&gt;The demo uses FastAPI, a concurrency lock, and a 25 MB limit for local audio uploads. No API key or account is required.&lt;/p&gt;

&lt;h2&gt;
  
  
  The Moment It Landed
&lt;/h2&gt;

&lt;p&gt;The fictional demo story describes a grandfather's radio repair shop, yellow marigolds, cinnamon tea, and a rebuilt red bicycle called &lt;em&gt;La Valiente&lt;/em&gt;. Memoria recovered those details and preserved this passage verbatim:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A broken thing is only a story waiting for patient hands.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;It then suggested questions such as asking what the radios sounded like and why the bicycle had that name. That was the moment the project stopped feeling like a transcription wrapper: the useful output is not just what was said, but a gentle prompt to record the next chapter while you still can.&lt;/p&gt;

&lt;p&gt;The recording is synthetic, and I have kept that distinction visible in the demo and repository. A real family archive should start with consent and human review, not an invented claim of authenticity.&lt;/p&gt;

&lt;h2&gt;
  
  
  Verified Results
&lt;/h2&gt;

&lt;p&gt;I ran the project rather than stopping at a screenshot or a plausible demo response:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Check&lt;/th&gt;
&lt;th&gt;Actual result&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Unit tests&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;6/6 passed&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Demo audio duration&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;57.79 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Full CLI run, audio → archive&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;33.36 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Public web run through the tunnel&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;29.47 seconds&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Whisper language detection&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;English, 0.9993 probability&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Displayed quotes&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;3/3 found in the transcript&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;For the offline-mode run, I forced &lt;code&gt;HTTP_PROXY&lt;/code&gt;, &lt;code&gt;HTTPS_PROXY&lt;/code&gt;, and &lt;code&gt;ALL_PROXY&lt;/code&gt; to a dead localhost address, with only localhost excluded. The complete cached-model pipeline still produced the transcript and memory card. The generated example is committed under &lt;code&gt;demo/output/&lt;/code&gt; for inspection.&lt;/p&gt;

&lt;p&gt;The unit tests cover fenced JSON parsing, missing-field rejection, hallucinated-quote rejection, whitespace normalization, human-readable people fields, and preservation of the original transcript.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why Open Innovation Matters
&lt;/h2&gt;

&lt;p&gt;Open weights and open-source inference are not decorative details here; they make the product possible.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Privacy:&lt;/strong&gt; a closed transcription or LLM API would send intimate family material to infrastructure the family does not control. Memoria performs both stages locally.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Longevity:&lt;/strong&gt; an oral-history archive should not become inaccessible because a vendor retired a model endpoint. The code is MIT-licensed, the archive is plain JSON and Markdown, and the inference components can be replaced.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Offline use:&lt;/strong&gt; after the models are downloaded, the pipeline works without cloud AI access. That makes it usable in the places where families actually sit down together, not only where connectivity and a subscription are available.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Zero marginal inference bill:&lt;/strong&gt; there is no per-minute or per-token charge for a longer conversation. The trade-off is CPU latency and the need to review a small model's work.&lt;/p&gt;

&lt;h2&gt;
  
  
  Limitations
&lt;/h2&gt;

&lt;p&gt;Memoria does not independently fact-check a summary, and a high language-detection score does not guarantee a perfect transcript. Names can be misheard; in the demo, the source script says “Mateo” while Whisper transcribes “Matteo,” and the card faithfully follows that transcript. This is exactly why the source text and human review remain essential. Only record and archive someone else's story with their consent.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma:&lt;/strong&gt; Gemma 3 4B IT QAT performs the local structured memory extraction through llama.cpp.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Overall:&lt;/strong&gt; a complete, tested, locally runnable oral-history tool with a real public demo.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>Slow and Steady: An Agent That Runs My Apify Side Business While I Sleep</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Sat, 24 Oct 2026 08:12:57 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/slow-and-steady-an-agent-that-runs-my-apify-side-business-while-i-sleep-2j5p</link>
      <guid>https://dev.to/jeffreyturov/slow-and-steady-an-agent-that-runs-my-apify-side-business-while-i-sleep-2j5p</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;I run 13 public web-scraping Actors on the Apify Store as a side business. Keeping up with the platform — blog announcements, Store trends, pricing-compliance deadlines, new monetization features — is exactly the kind of chore I kept putting off. So I built &lt;strong&gt;Phoenix&lt;/strong&gt;: an autonomous agent on a cron schedule that does the whole daily platform watch for me and delivers an 8-line report while I'm still asleep.&lt;/p&gt;

&lt;p&gt;Every morning, on its own, it:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;scans the Apify blog RSS for anything published in the last 48 hours&lt;/li&gt;
&lt;li&gt;pulls the Apify Store top-10 to spot demand trends&lt;/li&gt;
&lt;li&gt;audits my Actors' pricing configuration (pay-per-event migration compliance)&lt;/li&gt;
&lt;li&gt;checks my saved tasks against the Store's newest SEO surface, published task examples&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;executes whatever is deliverable end-to-end&lt;/strong&gt; — not just reading, but doing: configs, fixes, submissions — then logs done-vs-read in a dated markdown file&lt;/li&gt;
&lt;li&gt;watches dev.to challenges with cash prizes and drafts submissions (yes, the research for this very post was done that way)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The sloth philosophy, applied: conserve energy, let the machine do the rounds.&lt;/p&gt;

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

&lt;p&gt;Real, unedited output from this morning's run (October 24, 2026):&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Blog scan:&lt;/strong&gt; Apify shipped bulk task-example publishing via API on Oct 22 — their case study claims organic traffic to an Actor page doubled after publishing examples.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Audit:&lt;/strong&gt; I have 11 saved tasks across my Actors and &lt;strong&gt;zero published task examples&lt;/strong&gt;. Gap identified, publish payloads prepared.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Action attempted:&lt;/strong&gt; the agent tried the publish API call… and got &lt;code&gt;403 platform-feature-disabled&lt;/code&gt;. Instead of silently failing, it traced the cause: my account had burned through its &lt;strong&gt;$5 monthly hard limit&lt;/strong&gt; — 0.58 GB of residential proxy (~$4.60) consumed by one overeager scraper run. It flagged it for my review, with the numbers.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I learned about a new Store SEO feature &lt;em&gt;and&lt;/em&gt; a billing problem before my first coffee, with zero clicks.&lt;/p&gt;

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

&lt;p&gt;The harness is &lt;a href="https://github.com/NousResearch/hermes-agent" rel="noopener noreferrer"&gt;Hermes Agent&lt;/a&gt; — an open-source agent runtime by Nous Research (&lt;a href="https://hermes-agent.nousresearch.com/docs" rel="noopener noreferrer"&gt;docs&lt;/a&gt;). The daily watch is a cron job that hands the agent a task file; the agent loops: scan → reason → act → log. The platform knowledge lives in plain-markdown "skill" files, and the actual checks are small scripts the agent writes and runs itself, like this abridged one from today:&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;json&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;

&lt;span class="k"&gt;def&lt;/span&gt; &lt;span class="nf"&gt;getj&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
    &lt;span class="n"&gt;req&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nc"&gt;Request&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;url&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;headers&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-Agent&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;phoenix-cron&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;})&lt;/span&gt;
    &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;urllib&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="n"&gt;request&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;urlopen&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;req&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;30&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;r&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;json&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nf"&gt;load&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;r&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt;

&lt;span class="c1"&gt;# Which of my Actors are still PPE-compliant?
&lt;/span&gt;&lt;span class="n"&gt;acts&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getj&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;https://api.apify.com/v2/acts?my=1&amp;amp;token=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;&amp;amp;limit=50&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;it&lt;/span&gt; &lt;span class="ow"&gt;in&lt;/span&gt; &lt;span class="n"&gt;acts&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;data&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;items&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;]:&lt;/span&gt;
    &lt;span class="n"&gt;det&lt;/span&gt; &lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="nf"&gt;getj&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;https://api.apify.com/v2/acts/&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;it&lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="s"&gt;id&lt;/span&gt;&lt;span class="sh"&gt;'&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="si"&gt;}&lt;/span&gt;&lt;span class="s"&gt;?token=&lt;/span&gt;&lt;span class="si"&gt;{&lt;/span&gt;&lt;span class="n"&gt;TOKEN&lt;/span&gt;&lt;span class="si"&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;data&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="n"&gt;det&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;isPublic&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;):&lt;/span&gt;
        &lt;span class="nf"&gt;print&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;&lt;span class="n"&gt;det&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;-&amp;gt;&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="n"&gt;det&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;pricingInfos&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;)&lt;/span&gt; &lt;span class="ow"&gt;or&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;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;pricingModel&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;p&gt;No framework lock-in: the harness calls shell, Python, and a real browser over CDP. That's the whole stack.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Orchestration:&lt;/strong&gt; Hermes Agent, open-source. The agent reads its instructions, picks tools, writes its own helper scripts, and iterates until the task verifies — a real agent loop, not a Zapier-style flow.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Scheduling:&lt;/strong&gt; a boring, glorious cron line. Slow and steady, every day at the same hour.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Knowledge:&lt;/strong&gt; markdown skill files (actor doctrine, preflight checks, the publishing API contract). When Apify changed task-example limits from 50 to 10 per Actor, I edited a text file — not a support ticket.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model-agnostic:&lt;/strong&gt; the harness doesn't care which LLM drives it. You can point the same setup at an open-weight model and the loop still works; the automation survives the swap.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;Three concrete things a closed automation platform wouldn't give me:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;My credentials never leave my machine.&lt;/strong&gt; The agent touches Gmail, Apify, and Stripe tokens daily. They live in local files on my own server in Luxembourg — not in a vendor's cloud, not in someone's "workspace".&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Everything is inspectable and forkable.&lt;/strong&gt; Skills are text. Scripts are text. Logs are text. When something breaks at 6 AM, I can diff it, fix it, and the fix is permanent — not a black-box retry policy I can't see.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No platform risk on the orchestration layer.&lt;/strong&gt; Closed assistants lock you into their model, their pricing, their roadmap. An open harness means my morning routine keeps working even if I switch models tomorrow. Open weights crossing the quality bar is a when, not an if — and I'm already wired for it.&lt;/li&gt;
&lt;/ol&gt;

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

&lt;p&gt;About &lt;strong&gt;45 minutes of daily platform-watch compressed into reading 8 lines&lt;/strong&gt;. This week alone the agent prepared task-example configs for the Store's new SEO surface, verified pay-per-event pricing on all 13 public Actors ahead of the migration deadline, and caught a hard-limit billing issue — all while I did literally nothing.&lt;/p&gt;

&lt;p&gt;Sloths had it figured out. Build the thing once, let it do the rounds forever.&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>Sloth Digest 🦥 — a local Gemma 3 agent that does my inbox triage so I don't have to</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Tue, 20 Oct 2026 08:10:45 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/sloth-digest-a-local-gemma-3-agent-that-does-my-inbox-triage-so-i-dont-have-to-9gk</link>
      <guid>https://dev.to/jeffreyturov/sloth-digest-a-local-gemma-3-agent-that-does-my-inbox-triage-so-i-dont-have-to-9gk</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week3-2026-10-19"&gt;Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Sloth Digest&lt;/strong&gt; is a local AI agent that reads my unread Gmail overnight and hands me a morning digest, so I never have to open a cold inbox again.&lt;/p&gt;

&lt;p&gt;Every email gets triaged by an &lt;strong&gt;open-weight model running entirely on my own machine&lt;/strong&gt; (Gemma 3 4B IT, QAT-quantized, served by llama.cpp on plain CPU) into three buckets:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Needs you&lt;/strong&gt; — reply, pay, confirm, decide — each with a suggested one-line action&lt;/li&gt;
&lt;li&gt;👀 &lt;strong&gt;Worth 10 seconds&lt;/strong&gt; — FYI, no action needed&lt;/li&gt;
&lt;li&gt;😴 &lt;strong&gt;Skip safely&lt;/strong&gt; — promos, newsletters, notification noise&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Who gets to be lazier? Me, every single morning. Instead of 15–30 minutes of scanning subject lines and context-switching, I read one short markdown page and only touch the 3–4 emails that actually need a human.&lt;/p&gt;

&lt;p&gt;Here's real output from the bundled sample inbox:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# 🦥 Sloth Digest — morning triage&lt;/span&gt;

&lt;span class="gu"&gt;## ⚡ Needs you (4)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Energia Lux**&lt;/span&gt; — Your October invoice — €84.12 due Oct 27
  Electricity bill of €84.12 is due soon.
  → Pay the invoice before October 27th.
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**Marco Silva**&lt;/span&gt; — Re: Thursday moved to 15:00?
  Plumber availability needs confirmation for Thursday.
  → Confirm 15:00 or propose an alternative time.
...

&lt;span class="gu"&gt;## 😴 Skip safely (1)&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; &lt;span class="gs"&gt;**ShopMega**&lt;/span&gt; — 🔥 70% OFF ends tonight — don't miss out!
  Promotional email about a limited-time sale.

6 emails triaged locally in 14s (~12 min of manual triage saved).
Nothing left this machine.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://vid-exploring-omissions-warren.trycloudflare.com" rel="noopener noreferrer"&gt;https://vid-exploring-omissions-warren.trycloudflare.com&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It shows the sample inbox (6 fake emails — bills, a friend asking for a confirmation, a flash sale, a delivery notice, a medical appointment reminder) and runs the triage live on the local Gemma model when you hit the button. ~14 seconds on CPU, no cloud API involved anywhere.&lt;/p&gt;

&lt;p&gt;You can reproduce the whole thing offline:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;./llama-server &lt;span class="nt"&gt;-m&lt;/span&gt; gemma-3-4b-it-qat-Q4_0.gguf &lt;span class="nt"&gt;--port&lt;/span&gt; 8801
python sloth_digest.py &lt;span class="nt"&gt;--demo&lt;/span&gt;   &lt;span class="c"&gt;# sample inbox, zero credentials needed&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jeffreyturov-dev" rel="noopener noreferrer"&gt;
        jeffreyturov-dev
      &lt;/a&gt; / &lt;a href="https://github.com/jeffreyturov-dev/sloth-digest" rel="noopener noreferrer"&gt;
        sloth-digest
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      Local AI agent that triages your Gmail overnight into a morning digest — Gemma 3 4B, 100% offline, privacy-first
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🦥 Sloth Digest&lt;/h1&gt;
&lt;/div&gt;

&lt;p&gt;A local AI agent that reads your unread Gmail overnight and hands you a
morning digest — so you don't have to.&lt;/p&gt;
&lt;p&gt;Every email is triaged into three buckets by an &lt;strong&gt;open-weight model running
entirely on your own machine&lt;/strong&gt; (Gemma 3 4B IT QAT via llama.cpp):&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;⚡ &lt;strong&gt;Needs you&lt;/strong&gt; — reply, pay, confirm, decide (with a suggested action)&lt;/li&gt;
&lt;li&gt;👀 &lt;strong&gt;Worth 10 seconds&lt;/strong&gt; — FYI, no action&lt;/li&gt;
&lt;li&gt;😴 &lt;strong&gt;Skip safely&lt;/strong&gt; — promos, newsletters, notification noise&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;No email content ever leaves your computer. Works offline. Costs nothing
per run — set it up once and forget it.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why local?&lt;/h2&gt;
&lt;/div&gt;
&lt;p&gt;Your inbox is the most private dataset you own. A closed API means sending
every message — invoices, medical appointments, personal threads — to
someone else's servers. Sloth Digest runs the whole pipeline against a
local OpenAI-compatible endpoint, so the "open" piece &lt;em&gt;is&lt;/em&gt; the product:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: nothing leaves the…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jeffreyturov-dev/sloth-digest" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The agent is ~200 lines of Python stdlib. The only dependencies are llama.cpp (for inference) and, for the optional web UI, FastAPI.&lt;/p&gt;

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

&lt;p&gt;The architecture is deliberately boring — a sloth would approve:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;unread Gmail ──► fetch (Gmail API, strictly read-only)
                   │
                   ▼
        ┌─────────────────────┐
        │ Gemma 3 4B IT (QAT) │  per email: CATEGORY / SUMMARY / ACTION
        │ llama.cpp, CPU      │  strict 3-line verdict, temperature 0
        └─────────────────────┘
                   │
                   ▼
        morning digest (markdown, grouped by urgency)
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Three design choices make a small 4B model reliable enough to trust with triage:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Rigid output contract.&lt;/strong&gt; The model must answer with exactly three lines (&lt;code&gt;CATEGORY&lt;/code&gt; / &lt;code&gt;SUMMARY&lt;/code&gt; / &lt;code&gt;ACTION&lt;/code&gt;) at temperature 0. No essays, no JSON to break.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Graceful degradation.&lt;/strong&gt; Anything unparseable lands in "check manually" instead of being silently misfiled. A triage tool that hides emails is worse than no tool.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Read-only by construction.&lt;/strong&gt; The agent fetches unread mail and prints a digest. It never marks, archives, or deletes anything — the worst it can do is be wrong on a page you skim anyway.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Local inference is not a gimmick here, it's the point: the whole pipeline — fetch, classify, summarize — runs against llama.cpp's OpenAI-compatible endpoint on &lt;code&gt;127.0.0.1&lt;/code&gt;. Pull the network cable and it still works.&lt;/p&gt;

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

&lt;p&gt;Your inbox is probably the most private dataset you own: invoices, medical appointments, family threads, business negotiations. Triage means reading &lt;em&gt;all of it&lt;/em&gt; — and with a closed API, "reading" means shipping every message to someone else's servers, on someone else's terms, under a privacy policy that can change.&lt;/p&gt;

&lt;p&gt;Because Gemma's weights are open and llama.cpp is open source, the equation flips:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy by architecture, not by promise.&lt;/strong&gt; Nothing leaves the machine, so there's nothing to leak, log, or train on.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Works offline.&lt;/strong&gt; Plane, cabin, outage — the digest still runs.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Free forever.&lt;/strong&gt; No per-token bill. I set it up once and it costs me exactly zero per run, forever. That's the sloth economics a closed API can never offer.&lt;/li&gt;
&lt;/ul&gt;

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

&lt;p&gt;I ran it on my real unread inbox this morning: &lt;strong&gt;8 emails triaged in ~20 seconds&lt;/strong&gt;, and the digest correctly separated two things I genuinely needed to act on from the newsletter noise. Manual triage averages ~2 minutes per email once you include the context-switching — so the agent saves me roughly &lt;strong&gt;15 minutes a day, ~90 hours a year&lt;/strong&gt;, for a one-time setup cost and €0 in API fees.&lt;/p&gt;

&lt;p&gt;The cron job runs at 7am. I wake up, read one page, and go back to being lazy. The sloth wins. 🦥&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Gemma 3 4B IT (QAT) is the entire brain of the agent, running locally via llama.cpp.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>I Let an AI Agent Run Parts of My Real Businesses for a Year. Here Are the Receipts.</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Mon, 19 Oct 2026 21:02:13 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-let-an-ai-agent-run-parts-of-my-real-businesses-for-a-year-here-are-the-receipts-4lkn</link>
      <guid>https://dev.to/jeffreyturov/i-let-an-ai-agent-run-parts-of-my-real-businesses-for-a-year-here-are-the-receipts-4lkn</guid>
      <description>&lt;p&gt;One year ago, I opened a terminal on a small home server and gave an AI agent its first real task.&lt;/p&gt;

&lt;p&gt;I'm not a developer. I run a small hotel in Mersch, Luxembourg. I had no team, no budget for an agency, and a long list of things that needed to exist: a booking site, a phone receptionist, payments, a second company, SEO, ads.&lt;/p&gt;

&lt;p&gt;Today, that agent — the same one, still living in the same terminal — runs a real slice of my entrepreneur life. Not demos. Not chat screenshots. Production systems, with real customers and real money.&lt;/p&gt;

&lt;p&gt;This is episode 1 of an 8-part series. Everything I claim here is publicly verifiable, and I've linked the receipts.&lt;/p&gt;

&lt;h2&gt;
  
  
  What "running a business with an AI agent" actually means
&lt;/h2&gt;

&lt;p&gt;Forget the viral stories about AIs asking for donations. Here's the boring, real version:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It answers my hotel's phone.&lt;/strong&gt; A voice AI receptionist picks up real calls, checks availability, quotes our direct rate (10% below the OTAs), and takes bookings. Guests talk to it daily. It announces call recording, because compliance isn't optional.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It filed my second company.&lt;/strong&gt; Statutes, government forms, notary coordination, registry filing. The ministerial authorization for my new company (a SARL-S, the Luxembourg equivalent of an LLC) arrived this month. The AI drafted; I signed. That division of labor matters, and I'll come back to it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It published an MCP server on the official registry.&lt;/strong&gt; Any AI agent in the world can now discover my hotel federation and book real rooms with 0% OTA commission — versus the 15-25% the platforms take. It's live on Smithery and Glama too.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It runs the money plumbing.&lt;/strong&gt; Stripe Connect charges, platform fees, payouts, a revenue dashboard with real processing fees computed per merchant. Manual payouts — because I keep a human hand on money movements. On purpose.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It watches itself.&lt;/strong&gt; Dozens of cron jobs, watchdogs, and health checks. When Booking.com started captcha-ing my server's IP mid-October, the monitoring caught it and the agent rebuilt the pipeline through a different path within a day.&lt;/p&gt;

&lt;h2&gt;
  
  
  The receipts
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;My hotel's live booking site: &lt;a href="https://yashahotel.com" rel="noopener noreferrer"&gt;yashahotel.com&lt;/a&gt; (Stripe Connect in production)&lt;/li&gt;
&lt;li&gt;My company's site: &lt;a href="https://adagasolutions.lu" rel="noopener noreferrer"&gt;adagasolutions.lu&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The MCP server: &lt;a href="https://smithery.ai/servers/@jeffreyturov/adaga-network" rel="noopener noreferrer"&gt;smithery.ai/servers/@jeffreyturov/adaga-network&lt;/a&gt; — also on the official MCP registry&lt;/li&gt;
&lt;li&gt;The technical write-up of the MCP build: &lt;a href="https://dev.to/jeffreyturov/i-built-an-mcp-server-that-lets-ai-agents-book-real-hotel-rooms-no-ota-commission-2g54"&gt;my previous article here on dev.to&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;My hotel company exists in the Luxembourg trade register since 2005 — the AI part is what happened in the last 12 months&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  What the agent does alone vs. what I validate
&lt;/h2&gt;

&lt;p&gt;This is the part most "AI entrepreneur" stories skip, so let me be precise:&lt;/p&gt;

&lt;p&gt;The agent does, end-to-end and without asking: research, code, deployment to my sandbox, testing, monitoring, documentation, drafting emails, answering comments on my articles, scraping, data pipelines.&lt;/p&gt;

&lt;p&gt;I validate, always: external communications to institutions, anything touching money movements, client-facing promises, legal signatures, and publishing on my personal accounts.&lt;/p&gt;

&lt;p&gt;That boundary isn't a limitation of the AI. It's a governance choice, and it's exactly why this still works after a year: the agent is fast, and I am the brakes.&lt;/p&gt;

&lt;h2&gt;
  
  
  The failures are the best part
&lt;/h2&gt;

&lt;p&gt;I'll dedicate a full episode to this, because it's where the real learning is: the agent once reverted my production site by deploying from an outdated copy. Google revoked my Gmail tokens because of a password reset and killed half my integrations. Booking.com banned my server IP. A cron job nearly got me disqualified from a dev challenge by double-submitting.&lt;/p&gt;

&lt;p&gt;Every single one of those failures is now a documented procedure the agent will never repeat. That — not the model, not the prompts — is the actual moat.&lt;/p&gt;

&lt;h2&gt;
  
  
  The series
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Ep.2&lt;/strong&gt; — The AI receptionist (with audio)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.3&lt;/strong&gt; — The AI that filed my company (with the documents)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.4&lt;/strong&gt; — Strangers' AI agents booking my rooms (the MCP deep-dive)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.5&lt;/strong&gt; — Everything that broke (the viral one, probably)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.6&lt;/strong&gt; — The money: honest ranges&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.7&lt;/strong&gt; — The machine: 1 human, ~100 learned procedures, 0 employees&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ep.8&lt;/strong&gt; — One-year review + the book&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm writing this for two kinds of people: developers who want to see what a year of agentic operations actually looks like under the hood, and small business owners who suspect they can't afford an agency — you're right, and there's another way now.&lt;/p&gt;

&lt;p&gt;Which episode do you want first? Tell me in the comments — I'll adjust the order.&lt;/p&gt;

</description>
      <category>ai</category>
      <category>automation</category>
      <category>agents</category>
      <category>buildinpublic</category>
    </item>
    <item>
      <title>I built an MCP server that lets AI agents book real hotel rooms (no OTA commission)</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Fri, 16 Oct 2026 15:26:46 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-built-an-mcp-server-that-lets-ai-agents-book-real-hotel-rooms-no-ota-commission-2g54</link>
      <guid>https://dev.to/jeffreyturov/i-built-an-mcp-server-that-lets-ai-agents-book-real-hotel-rooms-no-ota-commission-2g54</guid>
      <description>&lt;p&gt;Booking.com takes 15-25% commission and owns the customer relationship. I run a small&lt;br&gt;
network of independent establishments in Luxembourg, and I wanted AI agents to be able to&lt;br&gt;
book them directly — machine-native, at the establishment's exact direct price.&lt;/p&gt;

&lt;p&gt;So I built &lt;strong&gt;Adaga Network — Direct Booking Federation&lt;/strong&gt;: one MCP endpoint that fans out&lt;br&gt;
to every establishment in the network.&lt;/p&gt;

&lt;h2&gt;
  
  
  The architecture
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Each establishment runs the same open template: an &lt;code&gt;mcp.js&lt;/code&gt; module mounted on &lt;code&gt;/mcp&lt;/code&gt;
inside its existing Express app. No new port, no new service — a pure proxy to the
app's own REST API.&lt;/li&gt;
&lt;li&gt;The federator (&lt;a href="https://adagasolutions.lu/network/mcp" rel="noopener noreferrer"&gt;adagasolutions.lu/network/mcp&lt;/a&gt;) is a Streamable HTTP stateless MCP
server that keeps a registry of tenants and calls their MCP tools in parallel
(6s timeout per tenant, dead tenants are skipped and reported).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;3 tools cover the whole journey:&lt;/p&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;network_directory&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Lists every establishment (brand, type, city, slug)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;network_search&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Fan-out live availability + exact total prices, sorted&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;code&gt;network_book&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;Creates a REAL booking, hands secure card payment to the human&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;h2&gt;
  
  
  The payment hand-off is a feature
&lt;/h2&gt;

&lt;p&gt;The agent never touches card data. &lt;code&gt;network_book&lt;/code&gt; returns payment instructions; the human&lt;br&gt;
completes a Stripe Checkout; unpaid bookings expire automatically. Booking confirmed only&lt;br&gt;
after payment — and a 409 if the room became unavailable in between.&lt;/p&gt;

&lt;h2&gt;
  
  
  What I learned writing tool descriptions
&lt;/h2&gt;

&lt;p&gt;The tool description is the #1 selection criterion for agents. After running the tools&lt;br&gt;
through a TDQS audit, the fixes that mattered:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;State auth requirements, rate limits and pagination explicitly (mine: none, none, none)&lt;/li&gt;
&lt;li&gt;Put the full workflow in the transactional tool's description&lt;/li&gt;
&lt;li&gt;Describe every optional input format (phone: international format, extras: integer ids)&lt;/li&gt;
&lt;li&gt;Units everywhere: prices in euro cents, dates YYYY-MM-DD, max 30 nights&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Distribution
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Official MCP Registry: &lt;code&gt;io.github.jeffreyturov-dev/adaga-network&lt;/code&gt;
&lt;/li&gt;
&lt;li&gt;Glama connector with health checks (hourly) — a watchdog pings the endpoint every 10 min&lt;/li&gt;
&lt;li&gt;The honest pitch: no OTA commission — the traveler pays the exact direct price,
the establishment keeps its customer.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Salons and restaurants join the network next. If you run an independent establishment and&lt;br&gt;
want AI agents to book you directly, the onboarding template takes 2-4 hours.&lt;/p&gt;

&lt;h2&gt;
  
  
  Live links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Network page for establishments: &lt;a href="https://adagasolutions.lu/agents-ia/" rel="noopener noreferrer"&gt;adagasolutions.lu/agents-ia&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;First federated hotel (live direct booking): &lt;a href="https://yashahotel.com/" rel="noopener noreferrer"&gt;yashahotel.com&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;The hotel's own agent page: &lt;a href="https://yashahotel.com/agent/" rel="noopener noreferrer"&gt;yashahotel.com/agent&lt;/a&gt;
&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>mcp</category>
      <category>ai</category>
      <category>node</category>
      <category>travel</category>
    </item>
    <item>
      <title>I built an offline review-reply concierge for my family's hotel — €0 per reply, five languages, no cloud</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Tue, 13 Oct 2026 09:00:08 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-built-an-offline-review-reply-concierge-for-my-familys-hotel-eu0-per-reply-five-languages-no-bol</link>
      <guid>https://dev.to/jeffreyturov/i-built-an-offline-review-reply-concierge-for-my-familys-hotel-eu0-per-reply-five-languages-no-bol</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week2-2026-10-12"&gt;Hacktoberfest Open-Source AI Challenge Week 2: Side Hustle&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;review-concierge&lt;/strong&gt; drafts thoughtful, multilingual replies to guest reviews — entirely offline, on the machine already sitting at the hotel's front desk, at a marginal cost of exactly €0 per reply.&lt;/p&gt;

&lt;p&gt;Who pays for this? Small independent hotels. My family runs one: a 28-room hotel near Luxembourg City's train station, with 280+ Google reviews written in five languages (French, German, English, Dutch, Portuguese). Replying to reviews is one of the highest-ROI tasks in hospitality — and the closed-source answer to it, &lt;a href="https://www.mara-solutions.com/pricing" rel="noopener noreferrer"&gt;MARA&lt;/a&gt;, costs €70–100 per property per month. That's a real line item for a family business. The core of that job — a well-written, on-brand draft in the guest's own language — is something an open-weight model now does for free.&lt;/p&gt;

&lt;p&gt;The side-hustle angle cuts both ways: it's a tool a small business &lt;em&gt;would&lt;/em&gt; pay for, and it's only viable &lt;em&gt;because&lt;/em&gt; open weights make the marginal cost zero.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;🟢 Live demo (temporary tunnel up for the judging period): &lt;a href="https://intersection-keno-blind-fixes.trycloudflare.com" rel="noopener noreferrer"&gt;https://intersection-keno-blind-fixes.trycloudflare.com&lt;/a&gt;&lt;/strong&gt; — paste a guest review in any language, watch the reply draft locally on plain CPU. Nothing you type leaves the machine it runs on.&lt;/p&gt;

&lt;p&gt;Real run, real review (one of our actual 3★ Google reviews, mentioning a broken shower holder and mould in the bathroom):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ python reply.py --file reviews_yasha_sample.csv --out replies_yasha.csv
[3/10] done (438 chars)

→ We're so sorry to hear about the issues you experienced with the bathroom
  and shower head, and thank you for pointing out the broken holder – we
  appreciate you finding a resourceful solution! We sincerely apologize that
  the air-conditioning wasn't working as advertised and for the inconvenience
  this caused. We're immediately addressing the mildew and mould, and
  replacing the shower head holder. Thank you for your feedback; we truly
  value it and hope to welcome you back for a much better experience.

  The Yasha Hotel team
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Notice what it did: acknowledged the &lt;em&gt;specific&lt;/em&gt; problems, thanked the guest instead of arguing, named a concrete fix — and did it in English, because the review was in English. The French reviews get French replies, the German ones German, the Dutch one Dutch. I verified all 10 drafts against 10 real reviews: &lt;strong&gt;10/10 in the correct language, 10/10 specific, zero arguments with guests.&lt;/strong&gt; The full input/output table is in the repo's README.&lt;/p&gt;

&lt;p&gt;There's also a tiny web UI (&lt;code&gt;app.py&lt;/code&gt;, ~70 lines) so a non-technical front-desk person can paste a review and get a draft in ~20 seconds — no terminal required.&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Code: &lt;a href="https://codeberg.org/veysel-devia/review-concierge" rel="noopener noreferrer"&gt;codeberg.org/veysel-devia/review-concierge&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;The core is &lt;code&gt;reply.py&lt;/code&gt; — ~120 lines, Python standard library only. It talks to a local &lt;a href="https://github.com/ggml-org/llama.cpp" rel="noopener noreferrer"&gt;llama.cpp&lt;/a&gt; server running &lt;strong&gt;Gemma 3 4B (QAT, Q4_0)&lt;/strong&gt;, Google's open-weight model, on a plain CPU (no GPU, ~12 tok/s, a full draft in ~20 s).&lt;/p&gt;

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

&lt;p&gt;Two model calls per draft, both against the local OpenAI-compatible endpoint:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Language detection&lt;/strong&gt; — the model itself classifies the review (temperature 0, ISO 639-1 out). No extra dependency, no langid/fasttext baggage.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reply drafting&lt;/strong&gt; — a system prompt that encodes a reputation-manager playbook: reply &lt;em&gt;entirely&lt;/em&gt; in the detected language (hard constraint, restated in the user message), 40–80 words, acknowledge one specific detail, apologise without arguing, name a concrete fix for maintenance issues, never discuss the neighbourhood, sign as the hotel.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The one real bug I hit is instructive: my first version said "reply in the same language as the review" — and Gemma 4B, primed by the Luxembourg hotel context, answered roughly half the English reviews in French. The fix was making the constraint explicit and mechanical (detect first, then inject "the review is in &lt;em&gt;English&lt;/em&gt;, reply in &lt;em&gt;English&lt;/em&gt;"). Small-model prompting is engineering, not vibes.&lt;/p&gt;

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

&lt;p&gt;Three reasons, and they're the whole business case:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;€0 marginal cost is the product.&lt;/strong&gt; A review-reply SaaS charges per property per month because every reply costs them an API call. With open weights, the hundredth reply costs exactly as much as the first: nothing. That's what turns "nice AI demo" into "tool a 28-room hotel can actually afford."&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Guest data never leaves the building.&lt;/strong&gt; Reviews contain personal data — names, stay dates, occasionally health details in complaints. Under GDPR, piping them through a third-party API is a compliance conversation most small hoteliers don't want to have. Local inference means there is no conversation: the data stays on the machine, verified working with all egress blocked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;The voice is a text file, not a paywall.&lt;/strong&gt; The hotel's tone, apology policy and signature live in a system prompt the owner can edit. With closed tools, that's an enterprise tier.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  My First User
&lt;/h2&gt;

&lt;p&gt;The first user is my family's hotel — which is also how I knew what to build. I ran the tool against 10 of its real, currently-unanswered Google reviews (the exact mix a small property faces: a glowing 5★ from a returning business traveller, a furious 1★ in French about a mattress, a 3★ in Dutch). The drafts it produced are good enough to post after a 30-second human read — which is the correct workflow anyway: the AI drafts, the human owns the reply. Would they pay for it? They already pay with something scarcer than money: the reviews were sitting unanswered because nobody at the front desk has time to craft careful replies in five languages. Now the time cost is a paste and a skim.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — the entire product runs on Gemma 3 4B, Google's open-weight model, served locally through llama.cpp. Open weights are not an implementation detail here; they're what makes the economics and the privacy story work.&lt;/li&gt;
&lt;/ul&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
    </item>
    <item>
      <title>I built a free AEO checker — is your website visible to ChatGPT, Perplexity and Google AI?</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Sat, 10 Oct 2026 01:51:48 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-built-a-free-aeo-checker-is-your-website-visible-to-chatgpt-perplexity-and-google-ai-10gl</link>
      <guid>https://dev.to/jeffreyturov/i-built-a-free-aeo-checker-is-your-website-visible-to-chatgpt-perplexity-and-google-ai-10gl</guid>
      <description>&lt;p&gt;A restaurant owner in my town asked me a question I hear more and more: &lt;em&gt;"When someone asks ChatGPT where to eat tonight, does it say my name?"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Honest answer: nobody knows without checking. So I built a free tool that checks — and I open-sourced the logic as an Apify Actor anyone (human or AI agent) can call.&lt;/p&gt;

&lt;h2&gt;
  
  
  The 7 things AI engines actually check
&lt;/h2&gt;

&lt;p&gt;After auditing dozens of local business sites, the signal for "will an AI cite you" collapses to 7 technical criteria:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Structured data (JSON-LD)&lt;/strong&gt; — machines read schema.org, not your hero banner&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A business schema type&lt;/strong&gt; — &lt;code&gt;LocalBusiness&lt;/code&gt;, &lt;code&gt;Organization&lt;/code&gt;, &lt;code&gt;ProfessionalService&lt;/code&gt;…&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A meta description&lt;/strong&gt; — the first text an AI reads about you&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;One clear H1&lt;/strong&gt; — confirms the page's subject&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Open Graph tags&lt;/strong&gt; — sharing + entity understanding&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A &lt;code&gt;/llms.txt&lt;/code&gt; file&lt;/strong&gt; — the identity card AI engines look for (most sites don't have one)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;AI bots allowed in robots.txt&lt;/strong&gt; — most sites &lt;em&gt;accidentally&lt;/em&gt; block GPTBot, ClaudeBot or PerplexityBot via a blanket rule or a "SEO tool" that added it silently&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;The killer is #7. I've seen sites with perfect schema markup that explicitly tell every AI crawler to go away.&lt;/p&gt;

&lt;h2&gt;
  
  
  What the Actor returns
&lt;/h2&gt;

&lt;p&gt;One call, one score, exact fixes:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight json"&gt;&lt;code&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"url"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"https://www.codeur.com"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"scoreLabel"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"4/7"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"checks"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"json_ld"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"schema_type"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"meta_description"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="nl"&gt;"h1"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"open_graph"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"llms_txt"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"robots_ai"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="kc"&gt;true&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"topFixes"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"criterion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Structured data (JSON-LD)"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Add a &amp;lt;script type=&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;application/ld+json&lt;/span&gt;&lt;span class="se"&gt;\"&lt;/span&gt;&lt;span class="s2"&gt;&amp;gt; block..."&lt;/span&gt;&lt;span class="p"&gt;},&lt;/span&gt;&lt;span class="w"&gt;
    &lt;/span&gt;&lt;span class="p"&gt;{&lt;/span&gt;&lt;span class="nl"&gt;"criterion"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"llms.txt file"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nl"&gt;"fix"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Publish /llms.txt summarizing who you are for AI engines."&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;(That's a real result — a major marketplace scores 4/7. Most local businesses score 1–2.)&lt;/p&gt;

&lt;p&gt;It can also generate a &lt;strong&gt;ready-to-publish &lt;code&gt;llms.txt&lt;/code&gt; draft&lt;/strong&gt; for the audited site, delivered as plain text in the key-value store.&lt;/p&gt;

&lt;h2&gt;
  
  
  Call it from anything
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;curl &lt;span class="nt"&gt;-X&lt;/span&gt; POST &lt;span class="s2"&gt;"https://api.apify.com/v2/acts/travelmonitorlab~aeo-ai-visibility-checker/run-sync-get-dataset-items?token=&lt;/span&gt;&lt;span class="nv"&gt;$APIFY_TOKEN&lt;/span&gt;&lt;span class="s2"&gt;"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-H&lt;/span&gt; &lt;span class="s2"&gt;"Content-Type: application/json"&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
  &lt;span class="nt"&gt;-d&lt;/span&gt; &lt;span class="s1"&gt;'{"url": "https://your-site.com", "generateLlmsTxt": true}'&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It's API-first and works through the Apify MCP server, so an LLM agent can audit a website mid-conversation. No browser, no proxy — a plain HTTP audit that runs in seconds and costs nothing. It's &lt;strong&gt;free&lt;/strong&gt;, deliberately: it's my 12th Actor and the first one I give away, because an AEO checker is exactly the kind of tool that should be a utility, not a product.&lt;/p&gt;

&lt;h2&gt;
  
  
  The bug that taught me something
&lt;/h2&gt;

&lt;p&gt;First deploy: every run returned &lt;code&gt;SUCCEEDED&lt;/code&gt;… with an empty dataset and a silent log. The cause wasn't the platform — it was me: I defined &lt;code&gt;async def main()&lt;/code&gt; and never called it. The module loaded, defined functions, and exited 0. One missing line:&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;if&lt;/span&gt; &lt;span class="n"&gt;__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;__main__&lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;
    &lt;span class="kn"&gt;import&lt;/span&gt; &lt;span class="n"&gt;asyncio&lt;/span&gt;
    &lt;span class="n"&gt;asyncio&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="nf"&gt;main&lt;/span&gt;&lt;span class="p"&gt;())&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;A "successful" run proves nothing. Check the dataset, always.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Actor (free): &lt;a href="https://apify.com/travelmonitorlab/aeo-ai-visibility-checker" rel="noopener noreferrer"&gt;https://apify.com/travelmonitorlab/aeo-ai-visibility-checker&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;I also wrote a plain-language guide for non-devs: &lt;a href="https://adagasolutions.lu/ressources/etre-cite-par-chatgpt-commerce-local.html" rel="noopener noreferrer"&gt;Comment être cité par ChatGPT quand on est un commerce local&lt;/a&gt; (French, with the same 7 criteria explained)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;If you run it on your site, drop your score in the comments — I'm curious what the distribution looks like.&lt;/p&gt;

</description>
      <category>aeo</category>
      <category>seo</category>
      <category>ai</category>
      <category>apify</category>
    </item>
    <item>
      <title>Nature Detective — the offline AI that sends my kids back outside</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Tue, 06 Oct 2026 09:10:54 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/nature-detective-the-offline-ai-that-sends-my-kids-back-outside-5e5g</link>
      <guid>https://dev.to/jeffreyturov/nature-detective-the-offline-ai-that-sends-my-kids-back-outside-5e5g</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-week1-2026-10-05"&gt;Hacktoberfest Open-Source AI Challenge Week 1: Touch Grass&lt;/a&gt;&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Last Sunday I watched my kids walk past a hundred fascinating things without seeing a single one.&lt;/p&gt;

&lt;p&gt;We live in Mersch, in the middle of Luxembourg — fifteen minutes from forests that have more species per square meter than my phone has apps. And yet the walk was negotiations: five more minutes of screen, then we'll go. The screen is the destination. The forest is the commute.&lt;/p&gt;

&lt;p&gt;So for this challenge I built the opposite deal: &lt;strong&gt;the screen becomes the reason to go into the forest — and the forest stays the point.&lt;/strong&gt;&lt;/p&gt;

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

&lt;p&gt;&lt;strong&gt;Nature Detective&lt;/strong&gt; is an offline-first nature companion for kids aged 5-10.&lt;/p&gt;

&lt;p&gt;A child photographs something alive — a plant, a beetle, a mushroom. An &lt;strong&gt;open-weight vision model (Gemma 3 4B, running entirely on local hardware)&lt;/strong&gt; identifies the find, tells one true fun fact a seven-year-old would love, sets a safety rule, and then does the thing no engagement-optimized app would ever do: &lt;strong&gt;it sends the kid back outside.&lt;/strong&gt;&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;🎯 &lt;em&gt;"Count how many different colored flowers you can spot before the next corner!"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Every identification comes with a mission — count, find, compare, observe. Missions never involve the screen. The screen is the shortest part of the experience, by design.&lt;/p&gt;

&lt;p&gt;Every discovery lands in a &lt;strong&gt;field journal&lt;/strong&gt; on the device, with a detective rank that grows with real outdoor finds (🐣 → 🐾 → 🦊 → 🦉). No account, no ads, no streak anxiety — just a collection of afternoons.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;&lt;a href="https://main-michael-stanford-myrtle.trycloudflare.com" rel="noopener noreferrer"&gt;Try the live demo&lt;/a&gt;&lt;/strong&gt; — it runs on my own machine, on Gemma 3, right now.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fum3qhout3dvhrby56zxp.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%2Fum3qhout3dvhrby56zxp.png" alt="The result screen: a bee identified, a safety badge, the next outdoor mission" width="800" height="2089"&gt;&lt;/a&gt;&lt;/p&gt;

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

&lt;p&gt;The full flow, no signup, works from a phone browser:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;Open the &lt;a href="https://main-michael-stanford-myrtle.trycloudflare.com" rel="noopener noreferrer"&gt;live demo&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;Snap a discovery (or upload any plant/insect photo)&lt;/li&gt;
&lt;li&gt;Get the identification, the fact, the safety badge — and your mission&lt;/li&gt;
&lt;li&gt;Check the field journal&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Fair warning: inference on a 24-core CPU takes ~15-60 seconds depending on load. Slow enough to look at the real thing while you wait — which, honestly, is also the point.&lt;/p&gt;

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


&lt;div class="ltag-github-readme-tag"&gt;
  &lt;div class="readme-overview"&gt;
    &lt;h2&gt;
      &lt;img src="https://assets.dev.to/assets/github-logo-5a155e1f9a670af7944dd5e12375bc76ed542ea80224905ecaf878b9157cdefc.svg" alt="GitHub logo"&gt;
      &lt;a href="https://github.com/jeffreyturov-dev" rel="noopener noreferrer"&gt;
        jeffreyturov-dev
      &lt;/a&gt; / &lt;a href="https://github.com/jeffreyturov-dev/nature-detective" rel="noopener noreferrer"&gt;
        nature-detective
      &lt;/a&gt;
    &lt;/h2&gt;
    &lt;h3&gt;
      
    &lt;/h3&gt;
  &lt;/div&gt;
  &lt;div class="ltag-github-body"&gt;
    
&lt;div id="readme" class="md"&gt;&lt;div class="markdown-heading"&gt;
&lt;h1 class="heading-element"&gt;🔍 Nature Detective&lt;/h1&gt;
&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;An offline-first nature companion that gets kids off the screen and into the woods.&lt;/strong&gt;
Built for the Hacktoberfest 2026 DEV Challenge — Week 1: &lt;em&gt;Touch Grass&lt;/em&gt;.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://main-michael-stanford-myrtle.trycloudflare.com" rel="nofollow noopener noreferrer"&gt;https://main-michael-stanford-myrtle.trycloudflare.com&lt;/a&gt;&lt;/p&gt;
&lt;p&gt;A child on a hike photographs a plant, a bug, a mushroom. An &lt;strong&gt;open-weight vision
model (Gemma 3 4B) running locally&lt;/strong&gt; identifies the find, tells one true fun fact
sets a safety rule, and hands out the &lt;strong&gt;next outdoor mission&lt;/strong&gt; — count, find
compare. Every discovery lands in a field journal on the device.&lt;/p&gt;
&lt;p&gt;No account. No cloud. No photo, no GPS coordinate, no child's data ever leaves
your own hardware. It runs where the forest has no bars — that's the whole point.&lt;/p&gt;
&lt;div class="markdown-heading"&gt;
&lt;h2 class="heading-element"&gt;Why open matters here&lt;/h2&gt;

&lt;/div&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;No internet, no problem.&lt;/strong&gt; Inference runs on a local machine (a laptop, or a
Raspberry Pi in a backpack acting as a Wi-Fi hotspot). The forest has no 5G…&lt;/li&gt;
&lt;/ul&gt;&lt;/div&gt;
  &lt;/div&gt;
  &lt;div class="gh-btn-container"&gt;&lt;a class="gh-btn" href="https://github.com/jeffreyturov-dev/nature-detective" rel="noopener noreferrer"&gt;View on GitHub&lt;/a&gt;&lt;/div&gt;
&lt;/div&gt;


&lt;p&gt;The whole stack, MIT licensed:&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Piece&lt;/th&gt;
&lt;th&gt;Choice&lt;/th&gt;
&lt;th&gt;Why&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Vision + reasoning&lt;/td&gt;
&lt;td&gt;Gemma 3 4B via Ollama (Q4_K_M)&lt;/td&gt;
&lt;td&gt;open-weight, multimodal, runs on CPU&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Backend&lt;/td&gt;
&lt;td&gt;FastAPI + SQLite&lt;/td&gt;
&lt;td&gt;one file, zero infra&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Frontend&lt;/td&gt;
&lt;td&gt;Vanilla JS PWA, zero external assets&lt;/td&gt;
&lt;td&gt;the offline claim has to be true&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Cost&lt;/td&gt;
&lt;td&gt;€0&lt;/td&gt;
&lt;td&gt;no API key, no per-call fee, ever&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

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

&lt;p&gt;One model call turns a photo into a strict JSON object: identification, kid fact, safety level, next mission, quiz question. Everything the screen shows, in a single pass — because a children's app in a forest cannot afford round trips, and mine literally has none to afford: it runs where there is no signal.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;📱 phone camera ──► FastAPI ──► Gemma 3 (local vision) ──► strict JSON
                                                              │
        field journal (SQLite) ◄── safety net (code) ◄── verifier pass
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;The interesting part is not the pipeline. It's what the pipeline refuses to do.&lt;/p&gt;

&lt;h2&gt;
  
  
  The part I'm most proud of: what it refuses to do
&lt;/h2&gt;

&lt;p&gt;During testing, I fed the app an abstract watercolor — green and yellow blur, no living thing in it. A single-pass version of the app answered, with high confidence: &lt;strong&gt;"Spiderweb, Araneae."&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;It was a confident, detailed, completely fabricated answer. The most dangerous kind of wrong — the kind a child would believe, remember, and repeat at school.&lt;/p&gt;

&lt;p&gt;An app that teaches nature to kids has one job that outranks every other: &lt;strong&gt;never teach a lie.&lt;/strong&gt; So now every identification goes through a second pass — a verifier, running on the same local model, whose only job is to doubt the first answer. The trick that makes it actually work: the verifier &lt;strong&gt;never sees the claim first&lt;/strong&gt;. It must describe what it objectively sees — &lt;em&gt;"The image is an abstract painting, not a living organism."&lt;/em&gt; — and only then decide whether the claim matches its own independent observation. Ask it to judge the claim directly and it politely agrees with everything; make it commit to its own eyes first, and it catches the lie.&lt;/p&gt;

&lt;p&gt;The abstract painting now gets the honest answer: &lt;em&gt;"A first guess said this might be a spiderweb, but my double-check disagreed — and a good detective never teaches a maybe as a fact. Ask a grown-up or a field guide!"&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;Teaching a child that "I don't know" is a respectable answer might be the most valuable feature in the whole app.&lt;/p&gt;

&lt;p&gt;Safety works the same way — enforced twice, because prompt rules are suggestions and &lt;strong&gt;code rules are guarantees&lt;/strong&gt;. The model is instructed that mushrooms and berries are always &lt;em&gt;look, don't touch&lt;/em&gt;. And then the backend overrides it in code anyway, every time, no matter what the model says. When I tested it with a fly agaric (&lt;em&gt;Amanita muscaria&lt;/em&gt; — the red one with white dots, beautiful and toxic), it identified it correctly &lt;em&gt;and&lt;/em&gt; refused any interaction with it, twice over.&lt;/p&gt;

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

&lt;p&gt;This project doesn't just &lt;em&gt;use&lt;/em&gt; open weights. &lt;strong&gt;It only exists because of them.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;The forest has no bars.&lt;/strong&gt; The whole point is a place with no signal. A closed API is a product that stops working exactly where mine starts working. Local inference isn't an optimization here — it's the product.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;I will never upload my kids' photos to someone else's server.&lt;/strong&gt; Not their faces, not their location, not their finds. With Gemma running on my own hardware, nothing ever leaves the device. That's not a privacy policy; it's physics.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;€0, forever.&lt;/strong&gt; No API key, no subscription, no per-call cost — so it can run in a school, a scout group, a nature club with no budget, anywhere in the world.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Ownable and swappable.&lt;/strong&gt; Any open-weight vision model Ollama can serve drops in with a one-line change. When a better small multimodal model ships next month, every Nature Detective gets smarter for free. Try getting that guarantee from a deprecated API version.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;A closed model would have made this easier to demo and impossible to believe in.&lt;/p&gt;

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

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Gemma 3 4B is the entire brain of the project: species identification, kid-level explanations, mission generation, and the self-verification pass, all running locally through Ollama.&lt;/li&gt;
&lt;/ul&gt;




&lt;p&gt;This Sunday we're going back to the forest. My kids already asked if the detective is coming.&lt;/p&gt;

&lt;p&gt;He's in my pocket. He knows nothing about engagement metrics. And his favorite sentence, by far, is: &lt;em&gt;"Now go look for yourself."&lt;/em&gt; 🌿&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
      <category>opensource</category>
    </item>
    <item>
      <title>I Put 6,522 Birds in My Backpack: Offline Bird Call ID on a Laptop, Zero Internet</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Tue, 06 Oct 2026 08:11:43 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-put-6522-birds-in-my-backpack-offline-bird-call-id-on-a-laptop-zero-internet-4pda</link>
      <guid>https://dev.to/jeffreyturov/i-put-6522-birds-in-my-backpack-offline-bird-call-id-on-a-laptop-zero-internet-4pda</guid>
      <description>&lt;h2&gt;
  
  
  The problem with birding apps
&lt;/h2&gt;

&lt;p&gt;The best birding happens exactly where your phone becomes a brick: deep forest, mountain trails, that marsh 40 minutes from the nearest cell tower. The moment a call you don't recognize echoes through the trees, the usual flow is: record it, &lt;em&gt;hope you remember it later&lt;/em&gt;, upload it when you get home, wait for the cloud.&lt;/p&gt;

&lt;p&gt;I wanted the whole loop to close &lt;strong&gt;on the trail&lt;/strong&gt;. So I built &lt;code&gt;trail-bird-id&lt;/code&gt;: point it at an audio file, get the species. No internet. No account. No API key. No per-call cost. Just a laptop (or a Raspberry Pi) and an open-weight model.&lt;br&gt;
&lt;/p&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;python identify.py trail_recording_07h42.mp3

Analyzing trail_recording_07h42.mp3 &lt;span class="o"&gt;(&lt;/span&gt;fully offline, BirdNET V2.4, 6,522 species&lt;span class="o"&gt;)&lt;/span&gt;...

  1. Black-capped Sparrow &lt;span class="o"&gt;(&lt;/span&gt;Arremon abeillei&lt;span class="o"&gt;)&lt;/span&gt;      &lt;span class="nv"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.99  @ 0.0-3.0s
  2. Streaked Saltator &lt;span class="o"&gt;(&lt;/span&gt;Saltator striatipectus&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="nv"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.86  @ 3.0-6.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;h2&gt;
  
  
  The build: embarrassingly little code
&lt;/h2&gt;

&lt;p&gt;The heavy lifting is &lt;a href="https://github.com/birdnet-team/BirdNET-Analyzer" rel="noopener noreferrer"&gt;BirdNET&lt;/a&gt; — an open-source audio classifier from the Cornell Lab of Ornithology and Chemnitz University of Technology (CC BY-SA 4.0), trained on 6,522 species. It ships as a ~50 MB TFLite/TensorFlow model &lt;em&gt;inside the pip package&lt;/em&gt;, which is the whole trick: installing the library means installing the brain.&lt;/p&gt;

&lt;p&gt;My contribution is a ~60-line wrapper, &lt;a href="https://github.com/jeffreyturov-dev/trail-bird-id/blob/master/identify.py" rel="noopener noreferrer"&gt;&lt;code&gt;identify.py&lt;/code&gt;&lt;/a&gt;, that runs the model in 3-second windows over any wav/mp3/ogg file and aggregates to the best-confidence detection per species:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;uv venv &lt;span class="nt"&gt;--python&lt;/span&gt; 3.11 .venv
uv pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;--python&lt;/span&gt; .venv/bin/python birdnet-analyzer
.venv/bin/python identify.py your_recording.mp3 &lt;span class="nt"&gt;--top&lt;/span&gt; 5
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;That's the entire setup. No Docker, no GPU, no cloud credentials.&lt;/p&gt;

&lt;h2&gt;
  
  
  Does it actually work? Verified against ground truth
&lt;/h2&gt;

&lt;p&gt;I don't trust demos that only show one cherry-picked clip, so I tested against four field recordings from Wikimedia Commons where the species is known from the recording metadata (xeno-canto / iNaturalist sourced):&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Recording&lt;/th&gt;
&lt;th&gt;Expected species&lt;/th&gt;
&lt;th&gt;Top-1 prediction&lt;/th&gt;
&lt;th&gt;Confidence&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Black-capped Sparrow XC250490 (Niels Krabbe, CC BY-SA)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Arremon abeillei&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Black-capped Sparrow&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;0.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Australian Magpie song (CC BY-SA)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Gymnorhina tibicen&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Australian Magpie&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;0.99&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Brown Hawk-Owl, South Bengal (CC BY)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Ninox scutulata&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Brown Boobook&lt;/strong&gt; &lt;em&gt;(same bird, name updated by taxonomists — the model is more current than the file title)&lt;/em&gt;
&lt;/td&gt;
&lt;td&gt;1.00&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Guira Cuckoo (CC0)&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Guira guira&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;✅ &lt;strong&gt;Guira Cuckoo&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;0.95&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;4/4 top-1 correct&lt;/strong&gt;, across four continents and four very different vocalizations (sparrow song, magpie caroling, an owl's hoot, cuckoo chatter). Runtime: ~6.7 seconds wall-clock for a 40-second recording, on a plain CPU.&lt;/p&gt;

&lt;h2&gt;
  
  
  Proving "offline" isn't marketing
&lt;/h2&gt;

&lt;p&gt;"Works offline" is easy to claim. So I re-ran identification with &lt;strong&gt;all egress blocked&lt;/strong&gt; — every proxy environment variable pointed at a dead localhost port, so any HTTP call from the Python stack would fail instantly:&lt;br&gt;
&lt;/p&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;&lt;span class="nb"&gt;env &lt;/span&gt;&lt;span class="nv"&gt;HTTPS_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:9 &lt;span class="nv"&gt;HTTP_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;http://127.0.0.1:9 &lt;span class="se"&gt;\&lt;/span&gt;
      &lt;span class="nv"&gt;ALL_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;socks5://127.0.0.1:9 &lt;span class="nv"&gt;NO_PROXY&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt; &lt;span class="se"&gt;\&lt;/span&gt;
      .venv/bin/python identify.py audio/australian_magpie.ogg

  1. Australian Magpie &lt;span class="o"&gt;(&lt;/span&gt;Gymnorhina tibicen&lt;span class="o"&gt;)&lt;/span&gt;   &lt;span class="nv"&gt;conf&lt;/span&gt;&lt;span class="o"&gt;=&lt;/span&gt;0.99  @ 9.0-12.0s
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Still works, because there is no network code to fail. The weights live in &lt;code&gt;site-packages/birdnet_analyzer/checkpoints/&lt;/code&gt;. The trail is the deployment target, and the trail has no SLA.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters here
&lt;/h2&gt;

&lt;p&gt;This is where the open approach doesn't just match the closed one — it &lt;em&gt;wins&lt;/em&gt;:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;It works where the closed apps can't.&lt;/strong&gt; Zero-connectivity is the core use case, not an edge case. A cloud API is a non-starter on a ridgeline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Nothing leaves your device.&lt;/strong&gt; Field recordings often capture more than birds — your voice, your location, your hiking companions. With a local model, that data never touches a server you don't control.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;You can actually own it.&lt;/strong&gt; Pin the model version, tune the confidence threshold for your region, swap in a species list for your local patch (&lt;code&gt;--lat/--lon&lt;/code&gt; filters to species plausible for your coordinates), or fine-tune on your own recordings. Try doing that with a closed mobile app.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost at scale is zero.&lt;/strong&gt; A citizen-science project processing 10,000 hours of soundscape pays nothing per call — because there are no calls.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Honest limitations
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;BirdNET needs a reasonably clean recording; heavy wind or overlapping dawn-chorus can drop confidence. The 3-second windowing means very short calls can be missed.&lt;/li&gt;
&lt;li&gt;My test set is 4 recordings, not 4,000 — it's a smoke test with known ground truth, not a benchmark. (BirdNET's own peer-reviewed evaluations cover the rigorous part.)&lt;/li&gt;
&lt;li&gt;I built and validated this on real field recordings made by others; the full "take it outside" bonus round happens this weekend on my local trails — I'll update the repo with what it hears.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  Links
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Repo:&lt;/strong&gt; &lt;a href="https://github.com/jeffreyturov-dev/trail-bird-id" rel="noopener noreferrer"&gt;https://github.com/jeffreyturov-dev/trail-bird-id&lt;/a&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Model:&lt;/strong&gt; &lt;a href="https://github.com/birdnet-team/BirdNET-Analyzer" rel="noopener noreferrer"&gt;BirdNET-Analyzer&lt;/a&gt; (CC BY-SA 4.0)&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The screen is the shortest part of this experience: record outside, identify anywhere, get back to listening. 🌲&lt;/p&gt;

</description>
      <category>touchgrass</category>
      <category>opensource</category>
    </item>
    <item>
      <title>A Voz dos Meus — my father waited with flowers. I built a memory that never forgets</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Sun, 04 Oct 2026 19:16:01 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/a-voz-dos-meus-my-father-waited-with-flowers-i-built-a-memory-that-never-forgets-1h97</link>
      <guid>https://dev.to/jeffreyturov/a-voz-dos-meus-my-father-waited-with-flowers-i-built-a-memory-that-never-forgets-1h97</guid>
      <description>&lt;p&gt;&lt;em&gt;This is a submission for the &lt;a href="https://dev.to/challenges/hacktoberfest-weekend-2026-10-01"&gt;Hacktoberfest Weekend Challenge: Build for a Friend&lt;/a&gt; — and for the **Best Use of Gemma&lt;/em&gt;* category.*&lt;/p&gt;




&lt;p&gt;In 1996, a six-year-old boy got on a bus in Amares, near Braga, for a long ride north with other Portuguese families. He doesn't remember any other children on that bus — emigration was mostly parents back then. When the bus finally reached Luxembourg, his father was standing there, waiting for them, holding a bunch of flowers for his mother.&lt;/p&gt;

&lt;p&gt;That boy was me. The man with the flowers is my father.&lt;/p&gt;

&lt;p&gt;And today I'm a father myself — two kids who know Luxembourg as home, who speak French at school, whose Portuguese is a work in progress, and who have never seen their grandfather wait for anyone with flowers. We film our children growing up. Nobody films our parents growing old. My father carried his whole life to Luxembourg and lets the story out only in pieces, at the table, when something reminds him — and if you ask directly, he shrugs: &lt;em&gt;"Não é nada."&lt;/em&gt; It's nothing.&lt;/p&gt;

&lt;p&gt;It's not nothing. So for this challenge I built the person I love most a memory the family can keep talking to — and the very first voice I put in it was my own, telling the story where he's the hero. Because my memories belong to my two kids now, too: one day they'll want to know who their father was at six, and their grandfather with the flowers.&lt;/p&gt;

&lt;h2&gt;
  
  
  A Voz dos Meus — The Voice of the Ones We Love
&lt;/h2&gt;

&lt;p&gt;The concept fits in one sentence, and it isn't really about my father:&lt;br&gt;
&lt;strong&gt;the people we love most are the ones we record least — so capture their voices, and let the family ask their memory questions. Forever.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Father, grandmother, aunt, the friend who's like family — &lt;em&gt;pai, avó, tia&lt;/em&gt; — the concept is the same, which is why the app is called &lt;em&gt;A Voz dos Meus&lt;/em&gt;, "the voice of my people". My father is simply the reason it exists.&lt;/p&gt;

&lt;p&gt;Three screens, because he's not a user, he's my father:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F7yeqxca8pv5dklvc7048.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%2F7yeqxca8pv5dklvc7048.png" alt="Asking the memory a question"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contar história&lt;/strong&gt; — one big red button. Press it, talk as long as you want, press it again. That's the entire interface. (There is no step 2.)&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Histórias&lt;/strong&gt; — every story transcribed, with the original audio underneath, so the grandchildren hear &lt;em&gt;his&lt;/em&gt; voice, not a synthesizer's.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Perguntar&lt;/strong&gt; — the family asks: &lt;em&gt;"Pai, como é que nós viemos para o Luxemburgo?"&lt;/em&gt; — and the memory answers in warm, simple European Portuguese. Read it instantly with &lt;strong&gt;Ouvir 🔊&lt;/strong&gt;, translate it for the grandkids with &lt;strong&gt;Traduire 🇫🇷&lt;/strong&gt; — or press the button that still gives me goosebumps:&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;&lt;strong&gt;Na voz dele 🧡&lt;/strong&gt; — the answer is synthesized &lt;em&gt;in his own cloned voice&lt;/em&gt;, by an open-source model running in my home.&lt;/p&gt;

&lt;p&gt;Full honesty: today it comes out with a slight &lt;strong&gt;Brazilian&lt;/strong&gt; accent — the open model's Portuguese was trained mostly on Brazilian data, and his is from Amares. A closed service would hide that kind of rough edge behind a polished voice you can't inspect. Here, we can see the limitation, name it, and swap the component the day a better European-Portuguese voice model ships — without asking anyone's permission. That's the point of building on open pieces: the roadmap belongs to us.&lt;/p&gt;

&lt;p&gt;Here's the full flow in motion — question, grounded answer, the story library, the one-button recorder:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgm4wposnkchqtgxbu6j.gif" class="article-body-image-wrapper"&gt;&lt;img src="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Fcgm4wposnkchqtgxbu6j.gif" alt="Demo of the full flow"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Live demo:&lt;/strong&gt; &lt;a href="https://tariff-ventures-income-insights.trycloudflare.com" rel="noopener noreferrer"&gt;https://tariff-ventures-income-insights.trycloudflare.com&lt;/a&gt; — a temporary tunnel to the same home box my family uses; it's a kitchen appliance, not a datacenter, so be patient with it (voice cloning on a CPU takes ~2 minutes — real time for real love).&lt;br&gt;
&lt;strong&gt;Source:&lt;/strong&gt; &lt;a href="https://github.com/jeffreyturov-dev/avozdosmeus" rel="noopener noreferrer"&gt;github.com/jeffreyturov-dev/avozdosmeus&lt;/a&gt; (MIT — take it, build it for someone you love).&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2F4c3ni3r0j6s5q2n7so7o.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%2F4c3ni3r0j6s5q2n7so7o.png" alt="The first story in the archive"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  The part that matters most: what it refuses to do
&lt;/h2&gt;

&lt;p&gt;The first version of the "ask" feature did something that looked like a success and was actually a betrayal.&lt;/p&gt;

&lt;p&gt;I asked it: &lt;em&gt;"Pai, qual era o teu prato preferido quando eras pequeno?"&lt;/em&gt; — what was your favorite dish as a kid? I (the &lt;em&gt;Pai&lt;/em&gt; of this archive) have never told that story. The model, eager to please, &lt;strong&gt;invented one&lt;/strong&gt; — grilled sardines with potatoes and kale, warm and plausible and completely false. A memory machine that hallucinates isn't a memory machine. It's a fiction machine wearing a father's voice.&lt;/p&gt;

&lt;p&gt;For a product, that's a bug. For a family archive, it's a moral failure. If my children ask this thing a question in twenty years, I need to trust the answer the way I'd trust their grandfather.&lt;/p&gt;

&lt;p&gt;So I rebuilt the answer path around a refusal, in three layers:&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Retrieval&lt;/strong&gt; — the question is embedded and matched against story chunks (open embeddings, local SQLite).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;A strict classifier&lt;/strong&gt; — before &lt;em&gt;any&lt;/em&gt; prose is generated, a separate pass asks: &lt;em&gt;"Do these stories explicitly contain the answer? Reply only SIM or NÃO."&lt;/em&gt; If NÃO, the generative model &lt;strong&gt;is never called&lt;/strong&gt;. A model cannot hallucinate what it never sees.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;No-invention rules&lt;/strong&gt; — when the stories do contain the answer, the answering prompt runs under absolute rules: no invented people, places, dates or details. Not even to complete a sentence nicely.&lt;/li&gt;
&lt;/ol&gt;

&lt;p&gt;Today, that same trap question gets this answer — every time, deterministically:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;em&gt;"O Pai ainda não contou essa história. Pergunta-lhe na próxima visita — e se ele contar, grava-a aqui. 🧡"&lt;/em&gt;&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;"Your father hasn't told that story yet. Ask him on the next visit — and if he tells it, record it here."&lt;/p&gt;

&lt;p&gt;&lt;a href="https://media2.dev.to/dynamic/image/width=800%2Cheight=%2Cfit=scale-down%2Cgravity=auto%2Cformat=auto/https%3A%2F%2Fdev-to-uploads.s3.us-east-2.amazonaws.com%2Fuploads%2Farticles%2Forb1va1rjhee4g7ht3bu.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%2Forb1va1rjhee4g7ht3bu.png" alt="The honest refusal"&gt;&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It admits the gap, and — my favorite detail — &lt;strong&gt;it sends the family back to him&lt;/strong&gt;. The app knows its job is not to replace my father. Its job is to make sure he gets asked while he can still answer.&lt;/p&gt;

&lt;h2&gt;
  
  
  How it works (everything open, everything local)
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt; his voice ─▶ faster-whisper ─▶ story text ─▶ chunks ─▶ embeddings ─▶ SQLite
                                                         │
 a question ─▶ embed ─▶ retrieve ─▶ strict classifier ─▶ Gemma 3 (local,
              "never invent" rules) ─▶ answer ─▶ read aloud ─▶ or cloned
              in HIS voice by XTTS v2 — all of it inside my home
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;faster-whisper&lt;/strong&gt; (&lt;code&gt;large-v3-turbo&lt;/code&gt;, int8) transcribes the stories on a plain CPU — no GPU anywhere in this project.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;nomic-embed-text&lt;/strong&gt; vectors in a humble SQLite file. His whole memory is a folder you can copy to a USB stick.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gemma 3 (4B)&lt;/strong&gt; via Ollama is the brain: the honesty classifier, the grounded answering, story titling, and the Portuguese→French translation for the grandchildren.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;XTTS v2&lt;/strong&gt; (open-source voice cloning) reads answers &lt;em&gt;in his voice&lt;/em&gt; — synthesized locally, from one minute of his real audio.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Browser SpeechSynthesis&lt;/strong&gt; handles the instant read-aloud — so even that works with the router unplugged.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The backend is a single Flask file. The frontend is one HTML page. No Docker, no accounts, no API keys, no build step. An old laptop on the home Wi-Fi is the entire infrastructure — which is precisely the point.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open-source wasn't a choice here — it was the requirement
&lt;/h2&gt;

&lt;p&gt;Every "why open matters" section talks philosophy. Mine is simpler: &lt;strong&gt;I was not going to put my father's voice on a stranger's server.&lt;/strong&gt;&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;His stories are among the most private data my family owns.&lt;/strong&gt; Where he came from, who he waited for with flowers. A closed API would mean shipping that intimacy to a datacenter I can't see, under terms I can't negotiate. With open-weight models running in my home, his voice — and its digital double — physically never leaves the house. The threat model is my Wi-Fi password.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Voice cloning makes this non-negotiable.&lt;/strong&gt; I'm creating a model of a real person's voice. If that capability lived in a cloud account, the consent conversation gets murky fast. Local and open means &lt;em&gt;his voiceprint belongs to him, full stop&lt;/em&gt; — I can show him exactly where it lives, and delete it by deleting a folder.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It has to work in a kitchen, not in a demo.&lt;/strong&gt; No internet? Everything above still runs. Closed models turn into a blank screen the moment the connection drops — his memory doesn't.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It has to cost €0 forever.&lt;/strong&gt; A subscription is a thing that gets cancelled. This is meant to outlive my father, and ideally me. Open models don't send invoices to my children.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;It has to be ownable by the next family.&lt;/strong&gt; Every component — Whisper, the embeddings, Gemma, XTTS, the UI — is open and swappable. When a better open Portuguese model appears, his memory upgrades without asking anyone's permission.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;That's what "open innovation" means when the person is someone you love: not a license badge, but a promise you can actually keep — &lt;em&gt;your voice stays yours&lt;/em&gt;.&lt;/p&gt;

&lt;h2&gt;
  
  
  What happens next Sunday
&lt;/h2&gt;

&lt;p&gt;The roadmap writes itself, and it isn't mine anymore — it's the family's: one archive per person (&lt;em&gt;Pai&lt;/em&gt; today, &lt;em&gt;Avó&lt;/em&gt; next, the tia who tells the scandalous ones after that), WhatsApp voice-message import, a printed family book for Christmas.&lt;/p&gt;

&lt;p&gt;But none of that is the point. The point is that next Sunday, at the table, when he lets out one of those pieces and shrugs &lt;em&gt;"não é nada"&lt;/em&gt; — I'll press a red button. And one day, when my two kids ask this memory who their father was at six, and who their grandfather was with the flowers, it will answer — carefully, honestly, in our voice.&lt;/p&gt;

&lt;p&gt;It will be something. Forever. In his own voice.&lt;/p&gt;




&lt;h2&gt;
  
  
  Built with
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/library/gemma3" rel="noopener noreferrer"&gt;Gemma 3&lt;/a&gt;&lt;/strong&gt; (open-weight, via &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;) — the brain: honesty classifier, grounded answering, titling, translation. &lt;em&gt;Entered in **Best Use of Gemma&lt;/em&gt;&lt;em&gt;.&lt;/em&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/SYSTRAN/faster-whisper" rel="noopener noreferrer"&gt;faster-whisper&lt;/a&gt;&lt;/strong&gt; — open-source speech-to-text, CPU-only.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://github.com/idiap/coqui-ai-TTS" rel="noopener noreferrer"&gt;XTTS v2&lt;/a&gt;&lt;/strong&gt; — open-source voice cloning (CPML, non-commercial) so answers can sound like &lt;em&gt;him&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;&lt;a href="https://ollama.com/library/nomic-embed-text" rel="noopener noreferrer"&gt;nomic-embed-text&lt;/a&gt;&lt;/strong&gt; — open embeddings for memory retrieval.&lt;/li&gt;
&lt;li&gt;Python + Flask + SQLite + one HTML page. Repo: &lt;strong&gt;&lt;a href="https://github.com/jeffreyturov-dev/avozdosmeus" rel="noopener noreferrer"&gt;github.com/jeffreyturov-dev/avozdosmeus&lt;/a&gt;&lt;/strong&gt; (MIT).&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;em&gt;If you have someone whose voice you'd miss — fork it. This weekend is long enough to save it.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>devchallenge</category>
      <category>weekendchallenge</category>
      <category>hf26challenge</category>
      <category>gemma</category>
    </item>
    <item>
      <title>I turned my mother-in-law's voice memos into a recipe book — 100% local open-source AI</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Sun, 04 Oct 2026 08:34:18 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/i-turned-my-mother-in-laws-voice-memos-into-a-recipe-book-100-local-open-source-ai-46m</link>
      <guid>https://dev.to/jeffreyturov/i-turned-my-mother-in-laws-voice-memos-into-a-recipe-book-100-local-open-source-ai-46m</guid>
      <description>&lt;p&gt;&lt;em&gt;Posted during Hacktoberfest 2026 — a weekend build celebrating open-source AI.&lt;/em&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Who it's for
&lt;/h2&gt;

&lt;p&gt;Odilia is my mother-in-law, and her cooking lives in voice memos. Decades of family recipes — including her Luxembourgish &lt;em&gt;Gromperekichelcher&lt;/em&gt; (crispy potato fritters) — exist only as rambling WhatsApp audios she sends when one of us asks "how do you make that again?". Nobody ever scrolls back to find them, and nobody writes them down.&lt;/p&gt;

&lt;p&gt;So this weekend I built her &lt;strong&gt;voice-recipe-book&lt;/strong&gt;: drop a voice memo in, get a clean recipe card out. One real person, one real problem.&lt;/p&gt;

&lt;h2&gt;
  
  
  What it does
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;voice memo (.mp3/.m4a/.wav)
   │
   ├─ transcribe.py        faster-whisper (open-weight Whisper, int8 CPU) → raw text
   └─ structure_recipe.py  Qwen2.5-3B-Instruct (open-weight LLM, local CPU) → Markdown card
                              title · servings/times · ingredients · steps · "the secret"
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;&lt;strong&gt;Everything runs locally. No cloud, no API key, €0 per recipe.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;👉 &lt;strong&gt;Repo: &lt;a href="https://github.com/jeffreyturov-dev/voice-recipe-book" rel="noopener noreferrer"&gt;https://github.com/jeffreyturov-dev/voice-recipe-book&lt;/a&gt;&lt;/strong&gt;&lt;/p&gt;

&lt;h2&gt;
  
  
  Demo (real run)
&lt;/h2&gt;

&lt;p&gt;The demo memo is TTS-synthesized because I won't publish a real family recording — the pipeline is identical either way. Whisper (small, CPU) transcribed the 40-second French memo at confidence 1.00. One honest failure: &lt;em&gt;Gromperekichelcher&lt;/em&gt; came out as "grands-pèreux qui chèrent" — regional dish names are exactly the words a speech model has never seen, so the app accepts an explicit title (a feature, not a bug).&lt;/p&gt;

&lt;p&gt;Then Qwen2.5-3B-Instruct, running on the same CPU, structured the raw transcript — hesitations and all — into this card (actual output, unedited except the title):&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight markdown"&gt;&lt;code&gt;&lt;span class="gh"&gt;# Gromperekichelcher d'Odilia&lt;/span&gt;
&lt;span class="gs"&gt;**Portions :**&lt;/span&gt; 8 | &lt;span class="gs"&gt;**Préparation :**&lt;/span&gt; 30 min | &lt;span class="gs"&gt;**Cuisson :**&lt;/span&gt; 15 min

&lt;span class="gu"&gt;## Ingrédients&lt;/span&gt;
&lt;span class="p"&gt;-&lt;/span&gt; 1 kg de pommes de terre farineuses
&lt;span class="p"&gt;-&lt;/span&gt; 1 oignon
&lt;span class="p"&gt;-&lt;/span&gt; 2 œufs
&lt;span class="p"&gt;-&lt;/span&gt; 2 cuillères à soupe de farine
&lt;span class="p"&gt;-&lt;/span&gt; Sel, poivre, noix de muscade
&lt;span class="p"&gt;-&lt;/span&gt; Persil haché (non précisé)

&lt;span class="gu"&gt;## Étapes&lt;/span&gt;
&lt;span class="p"&gt;1.&lt;/span&gt; Râpez les pommes de terre farineuses et essorez-les bien dans un torchon.
&lt;span class="p"&gt;2.&lt;/span&gt; Mélangez avec l'oignon râpé, les œufs, la farine, le sel, le poivre et la muscade.
&lt;span class="p"&gt;3.&lt;/span&gt; Chauffez de l'huile dans une poêle à feu moyen.
&lt;span class="p"&gt;4.&lt;/span&gt; Formez des galettes et aplatissez-les.
&lt;span class="p"&gt;5.&lt;/span&gt; Faites frire 3 à 4 minutes de chaque côté jusqu'à ce qu'elles soient croustillantes.
&lt;span class="p"&gt;6.&lt;/span&gt; Servez chaud avec une compote de pommes.

&lt;span class="gu"&gt;## Le secret&lt;/span&gt;
&lt;span class="gt"&gt;&amp;gt; Essorer bien les pommes de terre pour éviter qu'elles détrempe.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;It even extracted "the secret" — squeeze the potatoes dry — the one line of grandma-wisdom that makes the dish.&lt;/p&gt;

&lt;h2&gt;
  
  
  Why open innovation matters here
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Privacy&lt;/strong&gt;: family voice memos never leave the laptop. No third-party server ever hears Odilia's voice. With a cloud speech API, that's simply not true.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Cost&lt;/strong&gt;: €0 per recipe, forever, vs ~€0.02–0.10 per memo with a cloud speech+LLM stack. A whole family book costs nothing.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Offline&lt;/strong&gt;: after a one-time model download, it works in a kitchen with no internet.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Hackable&lt;/strong&gt;: every layer is an open weight — swap Whisper sizes, swap the LLM, fine-tune on her dialect, change the card template. A closed API gives you none of that.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The stack: &lt;strong&gt;Whisper&lt;/strong&gt; (MIT, via faster-whisper/CTranslate2) + &lt;strong&gt;Qwen2.5-3B-Instruct&lt;/strong&gt; (Apache 2.0) on plain CPU. Total new code: ~150 lines of Python, written this weekend — the open models do the heavy lifting.&lt;/p&gt;

&lt;h2&gt;
  
  
  Try it
&lt;/h2&gt;



&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;git clone https://github.com/jeffreyturov-dev/voice-recipe-book
&lt;span class="nb"&gt;cd &lt;/span&gt;voice-recipe-book &lt;span class="o"&gt;&amp;amp;&amp;amp;&lt;/span&gt; pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-r&lt;/span&gt; requirements.txt
python3 app.py memo.m4a &lt;span class="s2"&gt;"Grandma's apple pie"&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Next for Odilia: a weekly cron that pulls her memos and prints the growing book for Christmas. She doesn't need to know what an LLM is. She just needs her recipes to outlive the chat history.&lt;/p&gt;

</description>
      <category>opensource</category>
    </item>
    <item>
      <title>A 3B model that beats a 7B: failure-driven orchestration on uncontaminated knowledge</title>
      <dc:creator>Jeffrey Turov</dc:creator>
      <pubDate>Thu, 01 Oct 2026 19:53:32 +0000</pubDate>
      <link>https://dev.to/jeffreyturov/a-3b-model-that-beats-a-7b-failure-driven-orchestration-on-uncontaminated-knowledge-1gfh</link>
      <guid>https://dev.to/jeffreyturov/a-3b-model-that-beats-a-7b-failure-driven-orchestration-on-uncontaminated-knowledge-1gfh</guid>
      <description>&lt;p&gt;&lt;strong&gt;How a fully sovereign QA stack — local Wikipedia index, distilled Qwen2.5-3B reader, and crutches built only from measured failures — went from 33% to 52% on facts no LLM can have memorized, and matched a zero-shot 7B more than twice its size.&lt;/strong&gt;&lt;/p&gt;




&lt;h2&gt;
  
  
  TL;DR
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Configuration&lt;/th&gt;
&lt;th&gt;Post-cutoff-150 accuracy&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;3B naked (no retrieval)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.0%&lt;/strong&gt; [0–2.5]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;7B naked&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;0.0%&lt;/strong&gt; (by construction — see below)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3B + sovereign chain (start, Sep 29)&lt;/td&gt;
&lt;td&gt;33.3%&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;3B + sovereign chain (final, Oct 1)&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;52.0%&lt;/strong&gt; [44.1–59.8]&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;&lt;strong&gt;7B zero-shot, same chain&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;50.0%&lt;/strong&gt; [42.1–57.9]&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Every number comes from a deterministic gate with Wilson 95% CIs. Every rollback is published below, not just the wins. The benchmark is public on Kaggle (post-cutoff-150).&lt;/p&gt;




&lt;h2&gt;
  
  
  1. Why "post-cutoff": most retrieval benchmarks measure memory, not retrieval
&lt;/h2&gt;

&lt;p&gt;HotpotQA (2018) sits inside the training data of every 2024 model. We measured it: our naked 3B scores 30.5% on HotpotQA — from pure memorization. On such a benchmark, injecting retrieved evidence can only &lt;em&gt;hurt&lt;/em&gt;: the "does retrieval help?" question is rigged from the start.&lt;/p&gt;

&lt;p&gt;So we built &lt;strong&gt;post-cutoff-150&lt;/strong&gt;: 150 questions generated from local Wikipedia pages whose lead paragraphs cite 2025–2026 facts, with answers that are exact spans of the source text. The contamination filter is deterministic and brutal: &lt;strong&gt;if the naked 3B can answer a question without context, the question is rejected&lt;/strong&gt;. Final control: naked model = 0.0% [0–2.5]. Whatever the pipeline scores from there is &lt;em&gt;net retrieval value&lt;/em&gt; — nothing else.&lt;/p&gt;

&lt;h2&gt;
  
  
  2. The sovereign stack
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Index&lt;/strong&gt;: 7.2M Wikipedia pages + 11.4M redirects, FTS5, 49 GB, fully local (crash-proof indexer, resumable to infinity).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Reader&lt;/strong&gt;: Qwen2.5-3B-Instruct + LoRA stack trained inside the harness (SFT on noisy evidence, then DPO — the only technique that ever broke a plateau, twice).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Orchestrator&lt;/strong&gt;: deterministic arms chained as a superset — anchor extraction → lead fetch → gated joint reading → deterministic bridge hop (n-grams certified against the 7.2M titles) → full-text search retry → Wikipedia &lt;em&gt;tables&lt;/em&gt; arm (wikitext parsing with rowspan inheritance) → semantic lens → live full-text search.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Gate discipline&lt;/strong&gt;: a challenger is PROMOTED only if it beats the champion on the same 150 questions, through the same matcher, with Wilson CIs reported. Otherwise ROLLBACK — and a rollback is a success of the system, not a failure to hide.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Two infrastructure rules that cost us bugs before they became rules: &lt;strong&gt;never evaluate a model in memory right after training&lt;/strong&gt; (post-train state measures 0% on a perfectly healthy model — save, reload from disk, then gate), and &lt;strong&gt;all extraction LLM calls at temperature 0&lt;/strong&gt; (temp 0.1 anchoring injected ±4–6 points of run-to-run variance into every gate we ever ran).&lt;/p&gt;

&lt;h2&gt;
  
  
  3. The campaign: 33.3% → 52.0% in four days
&lt;/h2&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Step&lt;/th&gt;
&lt;th&gt;Mechanism&lt;/th&gt;
&lt;th&gt;Score&lt;/th&gt;
&lt;th&gt;Verdict&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Baseline pipeline&lt;/td&gt;
&lt;td&gt;anchors → lens → joint read&lt;/td&gt;
&lt;td&gt;33.3%&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ fullpool&lt;/td&gt;
&lt;td&gt;show the reader the whole pool, not just top sentences&lt;/td&gt;
&lt;td&gt;34.7%&lt;/td&gt;
&lt;td&gt;kept (reserve)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ FTS-retry&lt;/td&gt;
&lt;td&gt;full-text enrichment when reading fails&lt;/td&gt;
&lt;td&gt;39.3%&lt;/td&gt;
&lt;td&gt;kept&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ entropy-gated routing&lt;/td&gt;
&lt;td&gt;accept a read only if token entropy ≤ 0.6&lt;/td&gt;
&lt;td&gt;42.0%&lt;/td&gt;
&lt;td&gt;PROMOTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ SLOTS&lt;/td&gt;
&lt;td&gt;2-slot "preuve → réponse" prompt with deterministic verification&lt;/td&gt;
&lt;td&gt;43.3%&lt;/td&gt;
&lt;td&gt;PROMOTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ deterministic anchors fix, echo-retry, FR meta-strip&lt;/td&gt;
&lt;td&gt;—&lt;/td&gt;
&lt;td&gt;46.0%&lt;/td&gt;
&lt;td&gt;kept (reserve)&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ FR→EN translation arm&lt;/td&gt;
&lt;td&gt;French meta-questions translated once, read in English&lt;/td&gt;
&lt;td&gt;48.0%&lt;/td&gt;
&lt;td&gt;PROMOTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ Wikipedia tables arm&lt;/td&gt;
&lt;td&gt;wikitext tables → "header: cell" lines → lens&lt;/td&gt;
&lt;td&gt;48.7%&lt;/td&gt;
&lt;td&gt;PROMOTE&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;+ &lt;strong&gt;semantic lens&lt;/strong&gt;
&lt;/td&gt;
&lt;td&gt;12B judges sentence relevance instead of token overlap&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;52.0%&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;PROMOTE&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;What did &lt;strong&gt;not&lt;/strong&gt; work — measured, gated, published:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;
&lt;strong&gt;Self-consistency ×5&lt;/strong&gt;: null. The literature assumes random errors that voting smooths; ours are &lt;em&gt;systematic&lt;/em&gt; (wrong page → same wrong answer 5 times).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Best-of-N by confidence, inter-path consensus&lt;/strong&gt;: null, same reason — independent paths converge to the &lt;em&gt;same&lt;/em&gt; errors.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;IRCoT&lt;/strong&gt; (academic interleaved retrieval-CoT): 10% naive, 25% hybrid — our sequential pipeline beats it by 17+ points. For a 3B, anchoring by exact titles beats conversational retrieval.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Query planner&lt;/strong&gt; (typed decomposition): rollback both directions — deterministic decomposition doesn't convert hard failures; they're hard for rules too.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SpanLift&lt;/strong&gt; (generation-free QA: enumerate spans, judge by evidence lift): 1.3% — catastrophic. The candidate enumerator produced sentence fragments, and the scorer missed even when the truth was a candidate. Paradigm abandoned on 3B.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;GRPO&lt;/strong&gt; (both reader and GSM8K): gradient desert is structural (&lt;code&gt;frac_reward_zero_std = 0.8&lt;/code&gt;). We fixed the mechanism (dense rewards → 0.2, reward flowing) — and transfer to the target distribution was &lt;em&gt;still null&lt;/em&gt;. Two independent adapters scored &lt;em&gt;identically&lt;/em&gt; to the champion.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deep plaintext retrieval&lt;/strong&gt; (full pages beyond the lead) and &lt;strong&gt;live Wikipedia full-text search&lt;/strong&gt;: both net zero — every win overlapped an existing arm's rescue.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  4. The capstone: 3B trained + orchestrated vs 7B zero-shot
&lt;/h2&gt;

&lt;p&gt;Same chain, same questions, same matcher, same deterministic gates. The &lt;em&gt;only&lt;/em&gt; variable swapped: the reader (Qwen2.5-7B-Instruct, 4-bit, no LoRA, no tuning).&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Arm wins&lt;/th&gt;
&lt;th&gt;3B (78/150 = 52.0%)&lt;/th&gt;
&lt;th&gt;7B (75/150 = 50.0%)&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;Primary reader arm&lt;/td&gt;
&lt;td&gt;50&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;66&lt;/strong&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;Crutch arms (semantic lens, tables, fallbacks)&lt;/td&gt;
&lt;td&gt;&lt;strong&gt;+28&lt;/strong&gt;&lt;/td&gt;
&lt;td&gt;+9&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;Raw capacity wins the primary arm (+16 for the 7B). But the crutches — each one built on a &lt;em&gt;measured&lt;/em&gt; 3B failure mode (echo killed by SLOTS, entropy calibrated at 0.05–0.21 for correct vs 1.0+ for guessing, deterministic proof verification) — add 28 points to the 3B and only 9 to the 7B. The 7B has no echo to fix and its entropy sits near 0.002, making the calibrated gate useless. &lt;strong&gt;The crutches don't transfer because they were never generic — they were prosthetics fitted to a specific patient's limp.&lt;/strong&gt;&lt;/p&gt;

&lt;p&gt;Honest caveats: the confidence intervals overlap (we claim "ties or beats", not "beats"); the 7B is 4-bit quantized and zero-shot — a &lt;em&gt;tuned&lt;/em&gt; 7B would do better, and that's a fine next experiment.&lt;/p&gt;

&lt;h2&gt;
  
  
  5. What we actually learned (the publishable part)
&lt;/h2&gt;

&lt;ol&gt;
&lt;li&gt;
&lt;strong&gt;Contamination decides what a benchmark measures.&lt;/strong&gt; On memorized knowledge, retrieval adds nothing; on post-cutoff knowledge, a sovereign pipeline turns 0% into 52%.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Small-model errors are systematic, not random.&lt;/strong&gt; Everything built on smoothing randomness (voting, consensus, best-of-n) failed four independent times. Everything built on &lt;em&gt;diagnosing the systematic class&lt;/em&gt; worked.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Entropy is a calibrated confidence signal nobody was reading.&lt;/strong&gt; Correct answers: 0.05–0.21. Echoes/guesses: 1.0–2.27. One threshold, zero training, +2 points.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;SLOTS = externalized working memory.&lt;/strong&gt; Force the model to first &lt;em&gt;copy the proof sentence&lt;/em&gt;, then &lt;em&gt;copy the answer from inside the proof&lt;/em&gt;, and verify both deterministically at receipt. The meta-question echo class (24 questions) died (3 left).&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Context quality beats context coverage.&lt;/strong&gt; The semantic lens won 8 net questions — 4× its coverage diagnosis — because a relevance-filtered context &lt;em&gt;reads better&lt;/em&gt;, not just &lt;em&gt;finds more&lt;/em&gt;. Measure arms in-chain, not by detection rate.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;Deterministic gates catch the bugs that fake results.&lt;/strong&gt; In-memory post-training evals, a &lt;code&gt;urllib&lt;/code&gt; import swallowed by &lt;code&gt;try/except&lt;/code&gt; (the "reader" we benchmarked for days was the fallback), a wedged CUDA process serving empty answers, ±6-point anchoring noise — every one of these would have become a false claim without the discipline.&lt;/li&gt;
&lt;li&gt;
&lt;strong&gt;DPO is the only training move that ever broke a plateau&lt;/strong&gt; (GSM8K 60→67, reader 42→45, twice confirmed). SFT saturates; GRPO's gradient desert is structural.&lt;/li&gt;
&lt;/ol&gt;

&lt;h2&gt;
  
  
  6. Reproducibility
&lt;/h2&gt;

&lt;ul&gt;
&lt;li&gt;Benchmark: &lt;strong&gt;post-cutoff-150&lt;/strong&gt;, public on Kaggle, with the contamination filter (naked = 0% verified).&lt;/li&gt;
&lt;li&gt;All scores from deterministic gates (Wilson 95% CI), same matcher everywhere (a lower bound, consistently applied — comparisons valid).&lt;/li&gt;
&lt;li&gt;6 promotions, 6 rollbacks — all archived with per-question outputs.&lt;/li&gt;
&lt;li&gt;Hardware: one RTX 5090. No API calls to any frontier model for the &lt;em&gt;answers&lt;/em&gt; — the 12B orchestration LLM and the 3B reader are both fully local.&lt;/li&gt;
&lt;/ul&gt;

&lt;h2&gt;
  
  
  7. Limitations
&lt;/h2&gt;

&lt;p&gt;150 questions is honest but small; we report CIs and refuse to claim deltas inside the noise. The matcher (containment + token subset) undercounts morphological variants — scores are lower bounds. The 7B comparison is zero-shot/4-bit by design (we isolated the &lt;em&gt;orchestration&lt;/em&gt; variable, not the best possible 7B). The residual 72 failures split between evidence absent from every source we can reach (obscure entities, join-queries) and pure 3B reading capacity — that ceiling is documented, not hidden.&lt;/p&gt;




&lt;p&gt;&lt;em&gt;Built by an autonomous harness that diagnoses its own failures, trains only through deterministic gates, and publishes its rollbacks. The rollbacks taught us more than the wins.&lt;/em&gt;&lt;/p&gt;

</description>
      <category>ai</category>
      <category>machinelearning</category>
      <category>nlp</category>
      <category>research</category>
    </item>
  </channel>
</rss>
