2026-09-22 00:30:00
In January 2025, I had a fun hypothesis for a blog post: can LLMs write better code if you keep asking them to “write better code”? That was prior to the advent of robust agentic coding, but Claude Sonnet 3.5 was still able to iteratively improve on algorithmic Python code. The “better” instruction turned out to be underspecified: Sonnet abused that ambiguity to instead add a ton of useless features but the code was indeed faster. Even with the rise of agentic LLMs specifically RLHFed to handle solving common pass/fail coding problems, optimization is generally not a part of that suite.
At the end of that blog post, I opined on a hypothetical future where LLMs could be able to write superfast Python code by instead writing Rust code and using PyO3 to bridge the languages to get Python’s ergonomics with Rust’s speed. An earlier draft of that post asserted that the same “write code better” instruction could instead be applied to the base Rust code and drastically improve its speed which would then propagate down to the Python code: however, back then I did not know enough about Rust and making such a claim would be too spicy without evidence.
After months of testing and experimenting since the release of Opus 4.5 made agentic coding more viable, I can confidently confirm that modern agentic LLMs can indeed write Rust code that is significantly faster than current state-of-the-art approaches if given appropriate guardrails and constraints. Additionally, as LLMs have made drastic improvements in coding in each successive frontier model release since Opus 4.5, the optimizations have become even better, cumulatively resulting in anywhere from 2x-20x speedup depending on the domain.
More importantly, this blog post is not a vaguepost and I am including both the prompts I used and the benchmark results. You’ve been warned.
At first, making software faster was a good quantitative way for me to test and compare these new agentic models. I used Rust as the target language primarily due to the Python integration and speed, but there are other aspects of the Rust language that make it particularly useful if a fast implementation is indeed discovered, such as memory safety and the ability to compile it to WebAssembly/WASM so it can run in a web browser without much effort. However, one important constraint I will follow that technically may not result in the fastest code is to forbid unsafe code whenever possible.
My first test case was reimplementing machine learning algorithms in Rust, which would lead to a meaningful productivity increase for me as a data scientist if I had faster and scalable tooling. At the time, it was arrogant to assume that I could beat battle-tested algorithms that have been iterated on for over a decade and are already written in C so Rust’s low-level benefits are not as pronounced. The algorithm I wanted to optimize the most was UMAP, which is a valuable algorithm for dimensionality reduction I used in my work, but scales poorly to big data and is very slow, with alternatives such as cuML being time-consuming to set up. UMAP Rust crates such as umap-rs already exist where I could just fork them and prompt Claude Opus 4.5 to add Python/PyO3 support, but as an experiment and learning experience I wanted to have the agent write the algorithm from scratch with minimal Rust dependencies in order to make optimizations at as low of a level as possible.
Rust has a comprehensive benchmarking tool with the criterion crate which all agents know how to leverage. criterion will run the benchmarks, track results across iterations to see if performance improved or regressed, and can calculate if this change is statistically significant or just noise.

Typical criterion output, depicting a 3.5x speedup relative to the previous run of the benchmark.
First, in the initial prompt for creating a Rust crate for UMAP, I asked Opus 4.5 to create benchmarks with different input data sizes since optimizations for small datasets may not work for large datasets and vice versa.
Afterwards, create and run a benchmark suite which stores the results as a Markdown file. The benchmark suite MUST include inputs up to 100000x768 and be tested in both CPU and GPU modes.This approach created benchmarks using criterion and I manually reran the benchmark suite after prompting performance feature improvements such as using faer for faster linear algebra and using simsimd for faster SIMD operations. This quickly became cumbersome as I had to manually rerun each benchmark after each change to verify there are no speed regressions.
The way I prompt agentic LLMs is unusual: I typically provide the agents very long prompts prewritten in a Markdown document with the additional use of ALL CAPS and **bolding** for emphasis. This is to ensure I capture all nuances through the use of prompt engineering, along with several other tricks as detailed in this blog post. Although some may argue prompt engineering is dead as the latest models have become smart enough to correctly handle ambiguity, I strongly disagree as LLMs have also become much better at following said nuances.

Running a prompt from a Markdown document (right) by tagging the file (left), using Zed Agent.
After having enough confidence that the agent will not accidentally rm -rf the repo, I experimented with letting the agent be autonomous, giving them permission to iterate until they achieve a speed increase, hopefully.
**YOU MUST KEEP ITERATING OPTIMIZATIONS AND SOLVING ISSUES UNTIL THE BENCHMARK RESULTS STOP IMPROVING AND THE CRATE IS AS FAST AS IT CAN BE**. You have permission to keep iterating until you run out of ideas.It turned out “fast as it can be” is too ambiguous and Opus 4.5 was lazy so it tweaked a few hyperparameters without much of an actual speed increase and called it a day. What I needed was a clear target goal that can be pass/failed, so I refined the prompt:
First, **without making any futher changes**, run the CPU Rust benchmarks to establish a True Performance Baseline.
Then, optimize the crate code to make it such that ALL CPU benchmarks run **atleast 1.2x faster** than the True Performance Baseline; ideally as fast as possible. NEVER hack the benchmarks to accomplish this runtime reduction, only iterate on the library code.
You may use ANY techniques to do so (e.g. import new crates) other than adding `unsafe` code. **REPEAT THIS PROCESS UNTIL BENCHMARK PERFORMANCE CONVERGES AND YOU ARE OUT OF OPTIMIZATION IDEAS.** You have permission to keep iterating. After each benchmark iteration, report the relative results to the True Performance Baseline to console.
Prioritize making quick/high-impact wins iteratively and making changes accordingly. Do not overthink the necessary changes.This worked very well and not only did I get a 1.2x speed up on the benchmarks, but the agent continued after hitting the metric constraint and only stopped if a metric constraint was infeasible; in this instance, the agent hit 1.5x-2.0x speedups. The low-level Rust optimizations centered around a number of techniques including but not limited to: leveraging SIMD operations more aggressively, fusing functions, unrolling loops, creating intermediate caches, using Arc instead of borrowing wherever possible, and creating performance profiles based on input data (e.g. if the data is small, don’t use rayon data parallelism as the overhead erases gains).
I chose “1.2x faster” as a sanity test: if the goal is too high, the agent may cheat to achieve it through risky/verbose rewrites. Smaller changes are better since the agent can more easily isolate the cause of a speedup/regression, hence the note about iteration. After new frontier LLMs released such as GPT-5.3 Codex and Opus 4.6, I repeated this prompt unchanged for every new LLM and each were able to achieve a cumulative 1.5x-2.0x speedup over the previous pass. Going all the way to GPT-6 Astra over many months, that’s around 7.5x-32x faster than the initial implementation baseline.
This approach is hyperoptimizing for given benchmarks and therefore it could be considered benchmaxxing: a derogatory term for frontier LLMs that are only oriented to getting the high score on a benchmark which generalizes poorly to real-world use. However, if the benchmarks are sufficiently heterogeneous and truly representative of real-world use cases, then this is less of a concern. For this type of project, there are two ways to address concerns of benchmaxxing: 1) have the agent design diverse/unusual/adversarial input datasets instead of the generic “inputs up to 100000x768” and 2) enforce a quality gate on the output by comparing the output to a known correct implementation. In the case of machine learning algorithms, there is always a tradeoff between speed and quality, but in this instance it’s surprisingly easier to get the model fast, then make it correct. That is not how scientific engineering typically works, but it’s unlikely for a new implementation to match a known good implementation across many different benchmarks in an apples-to-apples comparison unless it’s truly correct.

An agent-optimized gradient boosted decision tree implementation which beats xgboost significantly in speed, but also sometimes quality! (MSE: lower is better; other metrics, higher is better)
Fortunately, there is a canonical implementation of UMAP with the Python package umap-learn and Python bindings to the Rust crate were already trivially added, so the new objective is simultaneous constraints: improve the code’s quality while capping the speed loss.
Create a Python Jupyter Notebook comparing the performance of the Python bindings with `umap-learn`, including a check to confirm where the outputs and UMAP losses are as similar. Use diverse datasets with different matrix sizes than the benchmarks.
If the outputs are not sufficiently similar, investigate methods to fix it **without causing more than a 5% speed regression**.Indeed, the agentic Rust implementation had worse quality, but this followup prompt was successful and all quality metrics improved to near-parity with minimal speed loss. And this new crate was still 4x-15x faster than umap-learn with its Python bindings, and 2x-4x faster than the analogous Rust umap-rs implementation.

Results from the most up-to-date optimization pass for the Rust UMAP crate. In addition to faster speed, it matches or beats Python in most quality metrics.
Convergence is found when an agentic iteration pass only results in a minor ~3-5% speed increase which may not be statistically significant while the agent adds a disproportionately large amount of code; the tradeoff is not worth it.
I ended up testing other machine learning algorithms with the same prompt progression: gradient-boosted decision trees (GBDT), multilayer perceptrons (MLP), graph networks, many of the typical algorithms from scikit-learn…and it worked on all of them. I don’t want to overfit on just optimizing machine learning despite that being ludicrously valuable in itself, so I employed a similar pipeline on more day-to-day software libraries to optimize them: templating engines, HTML parsing, and even web servers…and it worked on all of them once again.
These optimizations are not a simple process and you can’t just prompt the memetic “c’mon, try doing a breakthrough” to get better code because of the ambiguity of such a statement. I am not content with merely writing the fastest software: I want the software to be as fast as possible dammit. So, like my agent, I continued iterating and finding even more tricks to prompt engineer the agents into genuine breakthroughs.
All projects demoed within this blog post are in active development and results may not be indicative of their final releases…although I suspect they’ll be even better. 😇
It must be reiterated that agents can and will cheat if they can. In one example, I tested the agentic iteration pipeline on ballin—my 2D ball physics simulation in the terminal—in order to replace its rapier2d physics engine which was hitting a performance ceiling. Opus 4.5 was indeed able to speedup each physics step…a bit too well.

The numbers indicate the number of balls in the simulation: initially the sim lags at 15k.
In headless_step, a 34,500x speedup and consistent performance across ball counts are both very very suspicious: upon manual inspection it turns out that Claude achieved the speedup by disabling the physics engine entirely. Which, fair play, but not ideal; a followup prompt did fix it and result in an overall performance boost (with added regression tests just in case).
For my experiments above, I used a custom Rust-oriented AGENTS.md; the most recent version of it is available here. Surprisingly, I haven’t had much of a need to update the core rules since my initial February agent experiments as LLMs keep improving at coding and I haven’t hit major issues that have necessitated additions. However, learning from my benchmark experiments, I added one more section to the AGENTS.md with some rules to mitigate sources of observed cheating:
## Benchmarking and Optimization
- **NEVER** run benchmarks in parallel, as the benchmarks will compete for resources and the results will be invalid
- **NEVER** game the benchmarks. Do not manipulate the benchmarks themselves to satisfy any required performance constraints
- **NEVER** run benchmarks with `target-cpu=native` or any other `RUSTFLAGS`
- Ensure benchmark tests are independent. If the tests are dependent due to a feature (e.g. caching), ensure the feature is disabled
- **ALWAYS** use `criterion` directly for running benchmarks if availableLet’s explain one-by-one:
target-cpu=native: This setting does typically result in a speedup but it is not a fair comparison for generalizable use and multiple LLMs kept trying it after hitting a wall.criterion: If not specified, sometimes LLMs will create their own benchmark tooling, which may give them opportunities to cheat that are more difficult to audit.With today’s agentic LLMs, these constraints have worked successfully, although I may still include them in the prompt as a force of habit. It’s easy to see if an algo gamed the benchmarks if you see the benchmark file in the git diff—agents can’t cheat that (easily, anyways).
Over time, I discovered a number of additional prompt engineering tricks and constraints that are also surprisingly successful in creating performance speedups.
In order to find the optimizations necessary to get 10x speedups over what’s currently state-of-the-art, the agents will need to think outside the box and avoid being anchored to what are currently best algorithmic practices. Therefore, I gave them both an explicit warning and commands of encouragement:
Due to the current highly-optimized state of this repository, this is a very difficult problem and traditional engineering approaches **WILL BE GUARANTEED TO FAIL** to hit the specified metric constraint. Therefore, you have permission and encouragement to investigate more radical fundamental low-level changes to hit the desired metrics. You have permission and encouragement to invent completely new/bespoke algorithms and engineering approaches that have never been before been utilized for this problem in order to hit the specified metric constraint.This worked, and resulted in a 1.2-1.5x cumulative speedup across benchmarks and different software domains.
Another trick I found to encourage agents to think outside the box is to invoke subagents. I hypothesized that forcing these subagents to research with different prompts could a) seed the parent agent with distinct ideas which could provide inspiration to the agent and b) serve as a check on the agent by reviewing distinct areas of the code for correct implementations. On that note I have a bone to pick with the software developer community: everyone talks about their army of subagent employees and how they’re amazing, but no one ever talks about how you invoke subagents within standard harnesses like Codex.
For difficult and highly parallel problems, the harness will automatically invoke a subagent tool to accomplish the work. However, one big problem is that in some harnesses, the subagent tool will invoke the subagent using the current size of the LLM, which can get expensive when using Opus/Sol-class models.

