AI as an Empowering Technology
Table of Contents
This is a companion to my latest post about my tilezz project. It started as inline sections and footnotes, until it was clear that I was writing two posts at the same time, driven by the feeling that currently you still have to justify yourself for using LLMs, as if the fact itself is something shameful. Some people try to hide or obscure their use of AI, but I believe we need to find a rational way to be open about it.
In a different post on similar issues I already said that its not about the tool, but how you use it. This post expands on this theme in the context of a hobby project I take rather seriously and which gradually transitioned from being fully hand-written to being mostly AI-maintained, leaving me the more interesting role of the principal investigator and senior engineer.
Ideas Too Big to Explore Alone
Tilezz started as a fully hand-written repository, it was not “vibe-coded”. Its evolution can be traced following the posts I wrote about it. In my head I always have been 10 steps ahead of the implementation. I had this long-term vision – a computational tiling laboratory built on exact arithmetic – based on the key idea of using string- and automata-based techniques (which I am very familiar with) on boundaries of simple polygons that live on cyclotomic rings embedded in the complex plane.
I implemented the first prototype in Python using numpy, then ported it all to Rust. I built a trait and macro architecture to derive concrete rings from a minimal set of seed constants, all while learning more advanced Rust as well as bits and pieces of number theory and computational geometry along the way.
But over time, despite the best of my efforts, things started to get hairy, and due to the explorative nature of the project and incremental growth, the code needed re-engineering I had no time for. I coded myself into a corner and sunken-ship fallacy did not let me just throw away and rebuild what took me so much time and sweat to write.
At some point, I simply did not have enough motivational energy left to continue development, because I was already having a long backlog of larger ideas, held back by bad performance due to various implementation flaws and early architectural choices I made.
Since I started using coding agents, this project suddenly became manageable again, grew by a factor of 10 and allowed me to reach the point where I am right now. This probably would have taken years while using up most of my valuable free time if I had to do it without such tools.
By now there is probably not a line in the codebase left that was originally written by me, because the foundations have been completely rewritten and now run orders of magnitude faster than what I originally have built before I even knew what I was doing.
The flip side of the coin being that now I can produce code faster than I can understand it, which endangers my ability to steer the project well. That’s why I tried to ensure correctness by a paranoid level of validation, mostly by tests, and even going so far as to let AI formalize correctness proofs in Lean (some more on that later).
I guess that in the end, when using AI, I end up investing even more in setting up validation infrastructure for results than I would for my own hand-written code, to compensate for the fact that I cannot possibly understand every detail without essentially working it all out myself. However you turn it, human time and understanding remains the bottleneck.
The General Interaction Mode
In all of the tilezz work, my interaction with AI was of the following form:
- I explain an idea for a feature or optimization
- the LLM works out all the details
- we discuss until the plan sounds reasonable
- I let it implement and test it
After multiple rounds of this, inevitably something bubbles up pointing at skeletons hiding in the closet which I apparently let slip through, because I simply do not have the time to review everything in-depth or micro-manage the implementation and tests.
So I adopted the practice to regularly plan a “deep code review marathon”, both on granular level only looking at isolated subsystems at a time, and also holistically – to ensure that all pieces are built and connected to each other reasonably.
- I supply the vision, the constraints, and the engineering taste, and
- the LLM does the mechanical work of turning my ideas into executable code.
And of course, the output has issues exactly there where I either
- did not pay enough attention during the planning and briefing, or
- underspecified the shape of the desired solution, or
- did not invest enough time in reviewing before moving on.
All these tools are still new and like everybody else I am also still learning how to optimize this interaction loop, which includes improving my own attention and context management for this new kind of working. In this now world of top-down, constraint-driven development its easy to get out of touch with the codebase, and this is where problems begin.1
But despite all remaining warts with the current generation of LLMs, I really cannot complain – I always wanted a kind of coding and research assistant for my ideas, and now this dream has basically come true. What a time to be alive!
What I contribute to tilezz
Other people might have different perspectives and feelings about this and this is okay, but for me, code is just a means to realize an idea or create the tool I want to have. I care about the result, but not how I got there.
When I think of building a house, do I care whether I will hammer in each nail with my own bare hands, one by one, or do I just want it to get built the way I want? If I pay someone to build my house following my design, does it make it “not my house”?
Tilezz was and remains my brainchild. All ideas that try building bridges between geometry, string processing, automata and SMT solving are mine, and this is the premise of the whole project.
What AI contributed to tilezz
What is not mine anymore are implementation details. So what follows is an incomplete overview of the various ways how AI helped me push my hobby research project much further than I could ever hope for just a few years ago.
Implementation and refactoring at the speed of thought
The main booster is of course the core capability of current LLMs – to quickly implement acceptable and sometimes even good code. Unfortunately, from a higher vantage point, it still almost always needs critical questioning:
- is it duplicating something we already have?
- are the tests actually meaninful and non-circular?
- how could certain subsystems diverge so much in the first place?
Often I just need to ask the right questions, without flaws jumping out at me that I have
to call out, but I still have to regularly ask the questions to trigger the needed
reflection and cleanups, which is a different kind of annoying work. No amount of
AGENTS.md tweaking is able to turn an LLM into a reliable senior level engineer with
architectural taste producing well-designed solutions autonomously, at least today.
Heavily optimized ring implementation
A decent LLM understands low-level performance optimizations as well as number theory much better than I do, and honestly, I do not care much about learning these in-depth. So in the end, I let it rewrite all of the cyclotomic ring generation code, while preserving the existing clean shape: common traits, combined with parametrized generation macros.
This alone gave a speed-up of multiple orders of magnitude over my original implementation of ring element multiplication and operation-optimized implementations of the linear algebra implementing geometric checks, i.e. minimization of expensive multiplications in the first place.
The shadow prune optimization family
The one thing I would definitely not take credit for is the generalization of my naive distance-based pruning (i.e., abort if the walk is too far from the origin) into one that maximally utilizes the algebraic structure of the ring elements in all algebraic places. All I did ask for was asking the right question: “Is there maybe any structure in the cyclotomic rings that we can exploit, beyond the optimizations that are already implemented?”, and it came back with this idea.
The patch machinery
I had the snake and rat data structures already fully implemented before using LLMs, but the next step, patches consisting of multiple tiles from some fixed tileset, is something I never found the time to pursue. I have not written about it and I am not sure how interesting it is, because ultimately a patch is just a rat with a lot of overlay bookkeeping structure and optimizations. A rat is just a polygon, a clean combinatorial object. A patch however carries its whole construction history, making it very useful, but quite messy. Patches and related structures are the geometry layer where most interesting things live, but also the layer needing a huge amount of engineering work. Among other things, this includes all the needed auxiliary structures and algorithms for efficiently enumerating all candidate tile matches along a patch boundary.
The patch structure gradually evolved driven by needs of different use-cases and algorithms. All of it was written, refactored and re-refactored by AI. In its current form, it does follow my architectural design. I wanted to distinguish between different flavors of patches sharing a common trait, to avoid paying for the overhead caused by possibly unneeded information. So now there are multiple kinds of patches to choose from, depending on the use-case, all sharing a clean common abstraction, but I did not write a single line of that plumbing, just designed the target shape and orchestrated the work.
Proof formalization
For a long time I had the informal intuition that with a bounded number of steps, you should be able to bound how close two segments can be, and a vague idea how you could maybe actually realize segments that are arbitrarily close. This was relevant for my segment intersection optimization based on a spatial lookup grid. I wondered how many segments can be in the same bucket in a pathological case, and how rare is this case really?
After reading various articles about how some parts of the math community are embracing LLM-driven formalization with Lean, I invested an evening to understand the main concepts and how it connects to what I already know about Haskell and Coq, and then let AI do the formalization work.
By the same logic by which I do not have to understand every line of implementation, as long as I ensure that the invariants I care about are tested and requirements are satisfied, similarly I don’t have to fully understand the proof of a mathematical statement to understand what it says and make use of it.
Long story short – not only did it prove some bounds, but it also produced an implementation of the construction which I suspected to be possible, realizing for each maximal perimeter length $N$ a concrete rat that contains two unit length segments at some very small distance $\varepsilon(N)$, all based only on my very vague suggestion to “design suitable partial boundary gadgets” and use them strategically in some spiralling or zig-zag pattern to produce increasingly smaller gaps between segments.
The resulting outputs from this work thus both prove and also construct the upper bound of $\varepsilon(N)$, i.e. the smallest possible distance of two segments connected by a walk with at most $N$ unit steps, but for the lower bound you obviously cannot have a constructive artifact. So I actually let the AI sketch a natural language proof first and skimmed it. Knowing that AI produces plausible things which can still be subtly wrong and knowing the limits of my own ability and knowledge, this was not enough – so I let it write a formal Lean proof to get the matching lower bound for the minimal distance between segments on a perimeter-bounded path as well. You can fool me, but you cannot fool a mechanical proof system.
Am I now 100% certain everything checks out? No, but I would be even more insecure if I tried to prove it myself, given that I do not have any training in these areas of math at all. I am as confident about it as I would be as a reviewer reading a proof in a paper about a theory topic I am not deeply competent in, this is the humble truth. But I think I do not have to convince anyone that AI can produce correct mathematics, as there is so much movement and recent successes in that area that results speak for themselves.
The Lean proof work currently lives on an unpublished branch and needs some clean-up, but I hope that I will eventually integrate it into the repository properly, and maybe even write a post about the interesting segment distance bound I mentioned above.
To Label or Not to Label
Concerning the prose content of my blog, however, I have ambiguous feelings about AI usage. I really wanted to report on the progress and successes, but the kind of hobby projects I do increasingly depends on AI tools to figure out, build, and explain the details to me, because both in breadth and in depth otherwise I could not keep up.
The idealistic desire to postpone writing this post until I had the time to read up on all the missing theory would probably mean never writing it. So in the latest post about tilezz, I used AI to draft the text for most technical sections, something I never did before.
In an earlier post, I gave a promise that text in my blog is reserved for my own text, as it is a means of my self-expression. So I seemingly contradicted this promise of integrity. I guess I did shift some goalpost, I am aware of that. To not be outright misleading and self-contradicting, I decided to try at least to highlight AI-assisted prose clearly, if I do use it.
All the marker should mean is: “this is not my fully unaugmented voice speaking”, because a blog post is more than its technical content – it’s also tone and sentiment, emotional coloring and whatever makes an author authentic. And I have not fully made up my mind yet about the authenticity status of AI-assisted prose.
In hindsight, after I was done iterating on the AI-assisted prose and manually tweaking it when working on this post, I understood the algebraic content better myself, and actually found that the result looks like prose I almost could have authentically written – not word for word, but in spirit. And I think it was not faster than doing it myself, but just different. The same pattern applies to software development. Writing it still took more than one evening, so it saved quite some time, but it is not as if the post was done in an hour or two.
It is tempting to put in much less effort when using AI, because presence or absence of quality only becomes visible if you actually pay attention to details. Quality is not a superficial property of outputs. On the contrary, it is the depth and fine structure. So the true speedup from LLMs, when used responsibly, becomes not a factor of 100x, as it sometimes feels, but maybe a factor of 10x on some tasks, and a factor less than 2 in others. That is, if you actually take your time to make sure the details actually do look right.
Will I always continue highlighting AI-assisted prose?
Maybe, even if just to create some distance between the (partially) generated words and me. Or maybe not, if I find it too difficult to consistently draw the line between “the LLM wrote this” and “I (re)wrote this”.
Would I ever use AI to fully generate, say, poetry – and then publish it as mine? Or let it write a post, and then publish it without much thought?
No, of course not.
As I mentioned elsewhere, I do sometimes use LLMs to critique structure and coherence and to point out and fix spelling and grammar mistakes. It is like working with a hired an editor, but I am still the author.
For primarily technical content, I feel much less conflicted to let AI play a larger role in the process. But for other kinds of content, there is a huge grey area and slippery slope.
Whenever the flavor of the output or the human touch is part of the result, using AI – especially, using AI carelessly – feels wrong to me. I am absolutely not against using AI as part of a larger artistic process, but it needs to be just that – a well-used tool in human hands, not the whole act.
Summary
Thanks to LLMs, in many domains the distance from knowing what you want to getting it got dramatically shorter. It is now easy to quickly explore and iterate over ideas, refactor code, and get some complex algorithm implemented for which no suitable off-the-shelf library exists. It is also quick and easy to explore concepts that could be dead-ends, drop them without a feeling of loss, and much more feasible to rework even the foundations of a big project, if the existing design has major flaws.
You can paint ideas with a much larger brush than ever before, but you are still the one responsible for using it well. I firmly believe that in the right hands, it is an empowering technology, and I cannot wait for the day when sufficiently capable models can run locally on commodity hardware. Because only then does it really become a tool that helps to increase the sovereignty and flourishing of human beings, instead of increasing our dependence on large corporations.
What we all still have to figure out is the question of attribution. For technical work, most issues can be resolved pragmatically. I think nobody should feel betrayed if they find out that a tool was developed with the help of LLMs. A quality codebase is a quality codebase, a good tool is a good tool.
But for anything where creativity and character is part of the output, it becomes a much more difficult question, and I also still have not fully figured out for myself where I think the line should be drawn.