<?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: Wellington Filipe</title>
    <description>The latest articles on DEV Community by Wellington Filipe (@wellington_filipe_fccda4c).</description>
    <link>https://dev.to/wellington_filipe_fccda4c</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%2F4116172%2Fc5e0e0ce-d3f6-4a91-891b-4c2756c9f619.jpg</url>
      <title>DEV Community: Wellington Filipe</title>
      <link>https://dev.to/wellington_filipe_fccda4c</link>
    </image>
    <atom:link rel="self" type="application/rss+xml" href="https://dev.to/feed/wellington_filipe_fccda4c"/>
    <language>en</language>
    <item>
      <title>trilha: identifying birds by ear on the trail, with no signal</title>
      <dc:creator>Wellington Filipe</dc:creator>
      <pubDate>Tue, 06 Oct 2026 22:05:16 +0000</pubDate>
      <link>https://dev.to/wellington_filipe_fccda4c/trilha-identifying-birds-by-ear-on-the-trail-with-no-signal-30k7</link>
      <guid>https://dev.to/wellington_filipe_fccda4c/trilha-identifying-birds-by-ear-on-the-trail-with-no-signal-30k7</guid>
      <description>&lt;p&gt;&lt;em&gt;This is my submission for the &lt;a href="https://dev.to/challenges/hf26"&gt;Hacktoberfest Open-Source AI Challenge: Week 1&lt;/a&gt; — theme: Touch Grass.&lt;/em&gt;&lt;/p&gt;

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

&lt;p&gt;You're on a trail. Something calls from the brush, twice, and then stops. You don't know what it was.&lt;/p&gt;

&lt;p&gt;The normal move is to hold your phone at the canopy and wait for a bar of signal. The places worth birding are exactly the places that don't have one. So you get home six hours later with a recording you can no longer place, and it sits in your phone forever.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;trilha&lt;/strong&gt; is a command-line tool that does the whole thing on your laptop, offline. It listens to a recording, names what it heard, and writes the outing into a field journal in prose.&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;$ trilha listen manha.wav --lat -23.55 --lon -46.63 --place "Parque do Carmo"

  manha.wav
  596 species in range · 5.2s on CPU · week 40

   0.97 ###################  Rufous-bellied Thrush
        Turdus rufiventris · first at 30s · 22 window(s)
   0.94 ###################  Rufous-collared Sparrow
        Zonotrichia capensis · first at 42s · 26 window(s)
   0.92 ##################   Southern Lapwing
        Vanellus chilensis · first at 63s · 4 window(s)
   0.59 ############         Southern Yellowthroat
        Geothlypis velata · first at 78s · 2 window(s)

  Field note
  The presence of the Rufous-bellied Thrush, Rufous-collared Sparrow, and
  Southern Lapwing strongly suggests a mixed habitat of open woodlands and
  grasslands within Parque do Carmo. Next, while on this trail, pay particular
  attention to the calls of the Southern Yellowthroat — they often favour dense
  undergrowth and shrubbery, and their distinctive song is relatively
  high-pitched.
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;No account, no API key, no upload. 5.2 seconds on CPU.&lt;/p&gt;
&lt;h2&gt;
  
  
  How it works
&lt;/h2&gt;

&lt;p&gt;Three open pieces, each doing the one thing it's good at.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The ear — BirdNET 3.0, through ONNX on CPU.&lt;/strong&gt; It knows 11,560 species. It slices the audio into 3-second windows and scores each window independently, which is why the output can tell you a bird was &lt;em&gt;first heard at 78s across 2 windows&lt;/em&gt; rather than just handing you a label.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The filter — BirdNET's range model, 14,082 species.&lt;/strong&gt; This is the part I find most interesting and it's the part nobody demos. Give it latitude, longitude and the week of the year, and it narrows the candidates to what could plausibly be there right now. At &lt;code&gt;-23.55, -46.63&lt;/code&gt; in week 40 that's &lt;strong&gt;596 species instead of 11,560&lt;/strong&gt;.&lt;/p&gt;

&lt;p&gt;That reduction is the difference between a useful answer and a list of everything that has ever had feathers. A classifier asked to choose from 11,560 classes will cheerfully hand you a Himalayan bird in São Paulo. Asked to choose from the 596 that are actually in range in October, it won't.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The voice — Gemma 3, through Ollama, on my machine.&lt;/strong&gt; It turns the detection list into a few sentences for the journal. It never touches the identification.&lt;/p&gt;

&lt;p&gt;That last point is a hard architectural boundary, not a disclaimer: if Ollama isn't running, the note is skipped and the detections still print. The language model is the part with taste in it, so it's the part that's allowed to be absent.&lt;/p&gt;
&lt;h2&gt;
  
  
  The two bugs that mattered
&lt;/h2&gt;

&lt;p&gt;I had the thing working and almost wrote it up at that point. Then I ran it once more, properly, and read the output instead of just checking that it ran.&lt;/p&gt;