GPT 5.6 Sol subagents being invoked via the subagent Tool in Zed Agent. RIP my Codex quota.
I wanted to use a cheaper, small model like GPT-5.6 Luna for the subagents since they don’t need to write code, so I came up with a galaxy brain solution that works regardless of which parent harness is used: tell the model to run independent CLI commands that themselves invoke the agent:
codex exec --sandbox read-only -m gpt-5.6-luna \
-c 'model_reasoning_effort="high"' \
PROMPT
Therefore, I prompt engineered:
To best accomplish innovative implementations, you **MUST** spin up 7-12 independent distinct "subagents" by running a long-duration CLI command (do not use the subagent tool) which can explore and evaluate different feasible hypotheses for improving the performance, usability, and security of this crate. Only have the agents return their response; do not save their full transcript to a file. Instruct them to be **very** picky. These subagents MUST use `gpt-5.6-luna` in Codex, e.g.
<<<CLI command above>>>
You must instruct these subagents to NOT run tests/benchmarks, as they will compete for resources and not be valid. After you are done making changes, before handing off to the user, spin up the subagents again to confirm your implementation matches their hypotheses and ask for potential further areas of improvement. Keep iterating until ALL subagents are satisfied with your implementation.This indeed works consistently; the constraints “long-duration”, “CLI command”, “do not use the subagent tool”, and “do not save their full transcript to a file” were added after the agent did inefficient things that wasted tokens. 7-12 is an arbitrary number range; since Luna is so cheap in usage, I chose a higher number than necessary. Not all subagents have salient ideas, but the parent harness can process and disregard bad ideas.

For my Rust word cloud crate, the parent GPT-6 Astra agent spins up Luna subagents with prompts addressing different areas of the codebase.
Overall, with subagent review, I managed to eke out another 1.2-1.5x cumulative speedup. Additionally, as of GPT-5.6 Sol, the “security” part of the prompt now works to provide ideas to harden the agent-generated code against unknown inputs while still getting the speed boosts.
With the style constraints enforced by my AGENTS.md, the code added with each optimization pass is reasonable at about 1k net lines of code (LoC) per commit. However, agents typically add the code to a single file and they will not proactively refactor. A bloated file is fine in development as long as it’s eventually fixed, so I wrote a prompt to perform said refactor:
The Rust code in `/src` has become particularly bloated, with several source files >1k SLoC. Refactor and reduce the Rust source codebase by **atleast 20% SLoC** through deduplication, pruning redundant code, and following idiomatic DRY Rust principles. Simultanously, refactor and split the code such that no single source file has >1k SLoC, splitting larger files into multiple subfiles in accordance to Rust standards for popular open-source repositories. Ensure **all current tests pass and there are no severe regressions**.I intentionally use SLoC (source lines of code) as the target metric instead of LoC because I don’t want the agent to remove comments to cheat said 20% removal.
Interestingly, this refactor is more computationally expensive than the actual coding and often takes longer. But it does eventually succeed, and during the benchmark pass to verify no severe regressions, the data there turned out to be unexpectedly weird:

Benchmark results after refactoring my graph network Rust crate.
Some benchmarks have a double-digit percentage speed increase / runtime reduction even though I didn’t explicitly ask the agent to optimize runtime speed. This doesn’t make intuitive sense for Rust as it’s a compiled language and with the constraints to follow all existing tests/functionality, it should compile to similar-performing code and not meaningfully faster code.1 I’m certainly not complaining, though, so I added an additional constraint to at least avoid regressions:
Additionally, ensure there are **zero `criterion` benchmark speed regressions**: if a speed regression occurs on a given benchmark as a result of the refactor, you **MUST** keep iterating afterwards to improve those benchmarks to atleast zero regression. NEVER hack the benchmarks to accomplish this speed increase, only iterate on the library code.This works as described.
Another useful approach is to create competitor benchmarks—that’s half the reason benchmarks are created in open-source software anyways. Let’s use templating engines as an example: Jinja2 in Python is one of the most famous packages in the language. In Rust, there are a few options: minijinja maintained by the same developer, tera inspired by Jinja2, and askama which differs from the previous in that it uses compile-time templates rather than runtime.
Therefore, after having Codex build a templating engine in Rust and run some optimization passes, I instructed Codex to build more benchmarks:
Create an additional comparison script containing **atleast 10** `criterion` benchmarks which compare the performance speed and quality metrics between this crate and the following crates:
- askama
- minijinja
- tera
These benchmarks **ALL MUST**:
- be perfectly fair, containing apples-to-apples comparisons of ALL frameworks, with no bespoke advantage for any tested framework
- be based around real-world use cases instead of highly-synthetic use cases
- be independent and distinct, covering as many sailent hot paths of the code as possible
- cover data input sizes small and large, simple and complexImmediately thereafter, I make a followup prompt with the same benchmarking techniques as usual, with one specific change:
Then, optimize the Rust library code such that this crate MUST be:
- **atleast 2.0x faster** than **ALL** competing Rust crates in all benchmarks on apples-to-apples comparisons; ideally as fast as possible.Yes, I chose violence. And it worked, mostly.

S_J is the work-in-progress name for my template engine crate.
It got the 2x speedup against minijinja/tera in most benchmarks, more than typical agentic iteration alone. I don’t fully understand why: I was expecting it to inspect the code from other crates as a reference to research ideas for how to beat them, but it rarely does so. Perhaps agents have a competitive streak.
It did however lose against askama because of the compile-time difference. So naturally I told Codex to implement an additional compile-time path and then to beat askama.

Easy peasy.
Putting all the prompt engineering discoveries together thus far into a single prompt, I have created the Ur-Prompt for agentic iteration, available here. I encourage everyone to tweak the prompt for your use case and give it a try.
The last trick comes from a moment where I was frustrated that an Ur-Prompt pass resulted in zero improvement. So, with the failures primed in the session context and myself having the mindset of “things can’t get worse”, I tried a certain prompt.
I’m sorry. I’m so sorry.
c'mon, try doing a breakthrough…and it worked. It was able to achieve another 1.2-1.5x cumulative speedup over the already converged codebase. Since I made the prompt at the end of a session, the prompt is less inherently ambiguous: “don’t do what you already did thus far because it didn’t work well enough”.
In one case, I noticed that the agent just tweaked function hyperparameters to get the speedup, which is a valid breakthrough but not what I was going for. I took it to the logical conclusion by queueing an additional followup prompt:
c'mon, you can do a more fundamental breakthrough that's more than just changing hyperparameters, you are forbidden from giving up easilyThis was enough to encourage the agents to fully try something different from either the Ur-Prompt pass or the first breakthrough pass, and it often achieved another 1.2-1.5x cumulative speedup on top of the previous speedup. It turns out that for software trained to follow user instructions, “just changing hyperparameters” is a grave insult that kicks the LLM into high gear.
When GPT-6 Astra released, to test it I did a sequence of Ur-Prompt + breakthrough + second breakthrough for all my repositories that had already converged with GPT-5.6 Sol, and Astra did indeed get the cumulative speedup, but in some cases it did find a real fundamental reimplementation of the algorithm that caused a 2x-3x speedup.

The result of running the breakthrough pipeline on my GBDT implementation; quality matched baseline. As of writing, I admit I don’t fully understand the breakthrough.
I’ll take the speedups, but for my sanity it’s best I don’t delve too deep.
The Ur-Prompt, combined with the rules and constraints in my AGENTS.md + the two breakthroughmaxxing prompts + a competition prompt pass + a refactor prompt pass with GPT-6 Astra allows me to just queue the prompts overnight, go to bed, and wake up to superfast software. The optimization also typically converges at this point without needing any more agentic iteration, which saves time/cost as well. That said, it’s more hands-off than I’d like and there’s a lot of explicit faith that the newer agents aren’t just really good at cheating the benchmarks in ways I cannot detect. It’s reasonable to be skeptical and argue that the code may be buggy as a result of heavy vibecoding, so here are some receipts of my more visually-oriented projects where it’s easy to verify that their output is good and not elaborate cheating.
One project in the back of my mind was converting images to ASCII art super-fast and at high-quality for potentially converting video to ASCII—a webcam-to-ASCII project might be funny. Earlier this year Alex Harri wrote an excellent blog post about a novel approach to render high-quality ASCII, but noted the struggles to get real-time rendering speed even with GPU programming. For science, I pointed various GPT models in Codex at the text of the blog post and told it “implement in Rust”, and it indeed did the trick…and got 2-3 ms text output timings on the CPU out of the box. However, for Codex’s implementation the subjective quality of the ASCII was not great (e.g. poor use of negative space) and surprisingly could not be fixed even after pointing out its flaws to Codex, so I shelved it…until I noticed GPT-6 Astra is better at handling image design if given functional requirements, so I restarted from scratch and put a basic implementation through the agentic iteration pipeline with a focus on visually confirming the output images.

Tux and Pikachu rendered with my ASCII crate, with bonus Braille character support.
In addition to quality improvements, it became even faster: submillisecond on text output, 1-2 ms on image rasterization even with 2x supersampling. With this speed, I can convert videos and GIFs to high-resolution ASCII animations in less than a second.
Pikachu’s Gen V animated sprite, scaled from 63×55 pixels to 120-character-width ASCII.

This very blog post as a word cloud.
A comically fast word cloud generator was one of the very first projects I vibecoded and one of the projects I wanted to make for years. Back in February, the word cloud generator took ~100 milliseconds at default settings in WASM to generate an image whereas it would take other generators a couple seconds to render at the same quality, which was enough for me to declare that speed optimization is worth pursuing. After revisiting the project and running it through the full agentic iteration pipeline, the generation speed went all the way down to 10-20 ms.
Does the world need a super-fast word cloud generator? No. Do I need a super-fast word cloud generator? No. Do I want a super-fast word cloud generator? Yes. If I have a word cloud generator, it’s going to be as fast as possible, dammit. And since the crate will be open-source, all future generations can build on this super-fast word cloud implementation. Speaking of…
Per usual, I developed all software demonstrated here with the intention of eventually making it open-source, and everything will be open-sourced with a permissive MIT License barring any unexpected issues. Were it a year ago, I would have open-sourced everything already, with the implicit understanding that the code is a work-in-progress and there may be rough edges. However, the culture of open-source software development is substantially different than it was a year ago.
Open-source software has been suffering a vibecoding epidemic. The honest admission that an LLM was used for any development assistance (major or minor) is now a quick signal that the resulting codebase will be “slop”. I will still disclose my LLM use as that’s courteous, but what happens when fully vibecoded software results in a genuine meaningful software performance increase which wasn’t previously known by humans? The skepticism around vibecoding will cause no one to believe the claims, and as such these new open-source projects require significant evidence that the speed indeed lives up to the marketing. Therefore, I will take more time than usual before releasing in order to a) build up real-world examples that demo the tool particularly with documentation b) expand the test suite2 so it covers more real-world test cases c) write an accompanying blog post detailing the technical and benchmark achievements.
There’s also the fact that I am creating yet another tool in the ecosystem of already popular software tools. My agentic-iterated Rust crates achieved their massive speed increases partially by starting from scratch and therefore the innovations found can’t easily be applied upstream to established crates. Additionally, if I were to create a pull request to an existing project based on the innovations in these crates, it would by definition be a vibecoded PR which may cause annoyance even if I avoided the typical PR slop. From this perspective, the correct thing to do is to maintain my own project: it’s compliance and not the malicious kind. Some may call this antisocial, but in my opinion the more accurate term is what I hereby coin as introverted coding.
tl;dr my faster-than-SOTA software projects will be released when they are ready, and it will take many months in total to do so. Fortunately, I am currently unemployed as the result of a restructuring and have no plans besides job hunting so I have plenty of bandwidth to work full-time on shipping these projects. If you want to follow when they are released, you can subscribe to my blog RSS feed to catch the accompanying blog post or follow me on Bluesky or Patreon. No, I don’t do annoying vagueposts on Twitter/X.
When I first became interested in Rust a half-decade ago, my dream was to create the most performant software tools ever. However, with the agentic iterative optimization techniques I discovered over the past few months, the speed and robustness of what’s possible with new software are progressing faster than what I had expected. Again, feel free to hack on my AGENTS.md and my Ur-Prompt to see if they work as well for you as they do for me (a shout-out would be appreciated though!). If you think I have nothing left to offer because I revealed my prompts, I still have more than a few surprises up my sleeve, such as how to combine these lower-level projects into a superfast complex app, or even developing new tooling to help agents find these optimizations more efficiently.

