<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/">
  <channel>
    <title>Pi on Bartosz&#39;s blog</title>
    <link>https://ocytko.net/tags/pi/</link>
    <description>Recent content in Pi on Bartosz&#39;s blog</description>
    <generator>Hugo</generator>
    <language>en</language>
    <copyright>Bartosz Ocytko</copyright>
    <lastBuildDate>Sat, 21 Mar 2026 22:30:00 +0000</lastBuildDate>
    <atom:link href="https://ocytko.net/tags/pi/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>pi-autoresearch optimization loops</title>
      <link>https://ocytko.net/posts/pi-autoresearch-optimization-loops/</link>
      <pubDate>Sat, 21 Mar 2026 22:30:00 +0000</pubDate>
      <guid>https://ocytko.net/posts/pi-autoresearch-optimization-loops/</guid>
      <description>The autoresearch technique promoted by Andrej Karpathy is easily accessible in the pi coding agent.</description>
      <content:encoded><![CDATA[<p>After OpenClaw, <a href="https://github.com/karpathy/autoresearch">autoresearch</a> by Andrej Karpathy is the new cool kid on the block in the LLM-land.
The core idea is an optimization loop that continuously lets the LLM propose a changeset and verify whether it improves the baseline.
While Karpathy&rsquo;s code was focused on optimizing training of a small language model on a single GPU, the method can be generalized to any optimization problem.
There are variants of autoresearch applied to specific problems, like GPU Kernels (<a href="https://github.com/RightNow-AI/autokernel">autokernel</a>)
as well as generic versions for coding agents: <a href="https://github.com/davebcn87/pi-autoresearch">pi-autoresearch</a> and its clone <a href="https://github.com/drivelineresearch/autoresearch-claude-code">autoresearch-claude-code</a>.</p>
<p>Since the <code>pi</code> plugin was released first, I decided to give it a spin and use the opportunity to try <a href="https://pi.dev">pi</a> for the first time as well.
For LLM access, I used Kimi-K2.5 model via <a href="https://openrouter.ai/">OpenRouter</a> with the API key budget explicitly set to $10.</p>
<p>In this post, we will explore how the autoresearch loop works using three different scenarios:</p>
<ul>
<li>algorithm optimization of a simple function</li>
<li>running unit tests for a Java multi-module project</li>
<li>optimizing test runtime for a golang library</li>
</ul>
<h2 id="how-does-the-plugin-work">How does the plugin work?</h2>
<p>The plugin provides <code>/autoresearch</code> to trigger the optimization. Under the hood, we have a <a href="https://github.com/davebcn87/pi-autoresearch/blob/main/skills/autoresearch-create/SKILL.md">skill</a>
that instructs the model to keep track of the optimization in <code>autoresearch.md</code> and uses <code>autoresearch.sh</code> for executing the verification step and capturing guardrail metrics.</p>
<p>The instructions, guardrails, and metrics for the optimization can be defined in <code>autoresearch.md</code>.
If not provided, these will be inferred from context and prompt. The plugin then triggers an <strong>infinite</strong> optimization loop.
An optimization candidate is applied to the code, the verification procedure is started and metrics are captured.
If the optimization run results in better metrics than the prior result, it&rsquo;s committed to git and taken as base to optimize further.</p>
<p>The optimization loop can be interrupted at any time and resumed just from the <code>autoresearch.md</code> file.
This also poses an opportunity to adjust the file manually, if needed.</p>
<h2 id="experiment-1-algorithm-optimization">Experiment 1: Algorithm optimization</h2>
<p>For my first exploration, I took a simple prime number finder loop and a test file verifying that this function works correctly as baseline.
Both were generated by an agent, though the exact code is of less importance here since we just want to understand how the autoresearch loop works.</p>
<p>I kicked off the optimization process via:</p>
<pre tabindex="0"><code>/autoresearch optimize the test runtime, adjust the algorithm as needed
</code></pre><p>The agent then looked up if there is any <code>autoresearch.md</code> plan provided.
Since there was none, it came up with own ideas and kicked off the work, correctly using test suite runtime as metric.
For each iteration, the agent outlines the idea, displays the changeset, runs the verification and summarizes the findings.
The intermediate output can be useful to follow the agent&rsquo;s actions and to verify that the loop is configured as intended.</p>
<p>After a few iterations, the agent decided that it completed its work and printed a nice summary of the findings:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">Autoresearch Complete! 🎉
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Final Result: 48% improvement (0.31s → 0.16s)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Optimizations Applied:
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">┌───┬──────────────────────────────────────────────────────────────────────────────────────┬────────┐
</span></span><span class="line"><span class="cl">│ # │ Optimization                                                                         │ Impact │
</span></span><span class="line"><span class="cl">├───┼──────────────────────────────────────────────────────────────────────────────────────┼────────┤
</span></span><span class="line"><span class="cl">│ 1 │ Hybrid approach: Use Sieve of Eratosthenes for n≥100, trial division for smaller n   │ Major  │
</span></span><span class="line"><span class="cl">├───┼──────────────────────────────────────────────────────────────────────────────────────┼────────┤
</span></span><span class="line"><span class="cl">│ 2 │ Odd-only sieve: Track only odd numbers ≥3, halving memory and operations             │ Major  │
</span></span><span class="line"><span class="cl">├───┼──────────────────────────────────────────────────────────────────────────────────────┼────────┤
</span></span><span class="line"><span class="cl">│ 3 │ bytearray + slice assignment: Faster marking than list with element-by-element loops │ Medium │
</span></span><span class="line"><span class="cl">├───┼──────────────────────────────────────────────────────────────────────────────────────┼────────┤
</span></span><span class="line"><span class="cl">│ 4 │ 6k±1 wheel in is_prime: Skip multiples of 2 and 3 in divisor checks                  │ Medium │
</span></span><span class="line"><span class="cl">├───┼──────────────────────────────────────────────────────────────────────────────────────┼────────┤
</span></span><span class="line"><span class="cl">│ 5 │ Tight upper bound: Reduced margin from +10 to +5 for smaller allocations             │ Minor  │
</span></span><span class="line"><span class="cl">└───┴──────────────────────────────────────────────────────────────────────────────────────┴────────┘
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Key Algorithmic Changes:
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> Before: Trial division (O(n²)) for all n
</span></span><span class="line"><span class="cl"><span class="k">-</span> After: Sieve (O(n log log n)) for n≥100, optimized trial division for smaller n
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Performance at n=5000:
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">-</span> Trial division: ~18 ms → ~0.77 ms (23× faster)
</span></span><span class="line"><span class="cl"><span class="k">-</span> Sieve: ~3.3 ms → ~0.77 ms (4× faster)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">The optimizations have been committed to git and future ideas documented in autoresearch.ideas.md.
</span></span></code></pre></div><p>Then, the autoresearch plugin detected this scenario and kindly decided to ask the agent to give it another go:</p>
<blockquote>
<p><em>Autoresearch loop ended (likely context limit). Resume the experiment loop — read autoresearch.md and git log for context. Check autoresearch.ideas.md for promising</em>
<em>paths to explore. Prune stale/tried ideas. Be careful not to overfit to the benchmarks and do not cheat on the benchmarks.</em></p>
</blockquote>
<p>After a few more iterations, the agent decided that it ran out of ideas:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">Autoresearch Session Complete
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Final Result: 48% improvement (0.31s → 0.16s)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gu">### Summary of This Session
</span></span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Attempted optimizations (all rejected):
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">┌────────────────────────┬───────────────────┬───────────────────────────────────────┐
</span></span><span class="line"><span class="cl">│ Optimization           │ Result            │ Reason                                │
</span></span><span class="line"><span class="cl">├────────────────────────┼───────────────────┼───────────────────────────────────────┤
</span></span><span class="line"><span class="cl">│ 30-wheel factorization │ ❌ Slower (0.19s) │ List indexing/modulo overhead         │
</span></span><span class="line"><span class="cl">├────────────────────────┼───────────────────┼───────────────────────────────────────┤
</span></span><span class="line"><span class="cl">│ Cached prime list      │ ❌ Slower (0.23s) │ Cache management overhead             │
</span></span><span class="line"><span class="cl">├────────────────────────┼───────────────────┼───────────────────────────────────────┤
</span></span><span class="line"><span class="cl">│ Local variable lookups │ ❌ Slower (0.19s) │ No benefit for C-level math functions │
</span></span><span class="line"><span class="cl">├────────────────────────┼───────────────────┼───────────────────────────────────────┤
</span></span><span class="line"><span class="cl">│ Threshold 90 or 110    │ ❌ Slower         │ n=100 is near-optimal                 │
</span></span><span class="line"><span class="cl">└────────────────────────┴───────────────────┴───────────────────────────────────────┘
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Current best approach remains:
</span></span><span class="line"><span class="cl"><span class="k">-</span> Hybrid: trial division for n &lt; 100, sieve for n ≥ 100
</span></span><span class="line"><span class="cl"><span class="k">-</span> Odd-only sieve with bytearray and slice assignment
</span></span><span class="line"><span class="cl"><span class="k">-</span> 6k±1 wheel in is_prime
</span></span><span class="line"><span class="cl"><span class="k">-</span> Tight upper bound (+5 margin)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Performance at n=5000: ~0.71ms (23× faster than original)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">All findings documented in autoresearch.ideas.md. No further low-hanging fruit identified without adding complexity (numpy, Cython, etc.).
</span></span></code></pre></div><p>The autoresearch plugin includes a simple widget that shows the progress so far, the baseline and optimization progress, expressed in metrics (e.g. test runtime in seconds).
When the research loop is finished, this widget provides a nice summary of the actions:</p>
<figure class="align-center ">
    <img loading="lazy" src="prime-finder-test-runtime.png#center"
         alt="Screenshot of prime number finder optimization loop with results of each optimization"/> <figcaption>
            <p>Summary of the executed optimization runs</p>
        </figcaption>
</figure>

<p>The experiment with 8 runs cost me $0.284 in API costs and yielded a 47% improvement in test runtime.</p>
<h2 id="experiment-2-unit-test-execution-optimization">Experiment 2: Unit test execution optimization</h2>
<p>For the next test, I chose <a href="https://github.com/zalando/riptide">riptide</a> - a Java multi-module project implementing Client-side request routing in Spring.
Here, I wanted to optimize the unit test execution as running the tests took about two minutes. The research loop was triggered with:</p>
<pre tabindex="0"><code>/autoresearch optimize the unit test runtime in this project; tests can be run with &#39;./mvnw clean test -Dmaven.build.cache.skipCache=true&#39;
</code></pre><p>This verification ensures that regardless of code changes, the test runs are comparable. Autoresearch decided on two metrics as guardrails for the
optimization: <em>total runtime in seconds</em> and the <em>number of tests executed</em>. Quite smart, as this ensures that the model cannot cheat by removing tests.</p>
<p>The optimization loop studied the maven configuration and decided to leverage parallelization, batch mode, tune GC parameters, and drop plugins
that in its opinion were not essential for running the tests. One could argue whether dropping code coverage calculation is a good choice,
though it could be indeed skipped for local runs and kept only for CI builds.
After a few runs, the test execution was extended to capture the execution time of individual tests, which revealed that a subset of tests had
waiting times due to socket communication, as expected for the library.</p>
<p>After &gt;30 experiments and $0.686 in API spend, we got a 38% improvement in test runtime.</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl"> Autoresearch Complete: 38-39% Improvement Achieved ✅
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> After 31 experiments, the test runtime has been reduced from 113.4s to ~70s (38-39% improvement).
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ### Final Configuration Summary
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ┌────────────────────┬───────────────────────────────────────────────────────────────────────────┐
</span></span><span class="line"><span class="cl"> │ File               │ Key Settings                                                              │
</span></span><span class="line"><span class="cl"> ├────────────────────┼───────────────────────────────────────────────────────────────────────────┤
</span></span><span class="line"><span class="cl"> │ .mvn/jvm.config    │ -XX:+UseParallelGC&lt;br&gt;-Xmx2g                                              │
</span></span><span class="line"><span class="cl"> ├────────────────────┼───────────────────────────────────────────────────────────────────────────┤
</span></span><span class="line"><span class="cl"> │ .mvn/maven.config  │ -B&lt;br&gt;-T2C                                                                │
</span></span><span class="line"><span class="cl"> ├────────────────────┼───────────────────────────────────────────────────────────────────────────┤
</span></span><span class="line"><span class="cl"> │ pom.xml (Surefire) │ argLine: -XX:+UseParallelGC -Xms512m -Xmx512m                             │
</span></span><span class="line"><span class="cl"> ├────────────────────┼───────────────────────────────────────────────────────────────────────────┤
</span></span><span class="line"><span class="cl"> │ autoresearch.sh    │ Skip flags: jacoco, duplicate-finder, checkstyle, spotbugs, pmd, enforcer │
</span></span><span class="line"><span class="cl"> └────────────────────┴───────────────────────────────────────────────────────────────────────────┘
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ### Results
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">-</span> Baseline: 113.4s
</span></span><span class="line"><span class="cl"> <span class="k">-</span> Best: 69.2s (39.0% improvement)
</span></span><span class="line"><span class="cl"> <span class="k">-</span> Typical: 69-72s (~38% improvement)
</span></span><span class="line"><span class="cl"> <span class="k">-</span> All tests pass: ✅ (except occasional flaky ChaosPluginTest)
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ### Optimization Breakdown
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">1.</span> Maven parallel build (-T2C) — ~26%
</span></span><span class="line"><span class="cl"> <span class="k">2.</span> Skip JaCoCo — ~14%
</span></span><span class="line"><span class="cl"> <span class="k">3.</span> JVM opts + argLine — ~3%
</span></span><span class="line"><span class="cl"> <span class="k">4.</span> Skip duplicate-finder — ~2%
</span></span><span class="line"><span class="cl"> <span class="k">5.</span> Batch mode (-B) — ~0.4%
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> ### Failed Experiments
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> <span class="k">-</span> Compiler fork, Surefire forkCount&gt;1, G1GC, 4GB heap, Smart builder, offline mode — all slower or cause failures
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"> The optimization has reached a plateau. The remaining ~70s is dominated by actual test execution time across 20+ modules, which cannot be reduced through Maven
</span></span><span class="line"><span class="cl"> configuration alone.
</span></span></code></pre></div><p>The image below shows all attempted optimization runs, which includes a more realistic scenario than in the first experiment.
We have not only discarded runs, but also optimizations that resulted in crashes or inability to run the test in the first place:</p>
<figure class="align-center ">
    <img loading="lazy" src="riptide-unit-test-runtime.png#center"
         alt="Screenshot of riptide optimization loop with results of each optimization"/> <figcaption>
            <p>Summary of the executed optimization runs for riptide</p>
        </figcaption>
</figure>

<p>As before, the harness attempted to proceed with the optimization job, but this time the execution was interrupted by a failure from OpenRouter as I ran out of credits before the API key limit was reached.</p>
<h2 id="experiment-3-golang-library">Experiment 3: golang library</h2>
<p>Next, I took <a href="https://github.com/zalando/go-keyring">go-keyring</a>, a library with a cross-platform keyring interface for Go, as my next test subject.
I kicked off the optimization loop with a rather lazy prompt:</p>
<pre tabindex="0"><code>/autoresearch try optimizing the test runtime and memory usage
</code></pre><p>I mainly wanted to see how easily the model will be led astray throughout the optimization process.
After 18 runs and $0.516 in API tokens, the model tried the following optimizations:</p>
<figure class="align-center ">
    <img loading="lazy" src="go-keyring-test-memory.png#center"
         alt="Screenshot of go-keyring optimization loop with results of each optimization"/> <figcaption>
            <p>Summary of the executed optimization runs for go-keyring</p>
        </figcaption>
</figure>

<p>Two key changes were:</p>
<ul>
<li><code>Replace regex with lookup table in shellescape.Quote - reduces allocations and removes regex dependency</code></li>
<li><code>Add testing.Short() checks to skip OS-level tests in short mode - reduces test time by ~55%</code></li>
</ul>
<p>While the first one may indeed be more efficient for the test data in the code, the second one is borderline cheating.</p>
<p>Another interesting behavior was the model deciding to add microbenchmarks for certain functions used in the code.
This is likely a result of golang having built-in support for benchmarks in its test command.
Throughout the optimization loop, the verification was extended to include benchmarks:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-md" data-lang="md"><span class="line"><span class="cl">Now let me run the benchmarks to see the performance.
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">run_experiment cd /Users/bartek/repos/go-keyring &amp;&amp; go test -bench=BenchmarkQuote -benchmem -benchtime=0.3s -run=^$ ./internal/shellescape/ 2&gt;&amp;1 (timeout: 60s)
</span></span><span class="line"><span class="cl">✅ wall: 3.0s (truncated)
</span></span><span class="line"><span class="cl">… 8 more lines
</span></span><span class="line"><span class="cl">BenchmarkQuoteLongSafe-12          19541719           19.25 ns/op          0 B/op          0 allocs/op
</span></span><span class="line"><span class="cl">BenchmarkQuoteLongEscape-12         7272855           46.32 ns/op         80 B/op          1 allocs/op
</span></span><span class="line"><span class="cl">PASS
</span></span><span class="line"><span class="cl">ok     github.com/zalando/go-keyring/internal/shellescape   2.768s
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">Excellent! The benchmarks show good performance. Now let me also run the regular tests to make sure everything still works.
</span></span></code></pre></div><h3 id="viewing-results">Viewing results</h3>
<p>When the optimization loop is stopped, the output is saved as <code>autoresearch.jsonl</code>, <code>autoresearch.ideas.md</code> and in git commits.
The JSONL file has all the commit ids and basic metadata on the optimization run and is the base for the terminal widget shown in the terminal and screenshots included in the post.</p>
<p>Let&rsquo;s take a look at one example optimization commit. First, its metadata:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-json" data-lang="json"><span class="line"><span class="cl"><span class="err">$</span> <span class="err">cat</span> <span class="err">autoresearch.jsonl</span> <span class="err">|</span> <span class="err">grep</span> <span class="err">d</span><span class="mi">636</span><span class="err">c</span><span class="mi">43</span> <span class="err">|</span> <span class="err">jq</span> <span class="err">.</span>
</span></span><span class="line"><span class="cl"><span class="p">{</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;run&#34;</span><span class="p">:</span> <span class="mi">4</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;commit&#34;</span><span class="p">:</span> <span class="s2">&#34;d636c43&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;metric&#34;</span><span class="p">:</span> <span class="mf">0.865</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;metrics&#34;</span><span class="p">:</span> <span class="p">{},</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;status&#34;</span><span class="p">:</span> <span class="s2">&#34;keep&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;description&#34;</span><span class="p">:</span> <span class="s2">&#34;Remove redundant slice operation in keyring_darwin.go - use string(out) instead of string(out[:])&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;timestamp&#34;</span><span class="p">:</span> <span class="mi">1774123478289</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;segment&#34;</span><span class="p">:</span> <span class="mi">0</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;confidence&#34;</span><span class="p">:</span> <span class="mi">2</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">  <span class="nt">&#34;asi&#34;</span><span class="p">:</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;hypothesis&#34;</span><span class="p">:</span> <span class="s2">&#34;Redundant slice operation creates unnecessary overhead&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;keyring_pkg_time&#34;</span><span class="p">:</span> <span class="s2">&#34;0.560s vs 0.571s baseline&#34;</span><span class="p">,</span>
</span></span><span class="line"><span class="cl">    <span class="nt">&#34;change&#34;</span><span class="p">:</span> <span class="s2">&#34;string(out[:]) -&gt; string(out)&#34;</span>
</span></span><span class="line"><span class="cl">  <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span>
</span></span></code></pre></div><p>Next, the actual code change:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-diff" data-lang="diff"><span class="line"><span class="cl">$ git show d636c43
</span></span><span class="line"><span class="cl">[...]
</span></span><span class="line"><span class="cl"><span class="gh">diff --git a/keyring_darwin.go b/keyring_darwin.go
</span></span></span><span class="line"><span class="cl"><span class="gh">index 5e3ae1b..e1ea9e4 100644
</span></span></span><span class="line"><span class="cl"><span class="gd">--- a/keyring_darwin.go
</span></span></span><span class="line"><span class="cl"><span class="gi">+++ b/keyring_darwin.go
</span></span></span><span class="line"><span class="cl"><span class="gu">@@ -53,7 +53,7 @@ func (k macOSXKeychain) Get(service, username string) (string, error) {
</span></span></span><span class="line"><span class="cl">                return &#34;&#34;, err
</span></span><span class="line"><span class="cl">        }
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="gd">-       trimStr := strings.TrimSpace(string(out[:]))
</span></span></span><span class="line"><span class="cl"><span class="gi">+       trimStr := strings.TrimSpace(string(out))
</span></span></span><span class="line"><span class="cl">        // if the string has the well-known prefix, assume it&#39;s encoded
</span></span><span class="line"><span class="cl">        if strings.HasPrefix(trimStr, encodingPrefix) {
</span></span><span class="line"><span class="cl">                dec, err := hex.DecodeString(trimStr[len(encodingPrefix):])
</span></span></code></pre></div><p>Successful optimizations are stacked on top of one another, meaning that the optimization we looked at was applied on top of others:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-sh" data-lang="sh"><span class="line"><span class="cl">$ git log d636c43
</span></span><span class="line"><span class="cl">commit d636c43b02d580b335ddada3b6e6962cbfa95717
</span></span><span class="line"><span class="cl">Date:   Sat Mar <span class="m">21</span> 21:04:38 <span class="m">2026</span> +0100
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Remove redundant slice operation in keyring_darwin.go - use string<span class="o">(</span>out<span class="o">)</span> instead of string<span class="o">(</span>out<span class="o">[</span>:<span class="o">])</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Result: <span class="o">{</span><span class="s2">&#34;status&#34;</span>:<span class="s2">&#34;keep&#34;</span>,<span class="s2">&#34;test_duration&#34;</span>:0.865<span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">commit 80baf8a9901dbb4abb67607c533272aacbd130cc
</span></span><span class="line"><span class="cl">Date:   Sat Mar <span class="m">21</span> 21:03:29 <span class="m">2026</span> +0100
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Replace regex with lookup table in shellescape.Quote - reduces allocations and removes regex dependency
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Result: <span class="o">{</span><span class="s2">&#34;status&#34;</span>:<span class="s2">&#34;keep&#34;</span>,<span class="s2">&#34;test_duration&#34;</span>:0.872<span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">commit de9cead40a8e5c62a99bc7245c90621dda56a545
</span></span><span class="line"><span class="cl">Date:   Sat Mar <span class="m">21</span> 21:01:56 <span class="m">2026</span> +0100
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Baseline measurement - no optimizations
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    Result: <span class="o">{</span><span class="s2">&#34;status&#34;</span>:<span class="s2">&#34;keep&#34;</span>,<span class="s2">&#34;test_duration&#34;</span>:0.88<span class="o">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">commit e0dd98fca908ebee87a334a3c4d91b58a0f0fb6b <span class="o">(</span>tag: v0.2.7, origin/master, origin/HEAD<span class="o">)</span>
</span></span></code></pre></div><p>The metadata file helps choose the optimizations to merge or cherry-pick.</p>
<h2 id="summary">Summary</h2>
<p>The <a href="https://github.com/davebcn87/pi-autoresearch">pi-autoresearch</a> plugin makes it extremely easy to trigger optimization loops.
The optimization flow mimics what a human would do, just running infinitely on autopilot with all of its appeal and dangers.
The API key budget limit was a nice way to ensure predictable spending, but may not be a capability that is easily accessible to everyone,
requiring other guardrails for spend control. Watching the optimizations being applied reminded me of <a href="https://en.wikipedia.org/wiki/Simulated_annealing">simulated annealing</a> optimizations
that I had been writing during university times for <a href="https://en.wikipedia.org/wiki/Flow-shop_scheduling">flow-shop scheduling</a> problems.</p>
<p>The simple examples described in this post show how important a proper verification routine is, given that it determines the course of action for the optimization workflow.
The faster the verification run, the more loops can be run in a unit of time. It&rsquo;s also important to clearly scope the optimization run.
If you&rsquo;re just interested in optimizing a single function, create a test scoped to it and ensure the verification runs only this single test.
The golang experiment was intentionally prompted with high ambiguity, showing how the model will attempt exploring different avenues of optimization, even if implicitly undesired by the user.
It&rsquo;s better to be specific in the research loop design, rather than taking chances that the model will do the right thing.</p>
<p>The optimization runs are also subject to the classic pitfalls of machine learning. If the tests are too narrow (e.g. checking prime numbers up to 5000),
the &ldquo;optimized&rdquo; code may be overfitting to this exact test suite. This property will hopefully lead to an increase in test coverage in projects that choose to use autoresearch.
Additionally, it&rsquo;s highly advisable to inspect the attempted optimizations, before celebrating the optimization wins too early.</p>
<p>Cost-wise, the Kimi-K2.5 model has a clear advantage with its $0.45/$2.25 input/output price per 1M tokens, making my experiment rather cheap.
However, I have to note that the experiments were interrupted by me running out of credits on OpenRouter, so it&rsquo;s unknown how long these would have been running otherwise.
Costs are driven by the size of the code files the model needs to analyze to be able to propose optimizations and most importantly, by the size of the output that verification step generates.
Long and verbose build output as well as any screenshots that models would be analyzing will definitely drive up the costs here.</p>
<p>I definitely look forward to applying pi-autoresearch in more projects. You should too!</p>
]]></content:encoded>
    </item>
  </channel>
</rss>