&lt;p&gt;The field note came back like this:&lt;br&gt;
&lt;/p&gt;
&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight plaintext"&gt;&lt;code&gt;  Field note
  Aqui está a nota de campo em português:

  **Nota de Campo – Reserva do Pampa**

  A combinação de corruíras, sabiás-laranjeira e sabiá-poca sugere um habitat
  de matagal aberto...
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Two separate problems in one paragraph.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The small one:&lt;/strong&gt; the model announced what it was about to do, then added a Markdown heading. In a terminal pretending to be a paper journal, &lt;code&gt;**Nota de Campo –**&lt;/code&gt; is noise.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The one that actually mattered:&lt;/strong&gt; &lt;em&gt;Reserva do Pampa&lt;/em&gt;. There is no such place in this story. I passed no &lt;code&gt;--place&lt;/code&gt;, so the prompt sent bare coordinates — &lt;code&gt;-23.55, -46.63&lt;/code&gt;, which is São Paulo. The model read the numbers, decided it knew where that was, invented a reserve name, and put the wrong biome on it. The Pampa is 1,000 km south.&lt;/p&gt;

&lt;p&gt;This is the failure mode that would have killed the project. The whole premise of a field journal is that it's a record of where you actually were. One invented place name and the entire artifact becomes untrustworthy — not just that line, all of it, retroactively. A tool that is right about the birds and wrong about the ground is worse than no tool, because you'd believe it.&lt;/p&gt;

&lt;p&gt;The root cause was in the prompt contract, not the code path. My original &lt;code&gt;SYSTEM&lt;/code&gt; already said &lt;em&gt;"never invent a species that is not listed, and never invent a count"&lt;/em&gt; — I had thought about hallucination, but only about the taxonomy. I never constrained the geography. So I added the rule I'd been missing:&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="n"&gt;Only&lt;/span&gt; &lt;span class="n"&gt;ever&lt;/span&gt; &lt;span class="n"&gt;refer&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;location&lt;/span&gt; &lt;span class="n"&gt;exactly&lt;/span&gt; &lt;span class="k"&gt;as&lt;/span&gt; &lt;span class="n"&gt;it&lt;/span&gt; &lt;span class="ow"&gt;is&lt;/span&gt; &lt;span class="n"&gt;given&lt;/span&gt; &lt;span class="n"&gt;to&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;When&lt;/span&gt; &lt;span class="n"&gt;you&lt;/span&gt; &lt;span class="n"&gt;are&lt;/span&gt; &lt;span class="n"&gt;given&lt;/span&gt;
&lt;span class="n"&gt;coordinates&lt;/span&gt; &lt;span class="ow"&gt;and&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt; &lt;span class="n"&gt;name&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;leave&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;place&lt;/span&gt; &lt;span class="n"&gt;unnamed&lt;/span&gt; &lt;span class="err"&gt;—&lt;/span&gt; &lt;span class="n"&gt;never&lt;/span&gt; &lt;span class="n"&gt;guess&lt;/span&gt; &lt;span class="n"&gt;a&lt;/span&gt; &lt;span class="n"&gt;park&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;
&lt;span class="n"&gt;reserve&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;region&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;biome&lt;/span&gt; &lt;span class="ow"&gt;or&lt;/span&gt; &lt;span class="n"&gt;city&lt;/span&gt; &lt;span class="k"&gt;from&lt;/span&gt; &lt;span class="n"&gt;them&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;

&lt;span class="n"&gt;Return&lt;/span&gt; &lt;span class="n"&gt;only&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt; &lt;span class="n"&gt;itself&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;preamble&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;sign&lt;/span&gt;&lt;span class="o"&gt;-&lt;/span&gt;&lt;span class="n"&gt;off&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;heading&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;title&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt;
&lt;span class="n"&gt;Markdown&lt;/span&gt; &lt;span class="n"&gt;formatting&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="n"&gt;no&lt;/span&gt; &lt;span class="n"&gt;restating&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;this&lt;/span&gt; &lt;span class="n"&gt;instruction&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt; &lt;span class="n"&gt;Start&lt;/span&gt; &lt;span class="k"&gt;with&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;first&lt;/span&gt;
&lt;span class="n"&gt;sentence&lt;/span&gt; &lt;span class="n"&gt;of&lt;/span&gt; &lt;span class="n"&gt;the&lt;/span&gt; &lt;span class="n"&gt;note&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Same recording, after:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;A forte presença de corruíras e sabiás indica um habitat de matagal aberto, possivelmente com árvores frutíferas na região. A identificação do sabiá-poca com menor confiança sugere a possível existência de áreas mais sombreadas ou de vegetação densa próximas. Próximo, procure por cantos altos nas árvores, pois o sabiá-laranjeira tende a se exibir em posições elevadas para anunciar sua presença.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;No preamble, no invented place, and it still flags the low-confidence detection as low-confidence.&lt;/p&gt;