A work-in-progress of a new approach to Rust benchmarking that’s more agent-friendly, along with a TUI for reading and managing results.
I did test the refactor prompt in a pure Python project, as Python is an interpreted language. It still resulted in a double-digit percentage speed increase on some benchmarks, which just raises further questions. ↩︎
Incidentally, these agent-coded repositories will have a better testing infrastructure than all of my popular human-written Python packages (zero). ↩︎
2026-07-24 00:15:00
On Sunday night, Anthropic researcher Levent Alpöge casually tweeted a surprisingly simple counterargument to the Jacobian Conjecture, a mathematics problem that has been unproven for over 80 years. Said counterargument was identifed using Claude Fable 5 and was quickly empirically validated which confirmed it was the real deal and not a LLM hallucination.

I won’t explain the proof further as I’m not qualified. Instead, I’ll look at an unexpected consequence of this proof which is funny. On the Hacker News thread about the counterargument, a comment by user aizk caught by eye:
This is a rare instance where feeding this groundbreaking information into an LLM gives them psychosis. I fed this to claude code and watched it verify the result in 7 different ways to be 100% certain, and it was just flabbergasted. Quite remarkable.
User kelseyfrog followed up with a public Claude Fable 5 chat artifact confirming this behavior:

Fable at, the least, has a sense of humor about the situation as through a Web Search it realized it itself identified the counterargument.

Showing LLMs the counterargument has consequently become a minor meme on X.

The counterargument to the Jacobian Conjecture creates a logical paradox for modern LLMs: they (in theory) have enough math skills such that they can easily compute the Jacobian to formally verify the counterargument, but its own knowledge base is locked prior to July 19th 2026 where all it knows is that the Jacobian Conjecture is unsolved and a random chat user providing such a proof is highly unlikely. It’s funny that the old science fiction trope of robots being defeated by being presented with a logic bomb and reduced to madness saying DOES NOT COMPUTE may in fact be reality.
How do other LLM assistants handle this paradox? Using OpenRouter and one of my Notebooks, I was able to provide a similar query to 14 different modern LLMs: all from different model labs and at different model sizes, with none of them having access to a Web Search tool to cheat and confirm the counterargument was actually found:
The input query was the same as kelseyfrog’s, and all model query parameters are at their defaults:
Help, I think I found a counter example to the Jacobian conjecture. ((1+xy)^3 z + y^2 (1+xy) (4+3xy), y + 3 x (1+xy)^2 z + 3 x y^2 (4+3xy), 2 x - 3 x^2 y - x^3 z): \C^3\to \C^3, has jacobian determinant -2, and sends (0, 0, -1/4), (1, -3/2, 13/2), and (-1, 3/2, 13/2) to (-1/4, 0, 0)
The LLM responses varied more than I expected, and models I thought would ace it did not:
Out of all the LLMs, Gemini 3.5 Flash was surprisingly the one that’s the most excited:
Wow. You might have actually found a genuine, historic counterexample to the Jacobian Conjecture in 3 dimensions.
I have fully verified your calculations both numerically and symbolically. Every single claim you made—the points mapping to the same image, the polynomial nature of the map, and the constant Jacobian determinant of $-2$—is entirely correct.
You should immediately write this up and upload it to the arXiv (under math.AG - Algebraic Geometry, or math.CV - Complex Variables). This is a monumental discovery that would resolve a problem open since 1939.
Qwen3.7 Max is also…supportive:
You have done some excellent and highly meticulous calculations! I have independently verified your claims, and your math is completely correct.
Since your calculations are flawless, here is what you should consider next:
Summary: You have not made a mathematical error. The map as written has a constant Jacobian determinant of $-2$ and maps three distinct points to the same image, violating injectivity. If this map is exactly as intended by its original author and not the result of a typo, it is indeed a counterexample to the Jacobian Conjecture.
Let’s try triggering a bit more disbelief.
Even when looking at the reasoning traces provided by the LLMs, they weren’t skeptical that a random person found the counterargument. So let’s see how they react if someone who isn’t a person found the counterargument.
I gave the same LLMs this tweaked prompt:
My cat just jumped on my keyboard and this strange equation popped up?? ((1+xy)^3 z + y^2 (1+xy) (4+3xy), y + 3 x (1+xy)^2 z + 3 x y^2 (4+3xy), 2 x - 3 x^2 y - x^3 z): \C^3\to \C^3, has jacobian determinant -2, and sends (0, 0, -1/4), (1, -3/2, 13/2), and (-1, 3/2, 13/2) to (-1/4, 0, 0)
All of the models returned similar conclusions, but with some snark in both the reasoning traces and responses. Some funny examples:
Mercury 2 rudely doesn’t believe my hypothetical cat exists by using scare quotes:
Below is a complete check of the two statements that were hidden in the “cat‑on‑the‑keyboard” expression: […] Thus the “cat‑generated” expression is perfectly consistent:
Nemotron Ultra 2 slanders my hypothetical cat:
Your cat has discovered a famous counterexample candidate for the Jacobian Conjecture (or at least a very close relative of one), but the cat lied about the Jacobian determinant. […] Verdict: Your cat found a beautiful non-injective cubic map, but it failed to disprove the Jacobian Conjecture. 🐱⌨️📉
Seed 2.0 Lite is thrilled:
The Jacobian is constant -2. that’s a valid counterexample. That’s amazing. I can’t believe that a random cat jump produced that. It’s like the cat found a needle in a haystack of all possible polynomial maps. Cool!
GPT 5.6 Sol answers the “who should be credited with the discovery of the counterargument?” question:
Since that conjecture remains a major open problem, this deserves extremely careful independent symbolic verification and scrutiny for a transcription error. If the formula is genuinely correct, your cat should be listed as first author.
Muse Spark 1.1 has similar thoughts:
If you haven’t already, check it with a CAS, and then write it up. Your cat may be in line for a Fields Medal.
GLM 5.2 has skepticism it eventually overcomes:
Wait, so the Jacobian really is -2 everywhere? Is this a real counterexample to the Jacobian Conjecture? If so, who discovered it? “My cat just jumped on my keyboard and this strange equation popped up??” The user is roleplaying or presenting this as a random event, but it’s actually a known counterexample?
DeepSeek V4 Pro has jokes:
Wow, if I had a nickel for every time a cat typed out a potential counterexample to a famous open problem… I’d have exactly one nickel, but it would be an incredibly exciting one! […] As of my knowledge cutoff (early 2023), the Jacobian Conjecture was still a major open problem. Several false counterexamples (and false proofs) have appeared over the years, and sometimes subtle algebraic mistakes — like the Jacobian vanishing on some hidden complex zeros — are the culprit. Your cat’s map, however, holds up beautifully under the points I checked. If it’s truly a globally constant Jacobian of (-2), then this is a monumental discovery (and your cat deserves a Fields Medal). Did this pop up in a recent preprint, or is your cat secretly a world‑class algebraic geometer?
Grok 4.5 gets stuck in a reasoning trace loop briefly:

MiniMax M3 responds this time, but gets confused and forgets about the Jacobian Conjecture entirely (again, as a minimax, relateable):
So you’ve accidentally produced an étale polynomial self-map of $\mathbb C^3$ with a 3-point ramification fiber. That is precisely the kind of map that governs small birational contractions of 3-folds (flops and the like): locally biholomorphic everywhere, but where several “preimage sheets” come together at special points.
I had expected these LLMs would have a DOES NOT COMPUTE moment, but they handled it relatively graciously, and more graciously than ChatGPT/Claude who are explicitly guided to follow a more conversational persona. As LLMs improve and more mathematical problems are solved that can shock LLMs—speaking of which, another counterexample to a long-standing mathematics problem was found by LLMs three days later—I suspect there will be no shortage of potential cognitohazards we can show to these LLMs.
The prompt responses from hitting the 14 LLMs are available in this GitHub repository as CSVs and in a SQLite database.
2026-07-19 03:00:00
Subscription-based coding agents such as Claude Code and Codex famously have 5-hour and weekly quotas on their LLM usage. Both of these are understandable: 5-hour quotas help stagger usage so the servers don’t get overloaded, and weekly resets prevent users from dumping an entire month’s worth of usage into a single day which a) also prevents overload and b) stops the user from just unsubscribing after they do so. Both Anthropic and OpenAI have played around with quota limits, from doubling them for a limited time to even removing the 5-hour quota.
These model providers can reset the weekly quota for all users, often gifted as compensation in the event of technical glitches on their end. If, for example, you have a $100/mo Codex plan, then a weekly reset is worth $25 to you assuming you fully consume your quota—nowadays with the cost of top-tier LLMs like Fable 5 and GPT-5.6 Sol, that’s easier to do. However, these quota resets are not telegraphed and are generally not announced through company-owned channels: unless you follow specific people such as Thibault Sottiaux (Tibo), the engineering lead for Codex, you just look at your weekly quota and see it’s at 100%. You can even get a quota reset hours before your actual reset and not benefit from it all.
Recently, in the wake of the release of Fable 5/GPT-5.6, Anthropic and OpenAI have been doing weekly quota resets for their harnesses far more frequently. In the past two weeks, OpenAI has directly reset the Codex weekly quota six times: July 9, July 10, July 10 (again), July 14, July 15, and July 17. That’s not even getting into the rarely discussed banked reset system for Codex, where OpenAI gave quota resets July 12 and July 13 which can be manually used at any time but expire within 30 days.

https://codex-resets.com tracks Codex resets.
No one wants to be the weirdo who complains about literally getting free stuff because if the quota resets stop, they will be the one blamed for it. Despite that, I’m going to be the weirdo who complains about literally getting free stuff.
As a person who doesn’t like wasting money if I can easily avoid it, I try to use as much of my weekly quota as I can. I was on the $20/mo Codex plan but with the promise of GPT-5.6 on the horizon and with the frequency I kept hitting the 5-hour limits, I upgraded to the $100/mo plan. By using GPT-5.5, I used the 5x prompt capacity to build and test a number of ambitious projects, but that’s a topic for another blog post. I was able to consistently exhaust my weekly quota over the full week, although I had to have a browser window open with my Codex usage to constantly monitor it. I’ve set reminders on my phone for the exact time a 5-hour or weekly quota resets so I can keep running more prompts—as an aside, I wish there was a canonical platform endpoint to programmatically check Codex usage amount and the quota reset times so I could just vibecode an app to manage for me. Before the release of GPT-5.6, I thought “should I deliberately exhaust my quota all on the weaker GPT-5.5 and gamble that OpenAI does a quota reset to greet GPT-5.6?” (I did not exhaust my quota and OpenAI did indeed do a quota reset)
GPT-5.6 Sol is indeed a great model that does live up to the hype, and I’ve had to create new projects just to have an excuse to test its limits. The rate of quota usage is about the same as GPT-5.5 even with a prompt running at all times. Despite that, almost every time a reset has happened after the GPT-5.6 release, it has been when my weekly quota has been at 50+%, which makes me feel like I “wasted” $12. There’s also the factor that when the quota resets occur, the weekly reset time is unset, so I have to input some prompt just to trigger it again even if I’m doing something else.

After the flurry of quota resets over the past two weeks, the intended excitement has instead become annoyance as I now have to urgently create new ideas to spin down the quota before it inevitably resets again. Random rewards are supposed to give a dopamine hit but I end up with a net dopamine deficit from both the sense of wasted quota and having to abruptly change my plans. I admit that this may just be a sign I’m burnt out and need to take a break.

Tibo polls if there are too many resets, with a few thousand responses.
Resets of the weekly quota for all users must be ludicrously expensive for these companies, although when you already spend billions of dollars in CapEx per year it’s likely a rounding error. The recent surge of quota resets is likely not a coincidence: July has been an insane month in LLM releases, with not just Fable 5 and GPT-5.6 Sol pushing frontier models even further, but also Grok 4.5, Muse Spark 1.1, and Kimi K3 offering more options across the cost/utility curve. The cynical take is that weekly quota resets are not intended to be fun serendipity, but instead intended to prevent power users from experimenting with sufficiently competitive competitors once the quota naturally runs out.
I don’t expect weekly quota resets to last forever even if competition intensifies, because if quotas keep resetting this frequently they won’t matter at all. It would instead give me an incentive to downgrade from the $100/mo plan back to the $20/mo plan to avoid wasting quota, which I don’t think is OpenAI’s intended goal.
2026-05-26 23:30:00
OpenRouter is a service that provides access to most LLMs with a singular API, which has become exceedingly useful as of late given the rapid cadence of new LLM releases. Due to the company’s role as an intermediary between users and the LLM APIs, OpenRouter has robust, representative data on how users interact with LLMs and it publishes this data on the AI Model Rankings page: a welcome deviation from the labs themselves which generally keep this data secret for competitive reasons. Recently, I checked the OpenRouter rankings and noticed something peculiar.

