<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>ai on Saurabh Kumar</title><link>https://saurabh-kumar.com/tags/ai/</link><description>Recent content in ai on Saurabh Kumar</description><generator>Hugo</generator><language>en-US</language><copyright>Copyright © 2025, Saurabh Kumar.</copyright><lastBuildDate>Mon, 19 Oct 2026 12:00:00 +0000</lastBuildDate><atom:link href="https://saurabh-kumar.com/tags/ai/index.xml" rel="self" type="application/rss+xml"/><item><title>EmbeddingGemma 2</title><link>https://saurabh-kumar.com/til/1792411200/</link><pubDate>Mon, 19 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1792411200/</guid><description>&lt;p&gt;&lt;a href="https://blog.google/innovation-and-ai/technology/developers-tools/embeddinggemma-2/" rel="noopener external"&gt;EmbeddingGemma 2&lt;/a&gt; puts text, code, images, video and audio (or a mix) into one 768-dimension vector, from a 740M open model under Apache 2.0. Image, video and audio tokens are spliced into the text sequence and run through one encoder, so a text query can find a video moment or a scanned PDF page without captioning or transcribing first.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;It&amp;rsquo;s not the quality leader. Text is flat against v1; the gain is code (MTEB Code 68.8 to 78.7). Audio (49) and video (51) scores are modest. All numbers are Google&amp;rsquo;s own.&lt;/li&gt;&#10;&lt;li&gt;Every major vendor ships one-space multimodal embeddings now: &lt;a href="https://www.amazon.science/publications/amazon-nova-multimodal-embeddings-technical-report-and-model-card" rel="noopener external"&gt;Amazon Nova&lt;/a&gt;, Gemini Embedding 2, Voyage, Cohere, and the open Qwen3-VL-Embedding, which reports higher image and document scores but has no audio. Pick by constraint. EmbeddingGemma 2&amp;rsquo;s niche is open weights plus audio.&lt;/li&gt;&#10;&lt;li&gt;Matryoshka truncation isn&amp;rsquo;t free: its MMEB score falls from 59.0 at 768 dimensions to 45.7 at 128.&lt;/li&gt;&#10;&lt;li&gt;The interesting part is the system around the model. &lt;a href="https://github.com/ARahim3/DigUp" rel="noopener external"&gt;DigUp&lt;/a&gt; is local file search on it: one vector per photo, PDF page, video shot and 30 s of audio, each pointing back to its page or second, next to an FTS5 keyword index, because no embedding holds an invoice number exactly.&lt;/li&gt;&#10;&lt;li&gt;DigUp fuses the two by turning cosine into a per-query z-score (how far a match stands out for this query), gating on it, and adding a word-coverage bonus. It beat reciprocal-rank fusion on their evals (R@1 0.88 vs 0.73) and can honestly say &amp;ldquo;nothing found&amp;rdquo;. Small, author-run eval.&lt;/li&gt;&#10;&lt;li&gt;It also pins llama.cpp, records the model version in the index, and checks every upgrade against stored reference vectors. Vectors from two runtimes don&amp;rsquo;t mix, and nothing errors when they do.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>How the Claude Code team does code review</title><link>https://saurabh-kumar.com/til/1792238400/</link><pubDate>Sat, 17 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1792238400/</guid><description>&lt;p&gt;&lt;a href="https://claude.dev/blog/solving-code-review-at-anthropic/" rel="noopener external"&gt;How the Claude Code team does code review&lt;/a&gt;. Claude writes ~90% of Claude Code, so nobody reads every diff. They swapped &amp;ldquo;did a human read it?&amp;rdquo; for &amp;ldquo;which check, automated or human, should catch what could go wrong here?&amp;rdquo;, and optimise for the least human time per change, with revert and incident rates as the guardrail.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The bit I missed on first read: review noise costs more when the author is an agent. A person waves off a nit in seconds; an agent fixes every one, which is a new commit and another full review. One PR ran 50+ rounds overnight, and ~59% of later findings were on code already there at round one. Fix: cap at three rounds, re-review only the new diff, flag the class of bug, never repeat a finding.&lt;/li&gt;&#10;&lt;li&gt;The bot posts findings, never approvals. Human approvals (zero, one or two) are set by the change&amp;rsquo;s risk, not by who wrote it. Whoever prompted the agent counts as a reviewer, and an approval covers one commit, so an agent&amp;rsquo;s next push dismisses it.&lt;/li&gt;&#10;&lt;li&gt;Their reviewer&amp;rsquo;s eval is their own past incidents and reverts, run daily. Noise is measured by what authors did with each comment: fixed it, said it was wrong, intentional, or not worth it.&lt;/li&gt;&#10;&lt;li&gt;Versus &lt;a href="https://saurabh-kumar.com/til/weekly/2026-04-26.html#ankit-jain" rel="noopener external"&gt;Ankit Jain&amp;rsquo;s review layers I noted in April&lt;/a&gt;: this is the production version, with no spec-review layer and fast rollback under everything.&lt;/li&gt;&#10;&lt;li&gt;Read the numbers carefully: the share of PRs reverted fell 40% while PR volume doubled, so the count of reverts still rose (~1.2x). Also: the authors sell the reviewer.&lt;/li&gt;&#10;&lt;li&gt;Where I push back: &amp;ldquo;nobody needs a full mental model, ask Claude for an explainer of the slice you&amp;rsquo;re changing.&amp;rdquo; An explainer recovers what the code does, not why it was built that way, which is the &lt;a href="https://addyosmani.com/blog/comprehension-debt/" rel="noopener external"&gt;comprehension debt&lt;/a&gt; Addy Osmani (a co-author here) wrote about. People should spend review on whether to ship it and on direction, not line-level correctness.&lt;/li&gt;&#10;&lt;li&gt;How I read it: had Claude explain it against my notes, then quiz me. The question I got wrong taught me the most.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>'AI' is Not a Tool</title><link>https://saurabh-kumar.com/til/1791892800/</link><pubDate>Tue, 13 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791892800/</guid><description>&lt;p&gt;Rich Hickey&amp;rsquo;s &lt;a href="https://www.linkedin.com/pulse/ai-tool-rich-hickey-kjl8e" rel="noopener external"&gt;&amp;quot;&amp;lsquo;AI&amp;rsquo; is Not a Tool&amp;quot;&lt;/a&gt; vs Huahai Yang&amp;rsquo;s &lt;a href="https://yyhh.org/blog/2026/10/ai-is-a-tool" rel="noopener external"&gt;&amp;ldquo;AI Is a Tool. Using It Well Takes Skill.&amp;rdquo;&lt;/a&gt;. Hickey: a tool is something you wield, and wielding is where expertise comes from, so an LLM is automation you abdicate work to. Yang: a compiler also works unattended and is still a tool; the line is skilled use vs delegating without oversight. Arguing over the label is a dead end. What matters is which skill survives.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Hickey&amp;rsquo;s best line: you are &amp;ldquo;only a software update from everything you&amp;rsquo;ve &amp;rsquo;learned&amp;rsquo; being rendered obsolete.&amp;rdquo; Model quirks and prompt recipes expire. Verification, choosing context and domain judgment carry over to the next model.&lt;/li&gt;&#10;&lt;li&gt;&lt;a href="https://www.sciencedirect.com/science/article/pii/0005109883900468" rel="noopener external"&gt;Bainbridge&amp;rsquo;s Ironies of Automation&lt;/a&gt; said it in 1983: automate the work and the operator is left monitoring, which needs the skill they stopped practicing. My rule: do the work, then ask the model. Don&amp;rsquo;t ask the model instead.&lt;/li&gt;&#10;&lt;li&gt;Best reply to &amp;ldquo;LLMs are slot machines&amp;rdquo; in the &lt;a href="https://news.ycombinator.com/item?id=50063427" rel="noopener external"&gt;HN thread&lt;/a&gt;: a &lt;a href="https://en.wikipedia.org/wiki/Las_Vegas_algorithm" rel="noopener external"&gt;Las Vegas algorithm&lt;/a&gt; is random but always correct; only the runtime varies. Randomness plus a verifier gets you accuracy. Without the verifier you&amp;rsquo;re just pulling the lever.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Rampart</title><link>https://saurabh-kumar.com/til/1791720000/</link><pubDate>Sun, 11 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791720000/</guid><description>&lt;p&gt;Small on-device models are getting good enough to sit in front of the big ones. &lt;a href="https://ndstudio.gov/posts/say-hello-to-rampart" rel="noopener external"&gt;Rampart&lt;/a&gt; from the National Design Studio redacts PII in the browser before a prompt reaches any LLM: regexes with checksum or structural checks for cards (Luhn), SSNs, emails, URLs and IPs, plus a fine-tuned MiniLM for names, phones, account numbers, government IDs and street addresses.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;14.7 MB Q4 ONNX (~15 MB with tokenizer), 98.4% private-term recall across seven Latin-script languages. p50 is 3.9 ms on WebGPU but 12.6 ms on the default WASM backend. OpenAI&amp;rsquo;s Privacy Filter is ~2.8 GB; Presidio scores 65% on the same test set.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;guard.protect()&lt;/code&gt; swaps PII for placeholders like &lt;code&gt;[GIVEN_NAME_1]&lt;/code&gt;, and &lt;code&gt;guard.reveal()&lt;/code&gt; fills them back into the reply. The LLM never sees the name; the user still does.&lt;/li&gt;&#10;&lt;li&gt;Default-deny: every detected label is redacted except city, state and ZIP, so the assistant keeps coarse location without the street line.&lt;/li&gt;&#10;&lt;li&gt;Know the gaps: names in non-Latin scripts recall ~14% (Hindi 6%, Arabic 5%), and it scores 86.4% on a hostile-input suite. The repo calls it harm reduction, not perfect protection.&lt;/li&gt;&#10;&lt;li&gt;Same size class, different job: &lt;a href="https://cactuscompute.com/blog/whistle" rel="noopener external"&gt;Cactus Whistle&lt;/a&gt; is 16.9 MB speech-to-text in seven languages, 11 ms to first token on an M4 Pro CPU (Whisper base: 145 MB, 73 ms). It loads into the same engine as &lt;a href="https://huggingface.co/Cactus-Compute/needle3" rel="noopener external"&gt;Needle 3&lt;/a&gt;, their tool-calling model, so one call takes audio in and returns &lt;code&gt;set_lights(room=&amp;quot;kitchen&amp;quot;, on=false)&lt;/code&gt;. Voice commands for home automation with no cloud round trip.&lt;/li&gt;&#10;&lt;li&gt;The pattern: a tiny specialist at the edge does one narrow job (redact, transcribe, route), and the big model only sees what it needs. Both sets of benchmarks are vendor-reported.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Sebastian Raschka's history of text classifiers</title><link>https://saurabh-kumar.com/til/1791633601/</link><pubDate>Sat, 10 Oct 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791633601/</guid><description>&lt;p&gt;&lt;a href="https://magazine.sebastianraschka.com/p/classifier-history-and-jev" rel="noopener external"&gt;Sebastian Raschka&amp;rsquo;s history of text classifiers&lt;/a&gt; puts numbers on why &lt;a href="https://typesafe.ai/blog/introducing-system-one-models-and-jev" rel="noopener external"&gt;Jev&lt;/a&gt; feels new. On IMDb, bag-of-words + logistic regression gets 89.9%, a fine-tuned ModernBERT about 95%, and Jev 96.5% zero-shot for $0.65 across 25k reviews. Under the hood it is likely still a pretrained transformer with a one-number head that scores each answer option. The hard part is broad training data and calibrated probabilities. What changes is the cost of starting: no labelling, training or hosting per task. Specialists still win on huge volume, the last point of accuracy, or data that can&amp;rsquo;t leave your network. Within a month &lt;a href="https://developers.openai.com/api/docs/guides/decisions" rel="noopener external"&gt;OpenAI&lt;/a&gt; and &lt;a href="https://commandline.microsoft.com/microsoft-decision-1-model-foundry/" rel="noopener external"&gt;Microsoft&lt;/a&gt; shipped the same yes/no, choice and score contract.&lt;/p&gt;&#10;</description></item><item><title>Microsoft eXecution Container (MXC)</title><link>https://saurabh-kumar.com/til/1791633600/</link><pubDate>Sat, 10 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791633600/</guid><description>&lt;p&gt;&lt;a href="https://github.com/microsoft/mxc" rel="noopener external"&gt;Microsoft eXecution Container (MXC)&lt;/a&gt; is an embeddable SDK for running untrusted commands under OS-enforced policies, not a hosted sandbox service.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Defaults: Seatbelt on macOS, Bubblewrap on Linux, ProcessContainer on Windows. SDKs for Node, Rust, and .NET.&lt;/li&gt;&#10;&lt;li&gt;Restricts filesystem and network access. macOS hostname allowlists need a caller-managed proxy; not every backend is a VM.&lt;/li&gt;&#10;&lt;li&gt;Useful boundary: sandbox the command where it executes, not just the agent&amp;rsquo;s tool-composition script. Isolation limits damage; it does not make generated output trustworthy.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Pi Pocket's subagent implementation</title><link>https://saurabh-kumar.com/til/1791547202/</link><pubDate>Fri, 09 Oct 2026 12:00:02 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791547202/</guid><description>&lt;p&gt;&lt;a href="https://github.com/TannerMidd/pi-pocket/blob/3e6046612db4358b599da302d5c96bd407d9de5c/src/server/extensions/subagents.ts" rel="noopener external"&gt;Pi Pocket&amp;rsquo;s subagent implementation&lt;/a&gt; gives each worker a retained conversation and uses durable tasks to deliver messages and report answers. Request IDs and recorded answer IDs prevent duplicate delivery and reporting after restart. The idea worth stealing: make the handoff durable, not just the worker.&lt;/p&gt;&#10;</description></item><item><title>Arnav Gupta's pi-subagent-manager</title><link>https://saurabh-kumar.com/til/1791547201/</link><pubDate>Fri, 09 Oct 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791547201/</guid><description>&lt;p&gt;&lt;a href="https://github.com/championswimmer/pi-subagent-manager" rel="noopener external"&gt;Arnav Gupta&amp;rsquo;s pi-subagent-manager&lt;/a&gt; makes context inheritance explicit. Nested threads receive their parent&amp;rsquo;s snapshot; independent roots start without its history. Threads stay around for steering and resume. I want synchronous coordination by default, forks for continuity and fresh context for independent review.&lt;/p&gt;&#10;</description></item><item><title>Armin Ronacher's subagent extension</title><link>https://saurabh-kumar.com/til/1791547200/</link><pubDate>Fri, 09 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791547200/</guid><description>&lt;p&gt;&lt;a href="https://github.com/mitsuhiko/agent-stuff/blob/main/extensions/subagent.ts" rel="noopener external"&gt;Armin Ronacher&amp;rsquo;s subagent extension&lt;/a&gt; runs one observable Pi child at a time in tmux. The parent waits for the result, while I can attach to inspect or intervene. Worth preserving when adding hierarchy: synchronous coordination must not mean invisible execution.&lt;/p&gt;&#10;</description></item><item><title>Wispr Flow</title><link>https://saurabh-kumar.com/til/1791460800/</link><pubDate>Thu, 08 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791460800/</guid><description>&lt;p&gt;Back on &lt;a href="https://wisprflow.ai/" rel="noopener external"&gt;Wispr Flow&lt;/a&gt; after six months on local speech-to-text (&lt;a href="https://github.com/altic-dev/FluidVoice" rel="noopener external"&gt;FluidVoice&lt;/a&gt; with Parakeet, plus Whisper). Local transcription is fine. The cleanup is what&amp;rsquo;s missing: fixing misheard jargon ate the time dictation saved. Flow&amp;rsquo;s correction pass, OS-level integration and phone keyboard win. It matters more now because models follow high-level intent well: I speak the intent plus constraints in one go and let the LLM structure it. Whisper mode works even when you speak softly. Want to try it? My &lt;a href="https://wisprflow.ai/r?SAURABH2220" rel="noopener external"&gt;referral link&lt;/a&gt; gets you a free month of Pro, and I get one once you&amp;rsquo;ve dictated 2,000 words.&lt;/p&gt;&#10;</description></item><item><title>Pi Durable</title><link>https://saurabh-kumar.com/til/1791374400/</link><pubDate>Wed, 07 Oct 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1791374400/</guid><description>&lt;p&gt;&lt;a href="https://earendil.com/posts/pi-durable/" rel="noopener external"&gt;Pi Durable&lt;/a&gt; is Earendil&amp;rsquo;s experimental agent harness, shipped alongside Pi 1.0. &lt;a href="https://github.com/TannerMidd/pi-pocket" rel="noopener external"&gt;TannerMidd/pi-pocket&lt;/a&gt; is a mobile-first, multiplayer web app for Pi built on it.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Every model call and tool call is a task that checkpoints to storage (SQLite, JSONL, or memory) before moving on. Kill the process and a new one opens the same file and resumes.&lt;/li&gt;&#10;&lt;li&gt;Recovery is per step: a cut-off model request is resent with the partial answer kept and marked aborted. A cut-off tool call reruns only if it is replay-safe; otherwise the model is told it was interrupted.&lt;/li&gt;&#10;&lt;li&gt;A &lt;code&gt;requestId&lt;/code&gt; makes a submission exactly-once, so a client retrying after a crash gets the original back.&lt;/li&gt;&#10;&lt;li&gt;Forks see the parent transcript up to the fork point without copying it.&lt;/li&gt;&#10;&lt;li&gt;~15k lines total, small enough for an agent to read end to end.&lt;/li&gt;&#10;&lt;li&gt;pi-pocket: restart the server mid-run and the work continues. Several people can watch, steer, or queue messages on the same session.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;npm start -- --access tailscale&lt;/code&gt; binds only to the tailnet address, so my phone gets it with no tunnel.&lt;/li&gt;&#10;&lt;li&gt;The shift: the session lives in a SQLite file, not a terminal process, so the phone is just another client. Could replace my Moshi + tmux setup.&lt;/li&gt;&#10;&lt;li&gt;Caveat: anyone who can steer runs shell commands as you.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>pi-blackhole</title><link>https://saurabh-kumar.com/til/1788609600/</link><pubDate>Sat, 05 Sep 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1788609600/</guid><description>&lt;p&gt;Every coding agent (Claude Code, Codex, pi) compacts long sessions the same way: ask an LLM to prose-summarize, then summarize that summary, until load-bearing details erode (why a decision was made, which approaches got rejected). &lt;a href="https://github.com/k0valik/pi-blackhole" rel="noopener external"&gt;pi-blackhole&lt;/a&gt; swaps that for deterministic compaction: a zero-token &lt;code&gt;compile()&lt;/code&gt; that extracts structured sections (goal, files touched, commits, preferences, brief transcript). No LLM, no drift, no cost. The compile is only lossy in the window, not on disk: blackhole also ships a &lt;code&gt;recall&lt;/code&gt; tool (BM25 + regex over the raw session JSONL and the memory ledger) that re-fetches any dropped turn on demand, so compile + recall together is lossless where an LLM summary-of-a-summary destroys the original. It also adds observational memory: background observer/reflector workers append durable facts to per-session ledgers that survive every compaction, and &lt;code&gt;/blackhole-export&lt;/code&gt; walks every past-session JSONL under a git-root into a ranked context file. The clever part is what export trusts. A fact&amp;rsquo;s rank is driven by cross-session &lt;em&gt;recurrence&lt;/em&gt;, damped as &lt;code&gt;log(1 + distinctSessions)&lt;/code&gt; with a burst penalty, not raw counts, so one chatty session can&amp;rsquo;t inflate it. Recurrence-as-reinforcement is basically &lt;a href="https://saurabh-kumar.com/articles/2025/11/notes-on-understanding-hebbian-learning/" rel="noopener external"&gt;Hebbian&lt;/a&gt;: facts that keep firing across sessions get stronger. Dedup is a 3-pass funnel, exact then Levenshtein then Sørensen-Dice token-set, with a Levenshtein floor so keyword overlap alone can&amp;rsquo;t merge two distinct facts. Bonus: the OM workers run on their own model and billing, so I point them at &lt;code&gt;openai-codex/gpt-5.6-luna&lt;/code&gt; while the main thread stays on Claude.&lt;/p&gt;&#10;</description></item><item><title>Meat proxy</title><link>https://saurabh-kumar.com/til/1785844800/</link><pubDate>Tue, 04 Aug 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1785844800/</guid><description>&lt;p&gt;Someone finally named it: &lt;a href="https://gruhn.me/blog/2026-08-03/" rel="noopener external"&gt;meat proxy&lt;/a&gt;. You paste Claude&amp;rsquo;s answer into Slack or a PR review without reading it, and you have made yourself the pipe between me and a model I could have prompted myself, with better context. Everyone already knows that annoyance. Nobody had the word for it. The word is the useful part, because &amp;ldquo;don&amp;rsquo;t be a meat proxy&amp;rdquo; is something you can actually say to a colleague: it names a role instead of calling them lazy. Best line is about code review. Paste the ticket into Claude, paste the reviewer feedback back in, read neither, and the reviewers implemented your PR while you held the pipe. The bar before forwarding anything is to write it in your own words, &amp;ldquo;a decent certificate that you&amp;rsquo;ve done the prior steps.&amp;rdquo; 704 comments on &lt;a href="https://news.ycombinator.com/item?id=49151933" rel="noopener external"&gt;HN&lt;/a&gt;, barely any disagreement.&lt;/p&gt;&#10;</description></item><item><title>NVIDIA's NOOA framework</title><link>https://saurabh-kumar.com/til/1785240000/</link><pubDate>Tue, 28 Jul 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1785240000/</guid><description>&lt;p&gt;Python&amp;rsquo;s &lt;code&gt;Ellipsis&lt;/code&gt; literal (&lt;code&gt;...&lt;/code&gt;) is a real object (&lt;code&gt;type(...)&lt;/code&gt; is &lt;code&gt;&amp;lt;class 'ellipsis'&amp;gt;&lt;/code&gt;), not just a placeholder. Beyond type stubs and numpy slicing, &lt;a href="https://github.com/NVIDIA-NeMo/labs-OO-Agents" rel="noopener external"&gt;NVIDIA&amp;rsquo;s NOOA framework&lt;/a&gt; repurposes it as a runtime dispatch marker: methods with &lt;code&gt;...&lt;/code&gt; bodies are completed by an LLM at runtime, while methods with real bodies stay deterministic Python. The boundary between &amp;ldquo;code I control&amp;rdquo; and &amp;ldquo;code the model controls&amp;rdquo; becomes greppable: &lt;code&gt;grep -n '\.\.\.' agent.py&lt;/code&gt;. Clever reuse of existing syntax that linters and type checkers already accept.&lt;/p&gt;&#10;</description></item><item><title>marcelroed/gigatoken</title><link>https://saurabh-kumar.com/til/1784894400/</link><pubDate>Fri, 24 Jul 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1784894400/</guid><description>&lt;p&gt;Embedding pipelines do not feed whole documents straight into the model. They first use the embedding model&amp;rsquo;s tokenizer to measure and split text into token-bounded, overlapping chunks, then embed each chunk and store the vectors. Tokenizer must match the model. Tools like &lt;a href="https://github.com/marcelroed/gigatoken" rel="noopener external"&gt;marcelroed/gigatoken&lt;/a&gt; accelerate this preparation at corpus scale but do not create embeddings themselves.&lt;/p&gt;&#10;</description></item><item><title>baidu/Unlimited-OCR</title><link>https://saurabh-kumar.com/til/1782475200/</link><pubDate>Fri, 26 Jun 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1782475200/</guid><description>&lt;p&gt;&lt;a href="https://github.com/baidu/Unlimited-OCR" rel="noopener external"&gt;baidu/Unlimited-OCR&lt;/a&gt; (&lt;a href="https://arxiv.org/abs/2606.23050" rel="noopener external"&gt;paper&lt;/a&gt;) parses dozens of pages in one forward pass by fixing the real bottleneck in end-to-end OCR.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The problem: an OCR decoder types out the page token-by-token, and its KV cache grows unbounded with output length — so memory climbs and speed decays the longer the doc, forcing page-by-page loops that wipe memory each step.&lt;/li&gt;&#10;&lt;li&gt;The trick (Reference Sliding Window Attention): split context into two zones with different retention. Vision/prompt tokens stay fully visible and pinned forever (the &amp;ldquo;source book&amp;rdquo;); each token attends to only the last 128 of its own outputs (the &amp;ldquo;last few words you wrote&amp;rdquo;). KV cache becomes a fixed-size queue, so memory and TPS stay flat regardless of output length.&lt;/li&gt;&#10;&lt;li&gt;The non-obvious part: discarding history &lt;em&gt;improves&lt;/em&gt; accuracy (+6% on OmniDocBench, beating Qwen2.5-VL-72B at 3B/0.5B-active) — full attention can diverge on long dense output, and pinned vision tokens never blur.&lt;/li&gt;&#10;&lt;li&gt;Cheap to build: freeze DeepSeek-OCR&amp;rsquo;s encoder, fine-tune only the decoder with the swapped attention.&lt;/li&gt;&#10;&lt;li&gt;Generalizes past OCR: separate &lt;em&gt;what you reference&lt;/em&gt; from &lt;em&gt;what you remember&lt;/em&gt;.&lt;/li&gt;&#10;&lt;li&gt;Ran it on an M1 Mac (32GB) with R-SWA intact, no NVIDIA. The official HF model (&lt;code&gt;trust_remote_code&lt;/code&gt;) already ships R-SWA as a &lt;strong&gt;pure-PyTorch eager ring buffer&lt;/strong&gt; (&lt;code&gt;SlidingWindowLlamaAttention&lt;/code&gt;, picked via &lt;code&gt;attn_implementation=&amp;quot;eager&amp;quot;&lt;/code&gt;) — not buried in a CUDA/FlashAttention kernel. So the &amp;ldquo;port&amp;rdquo; was just stripping &lt;code&gt;.cuda()&lt;/code&gt;/&lt;code&gt;torch.autocast(&amp;quot;cuda&amp;quot;)&lt;/code&gt; and running bf16 on MPS; zero MPS op-gaps hit. bf16 works on MPS in torch 2.12.&lt;/li&gt;&#10;&lt;li&gt;Proof R-SWA is live: instrument the KV cache during decode — on a 540-token page it plateaus at &lt;code&gt;prefill(277) + window(128) = 405&lt;/code&gt; and never grows (full MHA would hit 817). That constant-cache assertion + Distinct-35≈1.0 are the decisive signals, not edit distance (too noisy vs a single reference).&lt;/li&gt;&#10;&lt;li&gt;Gotcha: feeding all pages to one &lt;code&gt;generate()&lt;/code&gt; (&lt;code&gt;infer_multi&lt;/code&gt;) &lt;strong&gt;loops at the document level&lt;/strong&gt; — the 128-token decode window forgets the doc was already transcribed while the vision prefix still shows every page, so it restarts from page 1. Fix = one &lt;code&gt;generate()&lt;/code&gt; per page (matches official &lt;code&gt;infer.py&lt;/code&gt; &lt;code&gt;PAGE_SIZE=1&lt;/code&gt;). Accuracy then lands at/below gemini-vs-docai inter-annotator noise (~0.31 mean edit). ~35–55 s/page on M1.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Many asymmetric embedding models need task prefixes on inputs, and skipping…</title><link>https://saurabh-kumar.com/til/1782216000/</link><pubDate>Tue, 23 Jun 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1782216000/</guid><description>&lt;p&gt;Many asymmetric embedding models need task prefixes on inputs, and skipping them quietly degrades relevance. Each model has its own scheme: nomic (&lt;code&gt;search_query:&lt;/code&gt;/&lt;code&gt;search_document:&lt;/code&gt;), E5 (&lt;code&gt;query:&lt;/code&gt;/&lt;code&gt;passage:&lt;/code&gt;), BGE (a query instruction sentence, bare docs) — not interchangeable. OpenAI &lt;code&gt;text-embedding-3-*&lt;/code&gt; and &lt;code&gt;all-MiniLM-L6-v2&lt;/code&gt; need none. Whether you add the prefix depends on the serving layer, not the model: raw endpoints (llama.cpp &lt;code&gt;/v1/embeddings&lt;/code&gt;, HF TEI, Ollama) send bare text so it&amp;rsquo;s on you, while sentence-transformers (&lt;code&gt;prompt_name=&amp;quot;query&amp;quot;&lt;/code&gt;) and vendor SDKs inject it for you. Adding &lt;code&gt;search_query:&lt;/code&gt;/&lt;code&gt;search_document:&lt;/code&gt; to a nomic-v1.5 call lifted cosine similarity on a real query/doc pair from 0.54 to 0.60 at zero cost.&lt;/p&gt;&#10;</description></item><item><title>How Prompt Caching Works</title><link>https://saurabh-kumar.com/articles/2026/06/how-prompt-caching-works/</link><pubDate>Sun, 21 Jun 2026 00:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/articles/2026/06/how-prompt-caching-works/</guid><description>&lt;p&gt;Today I learned how prompt caching actually works under the hood in LLM APIs&#10;(Anthropic, OpenAI, Bedrock, etc.), and why it makes long agent loops 5–10×&#10;cheaper and noticeably faster.&lt;/p&gt;&#10;&lt;h2 id="the-problem-it-solves"&gt;The problem it solves&lt;/h2&gt;&#10;&lt;p&gt;In an agent / chat loop, every request to the model looks almost identical to&#10;the previous one:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The same big &lt;strong&gt;system prompt&lt;/strong&gt; (rules, tool descriptions, skills…)&lt;/li&gt;&#10;&lt;li&gt;The same set of &lt;strong&gt;tool definitions&lt;/strong&gt;&lt;/li&gt;&#10;&lt;li&gt;The same &lt;strong&gt;history&lt;/strong&gt; of past user/assistant/tool messages, with only a few&#10;new tokens appended at the end&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Without caching, the provider has to re-tokenize and run a full attention&#10;pre-fill over all of that prefix on every turn. For a 100k-token context, that&#10;prefill dominates both cost and latency.&lt;/p&gt;&#10;&lt;h2 id="what-gets-cached-the-kv-state"&gt;What gets cached: the KV state&lt;/h2&gt;&#10;&lt;p&gt;When a transformer processes a prompt, it computes a &lt;strong&gt;key/value tensor pair&#10;for every token in every attention layer&lt;/strong&gt;. That KV state is what attention&#10;queries against on the next decoded token.&lt;/p&gt;&#10;&lt;p&gt;Prompt caching is, mechanically, &amp;ldquo;save those KV tensors for a prefix on the&#10;server, and reuse them next time the same prefix shows up.&amp;rdquo; Output tokens are&#10;never cached — only the prompt-side KV state.&lt;/p&gt;&#10;&lt;h2 id="the-lifecycle"&gt;The lifecycle&lt;/h2&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;Mark a cache breakpoint.&lt;/strong&gt; The client places a &lt;code&gt;cache_control&lt;/code&gt; marker&#10;(Anthropic) or relies on automatic prefix detection (OpenAI). Everything&#10;&lt;em&gt;before&lt;/em&gt; the marker is the cacheable prefix.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;First request → cache write.&lt;/strong&gt; The provider hashes the cacheable prefix&#10;(model id + tools + messages + cache key, byte-exact), runs the prefill,&#10;and stores the resulting KV tensors keyed by that hash. You&amp;rsquo;re billed&#10;&lt;code&gt;cache_write&lt;/code&gt; tokens, usually a bit &lt;em&gt;more&lt;/em&gt; than normal input&#10;(~1.25× for Anthropic 5-minute cache, ~2× for the 1-hour &amp;ldquo;extended&amp;rdquo;&#10;cache).&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Next request → cache read.&lt;/strong&gt; If the new request starts with exactly the&#10;same prefix, the provider looks up the hash, loads the stored KV state,&#10;and only runs fresh compute for the suffix after the last hit. You&amp;rsquo;re&#10;billed &lt;code&gt;cache_read&lt;/code&gt; tokens — often &lt;strong&gt;~10% of the normal input price&lt;/strong&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;TTL.&lt;/strong&gt; Entries expire after a TTL (Anthropic: 5 min default, 1 hour&#10;opt-in; OpenAI: a few minutes, automatic). Each hit typically refreshes&#10;the TTL, so an active session keeps the cache warm.&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;h2 id="what-invalidates-the-cache"&gt;What invalidates the cache&lt;/h2&gt;&#10;&lt;p&gt;The hash is over the &lt;strong&gt;exact bytes&lt;/strong&gt; of the prefix. Anything that changes&#10;those bytes forces a fresh &lt;code&gt;cache_write&lt;/code&gt;:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Editing the system prompt (even reordering sentences or adjusting&#10;whitespace)&lt;/li&gt;&#10;&lt;li&gt;Adding, removing, or reordering a tool definition&lt;/li&gt;&#10;&lt;li&gt;Inserting/editing/reordering an earlier message in the history&lt;/li&gt;&#10;&lt;li&gt;Switching model id or provider&lt;/li&gt;&#10;&lt;li&gt;Changing the cache key (some providers expose one for tenant isolation)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;The corollary: to maximize hit rate, agents should &lt;strong&gt;only append at the end&lt;/strong&gt;&#10;and keep system prompt and tools byte-stable across turns.&lt;/p&gt;&#10;&lt;h2 id="reading-the-usage-block"&gt;Reading the usage block&lt;/h2&gt;&#10;&lt;p&gt;Most providers return a usage object on every response with at least these&#10;fields:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;312&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// fresh, non-cached prompt tokens this turn&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;845&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// generated tokens&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cacheRead&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;47210&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="c1"&gt;// prompt tokens served from cache&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cacheWrite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="c1"&gt;// prompt tokens written to cache this turn&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A useful per-turn metric is the &lt;strong&gt;cache hit ratio&lt;/strong&gt;:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;cacheHitRate = cacheRead / (input + cacheRead + cacheWrite)&#10;&lt;/code&gt;&lt;/pre&gt;&lt;ul&gt;&#10;&lt;li&gt;&lt;code&gt;0%&lt;/code&gt; → nothing reused. Normal on the very first turn, or right after the&#10;prefix changed (tool list updated, history compacted, model swapped).&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;70–95%&lt;/code&gt; → healthy steady state for a long agent loop.&lt;/li&gt;&#10;&lt;li&gt;&lt;code&gt;cacheWrite &amp;gt; 0, cacheRead = 0&lt;/code&gt; → you just primed the cache; the next turn&#10;should be cheap if you don&amp;rsquo;t break the prefix.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="pricing-back-of-the-envelope"&gt;Pricing back-of-the-envelope&lt;/h2&gt;&#10;&lt;p&gt;Rough Anthropic numbers (Claude Sonnet, illustrative):&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Token type&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Price multiplier vs. input&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;input&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;1×&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;cache_write&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;~1.25× (5 min) / ~2× (1h)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;cache_read&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;~0.1×&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;code&gt;output&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;~5×&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;So a 100k-token prefix that&amp;rsquo;s hit 20 times across a session costs roughly:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;1 × 1.25 × 100k (one write)&#10;+ 19 × 0.1 × 100k (nineteen reads)&#10;= 1.25 × 100k + 1.9 × 100k&#10;≈ 3.15 × 100k input-equivalent&#10;&#10;vs. uncached:&#10;20 × 1 × 100k = 20 × 100k&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;A ~6× saving on the prompt side, plus a big latency win because the server&#10;skips the prefill.&lt;/p&gt;&#10;&lt;h2 id="gotchas"&gt;Gotchas&lt;/h2&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Minimum prefix size.&lt;/strong&gt; Most providers require the cacheable prefix to be&#10;above some threshold (e.g. 1024 tokens for Anthropic) before caching kicks&#10;in at all.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Position of the breakpoint matters.&lt;/strong&gt; Putting it after volatile content&#10;(like the latest user message) means nothing ever gets reused.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Tool definitions are part of the prefix.&lt;/strong&gt; Dynamically generated tool&#10;lists with shuffled order will silently destroy your hit rate.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Streaming vs. non-streaming&lt;/strong&gt; use the same cache; cache fields show up&#10;in the final usage event.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Cost accounting:&lt;/strong&gt; &lt;code&gt;cache_write&lt;/code&gt; is more expensive than &lt;code&gt;input&lt;/code&gt;, so a&#10;flaky prefix that constantly re-writes can be &lt;em&gt;worse&lt;/em&gt; than no caching at&#10;all.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Moshi</title><link>https://saurabh-kumar.com/til/1781524800/</link><pubDate>Mon, 15 Jun 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1781524800/</guid><description>&lt;p&gt;&lt;a href="https://getmoshi.app/" rel="noopener external"&gt;Moshi&lt;/a&gt; is the best SSH client I&amp;rsquo;ve found for iPhone — connects to your laptop&amp;rsquo;s tmux sessions over &lt;a href="https://mosh.org/" rel="noopener external"&gt;Mosh&lt;/a&gt;, making it rock-solid on mobile networks with roaming support. Perfect for an agent view on the go; I use it to monitor and interact with running pi/Claude sessions from my iPhone without losing the session on network switches.&lt;/p&gt;&#10;</description></item><item><title>My Local LLM On A 32 GB M1 Pro</title><link>https://saurabh-kumar.com/articles/2026/06/my-local-llm-on-a-32-gb-m1-pro/</link><pubDate>Sun, 14 Jun 2026 00:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/articles/2026/06/my-local-llm-on-a-32-gb-m1-pro/</guid><description>&lt;p&gt;My pi setup has a &lt;code&gt;fast&lt;/code&gt; mode: a slot I wired into my own config for the small, high-volume questions, pointed at Bedrock&amp;rsquo;s Haiku 4.5. It is a good model. Cheap, responsive, and it answers the kind of small, contained question I throw at it twenty times a day without complaining. It runs on third-party hardware, and those questions send code to a third party. I wanted to try running that slot locally.&lt;/p&gt;&#10;&lt;p&gt;None of that is a crisis. Bedrock is fast and it works. But the machine that could do the same job is sitting on my desk, and when Kyle Howells &lt;a href="https://ikyle.me/blog/2026/how-to-setup-a-local-coding-agent-on-macos" rel="noopener external"&gt;got Gemma 4 26B-A4B running locally on an M1 Max at 72 tokens per second&lt;/a&gt; and the &lt;a href="https://news.ycombinator.com/item?id=48507020" rel="noopener external"&gt;Hacker News thread&lt;/a&gt; filled up with people on smaller Macs reporting the same setup was workable, I wanted to know whether my own laptop could host that slot.&lt;/p&gt;&#10;&lt;p&gt;The catch is that my laptop is an M1 Pro with 32 GB of unified memory: half the RAM and roughly half the memory bandwidth of the machine in Kyle&amp;rsquo;s post. Copying his commands verbatim was never going to work. I needed to know what to change, in what order, and what to give up. So I did not copy the commands. I gave pi the article and asked it to build me a plan for my machine, and to interrogate me before it wrote a line.&lt;/p&gt;&#10;&lt;h2 id="i-handed-the-guide-to-an-llm-and-made-it-grill-me"&gt;I Handed The Guide To An LLM And Made It Grill Me&lt;/h2&gt;&#10;&lt;p&gt;The whole thing started with one message. Typos and all, this is what I actually sent:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;Act local AI setup expert working on M1 silicon with 32GB ram. Read though [the article] and also pull all the comments on this article on news.ycombinator.com article id 48507020. I&amp;rsquo;ve ollama and muse already installed there are models installed as well. Prep and action plan to set this up in the most performant and maintaibale way. Not sure quite sure if i need to uninstall previous ollama app and it&amp;rsquo;s modetls. Create and write a plan after analysing things. &lt;code&gt;/grill-me&lt;/code&gt;&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;The &lt;code&gt;/grill-me&lt;/code&gt; at the end is a small skill in my pi setup. It is two paragraphs of instructions that tell the model to walk down the decision tree one node at a time, recommend an answer at each step, and wait for my reply before moving on. It is not magic. It is a way of forcing the conversation I would otherwise have with myself in a notebook, except the model will not let me skip a step.&lt;/p&gt;&#10;&lt;p&gt;The interview was mostly the model proposing and me confirming in monosyllables. My replies, verbatim, were things like:&lt;/p&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;A&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;i would prefer ~/code/localLLM&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;A, also note i&amp;rsquo;ve &amp;ldquo;llm&amp;rdquo; cli already installed, and alias would be nice.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;blockquote&gt;&#10;&lt;p&gt;let&amp;rsquo;s go with recommendation, write the plan.&lt;/p&gt;&#10;&lt;/blockquote&gt;&#10;&lt;p&gt;That is the entire texture of it. A simple opening prompt that points at an existing guide and names my constraints, then a handful of one-word answers steering the branches. The model did the reading (the article and the full HN thread) and the arithmetic. I made the calls.&lt;/p&gt;&#10;&lt;p&gt;And it pushed back, which is the point. When I leaned toward the 65 K context window from the article, it did the memory math and showed me I would be swapping inside the first generation on 32 GB, so we settled on 32 K. When I waffled on keeping Ollama &amp;ldquo;just in case,&amp;rdquo; it pointed out I was at 89 % disk with a thirteen-gigabyte Ollama models directory I was not using. When I asked for an alias, it caught that &lt;code&gt;llm&lt;/code&gt; was already taken by &lt;a href="https://llm.datasette.io" rel="noopener external"&gt;Simon Willison&amp;rsquo;s tool&lt;/a&gt; and proposed &lt;code&gt;lcl&lt;/code&gt; instead. None of these are insights I lacked. They are checks I would have skipped because I was already mentally on the next step.&lt;/p&gt;&#10;&lt;p&gt;By the end there was a written record of every decision and its reason, in the repo, not in my head. That is where it should be.&lt;/p&gt;&#10;&lt;h2 id="the-decisions-that-fell-out"&gt;The Decisions That Fell Out&lt;/h2&gt;&#10;&lt;p&gt;The interview converged on a short list, and the reasoning behind each one is worth more than the choice itself.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Model: Gemma 4 26B-A4B, the MoE, not a dense model.&lt;/strong&gt; The single most important fact about local inference on a Mac is not how much RAM you have, it is how fast it can be read. Generation is bottlenecked on memory bandwidth. The M1 Max in Kyle&amp;rsquo;s article runs at 400 GB/s; my M1 Pro at roughly 200. Everything else being equal, I should expect about half his throughput. His 72 tokens per second becomes my 36. That is still faster than I read. It is also why a mixture-of-experts model is the right answer here: Gemma 4 26B-A4B holds 26 billion parameters but activates only four billion per token. The full weights sit in RAM, but each forward pass streams a fraction of them. The dense 27B sibling has the same disk footprint and meaningfully worse generation rate. It is probably smarter. It does not matter, because it is too slow for the slot I am filling.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Speculative decoding, on.&lt;/strong&gt; Gemma 4 ships an MTP draft head, a tiny network that predicts the next several tokens cheaply, which the main model accepts or rejects in one pass. When acceptance is high, you get most of those tokens almost for free. Kyle measured +24 % on his machine. The HN thread is full of caveats about acceptance rates and MoE models benefiting less. None of it changes the decision: turn it on, measure, keep what you get.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Context: 32 K, not 65 K.&lt;/strong&gt; The arithmetic the model made me do. A 65 K KV cache plus 16 GB of weights plus a draft model does not leave enough headroom on 32 GB once Chrome and an IDE are open.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;mise, not a shell script.&lt;/strong&gt; The article uses &lt;code&gt;huggingface-cli&lt;/code&gt; to fetch the GGUFs and a hand-written tmux script to launch the server. Both work. Both are also infrastructure you now own and will forget you configured. llama.cpp&amp;rsquo;s &lt;code&gt;-hf&lt;/code&gt; flag does the download without a Python virtualenv. mise&amp;rsquo;s &lt;code&gt;github:&lt;/code&gt; backend installs the prebuilt Metal binary and rolls it forward with &lt;code&gt;mise upgrade&lt;/code&gt;. The tmux script becomes a few named tasks in a &lt;code&gt;.mise.toml&lt;/code&gt;. The choice is not about elegance, it is about which moving piece you are willing to own. I already own mise.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Ollama goes.&lt;/strong&gt; I had it from earlier experiments, with &lt;code&gt;gemma4:latest&lt;/code&gt; and a couple of other models taking 13 GB, plus the app idling in the background on port 11434. Ollama is a wrapper around llama.cpp. Once you run &lt;code&gt;llama-server&lt;/code&gt; directly, the wrapper is in the way. So the app, the models directory, and the port all go, and the 13 GB comes back on a disk that was at 89 %.&lt;/p&gt;&#10;&lt;h2 id="the-setup-start-to-finish"&gt;The Setup, Start To Finish&lt;/h2&gt;&#10;&lt;p&gt;This is the reproducible part. You can follow it by hand, or point an LLM at this section and let it do the work. That is roughly how it got built in the first place. The numbers below are the actual results on my M1 Pro, not estimates.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;1. Decommission Ollama&lt;/strong&gt; (skip if you never had it):&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;osascript -e &lt;span class="s1"&gt;&amp;#39;tell application &amp;#34;Ollama&amp;#34; to quit&amp;#39;&lt;/span&gt; 2&amp;gt;/dev/null&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;pkill -f Ollama&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;rm -rf ~/.ollama /Applications/Ollama.app &lt;span class="c1"&gt;# reclaimed 13 GB&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;2. Install llama.cpp via mise.&lt;/strong&gt; The prebuilt arm64 binary has Metal and Accelerate baked in:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;mise use --global &lt;span class="s1"&gt;&amp;#39;github:ggml-org/llama.cpp@latest&amp;#39;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;llama-server --help &lt;span class="p"&gt;|&lt;/span&gt; grep -E -- &lt;span class="s1"&gt;&amp;#39;--spec-type|-hf&amp;#39;&lt;/span&gt; &lt;span class="c1"&gt;# confirm MTP + HF download support&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;The one thing worth verifying is that &lt;code&gt;--spec-type draft-mtp&lt;/code&gt; is present, since MTP support landed only recently. On my install (build &lt;code&gt;b9631&lt;/code&gt;) it was there. If it ever is not, a source build with &lt;code&gt;-DGGML_METAL=ON&lt;/code&gt; is the five-minute fallback.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;3. A project with mise tasks&lt;/strong&gt; at &lt;code&gt;~/code/localLLM/.mise.toml&lt;/code&gt;. The &lt;code&gt;[env]&lt;/code&gt; block makes the model cache live with the project; the tasks replace the shell script:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-toml" data-lang="toml"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;env&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;LLAMA_CACHE&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;{{ config_root }}/models&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tools&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;github:ggml-org/llama.cpp&amp;#34;&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;latest&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;start&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;description&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Start llama-server (Gemma 4 26B-A4B + MTP) in tmux&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;tmux has-session -t llama 2&amp;gt;/dev/null &amp;amp;&amp;amp; { echo running; exit 0; }&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;tmux new-session -d -s llama -c &amp;#34;&lt;/span&gt;&lt;span class="err"&gt;$&lt;/span&gt;&lt;span class="nx"&gt;PWD&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34; \\&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt; &amp;#34;&lt;/span&gt;&lt;span class="nx"&gt;llama-server&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-hf&lt;/span&gt; &lt;span class="nx"&gt;unsloth&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gemma-4-26B-A4B-it-GGUF&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;UD-Q4_K_XL&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-hfd&lt;/span&gt; &lt;span class="nx"&gt;unsloth&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;gemma-4-26B-A4B-it-GGUF&lt;/span&gt;&lt;span class="err"&gt;:&lt;/span&gt;&lt;span class="nx"&gt;Q8_0-MTP&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--spec-type&lt;/span&gt; &lt;span class="nx"&gt;draft-mtp&lt;/span&gt; &lt;span class="nx"&gt;--spec-draft-n-max&lt;/span&gt; &lt;span class="mi"&gt;3&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;-ngl&lt;/span&gt; &lt;span class="mi"&gt;999&lt;/span&gt; &lt;span class="nx"&gt;-fa&lt;/span&gt; &lt;span class="nx"&gt;on&lt;/span&gt; &lt;span class="nx"&gt;-c&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt; &lt;span class="nx"&gt;--parallel&lt;/span&gt; &lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--reasoning&lt;/span&gt; &lt;span class="nx"&gt;off&lt;/span&gt; &lt;span class="nx"&gt;--reasoning-budget&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--temp&lt;/span&gt; &lt;span class="mf"&gt;0.7&lt;/span&gt; &lt;span class="nx"&gt;--repeat-penalty&lt;/span&gt; &lt;span class="mf"&gt;1.1&lt;/span&gt; &lt;span class="nx"&gt;--repeat-last-n&lt;/span&gt; &lt;span class="mi"&gt;256&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nx"&gt;--host&lt;/span&gt; &lt;span class="mf"&gt;127.0&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="mf"&gt;0.1&lt;/span&gt; &lt;span class="nx"&gt;--port&lt;/span&gt; &lt;span class="mi"&gt;8082&lt;/span&gt; &lt;span class="err"&gt;\\&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="mi"&gt;2&lt;/span&gt;&lt;span class="err"&gt;&amp;gt;&amp;amp;&lt;/span&gt;&lt;span class="mi"&gt;1&lt;/span&gt; &lt;span class="err"&gt;|&lt;/span&gt; &lt;span class="nx"&gt;tee&lt;/span&gt; &lt;span class="nx"&gt;-a&lt;/span&gt; &lt;span class="nx"&gt;logs&lt;/span&gt;&lt;span class="err"&gt;/&lt;/span&gt;&lt;span class="nx"&gt;llama-server&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;log&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="s2"&gt;&amp;#34;&amp;#34;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;stop&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;tmux kill-session -t llama 2&amp;gt;/dev/null &amp;amp;&amp;amp; echo stopped || echo &amp;#39;not running&amp;#39;&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="nx"&gt;tasks&lt;/span&gt;&lt;span class="p"&gt;.&lt;/span&gt;&lt;span class="nx"&gt;status&lt;/span&gt;&lt;span class="p"&gt;]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="nx"&gt;run&lt;/span&gt; &lt;span class="p"&gt;=&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;curl -fsS http://127.0.0.1:8082/v1/models &amp;gt;/dev/null &amp;amp;&amp;amp; echo up || echo down&amp;#34;&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;A note on the port. The article uses 8080. On my machine 8080 and 8081 were already taken by Google &lt;code&gt;cloud-run&lt;/code&gt; processes, so the server silently answered on a port that was not mine until I noticed the responses carried a &lt;code&gt;Server: Google Frontend&lt;/code&gt; header. I moved everything to &lt;strong&gt;8082&lt;/strong&gt;. Check &lt;code&gt;lsof -i :8080&lt;/code&gt; before you assume a port is free.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;4. A one-key alias&lt;/strong&gt; in &lt;code&gt;~/.zshrc&lt;/code&gt;. &lt;code&gt;llm&lt;/code&gt; was taken, so &lt;code&gt;lcl&lt;/code&gt; for &amp;ldquo;local&amp;rdquo;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl&lt;span class="o"&gt;()&lt;/span&gt; &lt;span class="o"&gt;{&lt;/span&gt; mise -C &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$HOME&lt;/span&gt;&lt;span class="s2"&gt;/code/localLLM&amp;#34;&lt;/span&gt; run &lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="nv"&gt;$@&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;;&lt;/span&gt; &lt;span class="o"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-sh" data-lang="sh"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl start &lt;span class="c1"&gt;# boot the server (first run downloads the model)&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl status &lt;span class="c1"&gt;# is it up?&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;lcl stop &lt;span class="c1"&gt;# free the ~22 GB of RAM&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;&lt;strong&gt;5. First boot and download.&lt;/strong&gt; &lt;code&gt;lcl start&lt;/code&gt; triggers the download through llama.cpp&amp;rsquo;s &lt;code&gt;-hf&lt;/code&gt;. The real footprint came in smaller than the article&amp;rsquo;s &amp;ldquo;~21 GB&amp;rdquo; estimate. The total was &lt;strong&gt;18 GB&lt;/strong&gt;: 16 GB for the Q4_K_XL main model, &lt;strong&gt;1.2 GB&lt;/strong&gt; for the MTP head (an auxiliary head, not a full second model), and a 441 MB multimodal projector that gets pulled automatically even though I do not need images. Add &lt;code&gt;--no-mmproj&lt;/code&gt; if you want to skip it.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;6. Point pi at it.&lt;/strong&gt; A &lt;code&gt;local&lt;/code&gt; provider in &lt;code&gt;~/.pi/agent/models.json&lt;/code&gt;:&lt;/p&gt;&#10;&lt;div class="highlight"&gt;&lt;pre tabindex="0" class="chroma"&gt;&lt;code class="language-json" data-lang="json"&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;providers&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Local llama.cpp&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;baseUrl&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;http://127.0.0.1:8082/v1&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;api&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;openai-completions&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;apiKey&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;local&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;models&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[{&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;id&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;gemma-4-26B-A4B-it-UD-Q4_K_XL.gguf&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;name&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="s2"&gt;&amp;#34;Gemma 4 26B-A4B Q4 + MTP&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;reasoning&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="kc"&gt;false&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;[&lt;/span&gt;&lt;span class="s2"&gt;&amp;#34;text&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;],&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;contextWindow&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;32768&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;maxTokens&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;8192&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="nt"&gt;&amp;#34;cost&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="p"&gt;{&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;input&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;output&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;cacheRead&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt;&lt;span class="p"&gt;,&lt;/span&gt; &lt;span class="nt"&gt;&amp;#34;cacheWrite&amp;#34;&lt;/span&gt;&lt;span class="p"&gt;:&lt;/span&gt; &lt;span class="mi"&gt;0&lt;/span&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}]&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt; &lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;span class="line"&gt;&lt;span class="cl"&gt;&lt;span class="p"&gt;}&lt;/span&gt;&#10;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;And a &lt;code&gt;local&lt;/code&gt; mode in &lt;code&gt;modes.json&lt;/code&gt; pointing at it. The benchmark answers came back clean, so I gave it real work, and that is where it broke.&lt;/p&gt;&#10;&lt;h2 id="the-thinking-spiral"&gt;The Thinking Spiral&lt;/h2&gt;&#10;&lt;p&gt;The first real task I handed Gemma was trivial: rewrite a Hugo template so a list of tags renders as &amp;ldquo;tagged A, B and C&amp;rdquo; instead of &amp;ldquo;A B C&amp;rdquo;. A ten-line edit. Haiku does it in one tool call. Gemma read the file, started thinking, and never stopped. The session export shows a single reasoning block of &lt;strong&gt;32,000 characters&lt;/strong&gt;: it derived the correct off-by-one logic, then re-derived it, then second-guessed the whitespace, then re-read its own conclusion and started over. It never emitted the edit. I aborted it. I told it &amp;ldquo;you are going in loop.&amp;rdquo; It thought about that, too, and aborted again.&lt;/p&gt;&#10;&lt;p&gt;This is the failure mode the chat-window benchmarks hide. Gemma 4 IT ships with thinking on, and llama.cpp&amp;rsquo;s default reasoning budget is unlimited (&lt;code&gt;-1&lt;/code&gt;). In a chat that is fine; the model thinks, then answers. Inside an agent loop, where the next move is supposed to be a tool call, an unbounded thinking budget on a small model is a trap. There is nothing forcing the transition from reasoning to acting, and a 4B-active MoE is not big enough to find the exit on its own. The default sampling makes it worse: &lt;code&gt;--repeat-penalty&lt;/code&gt; defaults to &lt;code&gt;1.0&lt;/code&gt;, which is to say off, so nothing discourages the model from looping on its own reasoning.&lt;/p&gt;&#10;&lt;p&gt;My first instinct was the biggest hammer: turn thinking off entirely. It worked. But &amp;ldquo;it worked&amp;rdquo; is where most debugging stops and most of the understanding gets lost, so I made the model prove which flag was actually doing the work. I had pi reconstruct the exact failing scenario from the session export, same system prompt, same tools, same file, same prompt, and replay it against two server configs:&lt;/p&gt;&#10;&lt;pre tabindex="0"&gt;&lt;code&gt;--reasoning off --reasoning-budget 0 # A: no thinking&#10;--reasoning auto --reasoning-budget 1024 --repeat-penalty 1.1 # B: bounded thinking&#10;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Same prompt, same machine, here is what came back:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Config&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Reasoning emitted&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Outcome&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Original (unbounded, no repeat penalty)&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;32,000 chars&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;spiral, aborted, no edit&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;A — reasoning off&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;0 chars&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clean &lt;code&gt;read&lt;/code&gt; → &lt;code&gt;edit&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;B — bounded to 1024&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;518 chars&lt;/strong&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;clean &lt;code&gt;read&lt;/code&gt; → &lt;code&gt;edit&lt;/code&gt;&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;The lesson is in the third row. Capping the budget alone stopped the spiral: the model thought for 518 characters, decided, and acted. So the bug was never &amp;ldquo;thinking is bad.&amp;rdquo; It was &amp;ldquo;&lt;em&gt;unbounded&lt;/em&gt; thinking is bad,&amp;rdquo; and &lt;code&gt;--reasoning off&lt;/code&gt; is just the most aggressive way to bound it to zero. Both configs produced the same edit, the same slightly-clumsy Hugo logic. The extra reasoning in config B bought nothing on this task; that is a 4B-active model hitting its ceiling, not a budget I can tune my way around.&lt;/p&gt;&#10;&lt;p&gt;Two more things I only learned by testing. &lt;code&gt;--repeat-penalty&lt;/code&gt; is an orthogonal fix and worth keeping regardless: at &lt;code&gt;1.0&lt;/code&gt; it is off, and it guards the &lt;em&gt;content&lt;/em&gt; against loops, not just the thinking. And &lt;code&gt;--reasoning off&lt;/code&gt; does not lobotomise the model the way the name suggests. I gave it a reasoning trap with thinking off, and it still worked the problem out, step by step, &lt;em&gt;in its visible answer&lt;/em&gt;, and got it right. What the flag removes is the separate, hidden, budget-less thinking phase, not the model&amp;rsquo;s ability to reason. For an agent loop that is the right trade: when reasoning happens it is visible in the content and bounded by the task, instead of a hidden phase that can run away.&lt;/p&gt;&#10;&lt;p&gt;So for the &lt;em&gt;fast&lt;/em&gt; slot I keep it off. The whole point of the slot is a quick, direct tool call; the test showed bounded thinking adds latency with no quality gain here, and if I want real reasoning I switch to a cloud mode. That is also why &lt;code&gt;&amp;quot;reasoning&amp;quot;&lt;/code&gt; is &lt;code&gt;false&lt;/code&gt; in the model config above, with thinking disabled at the server there is nothing for pi to render. The middle ground is real and now proven, though: if I ever stand up a &lt;code&gt;local-deep&lt;/code&gt; mode for harder offline work, &lt;code&gt;--reasoning auto --reasoning-budget 1024&lt;/code&gt; is a configuration I have watched stay out of the ditch. It just wants its own server on its own port, because &lt;code&gt;--reasoning off&lt;/code&gt; is a hard gate you cannot toggle back on per request.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;What it actually does&lt;/strong&gt;, measured on the M1 Pro:&lt;/p&gt;&#10;&lt;table&gt;&#10;&#9;&lt;thead&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Metric&lt;/th&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;th&gt;Result&lt;/th&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/thead&gt;&#10;&#9;&lt;tbody&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Generation&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;34–36 tok/s&lt;/strong&gt; (predicted half of Kyle&amp;rsquo;s 72, got it)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Prompt ingestion&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;77 tok/s&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;Time to first token&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;~376 ms&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&#9;&#9;&lt;tr&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;MTP acceptance&lt;/td&gt;&#10;&#9;&#9;&#9;&#9;&#9;&lt;td&gt;&lt;strong&gt;~65 %&lt;/strong&gt; (e.g. 1350 of 2090 drafted tokens accepted)&lt;/td&gt;&#10;&#9;&#9;&#9;&lt;/tr&gt;&#10;&#9;&lt;/tbody&gt;&#10;&lt;/table&gt;&#10;&lt;p&gt;Half the bandwidth, half the tokens, just as predicted. The MTP acceptance beat what I feared. The article saw +24 % on a dense workload; here, two thirds of the drafted tokens landed.&lt;/p&gt;&#10;&lt;h2 id="how-i-actually-use-a-fast-model"&gt;How I Actually Use A Fast Model&lt;/h2&gt;&#10;&lt;p&gt;A &lt;code&gt;fast&lt;/code&gt; model in pi is not the thing I have a conversation with. It is the model that does the small, high-volume, latency-sensitive jobs around the edges of the real work, where Opus is overkill and the round-trip cost is what matters. Those are the jobs I would rather keep on my own machine, and the place where 36 tokens per second is plenty.&lt;/p&gt;&#10;&lt;p&gt;In my setup, that means:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/answer&lt;/code&gt; and &lt;code&gt;Ctrl+.&lt;/code&gt;&lt;/strong&gt; — a &lt;a href="https://github.com/theskumar/agent-stuff/blob/main/extensions/answer.ts" rel="noopener external"&gt;small pi extension I run&lt;/a&gt; that grabs the last assistant message when it ends on a batch of clarifying questions, extracts them into structured JSON via a side LLM call, and opens a TUI so I can answer them one at a time. The extraction is a contained, schema-shaped task, the sort of thing a fast model handles well. It defaults to &lt;code&gt;claude-bridge/claude-haiku-4-5&lt;/code&gt; and is configurable at &lt;code&gt;~/.pi/agent/extensions/answer.config.json&lt;/code&gt;, the line I now point at &lt;code&gt;local&lt;/code&gt;.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Branch summaries and compaction.&lt;/strong&gt; When I navigate away from a branch on &lt;code&gt;/tree&lt;/code&gt;, or a session outgrows its context window, pi summarizes the work to carry forward. By default those calls run on the active session model, which means a &lt;code&gt;/tree&lt;/code&gt; hop can cost an Opus summary. A &lt;a href="https://github.com/theskumar/agent-stuff/blob/main/extensions/tree-fast-summary.ts" rel="noopener external"&gt;small extension I run&lt;/a&gt; reroutes both to whatever the &lt;code&gt;fast&lt;/code&gt; mode points at, so the throwaway housekeeping never touches the expensive model.&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;&lt;code&gt;/mode fast&lt;/code&gt;&lt;/strong&gt; for the genuinely small questions, the &amp;ldquo;what&amp;rsquo;s the flag for X&amp;rdquo; and &amp;ldquo;rename this&amp;rdquo; and quick grep-and-explain ones, where I switch the whole session to the fast slot and switch back.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;None of these need a frontier model. They need something that responds now and gets the structure right. That is the bar Gemma 4 has to clear to take the slot from Haiku.&lt;/p&gt;&#10;&lt;p&gt;So I gave it the slot. I went back and forth here. The cautious move was to add a separate &lt;code&gt;local&lt;/code&gt; mode, keep &lt;code&gt;fast&lt;/code&gt; on Haiku, and switch into the local one when I felt like testing it. I know how that goes: I would never remember to switch, and a week later I would have no data. So &lt;code&gt;fast&lt;/code&gt; now points at Gemma 4 and the Haiku mode moved to &lt;code&gt;lcl&lt;/code&gt;, there if I want to compare. Local models can be brilliant in a chat window and brittle inside an agent loop, as the thinking spiral already showed, and the only way I learn which one Gemma 4 is here is by living with it in the slot that fires twenty times a day. If it holds up, on tool calling, on diff quality, on the ten-line edits that fill my day, it stays. If it does not, the rollback is one line in &lt;code&gt;modes.json&lt;/code&gt;.&lt;/p&gt;&#10;&lt;h2 id="what-i-am-not-doing-yet"&gt;What I Am Not Doing Yet&lt;/h2&gt;&#10;&lt;p&gt;I am not putting the server behind launchd. At ~22 GB resident on a 32 GB machine, leaving it running alongside Chrome and an IDE is asking for swap. On-demand &lt;code&gt;lcl start&lt;/code&gt; / &lt;code&gt;lcl stop&lt;/code&gt; is honest about the cost in a way always-on is not.&lt;/p&gt;&#10;&lt;p&gt;I am not running a second model on a second port. Maybe Qwen 3.6 35B-A3B earns a slot for the cases where Gemma is too weak. Maybe one model is enough. I will know in a week.&lt;/p&gt;&#10;&lt;h2 id="what-the-planning-helped-with"&gt;What the Planning Helped With&lt;/h2&gt;&#10;&lt;p&gt;The model researched the options; I chose the setup. The conversation made me write down why I chose each part, so I can refer back to it later.&lt;/p&gt;&#10;&lt;p&gt;I still need to see how the local model handles everyday work. If it does not hold up, I can switch the slot back.&lt;/p&gt;&#10;</description></item><item><title>fabric</title><link>https://saurabh-kumar.com/til/1780920001/</link><pubDate>Mon, 08 Jun 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1780920001/</guid><description>&lt;p&gt;&lt;a href="https://github.com/danielmiessler/fabric" rel="noopener external"&gt;faberic&lt;/a&gt; is a crowd-sourced collection of prompts bundeled with a cli to use them via piped interfaces.&lt;/p&gt;&#10;</description></item><item><title>karpathy/autoresearch</title><link>https://saurabh-kumar.com/til/1780920000/</link><pubDate>Mon, 08 Jun 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1780920000/</guid><description>&lt;p&gt;[karpathy/autoresearch] is a concept/framework that allows one to leverage LLM to run an optimization loops based on a criteria. &lt;a href="https://github.com/davebcn87/pi-autoresearch" rel="noopener external"&gt;davebcn87/pi-autoresearch&lt;/a&gt; takes this further has built a generic optimization long run loop method on top of pi agent. &lt;a href="https://gemini.google.com/app/7dcee9714f7955ce" rel="noopener external"&gt;gemini&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>Dynamic workflows</title><link>https://saurabh-kumar.com/til/1780056000/</link><pubDate>Fri, 29 May 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1780056000/</guid><description>&lt;p&gt;&lt;a href="https://claude.com/blog/introducing-dynamic-workflows-in-claude-code" rel="noopener external"&gt;Dynamic workflows&lt;/a&gt; in Claude Code. Claude dynamically writes orchestration scripts that spawn tens to hundreds of parallel subagents. It plans, decomposes into subtasks, fans out agents, and checks results before returning a coordinated answer. Adversarial agents try to refute findings, iterating until convergence. Enable via &lt;code&gt;ultracode&lt;/code&gt; setting or ask Claude to &amp;ldquo;create a workflow&amp;rdquo;. Notable: Jarred Sumner used it to port Bun from Zig to Rust (~750K lines, 11 days). Available on Max, Team, Enterprise plans. Consumes significantly more tokens than typical sessions.&lt;/p&gt;&#10;</description></item><item><title>Claude Code: The Unreasonable Effectiveness of HTML" / X</title><link>https://saurabh-kumar.com/til/1778328001/</link><pubDate>Sat, 09 May 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1778328001/</guid><description>&lt;p&gt;Use HTML instead of markdown to effectively plan and review. &lt;a href="https://x.com/trq212/status/2052809885763747935" rel="noopener external"&gt;Claude Code: The Unreasonable Effectiveness of HTML&amp;quot; / X&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>colbymchenry/codegraph</title><link>https://saurabh-kumar.com/til/1778328000/</link><pubDate>Sat, 09 May 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1778328000/</guid><description>&lt;p&gt;&lt;a href="https://github.com/colbymchenry/codegraph" rel="noopener external"&gt;colbymchenry/codegraph&lt;/a&gt;: Pre-indexed code knowledge graph for Claude Code — fewer tokens, fewer tool calls, 100% local&lt;/p&gt;&#10;</description></item><item><title>Ankit Jain on automated AI code review</title><link>https://saurabh-kumar.com/til/1776859200/</link><pubDate>Wed, 22 Apr 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1776859200/</guid><description>&lt;p&gt;A good collection of practices on automated AI code reviews by &lt;a href="https://www.latent.space/p/reviews-dead" rel="noopener external"&gt;Ankit Jain&lt;/a&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;The Scalability Crisis: Manual post-PR review is no longer viable. AI agents have nearly doubled code output, causing human review time to spike by 91%, creating a bottleneck that traditional workflows cannot solve.&lt;/li&gt;&#10;&lt;li&gt;The Upstream Pivot: Human value must shift from reviewing implementation to defining intent. Instead of checking syntax, humans spend their energy writing rigorous specs and acceptance criteria before the code is written, which the machine then uses to self-verify.&lt;/li&gt;&#10;&lt;li&gt;The Swiss-Cheese Defense: Rather than one &amp;ldquo;perfect&amp;rdquo; human gate, the model uses a stack of imperfect automated layers. By layering signals like agent competition, deterministic guardrails, and adversarial &amp;ldquo;red-team&amp;rdquo; agents, the system catches errors where their individual failure modes don&amp;rsquo;t overlap.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>pageindex</title><link>https://saurabh-kumar.com/til/1774353600/</link><pubDate>Tue, 24 Mar 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1774353600/</guid><description>&lt;p&gt;&lt;a href="https://github.com/VectifyAI/PageIndex/" rel="noopener external"&gt;pageindex&lt;/a&gt; generates a semantic tree-like json index of a lengthy document to allow for reasoning based RAG without the need for vectordb.&lt;/p&gt;&#10;</description></item><item><title>For generating embedding locally</title><link>https://saurabh-kumar.com/til/1771156803/</link><pubDate>Sun, 15 Feb 2026 12:00:03 +0000</pubDate><guid>https://saurabh-kumar.com/til/1771156803/</guid><description>&lt;p&gt;For generating embedding locally,&#10;&lt;a href="https://ollama.com/library/nomic-embed-text" rel="noopener external"&gt;nomic-embed-text&lt;/a&gt; is a large&#10;context length text encoder that&#10;&lt;a href="https://www.nomic.ai/news/nomic-embed-text-v1" rel="noopener external"&gt;surpasses&lt;/a&gt; OpenAI&#10;text-embedding-ada-002 and text-embedding-3-small performance on short and&#10;long context tasks. It has a balance of speed, 8k context, and accuracy for&#10;English-centric apps. BGE-M3, Qwen3-Embedding and E5-Small are other&#10;alternatives.&lt;/p&gt;&#10;</description></item><item><title>yichuan-w/LEANN</title><link>https://saurabh-kumar.com/til/1771156801/</link><pubDate>Sun, 15 Feb 2026 12:00:01 +0000</pubDate><guid>https://saurabh-kumar.com/til/1771156801/</guid><description>&lt;p&gt;&lt;a href="https://github.com/yichuan-w/LEANN" rel="noopener external"&gt;yichuan-w/LEANN&lt;/a&gt; is a RAG focused framework focused on efficient storage with built-in chunking strategies embedding model management and MCP server. &lt;a href="https://gemini.google.com/app/c627885d0bf34a2d" rel="noopener external"&gt;gemini&lt;/a&gt;&lt;/p&gt;&#10;</description></item><item><title>K-dense</title><link>https://saurabh-kumar.com/til/1771156800/</link><pubDate>Sun, 15 Feb 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1771156800/</guid><description>&lt;p&gt;&lt;a href="https://www.k-dense.ai/" rel="noopener external"&gt;K-dense&lt;/a&gt; known for using skills to enable deep research has published &lt;a href="ttps://github.com/K-Dense-AI/claude-scientific-skills"&gt;140+ skills&lt;/a&gt; related to scientic research including literature review, data analysis, etc.&lt;/p&gt;&#10;</description></item><item><title>Opus 4.6 launch</title><link>https://saurabh-kumar.com/til/1770379200/</link><pubDate>Fri, 06 Feb 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1770379200/</guid><description>&lt;p&gt;Opus 4.6 launch.&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;context compaction (beta) and 1M context window, enables longer agentic tasks without loosing context.&lt;/li&gt;&#10;&lt;li&gt;they claim it has found 500 Zero-Day Flaws in open-source projects (yet to see the proofs though)&lt;/li&gt;&#10;&lt;li&gt;agent teams, multiple agent coordinates with a leader agent. &lt;a href="https://code.claude.com/docs/en/agent-teams" rel="noopener external"&gt;https://code.claude.com/docs/en/agent-teams&lt;/a&gt;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>How AI assistance impacts the formation of coding skills</title><link>https://saurabh-kumar.com/til/1769860800/</link><pubDate>Sat, 31 Jan 2026 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1769860800/</guid><description>&lt;p&gt;Notes on &amp;ldquo;How AI assistance impacts the formation of coding skills&amp;rdquo; &lt;a href="https://www.anthropic.com/research/AI-assistance-coding-skills" rel="noopener external"&gt;Article&lt;/a&gt; &lt;a href="https://news.ycombinator.com/item?id=46820924" rel="noopener external"&gt;HN&lt;/a&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;AI speeds up coding but reduces deep understanding and mastery&lt;/li&gt;&#10;&lt;li&gt;Juniors (1-3 years experience) showed speed improvements with AI, but 4+ year developers showed no difference&lt;/li&gt;&#10;&lt;li&gt;Modern software work is more about requirements, specs, documentation, and communication than raw coding skill&lt;/li&gt;&#10;&lt;li&gt;Small sample size (n&amp;lt;8) and study design limitations make results questionable&lt;/li&gt;&#10;&lt;li&gt;Takeaways:&#10;&lt;ol&gt;&#10;&lt;li&gt;&lt;strong&gt;Use AI for high-scoring interaction patterns&lt;/strong&gt;: Ask conceptual questions and request explanations rather than just code generation&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Adopt AI for documentation and specs&lt;/strong&gt;: Multiple developers report dramatic improvements in tickets, PRs, and documentation quality&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Be deliberate about learning&lt;/strong&gt;: If using AI, actively practice explaining concepts and avoid pure copy-paste workflows&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Use AI to reduce grunt work&lt;/strong&gt;: Let it handle boilerplate, test writing, and repetitive tasks while focusing on architecture and requirements&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;The research confirms what many suspected: AI coding assistants create a real trade-off between speed and skill development, but the practical significance is hotly contested.&lt;/strong&gt; The critical question isn&amp;rsquo;t whether AI reduces learning (it does), but whether deep coding skill remains as valuable as expressing requirements clearly—and whether we&amp;rsquo;re comfortable with a generation of developers who can&amp;rsquo;t function without AI assistance.&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>LangGraph</title><link>https://saurabh-kumar.com/til/1766577600/</link><pubDate>Wed, 24 Dec 2025 12:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/til/1766577600/</guid><description>&lt;p&gt;&amp;ldquo;&lt;a href="https://docs.langchain.com/oss/python/langgraph/overview" rel="noopener external"&gt;LangGraph&lt;/a&gt; is an orchestration framework for building stateful multi-agent applications using LLMs. It provides low-level primitives such as nodes and edges, along with built-in features that give developers granular control over agent workflows, memory management and state persistence. This means developers can start with a simple pre-built graph and scale to complex, evolving agent architectures. With support for streaming, advanced context management and resilience patterns like model fallbacks and tool error handling, LangGraph enables you to build robust, production-grade agentic applications. Its graph-based approach ensures predictable, customizable workflows and simplifies debugging and scaling.&amp;rdquo;&lt;/p&gt;&#10;</description></item><item><title>Thoughtworks - Technology Radar vol 33</title><link>https://saurabh-kumar.com/til/1766404802/</link><pubDate>Mon, 22 Dec 2025 12:00:02 +0000</pubDate><guid>https://saurabh-kumar.com/til/1766404802/</guid><description>&lt;p&gt;Notes from &lt;a href="https://www.thoughtworks.com/content/dam/thoughtworks/documents/radar/2025/11/tr_technology_radar_vol_33_en.pdf" rel="noopener external"&gt;Thoughtworks - Technology Radar vol 33&lt;/a&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;text-to-sql solutions aren&amp;rsquo;t working as expected&lt;/li&gt;&#10;&lt;li&gt;pnpm, langGraph, and pydantic recommended for adoption&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item><item><title>Notes on Understanding Hebbian Learning</title><link>https://saurabh-kumar.com/articles/2025/11/notes-on-understanding-hebbian-learning/</link><pubDate>Wed, 05 Nov 2025 00:00:00 +0000</pubDate><guid>https://saurabh-kumar.com/articles/2025/11/notes-on-understanding-hebbian-learning/</guid><description>&lt;p&gt;Hebbian learning describes how correlated neural activity can strengthen connections. It is named after psychologist Donald Hebb, who proposed it in 1949.&lt;/p&gt;&#10;&lt;p&gt;The entire concept boils down to one beautiful phrase:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;&amp;ldquo;Neurons that fire together, wire together.&amp;rdquo;&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;That is a useful shorthand, not a complete account of learning. Timing and the synapses involved matter.&lt;/p&gt;&#10;&lt;h2 id="breaking-it-down"&gt;Breaking It Down&lt;/h2&gt;&#10;&lt;p&gt;Repeated, correlated activity can strengthen the connection between two neurons. The effect depends on timing and the synapse, rather than simultaneous activity always producing the same change.&lt;/p&gt;&#10;&lt;p&gt;Think of it like this:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;First time: &amp;ldquo;Oh, these two things happened together&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Fifth time: &amp;ldquo;Okay, these definitely seem related&amp;rdquo;&lt;/li&gt;&#10;&lt;li&gt;Hundredth time: &amp;ldquo;These are basically the same signal now&amp;rdquo;&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="real-examples-that-make-it-click"&gt;Real Examples That Make It Click&lt;/h2&gt;&#10;&lt;h3 id="learning-to-type"&gt;Learning to Type&lt;/h3&gt;&#10;&lt;p&gt;Remember learning to type? At first, finding the &amp;lsquo;Q&amp;rsquo; key required conscious thought. Your brain neurons for &amp;ldquo;want to type Q&amp;rdquo; and &amp;ldquo;move left pinky to top left&amp;rdquo; fired together&amp;hellip; over and over. Now? It&amp;rsquo;s automatic. Those neurons are strongly wired.&lt;/p&gt;&#10;&lt;h3 id="pavlovs-dogs-the-classic"&gt;Pavlov&amp;rsquo;s Dogs (The Classic)&lt;/h3&gt;&#10;&lt;p&gt;Bell rings → food appears → dogs salivate&lt;/p&gt;&#10;&lt;p&gt;Do this enough times, and the neurons for &amp;ldquo;bell sound&amp;rdquo; and &amp;ldquo;food is coming&amp;rdquo; become so tightly linked that just the bell makes dogs salivate. No food needed.&lt;/p&gt;&#10;&lt;h3 id="your-morning-coffee-ritual"&gt;Your Morning Coffee Ritual&lt;/h3&gt;&#10;&lt;p&gt;Why does the smell of coffee make you feel more awake even before you drink it? Neurons fired together thousands of times:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Smell of coffee + feeling of alertness&lt;/li&gt;&#10;&lt;li&gt;Sound of coffee maker + anticipation of energy&lt;/li&gt;&#10;&lt;li&gt;Taste of first sip + actual caffeine effect&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;Now they&amp;rsquo;re all wired together into one mega-circuit.&lt;/p&gt;&#10;&lt;h2 id="the-actual-mechanism-simplified"&gt;The Actual Mechanism (Simplified)&lt;/h2&gt;&#10;&lt;p&gt;What&amp;rsquo;s physically happening:&lt;/p&gt;&#10;&lt;ol&gt;&#10;&lt;li&gt;Neuron A fires&lt;/li&gt;&#10;&lt;li&gt;Neuron B fires (around the same time)&lt;/li&gt;&#10;&lt;li&gt;The synapse (connection) between them releases more receptors&lt;/li&gt;&#10;&lt;li&gt;The connection physically strengthens&lt;/li&gt;&#10;&lt;li&gt;Next time A fires, it&amp;rsquo;s easier to activate B&lt;/li&gt;&#10;&lt;/ol&gt;&#10;&lt;p&gt;It&amp;rsquo;s literally changing the structure of your brain. Learning is physical.&lt;/p&gt;&#10;&lt;h2 id="why-this-matters"&gt;Why This Matters&lt;/h2&gt;&#10;&lt;h3 id="for-understanding-yourself"&gt;For Understanding Yourself&lt;/h3&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;&lt;strong&gt;Habits:&lt;/strong&gt; They form because neurons wire together through repetition&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Memory:&lt;/strong&gt; Retrieving memories strengthens those neural pathways&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Skills:&lt;/strong&gt; Practice literally builds stronger brain connections&lt;/li&gt;&#10;&lt;li&gt;&lt;strong&gt;Trauma:&lt;/strong&gt; Unfortunately, negative associations work the same way&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h3 id="for-ai-and-machine-learning"&gt;For AI and Machine Learning&lt;/h3&gt;&#10;&lt;p&gt;Hebbian learning inspired artificial neural networks. Modern AI doesn&amp;rsquo;t use pure Hebbian learning anymore (it&amp;rsquo;s evolved into more sophisticated algorithms), but it started here.&lt;/p&gt;&#10;&lt;p&gt;The basic idea - connections that prove useful should get stronger - is still foundational.&lt;/p&gt;&#10;&lt;h2 id="the-flip-side-use-it-or-lose-it"&gt;The Flip Side: &amp;ldquo;Use It or Lose It&amp;rdquo;&lt;/h2&gt;&#10;&lt;p&gt;Here&amp;rsquo;s the thing nobody tells you: it works in reverse too.&lt;/p&gt;&#10;&lt;p&gt;Neurons that STOP firing together will gradually UNWIRE.&lt;/p&gt;&#10;&lt;p&gt;This is why:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;You forget languages you don&amp;rsquo;t practice&lt;/li&gt;&#10;&lt;li&gt;Old skills get rusty&lt;/li&gt;&#10;&lt;li&gt;Memories fade&lt;/li&gt;&#10;&lt;li&gt;Bad habits can actually be broken&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="the-math-optional"&gt;The Math (Optional)&lt;/h2&gt;&#10;&lt;p&gt;For the nerds: The basic Hebbian rule can be expressed as:&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Δw = η × x × y&lt;/strong&gt;&lt;/p&gt;&#10;&lt;p&gt;Where:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Δw = change in connection strength&lt;/li&gt;&#10;&lt;li&gt;η = learning rate&lt;/li&gt;&#10;&lt;li&gt;x = activity of first neuron&lt;/li&gt;&#10;&lt;li&gt;y = activity of second neuron&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;If both are active (positive values), the connection strengthens. Simple, elegant, powerful.&lt;/p&gt;&#10;&lt;h2 id="practical-takeaways"&gt;Practical Takeaways&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;For Learning Anything:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Repetition isn&amp;rsquo;t just helpful - it&amp;rsquo;s literally how learning works&lt;/li&gt;&#10;&lt;li&gt;Practice needs to be consistent (neurons need to fire together repeatedly)&lt;/li&gt;&#10;&lt;li&gt;Spacing matters (distributed practice &amp;gt; cramming)&lt;/li&gt;&#10;&lt;li&gt;Context matters (learning in similar contexts strengthens related neural patterns)&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;For Breaking Bad Habits:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;You need to stop the neurons from firing together&lt;/li&gt;&#10;&lt;li&gt;Replace the pattern with a new one (create competing neural pathways)&lt;/li&gt;&#10;&lt;li&gt;It takes time - you&amp;rsquo;re literally rewiring your brain&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;p&gt;&lt;strong&gt;For Building Skills:&lt;/strong&gt;&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Deliberate practice works because it forces specific neurons to fire together&lt;/li&gt;&#10;&lt;li&gt;Feedback loops help strengthen the right connections&lt;/li&gt;&#10;&lt;li&gt;Muscle memory is real - it&amp;rsquo;s Hebbian learning in motor cortex&lt;/li&gt;&#10;&lt;/ul&gt;&#10;&lt;h2 id="cool-extensions-of-this-idea"&gt;Cool Extensions of This Idea&lt;/h2&gt;&#10;&lt;p&gt;&lt;strong&gt;Spike-timing-dependent plasticity (STDP):&lt;/strong&gt; Order matters too. In this simplified account, neuron A firing before neuron B (within ~20ms) can strengthen the connection, while the reverse order can weaken it. The timing and effect depend on the synapse.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Competitive learning:&lt;/strong&gt; When neurons wire together, they can also suppress other connections. This is how your brain specializes - certain neuron clusters become experts at specific patterns.&lt;/p&gt;&#10;&lt;p&gt;&lt;strong&gt;Associative memory:&lt;/strong&gt; Hebbian associations offer a way to think about connections between faces, names, and songs. They are not a complete explanation of memory.&lt;/p&gt;&#10;&lt;hr&gt;&#10;&lt;p&gt;If you want to dive deeper:&lt;/p&gt;&#10;&lt;ul&gt;&#10;&lt;li&gt;Donald Hebb&amp;rsquo;s original work: &amp;ldquo;The Organization of Behavior&amp;rdquo; (1949)&lt;/li&gt;&#10;&lt;li&gt;Modern neuroscience textbooks on synaptic plasticity&lt;/li&gt;&#10;&lt;li&gt;Research on long-term potentiation (LTP) - the molecular mechanism behind Hebbian learning&lt;/li&gt;&#10;&lt;li&gt;AI/ML papers on Hebbian learning algorithms&lt;/li&gt;&#10;&lt;/ul&gt;&#10;</description></item></channel></rss>