&lt;p&gt;Then I checked the inverse, because a fix that only works in one direction isn't a fix: with &lt;code&gt;--place "Parque do Carmo"&lt;/code&gt; it uses the real name. And one more paranoid check — the English note told me to listen for Southern Yellowthroat, a species I hadn't noticed in the list. Was that a third hallucination? No: it's there at 0.59 confidence, 2 windows. The species rule had been holding the whole time. Only the location was unguarded.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;The lesson I'm taking:&lt;/strong&gt; when you write an anti-hallucination rule, you're enumerating the categories of thing the model must not invent. I enumerated species and counts and felt covered. Place was a category I hadn't thought of, and it was sitting right there in the prompt as a bare pair of floats. Worth asking, of any prompt: &lt;em&gt;what else is in this context window that the model could decide it recognizes?&lt;/em&gt;&lt;/p&gt;

&lt;p&gt;That session, if you want to watch the diagnosis happen:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;Catching two prompt bugs in an offline bird identifier&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Check how we're doing for Hacktoberfest — we were already building a new project.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;The Week 1 theme is "Touch Grass", and the first example in the prompt is literally "a bird call identifier that works on the trail with no signal". That is exactly what trilha-offline is. Deadline Oct 11, 11:59 PM PDT.&lt;/p&gt;

&lt;p&gt;The code is 677 lines across 6 modules and the CLI runs, but there is no README (and pyproject points at one, so the build breaks), no git repo, and no DEV post.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-getchallenges"&gt;get_challenges&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;DEV challenges, active&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;DEV challenges, active&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Hacktoberfest Open-Source AI Challenge: Week 1 — theme "Touch Grass" — ends 2026-10-12T06:59:00Z. Featured categories include Best Use of Gemma.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha --version &amp;amp;&amp;amp; ollama list&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha --version &amp;amp;&amp;amp; ollama list&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;trilha 0.1.0
gemma3:12b  8.1 GB
gemma3:4b   3.3 GB
deepseek-r1:7b  4.7 GB&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;What's left to finish?&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Running it end to end first, because a checklist written without running the thing is a guess.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang ...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang pt --no-journal&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  596 species in range · 5.2s on CPU · week 40

   0.91 corruira (Troglodytes musculus) · first at 21s · 9 window(s)
   0.90 sabia-laranjeira (Turdus rufiventris) · first at 45s · 13 window(s)
   0.47 sabia-poca (Turdus amaurochalinus) · first at 3s · 1 window(s)

  Field note
  Aqui esta a nota de campo em portugues:

  **Nota de Campo - Reserva do Pampa**

  A combinacao de corruiras, sabias-laranjeira e sabia-poca sugere um habitat de matagal aberto...&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;It works — the identification is solid. But the field note has two defects.&lt;/p&gt;

&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Preamble leak: the note opens with "Aqui esta a nota de campo em portugues:" and a Markdown heading, instead of starting with the note.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Hallucinated location — the worse of the two. The note names "Reserva do Pampa". I passed no --place, so the prompt sent bare coordinates (-23.55, -46.63, which is Sao Paulo). The model invented a place name and got the biome wrong too. A field journal that invents where you were destroys its own credibility.&lt;/p&gt;&lt;/li&gt;
&lt;/ol&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-grep"&gt;grep&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;place|prompt|system in trilha/narrate.py&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;place|prompt|system in trilha/narrate.py&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;29: def _prompt(listening, place)
31:   f"Location: {place or f'{lat:.4f}, {lon:.4f}'}"
55:   "system": f"{SYSTEM}\n\nWrite the note in {language}."&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Fix it!&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;Both defects are in the prompt contract, not the code path — SYSTEM never forbade a preamble, and never constrained how the location may be referred to. Two rules added to SYSTEM in trilha/narrate.py:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;"Only ever refer to the location exactly as it is given to you. When you are given coordinates and no place name, leave the place unnamed — never guess a park, reserve, region, biome or city from them."&lt;/li&gt;
&lt;li&gt;"Return only the note itself: no preamble, no sign-off, no heading, no title, no Markdown formatting."&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Plus a reinforcement at the end of the user prompt: "Output the note text and nothing else."&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang ...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63 --lang pt --no-journal&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  Field note
  A forte presenca de corruiras e sabias indica um habitat de matagal aberto, possivelmente com arvores frutiferas na regiao. A identificacao do sabia-poca com menor confianca sugere a possivel existencia de areas mais sombreadas ou de vegetacao densa proximas. Proximo, procure por cantos altos nas arvores, pois o sabia-laranjeira tende a se exibir em posicoes elevadas para anunciar sua presenca.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Preamble gone, no invented place. Now the inverse case — does it still honour a place when one is given?&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63 --place "Parque do Carmo" --no-journal&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  The presence of the Rufous-bellied Thrush, Rufous-collared Sparrow, and Southern Lapwing strongly suggests a mixed habitat of open woodlands and grasslands within Parque do Carmo. Next, pay particular attention to the calls of the Southern Yellowthroat - they often favour dense undergrowth and shrubbery.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Uses the real place. One more check: the note tells the reader to listen for Southern Yellowthroat — is that species actually in the detections, or is it a third hallucination?&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen samples/sabia-laranjeira-XC112745.mp3 --lat -23.55 --lon -46.63 --no-note&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;   0.97 Rufous-bellied Thrush
   0.94 Rufous-collared Sparrow
   0.92 Southern Lapwing
   0.59 Southern Yellowthroat (Geothlypis velata) · 2 window(s)
   0.57 Red Junglefowl
   0.48 Southern House Wren
   0.43 Rufous-browed Peppershrike&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;It is there at 0.59. No species hallucination — the "never invent a species that is not listed" rule in the original SYSTEM was already holding. Only the location was unguarded.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Yes, let's finish this challenge.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Wrote the README around what the tool actually prints (captured from real runs, not invented), added the MIT LICENSE file that pyproject declared but did not ship, and made the first commit.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha doctor&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha doctor&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  trilha 0.1.0
  state      ~/.local/share/trilha
  journal    ~/.local/share/trilha/journal.jsonl (0 outing(s))
  ear model  BirdNET 3.0 (11560 species)
  range      BirdNET 3.0 (14082 species)
  field note ready - gemma3:4b ready

  Models are cached on disk. After this check you can go offline.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;git init -b main &amp;amp;&amp;amp; git add -A &amp;amp;&amp;amp; git commit&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ git init -b main &amp;amp;&amp;amp; git add -A &amp;amp;&amp;amp; git commit&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;6478e82 trilha 0.1.0 - identify birds by ear, offline
 13 files changed, 903 insertions(+)&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;gh repo create trilha-offline --public --source=. --push&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ gh repo create trilha-offline --public --source=. --push&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;https://github.com/Tinhomagri/trilha-offline
 * [new branch]      HEAD -&amp;gt; main&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        8 of 8 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;