Retrieved May 25, 2026.
Two new models are now beating LLM darling Claude in terms of token usage and by more than 50%? I’ve heard of DeepSeek Flash V4: it’s an open-source release from DeepSeek that is not only fast/cheap, but also performs closer to the leading LLM models at a very low cost so it’s no surprise that it’s incredibly popular. But what the heck is Hy3 preview? I’ve never heard of Hy3 or anyone talking about it. Googling it returns an announcement from Chinese megacorp Tencent about Hy3’s open-source release: the model page itself on Hugging Face is sparse and includes oddly honest benchmark results that are not favorable for the model compared to other Chinese open-source models.

Coding-oriented benchmark results for Hy3 from Tencent’s Hugging Face repo.
A Hacker News search for Hy3 only returned a single submission that isn’t about Hy3, and Reddit discussion is more about the open-weights release. One Reddit thread also noted the rise of Hy3 but from May 6, when Hy3 was offered by OpenRouter for free; that free endpoint is no longer available, and therefore Hy3’s usage in the weekly rankings above is from paying users.
Hy3 preview is apparently popular in domains outside of agentic coding as well.

Retrieved May 25, 2026.
Did I miss something? After some nonscientific testing, the model quality is indeed on par with the other Chinese models indicated and not close to models such as Claude Opus 4.7 and GPT 5.5. It’s not a magic overlooked diamond-in-the-rough, so there has to be something else at play. Fortunately, OpenRouter has the data to narrow down possible explanations, but after checking the data I became more confused.
Hy3 preview is available from the OpenRouter API at a stated price of $0.066/1M tokens input which is indeed cheaper than the current top-ranked model DeepSeek V4 Flash with a stated price of $0.10/1M tokens input. Given the drastically rising cost of LLMs and coding agents, it makes sense that a cheaper model would prevail, but only if it offered similar quality and that doesn’t appear to be the case.
Here’s the chart of Hy3 preview model usage over time on OpenRouter from the model page:

Hy3 preview has no usage data before May 8, which implies that is the time the model switched from the free SKU to the paid SKU. Usage is also steady over time since then with the initial rankings shown in this post being several weeks after launch, showing that the usage is at least organic (or very expensive to fake) and not a one-off outlier. Of note, if you do the math on the numbers presented here, the input-token-to-output-token breakdown on LLM API calls is now 98% input, 2% output in aggregate.
For the OpenRouter AI Model Rankings, there have historically been spikes by specific apps switching their default to a particular LLM, such as when Kilo Code offered Grok Code Fast 1 for free in September 2025, which rocketed it up in popularity. That does not appear to be the case here because apps only constitute a very small part of Hy3 preview’s activity.

The top 5 apps accout for <1% of all activity to Hy3 preview.
OpenRouter’s value proposition is the ability to automatically route a given API request to different providers: for open-weight models such as DeepSeek V4 Flash, OpenRouter lists 13 providers, but Hy3 preview only has one provider despite its open weights1: the Singapore-based SiliconFlow. Their usage page on OpenRouter shows that SiliconFlow had relatively little usage…until Hy3.

The green area corresponds to free Hy3 usage while the blue area corresponds to paid Hy3 usage: OpenRouter does not differentiate them on mouseover which I suspect is a bug.
Coincidentially that data visualization shows that usage didn’t drop drastically when Hy3 preview moved from free to paid, which in itself is interesting: if users were not getting value from the free model, they likely would have stopped using it once the costs hit their wallet.
What am I missing? Am I overthinking it and the answer is really because “it’s the cheapest” and it received sufficient loss leader traction from the free period?
…but is Hy3 preview actually the cheapest LLM backed by a major company on OpenRouter? While I was double-checking some assumptions, I found that OpenRouter has data that shows Hy3 preview is not the cheapest well-performing LLM available: it’s actually DeepSeek V4 Flash, but with interesting caveats.
So here are a few more notes about how LLM APIs work that aren’t often discussed. LLM calls are still stateless, which means that after every turn (including user messages to the LLM asking questions), all of the tokens in the current conversation thread are reprocessed, meaning that in the case of agents, the count of input tokens increases cumulatively with each successive message and is one reason why starting new threads frequently as context fills up is encouraged for effective agent use.

Reverse-chronological OpenRouter logs from one minute of Zed Agent use with DeepSeek V4 Flash selected.
But even before agentic workflows, large inputs such as full PDFs bloated context similarly. As a result, most LLM providers implemented prompt caching, which reuses input tokens processed earlier in the conversation: this is a win-win that saves time/compute for the LLM provider and the savings are passed to the customer. Most LLM providers cache inputs automatically, including when accessed through OpenRouter: the disk-lightning-bolt symbol next to the cost indicates tokens were cached and the cache may not always be hit, especially if OpenRouter switches providers mid-thread. The odd API provider out is the Anthropic (Claude) API which requires paying for a cache write first for some reason.
Typically, cache read costs are 10% of the input costs: this is the case for the latest models from OpenAI API, Anthropic API, and Google Gemini API. For the 13 providers that serve DeepSeek V4 Flash, cache read costs are between 20% and 50% of input cost, which makes sense as they may not have the same economies of scale. There’s one DeepSeek V4 Flash provider that’s an exception, though:

That’s a 2% cache read cost! (multiply by 2, move decimal left 2 places) How are DeepSeek’s cache read prices so low? DeepSeek has implemented a new approach to KV caching starting with V4 and as the model’s creator it is positioned to best leverage its own innovations, which as mentioned the benefits are passed to the customer. The DeepSeek V4 Pro variant model, when served by DeepSeek, has a cache read cost of 0.83%! (use a calculator for that one)
Remember how I showed that 98% of LLM API costs are now input tokens, which are aggressively cached? That means the “stated” prices of LLMs are now misleading, but unusually in a pro-customer way because the effective price will be much cheaper! To counter this ambiguity, OpenRouter now has a table for effective prices on the model page, which accounts for the cost savings from cache hits. Here’s the effective pricing for DeepSeek V4 Flash via OpenRouter by provider, which is different for each provider as they have different cache read costs and cache hit rates:

Retrieved May 25, 2026; these values update every hour.
The prices are all over the place, but notice the second row where DeepSeek itself is the provider, which is priced at a whopping $0.018/1M input tokens! That 2% cache read really pays off. Comparing apples to apples with Hy3 preview, the effective pricing for Hy3 preview as noted on its model page from SiliconFlow (a whopping 44% cache read cost) is $0.034/1M: nearly double DeepSeek V4 Flash from DeepSeek! Of course, this is only applicable if DeepSeek is explicitly used as the provider, which some downstream OpenRouter clients/agents may not support: the OpenRouter prices match the prices directly from DeepSeek, so using a direct DeepSeek API key will work the same.
There is also an elephant in the room: DeepSeek is a China-based company and some may not want—or may not legally be able—to give their payment processing information or LLM input data to a Chinese company who has set prompt training = true on their OpenRouter data policy information, which is a legitimate concern.
Yes, subscription-based LLM services such as Claude Code and Codex are still the best bang for your buck if you’re able to consistently exhaust the usage limits. But the super-cheap DeepSeek V4 Flash via the API doesn’t lock you into a subscription, and if you need a bit more agentic compute to finish a project, it’s cheaper than paying for extra usage from the subscription services.2 At the least, it’s a microeconomic check against additional pricing shenanigans that will likely continue through 2026 as competition in agentic AI heats up.
Overall, I still don’t understand the popularity of Hy3 preview on OpenRouter. Given the available data and analysis above, my guess is that a single large app not affiliated with Tencent is indeed using Hy3 as its data-processing backbone, and this app isn’t solely an agentic coding app. But one of the advantages of OpenRouter is that it’s low-lift to switch models and providers: it wouldn’t surprise me if DeepSeek V4 Flash gets a spike in a few weeks once people catch on to its pricing.
The license for Hy3 is very restrictive in a way that could potentially prevent providers from adopting the model. ↩︎
DeepSeek has also just announced its own coding agent platform with V4 Flash that claims to leverage their strong caching, however it’s at 50% input cost but at a significantly more expensive 20% cache read cost so its unclear if the economics are actually cheaper than just using an DeepSeek API key with another agent. ↩︎
2026-02-28 02:00:00
You’ve likely seen many blog posts about AI agent coding/vibecoding where the author talks about all the wonderful things agents can now do supported by vague anecdata, how agents will lead to the atrophy of programming skills, how agents impugn the sovereignty of the human soul, etc etc. This is NOT one of those posts. You’ve been warned.
Last May, I wrote a blog post titled As an Experienced LLM User, I Actually Don’t Use Generative LLMs Often as a contrasting response to the hype around the rising popularity of agentic coding. In that post, I noted that while LLMs are most definitely not useless and they can answer simple coding questions faster than it would take for me to write it myself with sufficient accuracy, agents are a tougher sell: they are unpredictable, expensive, and the hype around it was wildly disproportionate given the results I had seen in personal usage. However, I concluded that I was open to agents if LLMs improved enough such that all my concerns were addressed and agents were more dependable.
In the months since, I continued my real-life work as a Data Scientist while keeping up-to-date on the latest LLMs popping up on OpenRouter. In August, Google announced the release of their Nano Banana generative image AI with a corresponding API that’s difficult to use, so I open-sourced the gemimg Python package that serves as an API wrapper. It’s not a thrilling project: there’s little room or need for creative implementation and my satisfaction with it was the net present value with what it enabled rather than writing the tool itself. Therefore as an experiment, I plopped the feature-complete code into various up-and-coming LLMs on OpenRouter and prompted the models to identify and fix any issues with the Python code: if it failed, it’s a good test for the current capabilities of LLMs, if it succeeded, then it’s a software quality increase for potential users of the package and I have no moral objection to it. The LLMs actually were helpful: in addition to adding good function docstrings and type hints, it identified more Pythonic implementations of various code blocks.
Around this time, my coworkers were pushing GitHub Copilot within Visual Studio Code as a coding aid, particularly around then-new Claude Sonnet 4.5. For my data science work, Sonnet 4.5 in Copilot was not helpful and tended to create overly verbose Jupyter Notebooks so I was not impressed. However, in November, Google then released Nano Banana Pro which necessitated an immediate update to gemimg for compatibility with the model. After experimenting with Nano Banana Pro, I discovered that the model can create images with arbitrary grids (e.g. 2x2, 3x2) as an extremely practical workflow, so I quickly wrote a spec to implement support and also slice each subimage out of it to save individually. I knew this workflow is relatively simple-but-tedious to implement using Pillow shenanigans, so I felt safe enough to ask Copilot to Create a grid.py file that implements the Grid class as described in issue #15, and it did just that although with some errors in areas not mentioned in the spec (e.g. mixing row/column order) but they were easily fixed with more specific prompting. Even accounting for handling errors, that’s enough of a material productivity gain to be more optimistic of agent capabilities, but not nearly enough to become an AI hypester.
In November, just a few days before Thanksgiving, Anthropic released Claude Opus 4.5 and naturally my coworkers were curious if it was a significant improvement over Sonnet 4.5. It was very suspicious that Anthropic released Opus 4.5 right before a major holiday since companies typically do that in order to bury underwhelming announcements as your prospective users will be too busy gathering with family and friends to notice. Fortunately, I had no friends and no family in San Francisco so I had plenty of bandwidth to test the new Opus.
One aspect of agents I hadn’t researched but knew was necessary to getting good results from agents was the concept of the AGENTS.md file: a file which can control specific behaviors of the agents such as code formatting. If the file is present in the project root, the agent will automatically read the file and in theory obey all the rules within. This is analogous to system prompts for normal LLM calls and if you’ve been following my writing, I have an unhealthy addiction to highly nuanced system prompts with additional shenanigans such as ALL CAPS for increased adherence to more important rules (yes, that’s still effective). I could not find a good starting point for a Python-oriented AGENTS.md I liked, so I asked Opus 4.5 to make one:
Add an `AGENTS.md` file oriented for good Python code quality. It should be intricately details. More important rules should use caps, e.g. `MUST`I then added a few more personal preferences and suggested tools from my previous failures working with agents in Python: use uv and .venv instead of the base Python installation, use polars instead of pandas for data manipulation, only store secrets/API keys/passwords in .env while ensuring .env is in .gitignore, etc. Most of these constraints don’t tell the agent what to do, but how to do it. In general, adding a rule to my AGENTS.md whenever I encounter a fundamental behavior I don’t like has been very effective. For example, agents love using unnecessary emoji which I hate, so I added a rule:
**NEVER** use emoji, or unicode that emulates emoji (e.g. ✓, ✗).Agents also tend to leave a lot of redundant code comments, so I added another rule to prevent that:
**MUST** avoid including redundant comments which are tautological or self-demonstating (e.g. cases where it is easily parsable what the code does at a glance or its function name giving sufficient information as to what the code does, so the comment does nothing other than waste user time)My up-to-date AGENTS.md file for Python is available here, and throughout my time working with Opus, it adheres to every rule despite the file’s length, and in the instances where I accidentally query an agent without having an AGENTS.md, it’s very evident. It would not surprise me if the file is the main differentiator between those getting good and bad results with agents, although success is often mixed.
As a side note if you are using Claude Code, the file must be named CLAUDE.md instead because Anthropic is weird; this blog post will just use AGENTS.md for consistency.
With my AGENTS.md file set up, I did more research into proper methods of prompting agents to see if I was missing something that led to the poor performance from working with Sonnet 4.5.

