<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Agentic Coding | Democratizing Data</title><link>https://chezo.uno/tags/agentic-coding/</link><atom:link href="https://chezo.uno/tags/agentic-coding/index.xml" rel="self" type="application/rss+xml"/><description>Agentic Coding</description><generator>HugoBlox Kit (https://hugoblox.com)</generator><language>en-us</language><copyright>©</copyright><lastBuildDate>Sun, 09 Aug 2026 16:52:00 -0700</lastBuildDate><image><url>https://chezo.uno/media/icon_hu_423f10ccd06de889.png</url><title>Agentic Coding</title><link>https://chezo.uno/tags/agentic-coding/</link></image><item><title>What I Learned Switching My Blog's Embedding Model for Recommendations, With an LLM as a Research Partner</title><link>https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/</link><pubDate>Sun, 09 Aug 2026 16:52:00 -0700</pubDate><guid>https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/</guid><description>&lt;h2 id="intro"&gt;Intro&lt;/h2&gt;
&lt;p&gt;This site&amp;rsquo;s related-article recommendations run on a tool I built myself:
, a CLI wrapper around
, which enriches Hugo front matter with various metadata.&lt;/p&gt;
&lt;p&gt;
itself only computes related articles via TF-IDF keyword similarity, but prelims-cli adds embedding-based recommendation on top. It&amp;rsquo;s built to run on GitHub Actions on every new post: no GPU, limited memory.&lt;/p&gt;
&lt;p&gt;I&amp;rsquo;d been using
for Japanese and
for English, the lightest models that still gave decent quality. Then
, a new small, lightweight embedding model, came out, and I tried it to see whether I could unify the Japanese and English models under one that also covers other languages.&lt;/p&gt;
&lt;p&gt;The related articles on this site now run on Bekko a25m. This post covers how I got there, and in particular the trouble I ran into running the experiments with Claude.&lt;/p&gt;
&lt;h2 id="how-to-use-it-for-embedding-based-recommendation-with-prelims-cli"&gt;How to use it for embedding-based recommendation with prelims-cli&lt;/h2&gt;
&lt;p&gt;Since prelims-cli v0.0.11, you can use Bekko embedding by setting the &lt;code&gt;multilingual&lt;/code&gt; option.&lt;/p&gt;
&lt;p&gt;Write a file like the one below as &lt;code&gt;embedding.yaml&lt;/code&gt;, and run &lt;code&gt;uvx --python 3.13 'prelims-cli[embedding]' --config embedding.yaml&lt;/code&gt; from GitHub Actions.&lt;/p&gt;
&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-yaml" data-lang="yaml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nt"&gt;handlers&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;target_path&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;content/blog&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;processors&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;- &lt;span class="nt"&gt;permalink_base&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;/post&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;type&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;embedding_recommender&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;language&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="l"&gt;multilingual&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;topk&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="m"&gt;3&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="nt"&gt;cache_db&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt;&lt;span class="w"&gt; &lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;.prelims_embedding_cache.db&amp;#34;&lt;/span&gt;&lt;span class="w"&gt;
&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The story starts with a comparison of the old and new embedding models, done with Claude Opus 5.&lt;/p&gt;
&lt;h2 id="first-attempt-and-why-i-gave-up"&gt;First attempt, and why I gave up&lt;/h2&gt;
&lt;p&gt;These are related articles on my own blog, so there&amp;rsquo;s no ground truth to evaluate against. I asked Opus 5, and it proposed a proxy metric: tag/category agreement rate. The full definition is in the report below, but roughly: the denominator is the number of top-k recommended articles that carry any tags or categories, and the metric counts how many of those share a tag or category with the source article.&lt;/p&gt;
&lt;p&gt;The result was noise-level: the metric&amp;rsquo;s resolution was ±5–7pt, while the difference between models was only about 2pt. Raising the input length from 2,000 to 8,000 characters improved English quality more than switching models did.&lt;/p&gt;
&lt;p&gt;
&lt;figure id="figure-body-length-distributions-per-language"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="English was embedding less than half of each body. With a median of 1,854 characters, longer posts were being compared on their introductions alone."
srcset="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319710621_hu_19958d015e55d060.webp 320w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319710621_hu_2dd53668b4e40852.webp 480w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319710621_hu_ae487b82c46b9541.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319710621_hu_19958d015e55d060.webp"
width="760"
height="290"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Body length distributions per language
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Since this runs in GitHub Actions CI, download time is processing time. Switching to Bekko a25m bumps the model size to 199 MB, well above the 35 MB and 52 MB of the models I&amp;rsquo;d been using. I concluded the benefit wasn&amp;rsquo;t worth the cost, and moved on.&lt;/p&gt;
&lt;h2 id="the-metric-was-broken"&gt;The metric was broken&lt;/h2&gt;
&lt;p&gt;That&amp;rsquo;s when Opus 5 pointed out that the denominator |P| in the tag/category agreement rate isn&amp;rsquo;t fixed. It moves with the recommendations themselves. Bekko happened to recommend tagged articles more often, which inflated its denominator and made its agreement rate look worse.&lt;/p&gt;
&lt;p&gt;Opus 5 had been noting this growing denominator in the HTML report the whole time. Neither of us, human or LLM, had read it. That&amp;rsquo;s the irony.&lt;/p&gt;
&lt;p&gt;An LLM&amp;rsquo;s evaluation looks plausible on the surface, but it will commit to a metric without checking the underlying data distribution first.&lt;/p&gt;
&lt;h2 id="two-ways-to-respond-to-a-broken-metric"&gt;Two ways to respond to a broken metric&lt;/h2&gt;
&lt;p&gt;I&amp;rsquo;d already half-suspected that a tag shared across more than one article was the exception rather than the rule. So I plotted the tag distribution to check.&lt;/p&gt;
&lt;p&gt;
&lt;figure id="figure-tag-distribution"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="Tag distribution"
srcset="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319788679_hu_ee8ced3516a3ea9c.webp 320w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319788679_hu_d1e797510c888979.webp 480w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319788679_hu_36df45903d7e1c06.webp 760w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319788679_hu_ee8ced3516a3ea9c.webp"
width="760"
height="315"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Tag distribution
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;Sure enough: in Japanese, 39 of 69 tags/categories appear on only a single article; in English, 19 of 26. Of the 4,465 possible pairs among the 95 tagged Japanese articles, only 212 (4.7%) share a label. There are barely any &amp;ldquo;correct answers&amp;rdquo; to measure against.&lt;/p&gt;
&lt;p&gt;Tag/category agreement rate simply doesn&amp;rsquo;t have the coverage to work as an overall metric.&lt;/p&gt;
&lt;p&gt;I decided Opus 5 was stuck in a local optimum on this one, so I switched to Fable 5 and asked for a new metric. Fable 5 proposed fixed-pool precision@k, which fixes the denominator problem, plus pairwise AUC. It dropped tag agreement entirely.&lt;/p&gt;
&lt;p&gt;But once I&amp;rsquo;d settled on that and moved back to Opus 5, it kept clinging to the metric it had originally proposed, still reporting agreement between tag agreement rate and the human evaluation I collected later.&lt;/p&gt;
&lt;h2 id="being-put-to-work-by-claude"&gt;Being put to work by Claude&lt;/h2&gt;
&lt;p&gt;I remembered the advice you hear again and again when evaluating anything in an AI agent context: look at the real data. So I had Claude build me an annotation tool and did the evaluation by hand.&lt;/p&gt;
&lt;p&gt;It struck me, working through the evaluation in the browser, how easily coding agents now throw together one-off annotation tools like this.&lt;/p&gt;
&lt;p&gt;
&lt;figure id="figure-annotation-tool"&gt;
&lt;div class="flex justify-center "&gt;
&lt;div class="w-full" &gt;
&lt;img alt="Annotation tool"
srcset="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319848952_hu_8b073c4d859d72a.webp 320w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319848952_hu_e837a0bfc05fe15.webp 480w, https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319848952_hu_640b245d1313fe58.webp 733w"
sizes="(max-width: 480px) 100vw, (max-width: 768px) 90vw, (max-width: 1024px) 80vw, 760px"
src="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/pasted-image-1786319848952_hu_8b073c4d859d72a.webp"
width="733"
height="760"
loading="lazy" data-zoomable /&gt;&lt;/div&gt;
&lt;/div&gt;&lt;figcaption&gt;
Annotation tool
&lt;/figcaption&gt;&lt;/figure&gt;
&lt;/p&gt;
&lt;p&gt;I stopped after annotating 162 of the 389 articles with recommendations: enough to confirm that all three top metrics favored the new model, with the difference staying non-negative even at the lower bound of the confidence interval.&lt;/p&gt;
&lt;p&gt;Somewhere around then, complaining to Claude about how exhausting the annotation was, I found out it had quietly been running an active learning technique that only shows you the cases where the two models&amp;rsquo; recommendations differ. The AI had slipped in an efficient (read: demanding) labeling method, and put me to work without my noticing.&lt;/p&gt;
&lt;h2 id="what-the-human-labels-actually-showed"&gt;What the human labels actually showed&lt;/h2&gt;
&lt;p&gt;What I saw in that painful hand-annotation, combined with the LLM&amp;rsquo;s own observations, tipped me toward adopting the new model.&lt;/p&gt;
&lt;p&gt;The LLM&amp;rsquo;s observations helped too.&lt;/p&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;For a write-up of a new album by the Real Group (a Swedish jazz a cappella group), a25m surfaces two reviews of earlier albums by the same artist; the old model returned a live announcement for a different artist entirely.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;For a review of &lt;em&gt;Utsukushii Kodomo&lt;/em&gt; (&amp;ldquo;Beautiful Children&amp;rdquo;) by Ira Ishida (a prolific Japanese novelist best known for the &lt;em&gt;Ikebukuro West Gate Park&lt;/em&gt; series and the 2003 Naoki Prize winner &lt;em&gt;4 TEEN&lt;/em&gt;), a25m surfaces &lt;em&gt;Akihabara@DEEP&lt;/em&gt; and &lt;em&gt;Ai ga Inai Heya&lt;/em&gt; (&amp;ldquo;The Room Without Love&amp;rdquo;), both by the same author; the old model matched on author only once.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;blockquote class="border-l-4 border-neutral-300 dark:border-neutral-600 pl-4 italic text-neutral-600 dark:text-neutral-400 my-6"&gt;
&lt;p&gt;For the report on Kawasaki Ruby Kaigi 01, a25m puts its sister event, Kanagawa Ruby Kaigi, at the top; the old model returned monthly meetup reports instead.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;It figured out that Kanagawa Ruby Kaigi and Kawasaki Ruby Kaigi are sister events, which impressed me. It also picked up on overlapping authors and artists, which felt satisfying to see. The two models have different habits, but this one was good enough to adopt.&lt;/p&gt;
&lt;p&gt;Going back through my own older posts, I noticed the early ones read like a diary and often ramble across several topics at once. That&amp;rsquo;s a good reminder of just how hard it is to recommend related articles for posts like that.&lt;/p&gt;
&lt;div class="sc-iframe"&gt;&lt;div style="overflow:auto;-webkit-overflow-scrolling:touch;border:1px solid #ddd;border-radius:6px;background:#fff;height:700px;"&gt;
&lt;iframe id="sc-iframe-f394093e-0" src="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/report.html" title="Comparing embedding models for related-article recommendation" loading="lazy"
style="display:block;width:100%;border:0;height:100%;"&gt;&lt;/iframe&gt;
&lt;/div&gt;
&lt;p style="margin-top:.25rem;font-size:.85em;text-align:right;"&gt;
&lt;a href="https://chezo.uno/blog/2026-08-09-what-i-learned-switching-my-blog-s-embedding-mode/report.html" target="_blank" rel="noopener"&gt;Open report in a new tab&lt;/a&gt;
&lt;/p&gt;
&lt;/div&gt;
&lt;h2 id="making-it-work-in-ci"&gt;Making it work in CI&lt;/h2&gt;
&lt;p&gt;With Bekko embedding qualitatively confirmed as an improvement, I cleared the remaining technical blockers in parallel:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Cached the model download from Hugging Face in GitHub Actions&lt;/li&gt;
&lt;li&gt;Optimized batching so embedding wouldn&amp;rsquo;t hit the memory limit&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;With those in place, embedding all 452 articles takes 3 minutes 8 seconds including the model download, or 2 minutes 51 seconds with the cache warm: a realistic runtime. In practice, embeddings are computed incrementally as new articles are added, so it&amp;rsquo;s even faster day to day.&lt;/p&gt;
&lt;p&gt;That&amp;rsquo;s how I ended up adopting Bekko a25m for related-article recommendation.&lt;/p&gt;
&lt;h2 id="three-takeaways"&gt;Three takeaways&lt;/h2&gt;
&lt;p&gt;Three things came out of this.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;An LLM&amp;rsquo;s evaluation is surface-level. Go look at the data yourself.&lt;/li&gt;
&lt;li&gt;Humans are creatures LLMs can put to work doing active learning.&lt;/li&gt;
&lt;li&gt;Opus 5 tends to cling to whatever method it originally proposed, so for ambiguous, open-ended tasks, Fable 5 was the better fit.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;1 and 3 weren&amp;rsquo;t news to me, but 2 was a genuine surprise. I&amp;rsquo;d always assumed coding agents try to maximize how much they can get out of me as the human in the loop. I didn&amp;rsquo;t expect one to quietly slip in such a demanding method.&lt;/p&gt;</description></item></channel></rss>