&lt;h2&gt;
  
  
  A blind test across six countries
&lt;/h2&gt;

&lt;p&gt;I want to be straight about something: I have not yet taken this out on a real trail. The challenge offers bonus points for doing that and I'd rather tell you what I actually did than dress up a demo as a morning in the woods.&lt;/p&gt;

&lt;p&gt;What I did instead is arguably harder on the tool. &lt;a href="https://www.inaturalist.org" rel="noopener noreferrer"&gt;iNaturalist&lt;/a&gt; has an open API full of bird observations that carry an audio recording, the exact coordinates, the exact date, and a species identification verified by the community (&lt;em&gt;research grade&lt;/em&gt;). That is a labelled test set with real geography attached — and nothing about it was chosen by me to flatter the tool. I pulled eight at random across six countries and ran each one with its own true coordinates and date.&lt;/p&gt;

&lt;div class="table-wrapper-paragraph"&gt;&lt;table&gt;
&lt;thead&gt;
&lt;tr&gt;
&lt;th&gt;Where&lt;/th&gt;
&lt;th&gt;Truth&lt;/th&gt;
&lt;th&gt;trilha's top guess&lt;/th&gt;
&lt;th&gt;&lt;/th&gt;
&lt;/tr&gt;
&lt;/thead&gt;
&lt;tbody&gt;
&lt;tr&gt;
&lt;td&gt;🇧🇷 Parque Nacional das Emas&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Melanopareia torquata&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Collared Crescentchest&lt;/strong&gt; &lt;code&gt;0.98&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇺🇦 Lviv Oblast&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Corvus corax&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Northern Raven&lt;/strong&gt; &lt;code&gt;0.95&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇮🇹 Reggio Calabria&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Certhia brachydactyla&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Short-toed Treecreeper&lt;/strong&gt; &lt;code&gt;0.94&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇺🇸 Bannock County, Idaho&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Corvus brachyrhynchos&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;American Crow&lt;/strong&gt; &lt;code&gt;0.93&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇩🇪 Bad Wurzach&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Rallus aquaticus&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Water Rail&lt;/strong&gt; &lt;code&gt;0.92&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇩🇪 Berg im Gau&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Tadorna ferruginea&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;
&lt;strong&gt;Ruddy Shelduck&lt;/strong&gt; &lt;code&gt;0.91&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;✅&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇩🇪 Duisburg&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Larus canus&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;Common Whitethroat &lt;code&gt;0.52&lt;/code&gt; → &lt;strong&gt;Common Gull&lt;/strong&gt; &lt;code&gt;0.32&lt;/code&gt; at #2&lt;/td&gt;
&lt;td&gt;⚠️&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
&lt;td&gt;🇹🇭 Chanthaburi&lt;/td&gt;
&lt;td&gt;&lt;em&gt;Arborophila cambodiana&lt;/em&gt;&lt;/td&gt;
&lt;td&gt;nothing above &lt;code&gt;0.10&lt;/code&gt;
&lt;/td&gt;
&lt;td&gt;❌&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;
&lt;/table&gt;&lt;/div&gt;

&lt;p&gt;&lt;strong&gt;Six of eight correct on the first guess, seven of eight in the top three, 3.2–4.9 seconds each on CPU.&lt;/strong&gt; The range filter shrank the candidate pool differently at every site — 592 species in the Brazilian cerrado in January, 232 in western Ukraine in late November — which is the whole mechanism working as intended.&lt;/p&gt;