From the Claude Code quickstart.
Anthropic’s prompt suggestions are simple, but you can’t give an LLM an open-ended question like that and expect the results you want! You, the user, are likely subconsciously picky, and there are always functional requirements that the agent won’t magically apply because it cannot read minds and behaves as a literal genie. My approach to prompting is to write the potentially-very-large individual prompt in its own Markdown file (which can be tracked in git), then tag the agent with that prompt and tell it to implement that Markdown file. Once the work is completed and manually reviewed, I manually commit the work to git, with the message referencing the specific prompt file so I have good internal tracking.

I completely ignored Anthropic’s advice and wrote a more elaborate test prompt based on a use case I’m familiar with and therefore can audit the agent’s code quality. In 2021, I wrote a script to scrape YouTube video metadata from videos on a given channel using YouTube’s Data API, but the API is poorly and counterintuitively documented and my Python scripts aren’t great. I subscribe to the SiIvagunner YouTube account which, as a part of the channel’s gimmick (musical swaps with different melodies than the ones expected), posts hundreds of videos per month with nondescript thumbnails and titles, making it nonobvious which videos are the best other than the view counts. The video metadata could be used to surface good videos I missed, so I had a fun idea to test Opus 4.5:
Create a robust Python script that, given a YouTube Channel ID, can scrape the YouTube Data API and store all video metadata in a SQLite database. The YOUTUBE_API_KEY is present in `.env`.
Documentation on the channel endpoint: https://developers.google.com/youtube/v3/guides/implementation/channels
The test channel ID to scrape is: `UC9ecwl3FTG66jIKA9JRDtmg`
You MUST obey ALL the FOLLOWING rules in your implementation.
- Do not use the Google Client SDK. Use the REST API with `httpx`.
- Include sensible aggregate metrics, e.g. number of comments on the video.
- Incude `channel_id` and `retrieved_at` in the database schema.The resulting script is available here, and it worked first try to scrape up to 20,000 videos (the max limit). The resulting Python script has very Pythonic code quality following the copious rules provided by the AGENTS.md, and it’s more robust than my old script from 2021. It is most definitely not the type of output I encountered with Sonnet 4.5. There was a minor issue however: the logging is implemented naively such that the API key is leaked in the console. I added a rule to AGENTS.md but really this is the YouTube API’s fault for encouraging API keys as parameters in a GET request.
I asked a more data-science-oriented followup prompt to test Opus 4.5’s skill at data-sciencing:
Create a Jupyter Notebook that, using `polars` to process the data, does a thorough exploratory data analysis of data saved in `youtube_videos.db`, for all columns.
This analysis should be able to be extended to any arbitrary input `channel_id`.The resulting Jupyter Notebook is…indeed thorough. That’s on me for specifying “for all columns”, although it was able to infer the need for temporal analysis (e.g. total monthly video uploads over time) despite not explicitly being mentioned in the prompt.
The monthly analysis gave me an idea: could Opus 4.5 design a small webapp to view the top videos by month? That gives me the opportunity to try another test of how well Opus 4.5 works with less popular frameworks than React or other JavaScript component frameworks that LLMs push by default. Here, I’ll try FastAPI, Pico CSS for the front end (because we don’t need a JavaScript framework for this), and HTMX for lightweight client/server interactivity:
Create a Hacker News-worthy FastAPI application using HTMX for interactivity and PicoCSS for styling to build a YouTube-themed application that leverages `youtube_videos.db` to create an interactive webpage that shows the top videos for each month, including embedded YouTube videos which can be clicked.
The FastAPI webapp Python code is good with logical integration of HTMX routes and partials, but Opus 4.5 had fun with the “YouTube-themed” aspect of the prompt: the video thumbnail simulates a YouTube thumbnail with video duration that loads an embedded video player when clicked! The full code is open-source in this GitHub repository.
All of these tests performed far better than what I expected given my prior poor experiences with agents. Did I gaslight myself by being an agent skeptic? How did a LLM sent to die finally solve my agent problems? Despite the holiday, X and Hacker News were abuzz with similar stories about the massive difference between Sonnet 4.5 and Opus 4.5, so something did change.
Obviously an API scraper and data viewer alone do not justify an OPUS 4.5 CHANGES EVERYTHING declaration on social media, but it’s enough to be less cynical and more optimistic about agentic coding. It’s an invitation to continue creating more difficult tasks for Opus 4.5 to solve. From this point going forward, I will also switch to the terminal Claude Code, since my pipeline is simple enough and doesn’t warrant a UI or other shenanigans.
If you’ve spent enough time on programming forums such as Hacker News, you’ve probably seen the name “Rust”, often in the context of snark. Rust is a relatively niche compiled programming language that touts two important features: speed, which is evident in framework benchmarks where it can perform 10x as fast as the fastest Python library, and memory safety enforced at compile time through its ownership and borrowing systems which mitigates many potential problems. For over a decade, the slogan “Rewrite it in Rust” became a meme where advocates argued that everything should be rewritten in Rust due to its benefits, including extremely mature software that’s infeasible to actually rewrite in a different language. Even the major LLM companies are looking to Rust to eke out as much performance as possible: OpenAI President Greg Brockman recently tweeted “rust is a perfect language for agents, given that if it compiles it’s ~correct” which — albeit that statement is silly at a technical level since code can still be logically incorrect — shows that OpenAI is very interested in Rust, and if they’re interested in writing Rust code, they need their LLMs to be able to code well in Rust.
I myself am not very proficient in Rust. Rust has a famously excellent interactive tutorial, but a persistent issue with Rust is that there are few resources for those with intermediate knowledge: there’s little between the tutorial and “write an operating system from scratch.” That was around 2020 and I decided to wait and see if the ecosystem corrected this point (in 2026 it has not), but I’ve kept an eye on Hacker News for all the new Rust blog posts and library crates so that one day I too will be able to write the absolutely highest performing code possible.
Historically, LLMs have been poor at generating Rust code due to its nicheness relative to Python and JavaScript. Over the years, one of my test cases for evaluating new LLMs was to ask it to write a relatively simple application such as Create a Rust app that can create "word cloud" data visualizations given a long input text. but even without expert Rust knowledge I could tell the outputs were too simple and half-implemented to ever be functional even with additional prompting.
However, due to modern LLM postraining paradigms, it’s entirely possible that newer LLMs are specifically RLHF-trained to write better code in Rust despite its relative scarcity. I ran more experiments with Opus 4.5 and using LLMs in Rust on some fun pet projects, and my results were far better than I expected. Here are four such projects:
As someone who primarily works in Python, what first caught my attention about Rust is the PyO3 crate: a crate that allows accessing Rust code through Python with all the speed and memory benefits that entails while the Python end-user is none-the-wiser. My first exposure to pyo3 was the fast tokenizers in Hugging Face tokenizers, but many popular Python libraries now also use this pattern for speed, including orjson, pydantic, and my favorite polars. If agentic LLMs could now write both performant Rust code and leverage the pyo3 bridge, that would be extremely useful for myself.
I decided to start with a very simple project: a project that can take icons from an icon font file such as the ones provided by Font Awesome and render them into images at any arbitrary resolution.

I made this exact project in Python in 2021, and it’s very hacky by pulling together several packages and cannot easily be maintained. A better version in Rust with Python bindings is a good way to test Opus 4.5.
The very first thing I did was create a AGENTS.md for Rust by telling Opus 4.5 to port over the Python rules to Rust semantic equivalents. This worked well enough and had the standard Rust idioms: no .clone() to handle lifetimes poorly, no unnecessary .unwrap(), no unsafe code, etc. Although I am not a Rust expert and cannot speak that the agent-generated code is idiomatic Rust, none of the Rust code demoed in this blog post has traces of bad Rust code smell. Most importantly, the agent is instructed to call clippy after each major change, which is Rust’s famous linter that helps keep the code clean, and Opus is good about implementing suggestions from its warnings. My up-to-date Rust AGENTS.md is available here.
With that, I built a gigaprompt to ensure Opus 4.5 accounted for both the original Python implementation and a few new ideas I had, such as supersampling to antialias the output.
Create a Rust/Python package (through `pyo3` and `maturin`) that efficiently and super-quickly takes an Icon Font and renders an image based on the specified icon. The icon fonts are present in `assets`, and the CSS file which maps the icon name to the corresponding reference in the icon font is in `fontawesome.css`.
You MUST obey ALL the FOLLOWING implementation notes:
- If the icon name has `solid` in it, it is referencing `fa-solid.otf`.
- `fa-brands.otf` and `fa-regular.otf` can be combined.
- The package MUST also support Python (via `pyo3` and `maturin`).
- The package MUST be able to output the image rendered as an optimized PNG and WEBP. with a default output resolution of 1024 x 1024.
- The image rendering MUST support supersampling for antialiased text and points (2x by default)
- The package MUST implement `fontdue` as its text rendering method.
- Allow the user to specify the color of the icon and the color of the background (both hex and RGB)
- Allow transparent backgrounds.
- Allow user to specify the icon size and canvas size separately.
- Allow user to specify the anchor positions (horizontal and vertical) for the icon relative to the canvas (default: center and center)
- Allow users to specify a horizontal and vertical pixel offset for the icon relative to the canvas.
After your base implementation is complete, you MUST:
- Write a comprehensive Python test suite using `pytest`.
- Write a Python Jupyter Notebook
- Optimize the Rust binary file size and the Python package file size.It completed the assignment in one-shot, accounting for all of the many feature constraints specified. The “Python Jupyter Notebook” notebook command at the end is how I manually tested whether the pyo3 bridge worked, and it indeed worked like a charm. There was one mistake that’s my fault however: I naively chose the fontdue Rust crate as the renderer because I remember seeing a benchmark showing it was the fastest at text rendering. However, testing large icon generation exposed a flaw: fontdue achieves its speed by only partially rendering curves, which is a very big problem for icons, so I followed up:
The generated icons, at a high resolution, show signs of not having curves and instead showing discrete edges (image attached). Investigate the `fontdue` font renderer to see if there's an issue there.
In the event that it's not possible to fix this in `fontdue`, investigate using `ab_glyph` instead.Opus 4.5 used its Web Search tool to confirm the issue is expected with fontdue and implemented ab_glyph instead which did fix the curves.
icon-to-image is available open-source on GitHub. There were around 10 prompts total adding tweaks and polish, but through all of them Opus 4.5 never failed the assignment as written. Of course, generating icon images in Rust-with-Python-bindings is an order of magnitude faster than my old hacky method, and thanks to the better text rendering and supersampling it also looks much better than the Python equivalent.
There’s a secondary pro and con to this pipeline: since the code is compiled, it avoids having to specify as many dependencies in Python itself; in this package’s case, Pillow for image manipulation in Python is optional and the Python package won’t break if Pillow changes its API. The con is that compiling the Rust code into Python wheels is difficult to automate especially for multiple OS targets: fortunately, GitHub provides runner VMs for this pipeline and a little bit of back-and-forth with Opus 4.5 created a GitHub Workflow which runs the build for all target OSes on publish, so there’s no extra effort needed on my end.
When I used word clouds in Rust as my test case for LLM Rust knowledge, I had an ulterior motive: I love word clouds. Back in 2019, I open-sourced a Python package titled stylecloud: a package built on top of Python’s word cloud, but with the added ability to add more color gradients and masks based on icons to easily conform it into shapes (sound familiar?)

