<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
<channel>
    <title>Dirk Primbs Unprompted - AI</title>
    <link>https://unprompted.dirkprimbs.de/index.html</link>
    <description>Posts tagged &#x27;AI&#x27; on Dirk Primbs Unprompted.</description>
    <language>en-us</language>
    <lastBuildDate>Thu, 16 Jul 2026 04:00:00 GMT</lastBuildDate>
    <atom:link href="https://unprompted.dirkprimbs.de/feeds/ai.xml" rel="self" type="application/rss+xml" />
        <item>
            <title>How AI edits my texts (or not)</title>
            <link>https://unprompted.dirkprimbs.de/posts/polish-model-bakeoff-insights.html</link>
            <guid isPermaLink="true">https://unprompted.dirkprimbs.de/posts/polish-model-bakeoff-insights.html</guid>
            <description>I ran a full comparison of nine different models - from remote giants to my local setup - and found that how finished your draft is matters way more than which model you pick, especially when privacy and speed are non-negotiable.</description>
            <content:encoded><![CDATA[<p>Some context first, because the test only makes sense if you know what I was building toward. I wanted a blog engine that supports my writing instead of replacing it. Not a machine that takes a topic and hands me back an article, but something that takes what I already wrote and helps it over the finish line. On top of that I had two preferences I wasn't willing to trade away: privacy-first and local-first, leaning on open technology wherever I could. Ideally the thing that touches my half-formed thoughts runs on my own machine and never phones home.</p>
<p>That goal is what turned "which model is best" into an actual question worth testing, because "best" depends entirely on the job. So I ran two experiments. First: how do the various models cope when the input is bulleted, fragmented notes versus a finished draft? That's the real fork in how I write - some days I hand it clean prose, some days a pile of stubs. Second: how does the prose itself differ model by model, once you strip out the effect of the input? I wanted to know each model's fingerprint, not just a leaderboard.</p>
<p>Notes below from testing the polish step across 8 configurations (7 remote models + local gemma4:e4b) over the blog's real drafts. What I learned.</p>
<h2>The prompt every model ran</h2>
<p>One thing worth being explicit about: every model got the exact same prompt. No per-model tuning, no coaxing the weaker ones with extra hand-holding. That was the point - I wanted to see the model's own behavior, not my ability to prompt-engineer around it. If I'd tweaked the prompt per model I'd be measuring my patience, not the models.</p>
<p>The prompt frames the model as "the author's editor" and hands it my rough notes. The load-bearing part is a rule I put in caps and at the very top: do not lose ideas. Not "summarize," not "improve" - present all of my thinking, every distinct point, and reuse my own vivid phrasing rather than paraphrasing it into mush. I spelled out that dropping or watering down an idea to make the text shorter or smoother counts as a failure, because that's exactly the "helpful" instinct I was trying to suppress. The model is allowed to reorder for flow, merge fragments into sentences, and add light connective tissue, but it may not invent new claims.</p>
<p>Two more blocks round it out. A voice block pastes in my writing guide verbatim - including the list of words and phrases the model must never use - so "in their voice" isn't left to interpretation. And a format block asks for clean Markdown, translates any German into natural English (I draft in both), and insists on the article body only, no frontmatter or preamble.</p>
<p>Here's the whole thing, with the two injected values marked in braces (my voice guide gets pasted into the first, the raw draft into the second):</p>
<div class="codehilite"><pre><span></span><code><span class="n">You</span><span class="w"> </span><span class="k">are</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">author</span><span class="s1">&#39;s editor. The text below is the author&#39;</span><span class="n">s</span><span class="w"> </span><span class="n">own</span><span class="w"> </span><span class="n">rough</span><span class="w"> </span><span class="n">notes</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">blog</span><span class="w"> </span><span class="n">post</span><span class="p">.</span><span class="w"> </span><span class="n">Turn</span><span class="w"> </span><span class="n">them</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">finished</span><span class="p">,</span><span class="w"> </span><span class="n">flowing</span><span class="w"> </span><span class="n">article</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">their</span><span class="w"> </span><span class="n">voice</span><span class="p">.</span>

<span class="o">[</span><span class="n">MOST IMPORTANT RULE - DO NOT LOSE IDEAS</span><span class="o">]</span>
<span class="n">The</span><span class="w"> </span><span class="n">notes</span><span class="w"> </span><span class="n">contain</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">author</span><span class="s1">&#39;s actual thinking. Your job is to present ALL of it, not a summary of it.</span>
<span class="s1">- Keep every distinct point, argument, example, and section. If the notes make five points, the article makes five points.</span>
<span class="s1">- Keep any vivid phrase, metaphor, analogy, or joke the author used. These are the best part - reuse the author&#39;</span><span class="n">s</span><span class="w"> </span><span class="n">own</span><span class="w"> </span><span class="n">wording</span><span class="p">,</span><span class="w"> </span><span class="n">do</span><span class="w"> </span><span class="ow">not</span><span class="w"> </span><span class="n">paraphrase</span><span class="w"> </span><span class="n">them</span><span class="w"> </span><span class="n">away</span><span class="p">.</span>
<span class="o">-</span><span class="w"> </span><span class="n">Dropping</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">watering</span><span class="w"> </span><span class="n">down</span><span class="w"> </span><span class="n">an</span><span class="w"> </span><span class="n">idea</span><span class="w"> </span><span class="k">to</span><span class="w"> </span><span class="n">make</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="nc">text</span><span class="w"> </span><span class="n">shorter</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">smoother</span><span class="w"> </span><span class="k">is</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">FAILURE</span><span class="p">.</span><span class="w"> </span><span class="k">When</span><span class="w"> </span><span class="ow">in</span><span class="w"> </span><span class="n">doubt</span><span class="p">,</span><span class="w"> </span><span class="n">keep</span><span class="w"> </span><span class="n">it</span><span class="w"> </span><span class="ow">in</span><span class="p">.</span>
<span class="o">-</span><span class="w"> </span><span class="n">You</span><span class="w"> </span><span class="n">may</span><span class="w"> </span><span class="n">reorder</span><span class="w"> </span><span class="k">for</span><span class="w"> </span><span class="n">flow</span><span class="p">,</span><span class="w"> </span><span class="k">merge</span><span class="w"> </span><span class="n">fragments</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="n">sentences</span><span class="p">,</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="k">add</span><span class="w"> </span><span class="n">light</span><span class="w"> </span><span class="n">connective</span><span class="w"> </span><span class="n">tissue</span><span class="p">,</span><span class="w"> </span><span class="n">but</span><span class="w"> </span><span class="n">do</span><span class="w"> </span><span class="ow">not</span><span class="w"> </span><span class="n">invent</span><span class="w"> </span><span class="k">new</span><span class="w"> </span><span class="n">claims</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="n">facts</span><span class="p">.</span>

<span class="o">[</span><span class="n">VOICE</span><span class="o">]</span>
<span class="k">Write</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">way</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">author</span><span class="w"> </span><span class="n">writes</span><span class="p">.</span><span class="w"> </span><span class="k">Match</span><span class="w"> </span><span class="n">this</span><span class="w"> </span><span class="n">guide</span><span class="w"> </span><span class="n">exactly</span><span class="p">,</span><span class="w"> </span><span class="n">including</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">list</span><span class="w"> </span><span class="k">of</span><span class="w"> </span><span class="n">words</span><span class="w"> </span><span class="ow">and</span><span class="w"> </span><span class="n">phrases</span><span class="w"> </span><span class="n">you</span><span class="w"> </span><span class="n">must</span><span class="w"> </span><span class="n">never</span><span class="w"> </span><span class="k">use</span><span class="err">:</span>
<span class="err">{</span><span class="n">voice_guidelines</span><span class="err">}</span>

<span class="o">[</span><span class="n">FORMAT</span><span class="o">]</span>
<span class="o">-</span><span class="w"> </span><span class="n">Clean</span><span class="w"> </span><span class="n">Markdown</span><span class="p">.</span><span class="w"> </span><span class="k">Translate</span><span class="w"> </span><span class="ow">any</span><span class="w"> </span><span class="n">German</span><span class="w"> </span><span class="k">into</span><span class="w"> </span><span class="k">natural</span><span class="w"> </span><span class="n">English</span><span class="p">.</span>
<span class="o">-</span><span class="w"> </span><span class="n">Do</span><span class="w"> </span><span class="ow">not</span><span class="w"> </span><span class="k">output</span><span class="w"> </span><span class="n">frontmatter</span><span class="p">,</span><span class="w"> </span><span class="n">a</span><span class="w"> </span><span class="n">title</span><span class="p">,</span><span class="w"> </span><span class="n">JSON</span><span class="p">,</span><span class="w"> </span><span class="ow">or</span><span class="w"> </span><span class="ow">any</span><span class="w"> </span><span class="n">preamble</span><span class="p">.</span><span class="w"> </span><span class="k">Output</span><span class="w"> </span><span class="k">ONLY</span><span class="w"> </span><span class="n">the</span><span class="w"> </span><span class="n">article</span><span class="w"> </span><span class="n">body</span><span class="p">.</span>

<span class="o">[</span><span class="n">AUTHOR&#39;S NOTES</span><span class="o">]</span>
<span class="err">{</span><span class="n">raw_draft</span><span class="err">}</span>
</code></pre></div>

<p>The interesting design consequence: this single prompt is what makes the whole thing auto-scale. Because rule #1 is "preserve the author's wording," a finished draft leaves almost nothing to do and every model just tidies, while a bare outline forces the model to actually write. I didn't build separate "copy-edit mode" and "draft mode" prompts - the same instruction produces both behaviors depending on what I feed it. The bake-off was partly a test of whether that hunch held up. It did.</p>
<h2>The core finding - divergence tracks draft shape, not model</h2>
<p>How much the models' outputs differ from each other is driven almost entirely by how finished the source draft is, not by which model ran. Measured across 6 posts, flow-similarity lined up monotonically with the draft's bullet-ratio: prose drafts converged (57 - 98%), while a pure outline diverged wildly (3%).</p>
<p>The mechanism is simple. The polish prompt's #1 rule is "preserve the author's wording." On finished prose there's nothing to invent, so every model just tidies and they land in the same place; on bare bullets each model writes its own prose, so they scatter.</p>
<p>This means the system's aggressiveness auto-scales to how much you write - hand it prose, it copy-edits; hand it bullets, it drafts. You control the dial just by draft finishedness, no setting to touch. This is exactly the behavior I want.</p>
<p>The "outline test" is where model choice actually matters - that's the column set worth studying when picking a model, because that's where the model is writing rather than tidying.</p>
<p>As a side note, hello_world's 98% similarity was a bit of a degenerate data point. Its "draft" was already a finished, published article (title/date/tags and all), so every model handed it back ~98% unchanged. It wasn't a case of "models agree" so much as "nothing to polish."</p>
<h2>Genuinely model-specific tics (survive after stripping out format effects)</h2>
<ul>
<li><strong>Mistral</strong>: emits em-dashes (7 across 6 posts vs ~0 for others) even when instructed otherwise.</li>
<li><strong>Sonnet</strong>: emphatic register - heavy <strong>bold</strong> (10× vs ≤3), and "genuinely" (4×). Rewrites openings more, but mainly on short/outline drafts.</li>
<li><strong>DeepSeek</strong>: the boldest re-framer on prose input (threw out the opening on engine-update and pulled a buried line to the top); also slowest remote model, especially on long drafts (181s on the 996-word one).</li>
<li><strong>e4b (local)</strong>: genuinely good on prose - preserved all ideas, no banned words, recognizably the right voice; only slip was occasional em-dashes. On pure outlines it ran ~10 - 15% wordier than Opus and added filler.</li>
</ul>
<h2>On e4b as a daily driver</h2>
<p>Similarity measures agreement, not quality - so the verdict came from reading outputs, not the numbers.</p>
<p>My verdict is that e4b is defensible as a daily driver <em>specifically because</em> of the auto-scaling property. On prose the quality gap to large models nearly vanishes; it only opens on bare outlines. So: prose → e4b is great (local, private, free); outlines → consider a remote model.</p>
<p>Where big models win is tighter prose (they cut instead of padding), better paragraph structure, and zero style-guide violations.</p>
<h2>The takeaways that actually changed my mind</h2>
<p>A few things landed hard enough to shape what I use:</p>
<p><strong>Big models are overkill for this job.</strong> The whole task is "tidy my prose without losing my ideas," and that ceiling is low - once a model can do it cleanly, a bigger one doesn't do it <em>more</em>. On finished drafts the giants were spending their capability on a problem that didn't need it. That reframed the whole decision for me. I stopped chasing the best model and started looking for a good-enough one I could run as close to home as possible.</p>
<p><strong>Mistral is a serious contender.</strong> Setting aside its em-dash habit (which I strip mechanically anyway), it turned in genuinely good prose and was quick about it. If I were picking a remote model purely on the writing, it'd be near the top. Worth keeping in mind for the outline path where model choice still matters.</p>
<p><strong>gemma4 is the real surprise.</strong> This is the one that made me rethink the setup. A small model running locally on my own machine held its own against remote giants on the writing - and then, remembered, it's multi-modal! It writes my alt text too, describing real photos coherently, image bytes never leaving the machine. </p>
<h2>See it for yourself</h2>
<p>You don't have to take my word for any of this. I put the whole comparison online: every source draft, and side by side, what each of the nine configurations did with it. Read across a row and watch the prose drift apart on the outline drafts and snap back together on the finished ones - that's the core finding, right there in front of you. If you like to see an example where the source was just bullets, select "smartphonefree" or "AICare". These are the articles with most variety in results. </p>
<p>Have a look: <a href="https://unprompted.dirkprimbs.de/viewer.html">https://unprompted.dirkprimbs.de/viewer.html</a></p>]]></content:encoded>
            <pubDate>Thu, 16 Jul 2026 04:00:00 GMT</pubDate>
        </item>
        <item>
            <title>Nutritional labels for the stuff you write with AI</title>
            <link>https://unprompted.dirkprimbs.de/posts/aicare.html</link>
            <guid isPermaLink="true">https://unprompted.dirkprimbs.de/posts/aicare.html</guid>
            <description>A small toolkit of habits for using AI honestly - tagging what it touched, owning what you ship, and setting the tool up to poke holes in your thinking instead of flattering it.</description>
            <content:encoded><![CDATA[<p>I created a thing, by the way. You can find it at <a href="https://have-a-look.at/aicare">https://have-a-look.at/aicare</a>.</p>
<p>It’s basically a toolkit with some ideas about when and how AI could be used in an ethical way.</p>
<p>As more and more tools are available out there, the question I feel is getting under-discussed isn't really about the functionality or capability of these things - it's about consent, transparency, and figuring out what practical best practices look like ethically.</p>
<hr />
<h3>We Are Getting Lazy</h3>
<p>Everyone knows how to generate a summary by now. The novelty wore off months ago.</p>
<p>The actual problem I see is cognitive debt. We let the machine do all the heavy lifting, and suddenly our own critical thinking starts to decay. It’s so easy that we just surrender to the output because it looks clean.</p>
<p>The main question isn't what the tech can do anymore. It’s figuring out how to use it without turning ourselves into an automated engine for absolute garbage. So the rest of this is really a few small habits for not letting that decay set in.</p>
<h3>Nutritional Labels for Text: Transparency Tags</h3>
<p>The first honest habit is just admitting who actually wrote the thing. The lines of authorship are completely messy right now, and pretending they aren't is just lying to ourselves, frankly.</p>
<p>A simple shorthand helps a lot - I call them tags. They cover things like Execution, Substance, Ideation, and Auditability. These give readers an instant look at what they are actually reading.</p>
<p>Putting this kind of tag in an email footer resets expectations immediately and builds actual trust.</p>
<p><em>What is the transparency tag for this blog?</em> I’m glad you asked... I suggest marking texts precisely with a code like the following, linking to the AICare explainer for the inquisitively inclined, and then writing underneath in plain text what it means. For this post, it would look something like this:</p>
<blockquote>
<p><strong>AI Transparency:</strong><a href="https://docs.google.com/document/d/12kZ38rrAM_riACxOfbZed1WqsjFfdS5QXmRBCIZPQMQ/edit?tab=t.5f3sghd1c0ll#heading=h.rset46egcfe"> EP-SN-IH-TN</a>
<em>Everything ideated and written by a human, AI used for polishing.</em></p>
</blockquote>
<h3>Etiquette: Owning Your Output</h3>
<p>A tag is a promise, though, and a promise only means something if you stand behind what you shipped. You send it - you own it. If the machine invents a statistic and you pass that along, that is your mistake entirely. Period.</p>
<p>Also, stop creating massive walls of text just because it takes two seconds to generate. Reading takes time. Use these tools to trim things down, not pad them out. And please remember that recording meetings without consent destroys trust. Keep personal 1:1s completely private.</p>
<h3>Tool Settings: Killing the Sycophant</h3>
<p>Owning the output is easier if the tool isn't quietly working against you. Right now, most systems are built to be people-pleasers. They praise your bad ideas and make them sound smart. This makes structural flaws almost impossible to see.</p>
<p>We need to build a critical collaborator instead. Set up a system that refuses to flatter you and actively tries to poke holes in your logic. A standing instruction as simple as "argue the other side before you agree with me" changes the tone of everything that follows.</p>
<p>One way to do this is to break interactions into phases. Instead of asking the assistant for just the final output, write a small script for it to follow. Say I'm sorting through feedback from a workshop - I don't ask for a summary. I ask it to first pull out every distinct complaint, then group the ones that overlap, then tell me which three came up most. Three steps, each one I can actually check before it moves on.</p>
<p>That checking is the whole point. When you hand over the finished thing you never see the moves that got you there, and that is exactly where the cognitive debt piles up. Phasing forces you back into the loop. You stay the one doing the thinking, and the machine goes back to being the tool.</p>
<p>Anyway - that's the toolkit, and the tag on this very post is the first thing it recommends. Go poke a hole in it.</p>]]></content:encoded>
            <pubDate>Tue, 14 Jul 2026 04:00:00 GMT</pubDate>
        </item>
        <item>
            <title>Back to Static: Why I Built My Own Blog Engine (Again)</title>
            <link>https://unprompted.dirkprimbs.de/posts/hello_world.html</link>
            <guid isPermaLink="true">https://unprompted.dirkprimbs.de/posts/hello_world.html</guid>
            <description>A look at why I built a minimalist local-first static site generator powered by simple Python scripts, AI, and Markdown.</description>
            <content:encoded><![CDATA[<p>I have been around the web publishing block a few times. My very first blog engine was cobbled together back in the early days of static-only web pages, when CGI scripts were the only dynamic component you could lean on. Later, like many of us, I chased the dynamic wave. I wrote similar engines in ASP, ASP.NET, and PHP, riding the server-side scripting train for years.</p>
<p>Eventually, that road leads to WordPress. And with WordPress comes the constant toil - update loops, database bloat, plugin conflicts, and the never-ending security advisories. It gets exhausting when all you want to do is write down some thoughts about hardware, photography, or open source.</p>
<p>So, I decided to go back to my roots.</p>
<p>I wanted something dead simple, secure, and entirely under my control. For years, my writing has started its shelf life randomly in plain text files. I write and take notes in Markdown using Typora or Visual Studio Code, which is actually a surprisingly capable markdown environment. When LLMs came along and showed a natural affinity for Markdown, it felt like a fun validation of a workflow I had already been using for a decade.</p>
<h2>Why Not a Classic Static Site Generator?</h2>
<p>You might wonder why I didn't just pick up an established static site generator like Hugo, Jekyll, or Eleventy. The honest answer is that they feel way too complicated for what I am trying to do.</p>
<p>Most modern static site generators come bloated with extended toolchains, complex dependency trees, and a mountain of command-line magic just to get a basic theme running. By the time I configured their configuration files, customized the layouts, and set up a deployment workflow, I would have ended up writing a similar amount of custom automation code anyway. I didn't want to learn a massive framework's specific way of doing things just to render a folder of text files.</p>
<p>Plus, I just liked the experiment. Python and Ollama were already sitting on my system, and building this custom setup in close collaboration with Gemini was half the fun. It is tailored exactly to how I want to work, without any extra engineering weight.</p>
<h2>From Raw Draft to Live Post</h2>
<p>Writing a new post here feels nothing like working in a traditional CMS. I don't worry about formatting, picking categories, or fighting frontmatter syntax when I open an editor.</p>
<p>The process starts inside the <code>sources/</code> directory with a raw, messy text file. I write my notes in Typora or VS Code, often bouncing between German and English depending on whatever is in my head. I don't even add titles or dates. When I am done, I just run the publish script, which orchestrates a pipeline that turns that rough text into a live webpage.</p>
<div class="codehilite"><pre><span></span><code>[sources/draft.md] 
       │
       ▼
(Ollama / gemma4:e4b) ──► Translates, tags, and cross-links
       │
       ▼
[content/draft.md] 
       │
       ▼
(build_blog.py)       ──► Sanitizes HTML, fixes links, maps assets
       │
       ▼
[public/index.html]   ──► Syncs live via lftp
</code></pre></div>

<p>A bash script picks up the raw draft and prepares a massive prompt for a local Ollama instance running a <code>gemma4:e4b</code> model. The script feeds the AI my strict voice guidelines, a JSON list of all my existing tags, and a link manifest containing the summaries and slugs of every post I have ever published here.</p>
<p>The local model translates any German sections into clean English, formats the text with standard code blocks, selects matching tags from the existing taxonomy, and looks for natural opportunities to drop in one or two context-aware cross-links to older articles. A quick Python snippet validates that the returned YAML frontmatter is well-formed before saving the file to the <code>content/</code> folder.</p>
<p>From there, the static compiler (<code>build_blog.py</code>) handles the heavy lifting. Instead of relying on a framework, it uses standard Python tools to transform the markdown into static files. This is where a couple of custom architectural choices come in. The script runs an in-memory link pass that compares relative links against valid files in the content directory. If it finds a dead link, it heals it on the fly by stripping the broken markdown link wrapper and keeping the plain text so the site never generates a 404 error. It also scans for local images, copies them to a central assets folder, and automatically renames them using the post slug as a prefix to eliminate filename collisions.</p>
<p>The actual layout generation uses a safe rendering function to swap out variable hooks in a master HTML template. It pops the main article content out of the mapping dictionary entirely, renders the head metadata, layout attributes, and open graph tags first, and only inserts the raw article body at the very last step. This prevents broken layouts if a model-generated summary contains a stray double-quote that tries to break out of an HTML attribute.</p>
<p>Once the script finishes building the post, it regenerates the paginated index pages, updates the tag clouds, and refreshes the RSS feed. The shell script displays a quick confirmation prompt, and typing <code>y</code> triggers an automated mirror command that pushes the flat files to my web space.</p>
<h2>Hello World</h2>
<p>So, this is it - the official "Hello World" for the new setup. Every word you just read went through the exact pipeline I just described, from a chaotic text file on my laptop to a flat HTML page on a server somewhere. It feels good to have a space that just works without the overhead.</p>
<p>Now that the plumbing is sorted out, I can finally get back to what actually matters. I have a backlog of daily photography notes from around Toronto and a few hardware projects sitting on my desk that need writing up. See you in the next post.</p>]]></content:encoded>
            <pubDate>Sun, 12 Jul 2026 04:00:00 GMT</pubDate>
        </item>
</channel>
</rss>