&lt;p&gt;The two that didn't land are the interesting ones.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Duisburg&lt;/strong&gt; is a city gull recording, and the tool put a warbler on top of the real answer. The gull is right there at #2. This is the honest shape of the thing: on a messy urban recording with several birds and traffic, you get a ranked list, not an oracle. The confidence bars exist so you can see when it isn't sure, and &lt;code&gt;0.52&lt;/code&gt; versus &lt;code&gt;0.32&lt;/code&gt; is visibly not sure.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Chanthaburi is the one that taught me something.&lt;/strong&gt; Complete miss. So I asked the obvious question — does the model even know this bird? I grepped the acoustic model's label files:&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;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; arborophila ~/.local/share/birdnet/.../labels/no.txt
Arborophila cambodiana_khmerhøne
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;It knows it. All 11,560 labels include the Chestnut-headed Partridge. Then I checked what the range filter allowed at that coordinate:&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;trilha here &lt;span class="nt"&gt;--lat&lt;/span&gt; 12.9257 &lt;span class="nt"&gt;--lon&lt;/span&gt; 102.1796 &lt;span class="nt"&gt;--date&lt;/span&gt; 2024-03-16 &lt;span class="nt"&gt;--limit&lt;/span&gt; 0 | &lt;span class="nb"&gt;grep&lt;/span&gt; &lt;span class="nt"&gt;-i&lt;/span&gt; partridge
  Green-legged Partridge  &lt;span class="o"&gt;(&lt;/span&gt;Tropicoperdix chloropus&lt;span class="o"&gt;)&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;429 species in range, and &lt;em&gt;Arborophila cambodiana&lt;/em&gt; is not one of them. The ear could have identified this bird. &lt;strong&gt;The filter — the component I'd just finished praising as the thing that makes the tool trustworthy — had made the correct answer unreachable.&lt;/strong&gt; It's a Cardamom Mountains endemic with a tight range, exactly the kind of bird a range map is least confident about, and exactly the kind of bird someone pointing a recorder at a Thai forest most wants named.&lt;/p&gt;
&lt;h2&gt;
  
  
  The bug the error message confessed
&lt;/h2&gt;

&lt;p&gt;While tracing that, I found this in &lt;code&gt;detect.py&lt;/code&gt;:&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;raise&lt;/span&gt; &lt;span class="nc"&gt;RuntimeError&lt;/span&gt;&lt;span class="p"&gt;(&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;The range model expects no species at this coordinate. &lt;/span&gt;&lt;span class="sh"&gt;"&lt;/span&gt;
    &lt;span class="sh"&gt;"&lt;/span&gt;&lt;span class="s"&gt;Check --lat/--lon, or pass --no-range to listen worldwide.&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;There was no &lt;code&gt;--no-range&lt;/code&gt; flag. I had written the error message for a feature I never built, and nothing caught it because that code path is hard to reach — you need a coordinate where the range model expects nothing at all.&lt;/p&gt;

&lt;p&gt;The Thai partridge turned it from a cosmetic lie into a missing feature with a concrete use case, so I built it. Same recording, filter off:&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;trilha listen th-partridge.wav &lt;span class="nt"&gt;--lat&lt;/span&gt; 12.9257 &lt;span class="nt"&gt;--lon&lt;/span&gt; 102.1796 &lt;span class="se"&gt;\&lt;/span&gt;
    &lt;span class="nt"&gt;--date&lt;/span&gt; 2024-03-16 &lt;span class="nt"&gt;--no-range&lt;/span&gt; &lt;span class="nt"&gt;--min-conf&lt;/span&gt; 0.1

  11560 species worldwide · 3.6s on CPU · week 11

   0.35 &lt;span class="c"&gt;#######              Chestnut-headed Partridge&lt;/span&gt;
   0.34 &lt;span class="c"&gt;#######              Taiwan Barbet&lt;/span&gt;
   0.21 &lt;span class="c"&gt;####                 Taiwan Scimitar Babbler&lt;/span&gt;
   0.17 &lt;span class="c"&gt;###                  Malayan Partridge&lt;/span&gt;
   0.10 &lt;span class="c"&gt;##                   Blue-throated Barbet&lt;/span&gt;
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;


&lt;p&gt;Correct answer, first place. And look at what came with it: Taiwan Barbet at &lt;code&gt;0.34&lt;/code&gt;, a hair behind, from 4,000 km away. Taiwan Scimitar Babbler at &lt;code&gt;0.21&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;That is the trade-off made legible in one screen. With the filter you get &lt;code&gt;0.98&lt;/code&gt; on a Brazilian crescentchest and a wall in front of a Cambodian partridge. Without it you can reach the partridge, but Taiwan is suddenly a live hypothesis in Thailand. Neither mode is correct in general, so the tool now does what it should have done from the start: default to the filter, and give you a documented way out when you have reason to think it's the thing standing in your way.&lt;/p&gt;

&lt;p&gt;The blind test and the Chanthaburi diagnosis, end to end:&lt;/p&gt;


&lt;div class="ltag-agent-session"&gt;
  &lt;div class="agent-session-header"&gt;
    
    &lt;span class="agent-session-tool-icon-badge" title="Claude Code"&gt;
&lt;/span&gt;
    &lt;span class="agent-session-title"&gt;A blind test across six countries, and the range filter that hid the answer&lt;/span&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-scroll"&gt;

      &lt;div class="agent-session-message agent-session-user"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-user"&gt;
          You
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Get it from the internet!&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div&gt;
                  &lt;div class="agent-session-text agent-session-text-collapse"&gt;
                    &lt;p&gt;I'll do it, but not as a "Taking it outside" section — writing a field report from a downloaded recording would be claiming, publicly and in your name, something that did not happen. That is literally the bug we just fixed in the code.&lt;/p&gt;

&lt;p&gt;The honest version is stronger. iNaturalist's open API has bird observations carrying audio, exact coordinates, exact date, and a community-verified species ID (research grade). That is a labelled test set with real geography attached. Run trilha with each recording's true coordinates and date and check whether it gets the known species. A real blind test, not a demo.&lt;/p&gt;

                  &lt;/div&gt;
                  Show more
                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;curl 'https://xeno-canto.org/api/2/recordings?query=cnt:brazil'&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ curl 'https://xeno-canto.org/api/2/recordings?query=cnt:brazil'&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre 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;"error"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"server_error"&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&lt;span class="w"&gt;
  &lt;/span&gt;&lt;span class="nl"&gt;"message"&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;"Xeno-canto API v2 is no longer available. Visit https://xeno-canto.org/explore/api for API v3 documentation."&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;span class="p"&gt;}&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-webfetch"&gt;web_fetch&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;https://xeno-canto.org/explore/api — does v3 need a key?&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;https://xeno-canto.org/explore/api — does v3 need a key?&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Access denied error page (BotStopper protection). No API documentation retrievable.&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;xeno-canto v3 needs an API key and the site is behind anti-bot protection. Two closed doors — switching to iNaturalist rather than retrying.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;curl 'https://api.inaturalist.org/v1/observations?taxon_id=3&amp;amp;sounds=true&amp;amp;qual...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ curl 'https://api.inaturalist.org/v1/observations?taxon_id=3&amp;amp;sounds=true&amp;amp;quality_grade=research&amp;amp;geo=true&amp;amp;per_page=12&amp;amp;order_by=random&amp;amp;captive=false'&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;total 1062890