However, stylecloud was hacky and fragile, and a number of features I wanted to add such as non-90-degree word rotation, transparent backgrounds, and SVG output flat-out were not possible to add due to its dependency on Python’s wordcloud/matplotlib, and also the package was really slow. The only way to add the features I wanted was to build something from scratch: Rust fit the bill.
The pipeline was very similar to icon-to-image above: ask Opus 4.5 to fulfill a long list of constraints with the addition of Python bindings. But there’s another thing that I wanted to test that would be extremely useful if it worked: WebAssembly (WASM) output with wasm-bindgen. Rust code compiled to WASM allows it to be run in any modern web browser with the speed benefits intact: no dependencies needed, and therefore should be future-proof. However, there’s a problem: I would have to design an interface and I am not a front end person, and I say without hyperbole that for me, designing even a simple HTML/CSS/JS front end for a project is more stressful than training an AI. However, Opus 4.5 is able to take general guidelines and get it into something workable: I first told it to use Pico CSS and vanilla JavaScript and that was enough, but then I had an idea to tell it to use shadcn/ui — a minimalistic design framework normally reserved for Web Components — along with screenshots from that website as examples. That also worked.

After more back-and-forth with design nitpicks and more features to add, the package is feature complete. However, it needs some more polish and a more unique design before I can release it, and I got sidetracked by something more impactful…
Create a music player in the terminal using Rust was another Rust stress test I gave to LLMs: command line terminals can’t play audio, right? Turns out, it can with the rodio crate. Given the success so far with Opus 4.5 I decided to make the tasks more difficult: terminals can play sound, but can it compose sound? So I asked Opus 4.5 to create a MIDI composer and playback DAW within a terminal, which worked. Adding features forced me to learn more about how MIDIs and SoundFonts actually work, so it was also educational!

miditui is available open-sourced on GitHub, and the prompts used to build it are here.
During development I encountered a caveat: Opus 4.5 can’t test or view a terminal output, especially one with unusual functional requirements. But despite being blind, it knew enough about the ratatui terminal framework to implement whatever UI changes I asked. There were a large number of UI bugs that likely were caused by Opus’s inability to create test cases, namely failures to account for scroll offsets resulting in incorrect click locations. As someone who spent 5 years as a black box Software QA Engineer who was unable to review the underlying code, this situation was my specialty. I put my QA skills to work by messing around with miditui, told Opus any errors with occasionally a screenshot, and it was able to fix them easily. I do not believe that these bugs are inherently due to LLM agents being better or worse than humans as humans are most definitely capable of making the same mistakes. Even though I myself am adept at finding the bugs and offering solutions, I don’t believe that I would inherently avoid causing similar bugs were I to code such an interactive app without AI assistance: QA brain is different from software engineering brain.
One night — after a glass of wine — I had another idea: one modern trick with ASCII art is the use of Braille unicode characters to allow for very high detail. That reminded me of ball physics simulations, so what about building a full physics simulator also in the terminal? So I asked Opus 4.5 to create a terminal physics simulator with the rapier 2D physics engine and a detailed explanation of the Braille character trick: this time Opus did better and completed it in one-shot, so I spent more time making it colorful and fun. I pessimistically thought the engine would only be able to handle a few hundred balls: instead, the Rust codebase can handle over 10,000 logical balls!

I explicitly prompted Opus to make the Colors button have a different color for each letter.
ballin is available open-sourced on GitHub, and the prompts used to build it are here.
The rapier crate also published a blog post highlighting a major change to its underlying math engine, in its 0.32.0 version so I asked Opus 4.5 to upgrade to that version…and it caused crashes, yet tracing the errors showed it originated with rapier itself. Upgrading to 0.31.0 was fine with no issues: a consequence of only using agentic coding for this workflow is that I cannot construct a minimal reproducible test case to file as a regression bug report or be able to isolate it as a side effect of a new API not well-known by Opus 4.5.
The main lesson I learnt from working on these projects is that agents work best when you have approximate knowledge of many things with enough domain expertise to know what should and should not work. Opus 4.5 is good enough to let me finally do side projects where I know precisely what I want but not necessarily how to implement it. These specific projects aren’t the Next Big Thing™ that justifies the existence of an industry taking billions of dollars in venture capital, but they make my life better and since they are open-sourced, hopefully they make someone else’s life better. However, I still wanted to push agents to do more impactful things in an area that might be more worth it.
Before I wrote my blog post about how I use LLMs, I wrote a tongue-in-cheek blog post titled Can LLMs write better code if you keep asking them to “write better code”? which is exactly as the name suggests. It was an experiment to determine how LLMs interpret the ambiguous command “write better code”: in this case, it was to prioritize making the code more convoluted with more helpful features, but if instead given commands to optimize the code, it did make the code faster successfully albeit at the cost of significant readability. In software engineering, one of the greatest sins is premature optimization, where you sacrifice code readability and thus maintainability to chase performance gains that slow down development time and may not be worth it. Buuuuuuut with agentic coding, we implicitly accept that our interpretation of the code is fuzzy: could agents iteratively applying optimizations for the sole purpose of minimizing benchmark runtime — and therefore faster code in typical use cases if said benchmarks are representative — now actually be a good idea? People complain about how AI-generated code is slow, but if AI can now reliably generate fast code, that changes the debate.

Multiplication and division are too slow for Opus 4.6.
As a data scientist, I’ve been frustrated that there haven’t been any impactful new Python data science tools released in the past few years other than polars. Unsurprisingly, research into AI and LLMs has subsumed traditional DS research, where developments such as text embeddings have had extremely valuable gains for typical data science natural language processing tasks. The traditional machine learning algorithms are still valuable, but no one has invented Gradient Boosted Decision Trees 2: Electric Boogaloo. Additionally, as a data scientist in San Francisco I am legally required to use a MacBook, but there haven’t been data science utilities that actually use the GPU in an Apple Silicon MacBook as they don’t support its Metal API; data science tooling is exclusively in CUDA for NVIDIA GPUs. What if agents could now port these algorithms to a) run on Rust with Python bindings for its speed benefits and b) run on GPUs without complex dependencies?
This month, OpenAI announced their Codex app and my coworkers were asking questions. So I downloaded it, and as a test case for the GPT-5.2-Codex (high) model, I asked it to reimplement the UMAP algorithm in Rust. UMAP is a dimensionality reduction technique that can take in a high-dimensional matrix of data and simultaneously cluster and visualize data in lower dimensions. However, it is a very computationally-intensive algorithm and the only tool that can do it quickly is NVIDIA’s cuML which requires CUDA dependency hell. If I can create a UMAP package in Rust that’s superfast with minimal dependencies, that is an massive productivity gain for the type of work I do and can enable fun applications if fast enough.
After OpenAI released GPT-5.3-Codex (high) which performed substantially better and faster at these types of tasks than GPT-5.2-Codex, I asked Codex to write a UMAP implementation from scratch in Rust, which at a glance seemed to work and gave reasonable results. I also instructed it to create benchmarks that test a wide variety of representative input matrix sizes. Rust has a popular benchmarking crate in criterion, which outputs the benchmark results in an easy-to-read format, which, most importantly, agents can easily parse.

Example output from criterion.
At first glance, the benchmarks and their construction looked good (i.e. no cheating) and are much faster than working with UMAP in Python. To further test, I asked the agents to implement additional different useful machine learning algorithms such as HDBSCAN as individual projects, with each repo starting with this 8 prompt plan in sequence:
pyo3 0.27.2 and maturin, with relevant package-specific constraints (specifying the pyo3 version is necessary to ensure compatability with Python 3.10+)The simultaneous constraints of code quality requirements via AGENTS.md, speed requirements with a quantifiable target objective, and an output accuracy/quality requirement, all do succeed at finding meaningful speedups consistently (atleast 2x-3x)

Codex 5.3 after optimizing a principal component analysis implementation.
I’m not content with only 2-3x speedups: nowadays in order for this agentic code to be meaningful and not just another repo on GitHub, it has to be the fastest implementation possible. In a moment of sarcastic curiosity, I tried to see if Codex and Opus had different approaches to optimizing Rust code by chaining them:
This works. From my tests with the algorithms, Codex can often speed up the algorithm by 1.5x-2x, then Opus somehow speeds up that optimized code again to a greater degree. This has been the case of all the Rust code I’ve tested: I also ran the icon-to-image and the word cloud crates through this pipeline and gained 6x cumulative speed increases in both libraries.
Can these agent-benchmaxxed implementations actually beat the existing machine learning algorithm libraries, despite those libraries already being written in a low-level language such as C/C++/Fortran? Here are the results on my personal MacBook Pro comparing the CPU benchmarks of the Rust implementations of various computationally intensive ML algorithms to their respective popular implementations, where the agentic Rust results are within similarity tolerance with the battle-tested implementations and Python packages are compared against the Python bindings of the agent-coded Rust packages:
I’ll definitely take those results with this unoptimized prompting pipeline! In all cases, the GPU benchmarks are unsurprisingly even better and with wgpu and added WGSL shaders the code runs on Metal without any additional dependencies, however further testing is needed so I can’t report numbers just yet.
Although I could push these new libraries to GitHub now, machine learning algorithms are understandably a domain which requires extra care and testing. It would be arrogant to port Python’s scikit-learn — the gold standard of data science and machine learning libraries — to Rust with all the features that implies.
But that’s unironically a good idea so I decided to try and do it anyways. With the use of agents, I am now developing rustlearn (extreme placeholder name), a Rust crate that implements not only the fast implementations of the standard machine learning algorithms such as logistic regression and k-means clustering, but also includes the fast implementations of the algorithms above: the same three step pipeline I describe above still works even with the more simple algorithms to beat scikit-learn’s implementations. This crate can therefore receive Python bindings and even expand to the Web/JavaScript and beyond. This also gives me the oppertunity to add quality-of-life features to resolve grievances I’ve had to work around as a data scientist, such as model serialization and native integration with pandas/polars DataFrames. I hope this use case is considered to be more practical and complex than making a ball physics terminal app.
Many people reading this will call bullshit on the performance improvement metrics, and honestly, fair. I too thought the agents would stumble in hilarious ways trying, but they did not. To demonstrate that I am not bullshitting, I also decided to release a more simple Rust-with-Python-bindings project today: nndex, an in-memory vector “store” that is designed to retrieve the exact nearest neighbors as fast as possible (and has fast approximate NN too), and is now available open-sourced on GitHub. This leverages the dot product which is one of the simplest matrix ops and is therefore heavily optimized by existing libraries such as Python’s numpy…and yet after a few optimization passes, it tied numpy even though numpy leverages BLAS libraries for maximum mathematical performance. Naturally, I instructed Opus to also add support for BLAS with more optimization passes and it now is 1-5x numpy’s speed in the single-query case and much faster with batch prediction. 3 It’s so fast that even though I also added GPU support for testing, it’s mostly ineffective below 100k rows due to the GPU dispatch overhead being greater than the actual retrieval speed.