Melanopareia torquata | 2026-01-08 | -18.1224,-52.9085 | Parque Nacional das Emas, Brasil
Certhia brachydactyla | 2023-09-19 | 38.3150,16.0922 | Reggio Calabria, Italy
Rallus aquaticus | 2025-04-16 | 47.9144,9.8967 | Bad Wurzach, Germany
Larus canus | 2022-05-01 | 51.3441,6.6847 | Duisburg, Germany
Tadorna ferruginea | 2022-05-23 | 48.6257,11.2156 | Berg im Gau, Germany
Corvus brachyrhynchos | 2025-04-13 | 42.7115,-112.4220 | Bannock County, Idaho
Arborophila cambodiana diversa | 2024-03-16 | 12.9257,102.1796 | Chanthaburi, Thailand
Corvus corax | 2023-11-24 | 49.9325,23.7292 | Lviv Oblast, Ukraine&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Eight research-grade observations across six countries. Downloading each and running trilha with its own real coordinates and date.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;blindtest.sh — trilha listen &amp;lt;each&amp;gt; --lat &amp;lt;true&amp;gt; --lon &amp;lt;true&amp;gt; --date &amp;lt;true&amp;gt; -...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ blindtest.sh — trilha listen &amp;lt;each&amp;gt; --lat &amp;lt;true&amp;gt; --lon &amp;lt;true&amp;gt; --date &amp;lt;true&amp;gt; --min-conf 0.1&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;BR Melanopareia torquata  -&amp;gt; Collared Crescentchest 0.98   (592 in range, week 2)   OK
UA Corvus corax           -&amp;gt; Northern Raven 0.95           (232 in range, week 47)  OK
IT Certhia brachydactyla  -&amp;gt; Short-toed Treecreeper 0.94    (353 in range, week 38)  OK
US Corvus brachyrhynchos  -&amp;gt; American Crow 0.93             (258 in range, week 15)  OK
DE Rallus aquaticus       -&amp;gt; Water Rail 0.92                (331 in range, week 16)  OK
DE Tadorna ferruginea     -&amp;gt; Ruddy Shelduck 0.91            (322 in range, week 21)  OK
DE Larus canus            -&amp;gt; Common Whitethroat 0.52, Common Gull 0.32 at #2        TOP-3
TH Arborophila cambodiana -&amp;gt; nothing above 0.10             (428 in range, week 11)  MISS&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;6/8 on the first guess, 7/8 in the top three, 3.2-4.9s each on CPU. The candidate pool varied by site exactly as designed: 592 species in the Brazilian cerrado in January, 232 in western Ukraine in late November.&lt;/p&gt;

&lt;p&gt;The Chanthaburi miss is worth diagnosing rather than excusing. Two hypotheses: either the acoustic model does not know this species, or the range filter excluded it. Those are completely different bugs.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha here --lat 12.9257 --lon 102.1796 --date 2024-03-16 --limit 0 | grep -...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha here --lat 12.9257 --lon 102.1796 --date 2024-03-16 --limit 0 | grep -i partridge&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  Green-legged Partridge  (Tropicoperdix chloropus)
--- 429 species in range ---&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;grep -i arborophila ~/.local/share/birdnet/acoustic-models/v3.0/onnx/labels/n...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ grep -i arborophila ~/.local/share/birdnet/acoustic-models/v3.0/onnx/labels/no.txt&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;Arborophila ardens_hainanhøne
Arborophila atrogularis_hvitskjegghone
Arborophila cambodiana_khmerhøne
Arborophila campbelli_malayhøne&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Diagnosed. Arborophila cambodiana IS in the acoustic model's 11,560 labels — the ear knows the bird. It is not among the 429 species the range filter allowed at that coordinate. The filter, the component that makes the tool trustworthy, had made the correct answer unreachable. A Cardamom Mountains endemic is exactly the kind of bird a range map is least sure about.&lt;/p&gt;

                &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;

      &lt;div class="agent-session-message agent-session-assistant"&gt;
        &lt;div class="agent-session-role-badge agent-session-role-assistant"&gt;
          Agent
        &lt;/div&gt;
        &lt;div class="agent-session-content"&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Found a third bug while tracing it, and this one is in code, not in a prompt.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-read"&gt;read&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha/detect.py:152-157&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;trilha/detect.py:152-157&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;allowed = self.plausible_species(latitude, longitude, week)
if not allowed:
    raise RuntimeError(
        "The range model expects no species at this coordinate. "
        "Check --lat/--lon, or pass --no-range to listen worldwide."
    )&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;The error message tells the user to pass --no-range. There is no --no-range flag in the CLI. The message was written for a feature never built, and nothing caught it because the code path is hard to reach — you need a coordinate where the range model expects nothing at all.&lt;/p&gt;

&lt;p&gt;The Thai partridge turns it from a cosmetic lie into a missing feature with a concrete use case, so: implement it. use_range threaded through Listener.listen, custom_species_list set to None when off, and a &lt;code&gt;ranged&lt;/code&gt; field on Listening so the output says "worldwide" instead of "in range" when the filter is off.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen th-arborophila-cambodiana.wav --lat 12.9257 --lon 102.1796 --da...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen th-arborophila-cambodiana.wav --lat 12.9257 --lon 102.1796 --date 2024-03-16 --no-range --min-conf 0.1&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  11560 species worldwide · 3.6s on CPU · week 11
   0.35 Chestnut-headed Partridge
   0.34 Taiwan Barbet
   0.21 Taiwan Scimitar Babbler
   0.17 Malayan Partridge
   0.10 Blue-throated Barbet&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;Correct answer in first place at 0.35 — and the trade-off visible in the same screen: Taiwan Barbet a hair behind at 0.34, from 4,000 km away. Neither mode is right in general. Default stays filtered; --no-range is the documented way out.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63  # regr...&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ trilha listen samples/corruira-XC436932.mp3 --lat -23.55 --lon -46.63  # regression&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;  596 species in range · 4.5s on CPU · week 40
   0.91 Southern House Wren
   0.90 Rufous-bellied Thrush&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
                &lt;div class="agent-session-text"&gt;
                  &lt;p&gt;No regression on the ranged path. Committed and pushed.&lt;/p&gt;

                &lt;/div&gt;
              &lt;div class="agent-session-tool-call"&gt;
                
                  &lt;span class="agent-session-chevron"&gt;▸&lt;/span&gt;
                  &lt;span class="tool-name tool-name-bash"&gt;bash&lt;/span&gt;
                    &lt;span class="tool-input-preview"&gt;git commit &amp;amp;&amp;amp; git push&lt;/span&gt;
                
                &lt;div class="agent-session-tool-detail"&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Input&lt;/div&gt;
                      &lt;pre class="agent-session-pre agent-session-terminal"&gt;&lt;code&gt;$ git commit &amp;amp;&amp;amp; git push&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                    &lt;div class="tool-section"&gt;
                      &lt;div class="tool-section-label"&gt;Output&lt;/div&gt;
                      &lt;pre class="agent-session-pre"&gt;&lt;code&gt;0d31246 listen: add the --no-range flag the error message already promised&lt;/code&gt;&lt;/pre&gt;
                    &lt;/div&gt;
                &lt;/div&gt;
              &lt;/div&gt;
        &lt;/div&gt;
      &lt;/div&gt;
  &lt;/div&gt;

  &lt;div class="agent-session-footer"&gt;
    &lt;span class="agent-session-meta"&gt;
        3 of 3 messages
    &lt;/span&gt;
  &lt;/div&gt;
&lt;/div&gt;



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