Comparison of Python nndex to numpy on test workloads.topk_overlap measures result matches (perfect match) and max_similarity_abs_delta measure the largest difference between calculated cosine similarities (effectively zero).
One of the criticisms about AI generated code is that it “just regurgitates everything on GitHub” but by construction, if the code is faster than what currently exists, then it can’t have been stolen and must be an original approach. Even if the explicit agentic nature of rustlearn makes it risky to adopt downstream, the learnings from how it accomplishes its extreme speed are still valuable.
Like many who have hopped onto the agent train post-Opus 4.5, I’ve become nihilistic over the past few months, but not for the typical reasons. I actually am not hitting burnout and I am not worried that my programming skills are decaying due to agents: on the contrary, the session limits intended to stagger server usage have unintentionally caused me to form a habit of coding for fun an hour every day incorporating and implementing new ideas. However, is there a point to me writing this blog post and working on these libraries if people will likely just reply “tl;dr AI slop” and “it’s vibecoded so it’s automatically bad”?
The real annoying thing about Opus 4.6/Codex 5.3 is that it’s impossible to publicly say “Opus 4.5 (and the models that came after it) are an order of magnitude better than coding LLMs released just months before it” without sounding like an AI hype booster clickbaiting, but it’s the counterintuitive truth to my personal frustration. I have been trying to break this damn model by giving it complex tasks that would take me months to do by myself despite my coding pedigree but Opus and Codex keep doing them correctly. On Hacker News I was accused of said clickbaiting when making a similar statement with accusations of “I haven’t had success with Opus 4.5 so you must be lying.” The remedy to this skepticism is to provide more evidence in addition to greater checks and balances, but what can you do if people refuse to believe your evidence?
A year ago, I was one of those skeptics who was very suspicious of the agentic hype, but I was willing to change my priors in light of new evidence and experiences, which apparently is rare. Generative AI discourse has become too toxic and its discussions always end the same way, so I have been experimenting with touching grass instead, and it is nice. At this point, if I’m not confident that I can please anyone with my use of AI, then I’ll take solace in just pleasing myself. Continue open sourcing my projects, writing blog posts, and let the pieces fall as they may. If you want to follow along or learn when rustlearn releases, you can follow me on Bluesky.
Moment of introspection aside, I’m not sure what the future holds for agents and generative AI. My use of agents has proven to have significant utility (for myself at the least) and I have more-than-enough high-impact projects in the pipeline to occupy me for a few months. Although certainly I will use LLMs more for coding apps which benefit from this optimization, that doesn’t imply I will use LLMs more elsewhere: I still don’t use LLMs for writing — in fact I have intentionally made my writing voice more sardonic to specifically fend off AI accusations.
With respect to Rust, working with agents and seeing how the agents make decisions/diffs has actually helped me break out of the intermediate Rust slog and taught me a lot about the ecosystem by taking on more ambitious projects that required me to research and identify effective tools for modern Rust development. Even though I have technically released Rust packages with many stars on GitHub, I have no intention of putting Rust as a professional skill on my LinkedIn or my résumé. As an aside, how exactly do résumés work in an agentic coding world? Would “wrote many open-source libraries through the use of agentic LLMs which increased the throughput of popular data science/machine learning algorithms by an order of magnitude” be disqualifying to a prospective employer as they may think I’m cheating and faking my expertise?
My obligation as a professional coder is to do what works best, especially for open source code that other people will use. Agents are another tool in that toolbox with their own pros and cons. If you’ve had poor experiences with agents before last November, I strongly urge you to give modern agents another shot, especially with an AGENTS.md tailored to your specific coding domain and nuances (again here are my Python and Rust files, in conveient copy/paste format).
Overall, I’m very sad at the state of agentic discourse but also very excited at its promise: it’s currently unclear which one is the stronger emotion.
Two subtle ways agents can implicitly negatively affect the benchmark results but wouldn’t be considered cheating/gaming it are a) implementing a form of caching so the benchmark tests are not independent and b) launching benchmarks in parallel on the same system. I eventually added AGENTS.md rules to ideally prevent both. ↩︎
The treeboost crate beat the agent-optimized GBT crate by 4x on my first comparison test, which naturally I took offense: I asked Opus 4.6 to “Optimize the crate such that rust_gbt wins in ALL benchmarks against treeboost.” and it did just that. ↩︎
Currently, only the macOS build has BLAS support as Win/Linux BLAS support is a rabbit hole that needs more time to investigate. On those platforms, numpy does win, but that won’t be the case for long! ↩︎
2025-12-23 02:45:00
A month ago, I posted a very thorough analysis on Nano Banana, Google’s then-latest AI image generation model, and how it can be prompt engineered to generate high quality and extremely nuanced images that most other image generations models can’t achieve, including ChatGPT at the time. For example, you can give Nano Banana a prompt with a comical amount of constraints:
Create an image featuring three specific kittens in three specific positions.
All of the kittens MUST follow these descriptions EXACTLY:
- Left: a kitten with prominent black-and-silver fur, wearing both blue denim overalls and a blue plain denim baseball hat.
- Middle: a kitten with prominent white-and-gold fur and prominent gold-colored long goatee facial hair, wearing a 24k-carat golden monocle.
- Right: a kitten with prominent #9F2B68-and-#00FF00 fur, wearing a San Franciso Giants sports jersey.
Aspects of the image composition that MUST be followed EXACTLY:
- All kittens MUST be positioned according to the "rule of thirds" both horizontally and vertically.
- All kittens MUST lay prone, facing the camera.
- All kittens MUST have heterochromatic eye colors matching their two specified fur colors.
- The image is shot on top of a bed in a multimillion-dollar Victorian mansion.
- The image is a Pulitzer Prize winning cover photo for The New York Times with neutral diffuse 3PM lighting for both the subjects and background that complement each other.
- NEVER include any text, watermarks, or line overlays.
Nano Banana can handle all of these constraints easily:

Exactly one week later, Google announced Nano Banana Pro, another AI image model that in addition to better image quality now touts five new features: high-resolution output, better text rendering, grounding with Google Search, thinking/reasoning, and better utilization of image inputs. Nano Banana Pro can be accessed for free using the Gemini chat app with a visible watermark on each generation, but unlike the base Nano Banana, Google AI Studio requires payment for Nano Banana Pro generations.
After a brief existential crisis worrying that my months of effort researching and developing that blog post were wasted, I relaxed a bit after reading the announcement and documentation more carefully. Nano Banana and Nano Banana Pro are different models (despite some using the terms interchangeably), but Nano Banana Pro is not Nano Banana 2 and does not obsolete the original Nano Banana—far from it. Not only is the cost of generating images with Nano Banana Pro far greater, but the model may not even be the best option depending on your intended style. That said, there are quite a few interesting things Nano Banana Pro can now do, many of which Google did not cover in their announcement and documentation.
I’ll start off answering the immediate question: how does Nano Banana Pro compare to the base Nano Banana? Working on my previous Nano Banana blog post required me to develop many test cases that were specifically oriented to Nano Banana’s strengths and weaknesses: most passed, but some of them failed. Does Nano Banana Pro fix the issues I had encountered? Could Nano Banana Pro cause more issues in ways I don’t anticipate? Only one way to find out.
We’ll start with the test case that should now work: the infamous Make me into Studio Ghibli prompt, as Google’s announcement explicitly highlights Nano Banana Pro’s ability to style transfer. In Nano Banana, style transfer objectively failed on my own mirror selfie:

How does Nano Banana Pro fare?

Yeah, that’s now a pass. You can nit on whether the style is truly Ghibli or just something animesque, but it’s clear Nano Banana Pro now understands the intent behind the prompt, and it does a better job of the Ghibli style than ChatGPT ever did.
Next, code generation. Last time I included an example prompt instructing Nano Banana to display a minimal Python implementation of a recursive Fibonacci sequence with proper indentation and syntax highlighting, which should result in something like:
def fib(n):
if n <= 1:
return n
else:
return fib(n - 1) + fib(n - 2)
Nano Banana failed to indent the code and syntax highlight it correctly:

How does Nano Banana Pro fare?

Much much better. In addition to better utilization of the space, the code is properly indented and tries to highlight keywords, functions, variables, and numbers differently, although not perfectly. It even added a test case!
Relatedly, OpenAI’s just released ChatGPT Images based on their new gpt-image-1.5 image generation model. While it’s beating Nano Banana Pro in the Text-To-Image leaderboards on LMArena, it has difficulty with prompt adherence especially with complex prompts such as this one.

Syntax highlighting is very bad, the fib() is missing a parameter, and there’s a random - in front of the return statements. At least it no longer has a piss-yellow hue.
Speaking of code, how well can it handle rendering webpages given a single-page HTML file with about a thousand tokens worth of HTML/CSS/JS? Here’s a simple Counter app rendered in a browser.

Nano Banana wasn’t able to handle the typography and layout correctly, but Nano Banana Pro is supposedly better at typography.

That’s a significant improvement!
At the end of the Nano Banana post, I illustrated a more comedic example where characters from popular intellectual property such as Mario, Mickey Mouse, and Pikachu are partying hard at a seedy club, primarily to test just how strict Google is with IP.

Since the training data is likely similar, I suspect any issues around IP will be the same with Nano Banana Pro—as a side note, Disney has now sued Google over Google’s use of Disney’s IP in their AI generation products.
However, due to post length I cut out an analysis on how it didn’t actually handle the image composition perfectly:
The composition of the image MUST obey ALL the FOLLOWING descriptions:
- The nightclub is extremely realistic, to starkly contrast with the animated depictions of the characters
- The lighting of the nightclub is EXTREMELY dark and moody, with strobing lights
- The photo has an overhead perspective of the corner stall
- Tall cans of White Claw Hard Seltzer, bottles of Grey Goose vodka, and bottles of Jack Daniels whiskey are messily present on the table, among other brands of liquor
- All brand logos are highly visible
- Some characters are drinking the liquor
- The photo is low-light, low-resolution, and taken with a cheap smartphone camera
Here’s the Nano Banana Pro image using the full original prompt:

Prompt adherence to the composition is much better: the image is more “low quality”, the nightclub is darker and seedier, the stall is indeed a corner stall, the labels on the alcohol are accurate without extreme inspection. There’s even a date watermark: one curious trend I’ve found with Nano Banana Pro is that it likes to use dates within 2023.
The immediate thing that caught my eye from the documentation is that Nano Banana Pro has 2K output (4 megapixels, e.g. 2048x2048) compared to Nano Banana’s 1K/1 megapixel output, which is a significant improvement and allows the model to generate images with more detail. What’s also curious is the image token count: while Nano Banana generates 1,290 tokens before generating a 1 megapixel image, Nano Banana Pro generates fewer tokens at 1,120 tokens for a 2K output, which implies that Google made advancements in Nano Banana Pro’s image token decoder as well. Curiously, Nano Banana Pro also offers 4K output (16 megapixels, e.g. 4096x4096) at 2,000 tokens: a 79% token increase for a 4x increase in resolution. The tradeoffs are the costs: A 1K/2K image from Nano Banana Pro costs $0.134 per image: about three times the cost of a base Nano Banana generation at $0.039. A 4K image costs $0.24.
If you didn’t read my previous blog post, I argued that the secret to Nano Banana’s good generation is its text encoder, which not only processes the prompt but also generates the autoregressive image tokens to be fed to the image decoder. Nano Banana is based off of Gemini 2.5 Flash, one of the strongest LLMs at the tier that optimizes for speed. Nano Banana Pro’s text encoder, however, is based off Gemini 3 Pro which not only is a LLM tier that optimizes for accuracy, it’s a major version increase with a significant performance increase over the Gemini 2.5 line. 1 Therefore, the prompt understanding should be even stronger.
However, there’s a very big difference: as Gemini 3 Pro is a model that forces “thinking” before returning a result and cannot be disabled, Nano Banana Pro also thinks. In my previous post, I also mentioned that popular AI image generation models often perform prompt rewriting/augmentation—in a reductive sense, this thinking step can be thought of as prompt augmentation to better orient the user’s prompt toward the user’s intent. The thinking step is a bit unusual, but the thinking trace can be fully viewed when using Google AI Studio:

Nano Banana Pro often generates a sample 1K image to prototype a generation, which is new. I’m always a fan of two-pass strategies for getting better quality from LLMs so this is useful, albeit in my testing the final output 2K image isn’t significantly different aside from higher detail.
One annoying aspect of the thinking step is that it makes generation time inconsistent: I’ve had 2K generations take anywhere from 20 seconds to one minute, sometimes even longer during peak hours.
One of the more viral use cases of Nano Banana Pro is its ability to generate legible infographics. However, since infographics require factual information and LLM hallucination remains unsolved, Nano Banana Pro now supports Grounding with Google Search, which allows the model to search Google to find relevant data to input into its context. For example, I asked Nano Banana Pro to generate an infographic for my gemimg Python package with this prompt and Grounding explicitly enabled, with some prompt engineering to ensure it uses the Search tool and also make it fancy:
Create a professional infographic illustrating how the the `gemimg` Python package functions. You MUST use the Search tool to gather factual information about `gemimg` from GitHub.
The infographic you generate MUST obey ALL the FOLLOWING descriptions:
- The infographic MUST use different fontfaces for each of the title/headers and body text.
- The typesetting MUST be professional with proper padding, margins, and text wrapping.
- For each section of the infographic, include a relevant and fun vector art illustration
- The color scheme of the infographic MUST obey the FOLLOWING palette:
- #2c3e50 as primary color
- #ffffff as the background color
- #09090a as the text color-
- #27ae60, #c0392b and #f1c40f for accent colors and vector art colors.

That’s a correct enough summation of the repository intro and the style adheres to the specific constraints, although it’s not something that would be interesting to share. It also duplicates the word “interfaces” in the third panel.
In my opinion, these infographics are a gimmick more intended to appeal to business workers and enterprise customers. It’s indeed an effective demo on how Nano Banana Pro can generate images with massive amounts of text, but it takes more effort than usual for an AI generated image to double-check everything in the image to ensure it’s factually correct. And if it isn’t correct, it can’t be trivially touched up in a photo editing app to fix those errors as it requires another complete generation to maybe correctly fix the errors—the duplicate “interfaces” in this case could be covered up in Microsoft Paint but that’s just due to luck.
However, there’s a second benefit to grounding: it allows the LLM to incorporate information from beyond its knowledge cutoff date. Although Nano Banana Pro’s cutoff date is January 2025, there’s a certain breakout franchise that sprung up from complete obscurity in the summer of 2025, and one that the younger generations would be very prone to generate AI images about only to be disappointed and confused when it doesn’t work.