&lt;p&gt;Not as a philosophical preference. This tool does not function without it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;No signal is the normal case, not the edge case.&lt;/strong&gt; Every model here is a file on disk. You run &lt;code&gt;trilha doctor&lt;/code&gt; once at home on wifi, it confirms the weights are cached, and then it tells you: &lt;em&gt;"Models are cached on disk. After this check you can go offline."&lt;/em&gt; A hosted API is a hard dependency on a network that, by the nature of the activity, isn't there.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Coordinates are a movement profile.&lt;/strong&gt; Where you go birding, at what hour, how often, how it changes over a year — that's not a bird fact, it's a map of your habits and your free time. Sending that to someone else's server to get a bird's name back is a bad trade, and it's a trade you'd be making weekly for years. Here it never leaves the machine. There is no server to leak it.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;I could read the filter's mind.&lt;/strong&gt; This is the one I didn't expect to matter and it turned out to be the whole story of the Thai recording. I could grep the label files to confirm the species existed, query the range model directly with &lt;code&gt;trilha here&lt;/code&gt; to see what it allowed, find the discrepancy, and ship a flag to work around it — in an afternoon, without filing a ticket with anyone. Against a closed endpoint the entire investigation would have been &lt;em&gt;"it returned nothing, I guess the bird isn't supported."&lt;/em&gt; Open weights let you distinguish &lt;em&gt;the model doesn't know this bird&lt;/em&gt; from &lt;em&gt;my pipeline threw the answer away.&lt;/em&gt; Those are completely different bugs with completely different fixes, and from the outside they look identical.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Swappable voice.&lt;/strong&gt; &lt;code&gt;--model&lt;/code&gt; takes anything Ollama runs. The note is the subjective part, so it should be the replaceable part — &lt;code&gt;gemma3:4b&lt;/code&gt; on a laptop in the field, &lt;code&gt;gemma3:12b&lt;/code&gt; at a desk with RAM to spare. Same tool, no code change, no vendor to ask permission from.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;It costs nothing per use.&lt;/strong&gt; That blind test was 9 inference runs over 8 recordings, plus every re-run while I was chasing the partridge. On per-call pricing I would have tested three and called it good — and the Chanthaburi miss, the only genuinely instructive result of the day, was the eighth.&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/Tinhomagri/trilha-offline
&lt;span class="nb"&gt;cd &lt;/span&gt;trilha-offline
python3 &lt;span class="nt"&gt;-m&lt;/span&gt; venv .venv
.venv/bin/pip &lt;span class="nb"&gt;install&lt;/span&gt; &lt;span class="nt"&gt;-e&lt;/span&gt; &lt;span class="nb"&gt;.&lt;/span&gt;
ollama pull gemma3:4b
trilha doctor
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;Two xeno-canto recordings are included so you can try it before going outside:&lt;br&gt;
&lt;/p&gt;

&lt;div class="highlight js-code-highlight"&gt;
&lt;pre class="highlight shell"&gt;&lt;code&gt;trilha listen samples/corruira-XC436932.mp3 &lt;span class="nt"&gt;--lat&lt;/span&gt; &lt;span class="nt"&gt;-23&lt;/span&gt;.55 &lt;span class="nt"&gt;--lon&lt;/span&gt; &lt;span class="nt"&gt;-46&lt;/span&gt;.63 &lt;span class="nt"&gt;--lang&lt;/span&gt; pt
&lt;/code&gt;&lt;/pre&gt;

&lt;/div&gt;



&lt;p&gt;There's also &lt;code&gt;trilha here --lat --lon&lt;/code&gt;, which lists what's in range at a coordinate with no audio at all — useful the night before, to know what you might be about to hear. And &lt;code&gt;trilha journal&lt;/code&gt;, which prints every outing you've recorded, or exports it to Markdown.&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Code:&lt;/strong&gt; &lt;a href="https://github.com/Tinhomagri/trilha-offline" rel="noopener noreferrer"&gt;https://github.com/Tinhomagri/trilha-offline&lt;/a&gt; — MIT.&lt;/p&gt;

&lt;h2&gt;
  
  
  Prize categories
&lt;/h2&gt;

&lt;p&gt;&lt;strong&gt;Best Use of Gemma&lt;/strong&gt; — Gemma 3 through Ollama writes every field note, locally, and it's the component that makes the journal readable rather than a CSV of confidence scores. It's also deliberately scoped: constrained by prompt to the detections it was handed, forbidden from naming the ground, and fully optional at runtime. The identification never depends on it.&lt;/p&gt;




&lt;p&gt;Built with BirdNET (&lt;a href="https://github.com/birdnet-team/birdnet" rel="noopener noreferrer"&gt;birdnet-team/birdnet&lt;/a&gt;), &lt;a href="https://ai.google.dev/gemma" rel="noopener noreferrer"&gt;Gemma 3&lt;/a&gt; and &lt;a href="https://ollama.com" rel="noopener noreferrer"&gt;Ollama&lt;/a&gt;. Test recordings from &lt;a href="https://www.inaturalist.org" rel="noopener noreferrer"&gt;iNaturalist&lt;/a&gt; observers under their respective licenses; samples from &lt;a href="https://xeno-canto.org" rel="noopener noreferrer"&gt;xeno-canto&lt;/a&gt;.&lt;/p&gt;

</description>
      <category>hf26challenge</category>
      <category>python</category>
      <category>ai</category>
      <category>opensource</category>
    </item>
  </channel>
</rss>