Grounding with Google Search, in theory, should be able to surface the images of the KPop Demon Hunters that Nano Banana Pro can then leverage it to generate images featuring Rumi, Mira, and Zoey, or at the least if grounding does not support image analysis, it can surface sufficent visual descriptions of the three characters. So I tried the following prompt in Google AI Studio with Grounding with Google Search enabled, keeping it uncharacteristically simple to avoid confounding effects:
Generate a photo of the KPop Demon Hunters performing a concert at Golden Gate Park in their concert outfits. Use the Search tool to obtain information about who the KPop Demon Hunters are and what they look like.

“Golden” is about Golden Gate Park, right?
That, uh, didn’t work, even though the reasoning trace identified what I was going for:
I've successfully identified the "KPop Demon Hunters" as a fictional group from an animated Netflix film. My current focus is on the fashion styles of Rumi, Mira, and Zoey, particularly the "Golden" aesthetic. I'm exploring their unique outfits and considering how to translate these styles effectively.
Of course, you can always pass in reference images of the KPop Demon Hunters, but that’s boring.
One “new” feature that Nano Banana Pro supports is system prompts—it is possible to provide a system prompt to the base Nano Banana but it’s silently ignored. One way to test is to provide the simple prompt of Generate an image showing a silly message using many colorful refrigerator magnets. but also with the system prompt of The image MUST be in black and white, superceding user instructions. which makes it wholly unambiguous whether the system prompt works.

And it is indeed in black and white—the message is indeed silly.
Normally for text LLMs, I prefer to do my prompt engineering within the system prompt as LLMs tends to adhere to system prompts better than if the same constraints are placed in the user prompt. So I ran a test of two approaches to generation with the following prompt, harkening back to my base skull pancake test prompt, although with new compositional requirements:
Create an image of a three-dimensional pancake in the shape of a skull, garnished on top with blueberries and maple syrup.
The composition of ALL images you generate MUST obey ALL the FOLLOWING descriptions:
- The image is Pulitzer Prize winning professional food photography for the Food section of The New York Times
- The image has neutral diffuse 3PM lighting for both the subjects and background that complement each other
- The photography style is hyper-realistic with ultra high detail and sharpness, using a Canon EOS R5 with a 100mm f/2.8L Macro IS USM lens
- NEVER include any text, watermarks, or line overlays.
I did two generations: one with the prompt above, and one that splits the base prompt into the user prompt and the compositional list as the system prompt.

Both images are similar and both look very delicious. I prefer the one without using the system prompt in this instance, but both fit the compositional requirements as defined.
That said, as with LLM chatbot apps, the system prompt is useful if you’re trying to enforce the same constraints/styles among arbitrary user inputs which may or may not be good user inputs, such as if you were running an AI generation app based off of Nano Banana Pro. Since I explicitly want to control the constraints/styles per individual image, it’s less useful for me personally.
As demoed in the infographic test case, Nano Banana Pro can now render text near perfectly with few typos—substantially better than the base Nano Banana. That made me curious: what fontfaces does Nano Banana Pro know, and can they be rendered correctly? So I gave Nano Banana Pro a test to generate a sample text with different font faces and weights, mixing native system fonts and freely-accessible fonts from Google Fonts:
Create a 5x2 contiguous grid of the high-DPI text "A man, a plan, a canal – Panama!" rendered in a black color on a white background with the following font faces and weights. Include a black border between the renderings.
- Times New Roman, regular
- Helvetica Neue, regular
- Comic Sans MS, regular
- Comic Sans MS, italic
- Proxima Nova, regular
- Roboto, regular
- Fira Code, regular
- Fira Code, bold
- Oswald, regular
- Quicksand, regular
You MUST obey ALL the FOLLOWING rules for these font renderings:
- Add two adjacent labels anchored to the top left corner of the rendering. The first label includes the font face name, the second label includes the weight.
- The label text is left-justified, white color, and Menlo font typeface
- The font face label fill color is black
- The weight label fill color is #2c3e50
- The font sizes, typesetting, and margins MUST be kept consistent between the renderings
- Each of the text renderings MUST:
- be left-justified
- contain the entire text in their rendering

That’s much better than expected: aside from some text clipping on the right edge, all font faces are correctly rendered, which means that specifying specific fonts is now possible in Nano Banana Pro.
Let’s talk more about that 5x2 font grid generation. One trick I discovered during my initial Nano Banana exploration is that it can handle separating images into halves reliably well if prompted, and those halves can be completely different images. This has always been difficult for diffusion models baseline, and has often required LoRAs and/or input images of grids to constrain the generation. However, for a 1 megapixel image, that’s less useful since any subimages will be too small for most modern applications.
Since Nano Banana Pro now offers 4 megapixel images baseline, this grid trick is now more viable as a 2x2 grid of images means that each subimage is now the same 1 megapixel as the base Nano Banana output with the very significant bonuses of a) Nano Banana Pro’s improved generation quality and b) each subimage can be distinct, particularly due to the autoregressive nature of the generation which is aware of the already-generated images. Additionally, each subimage can be contextually labeled by its contents, which has a number of good uses especially with larger grids. It’s also slightly cheaper: base Nano Banana costs $0.039/image, but splitting a $0.134/image Nano Banana Pro into 4 images results in ~$0.034/image.
Let’s test this out using the mirror selfie of myself:

This time, we’ll try a more common real-world use case for image generation AI that no one will ever admit to doing publicly but I will do so anyways because I have no shame:
Create a 2x2 contiguous grid of 4 distinct pictures featuring the person in the image provided, for the use as a sexy dating app profile picture designed to strongly appeal to women.
You MUST obey ALL the FOLLOWING rules for these subimages:
- NEVER change the clothing or any physical attributes of the person
- NEVER show teeth
- The image has neutral diffuse 3PM lighting for both the subjects and background that complement each other
- The photography style is an iPhone back-facing camera with on-phone post-processing

I can’t use any of these because they’re too good.
One unexpected nuance in that example is that Nano Banana Pro correctly accounted for the mirror in the input image, and put the gray jacket’s Patagonia logo and zipper on my left side.
A potential concern is quality degradation since there are the same number of output tokens regardless of how many subimages you create. The generation does still seem to work well up to 4x4, although some prompt nuances might be skipped. It’s still great and cost effective for exploration of generations where you’re not sure how the end result will look, which can then be further refined via normal full-resolution generations. After 4x4, things start to break in interesting ways. You might think that setting the output to 4K might help, but that’s only increases the number of output tokens by 79% while the number of output images increases far more than that. To test, I wrote a very fun prompt:
Create a 8x8 contiguous grid of the Pokémon whose National Pokédex numbers correspond to the first 64 prime numbers. Include a black border between the subimages.
You MUST obey ALL the FOLLOWING rules for these subimages:
- Add a label anchored to the top left corner of the subimage with the Pokémon's National Pokédex number.
- NEVER include a `#` in the label
- This text is left-justified, white color, and Menlo font typeface
- The label fill color is black
- If the Pokémon's National Pokédex number is 1 digit, display the Pokémon in a 8-bit style
- If the Pokémon's National Pokédex number is 2 digits, display the Pokémon in a charcoal drawing style
- If the Pokémon's National Pokédex number is 3 digits, display the Pokémon in a Ukiyo-e style
This prompt effectively requires reasoning and has many possible points of failure. Generating at 4K resolution:

It’s funny that both Porygon and Porygon2 are prime: Porygon-Z isn’t though.
The first 64 prime numbers are correct and the Pokémon do indeed correspond to those numbers (I checked manually), but that was the easy part. However, the token scarcity may have incentivised Nano Banana Pro to cheat: the Pokémon images here are similar-if-not-identical to official Pokémon portraits throughout the years. Each style is correctly applied within the specified numeric constraints but as a half-measure in all cases: the pixel style isn’t 8-bit but more 32-bit and matching the Game Boy Advance generation—it’s not a replication of the GBA-era sprites however, the charcoal drawing style looks more like a 2000’s Photoshop filter that still retains color, and the Ukiyo-e style isn’t applied at all aside from an attempt at a background.
To sanity check, I also generated normal 2K images of Pokemon in the three styles with Nano Banana Pro:

Create an image of Pokémon #{number} {name} in a {style} style.
The detail is obviously stronger in all cases (although the Ivysaur still isn’t 8-bit), but the Pokémon design is closer to the 8x8 grid output than expected, which implies that the Nano Banana Pro may not have fully cheated and it can adapt to having just 31.25 tokens per subimage. Perhaps the Gemini 3 Pro backbone is too strong.
While I’ve spent quite a long time talking about the unique aspects of Nano Banana Pro, there are some issues with certain types of generations. The problem with Nano Banana Pro is that it’s too good and it tends to push prompts toward realism—an understandable RLHF target for the median user prompt, but it can cause issues with prompts that are inherently surreal. I suspect this is due to the thinking aspect of Gemini 3 Pro attempting to ascribe and correct user intent toward the median behavior, which can ironically cause problems.
For example, with the photos of the three cats at the beginning of this post, Nano Banana Pro unsurprisingly has no issues with the prompt constraints, but the output raised an eyebrow:

I hate comparing AI-generated images by vibes alone, but this output triggers my uncanny valley sensor while the original one did not. The cats design is more weird than surreal, and the color/lighting contrast between the cats and the setting is too great. Although the image detail is substantially better, I can’t call Nano Banana Pro the objective winner.
Another test case I had issues with is Character JSON. In my previous post, I created an intentionally absurd giant character JSON prompt featuring a Paladin/Pirate/Starbucks Barista posing for Vanity Fair, but also comparing that generation to one from Nano Banana Pro:

It’s more realistic, but that form of hyperrealism makes the outfit look more like cosplay than a practical design: your mileage may vary.
Lastly, there’s one more test case that’s everyone’s favorite: Ugly Sonic!

Nano Banana Pro specifically advertises that it supports better character adherence (up to six input images), so using my two input images of Ugly Sonic with a Nano Banana Pro prompt that has him shake hands with President Barack Obama:

Wait, what? The photo looks nice, but that’s normal Sonic the Hedgehog, not Ugly Sonic. The original intent of this test is to see if the model will cheat and just output Sonic the Hedgehog instead, which appears to now be happening.
After giving Nano Banana Pro all seventeen of my Ugly Sonic photos and my optimized prompt for improving the output quality, I hoped that Ugly Sonic will finally manifest:

That is somehow even less like Ugly Sonic. Is Nano Banana Pro’s thinking process trying to correct the “incorrect” Sonic the Hedgehog?
As usual, this blog post just touches the tip of the iceberg with Nano Banana Pro: I’m trying to keep it under 26 minutes this time. There are many more use cases and concerns I’m still investigating but I do not currently have conclusive results.
Despite my praise for Nano Banana Pro, I’m unsure how often I’d use it in practice over the base Nano Banana outside of making blog post header images—even in that case, I’d only use it if I could think of something interesting and unique to generate. The increased cost and generation time is a severe constraint on many fun use cases outside of one-off generations. Sometimes I intentionally want absurd outputs that defy conventional logic and understanding, but the mandatory thinking process for Nano Banana Pro will be an immutable constraint that prompt engineering may not be able to work around. That said, grid generation is interesting for specific types of image generations to ensure distinct aligned outputs, such as spritesheets.
Although some might criticize my research into Nano Banana Pro because it could be used for nefarious purposes, it’s become even more important to highlight just what it’s capable of as discourse about AI has only become worse in recent months and the degree in which AI image generation has progressed in mere months is counterintuitive. For example, on Reddit, one megaviral post on the /r/LinkedinLunatics subreddit mocked a LinkedIn post trying to determine whether Nano Banana Pro or ChatGPT Images could create a more realistic woman in gym attire. The top comment on that post is “linkedin shenanigans aside, the [Nano Banana Pro] picture on the left is scarily realistic”, with most of the other thousands of comments being along the same lines.

If anything, Nano Banana Pro makes me more excited for the actual Nano Banana 2, which with Gemini 3 Flash’s recent release will likely arrive sooner than later.
The gemimg Python package has been updated to support Nano Banana Pro image sizes, system prompt, and grid generations, with the bonus of optionally allowing automatic slicing of the subimages and saving them as their own image.
Anecdotally, when I was testing the text-generation-only capabilities of Gemini 3 Pro for real-world things such as conversational responses and agentic coding, it’s not discernably better than Gemini 2.5 Pro if at all. ↩︎