At the start of the series I described a plant identification app that confidently told me a seedling was basil.
It wasn’t basil. It was annual mercury.
The problem wasn’t just that it was wrong, it was also that it didn’t know it might be wrong.
That moment wasn’t really about a plant name. It was about a system giving an answer with more certainty than it had earned.
A year later, the app I set out to build is live.
Over the course of the series, the project became a test of a broader idea: that an AI system under real-world constraints does not have to be one large model trying to do everything. It can be a set of specialised components, each doing a narrower job, composed into one product.
That test is finished. The app is the result.
What the year actually taught me
I thought I was building a plant identification system. Looking back, I spent most of the year learning the same lesson in different forms.
The dataset looked healthy when it wasn’t. The collectors looked like they were running when they weren’t. The training metrics looked reassuring when the validation accuracy was zero. The classifier looked like it was solving the problem when it was optimising for the wrong objective.
The failures were different. The pattern wasn’t.
A system can be running, producing outputs, and updating dashboards while still being wrong. Confidence and correctness are different things. The project kept teaching me that, sometimes gently, sometimes by wasting entire weeks.
So what do you do with that lesson? You make confidence change the product. It has to affect what the user sees, what the app recommends, and when the app asks for more evidence.
By the end, I realised the most important thing I had built around the species model was a way to handle uncertainty honestly.
What I built
The app is called Raychis. RAY-kiss. A deliberate misspelling of rachis, the botanical term for the central stem of a compound leaf.
The name fits the architecture. Species identification is the stem. Health assessment, disease detection, growth stage analysis, and care guidance branch from it.
The app combines several specialised components: an embedding model for species identification, separate analyses for health, disease and growth stage, a local retrieval system, and confidence logic that determines how the result is presented. All of those components run entirely on the device. There is no server, no image upload, and no account required for the core functionality. The photograph never leaves the phone.
That is a privacy feature, but it is also the architecture made visible: a specialised system for a specific task, running on the hardware already in the user’s hand.
How Raychis handles uncertainty
Every identification includes an indication of how much confidence to place in the result:
Strong Match. Good Match. Possible Match. Weak Match.
Those labels determine what the app does next; they aren’t just cosmetic.
On a Strong Match, the app commits. On a Weak Match, it doesn’t pretend. It shows alternatives, asks for another photograph, and explains that the answer may not be reliable.
That is the practical difference. “Basil, 62%” still leaves the user to decide what 62% means. “Possible Match” plus alternatives and a request for another angle tells the user what to do next.
That behaviour exists because of everything documented in the previous nine chapters. A trustworthy system is not one that is always right. A trustworthy system is one that knows when it might be wrong.
What the test produced
The species model cleared the 75% stage gate and reached 80% accuracy on the held-out launch evaluation set. The system runs comfortably inside its latency and memory budgets on the target hardware. The app works entirely offline.
By the measurable launch criteria I defined twelve months ago, the project succeeded.
That does not prove a universal rule. One successful product cannot do that. But it does demonstrate that another path exists. A composed system of specialised components can satisfy constraints that are difficult to optimise simultaneously in a general-purpose system. The dominant architectural pattern is not the only pattern. I know that because I spent a year building another one.
What I got wrong
The series documents most of these in detail.
I spent five months believing I had to build the system alone and nearly lost the project because of it. I spent months training the wrong objective because the serving problem and the training problem weren’t actually the same thing. I spent weeks trusting metrics from a system whose critical validation path wasn’t executing. I spent months fixing symptoms before understanding causes.
The failures were technical, but the pattern underneath them was about trust. Every one of them involved believing a signal that turned out to be incomplete. Running, working, and being trustworthy are three different things.
What changed about how I work
The machine learning codebase grew to around 93,000 lines of Python. The mobile application ended at around 60,000 lines.
The contrast between the two taught me something unexpected. The ML project was almost entirely hand-written. The mobile application wasn’t. By the time I started building the app itself, I was using an AI coding agent the way a senior engineer uses a junior team member. I wrote the architecture. I made the decisions. I defined the constraints. The agent implemented the work, which I reviewed and then integrated.
That worked because by then I knew what I wanted. More importantly, I knew what the system needed. The year I spent building the machine learning stack had taught me how to specify problems precisely enough for somebody else to solve them. Eventually that somebody else became an AI agent. The thinking was still mine. The implementation became collaborative.
I wouldn’t recommend the path I took. I’m not sure I could have taken a shorter one.
What’s shipped
Raychis is live today. It supports 5,000 species, runs identification and health assessment entirely on-device, and presents results according to how confident the system is. No photograph is uploaded, and no account is required for the core functionality.
Disease detection and growth-stage analysis are included as experimental features. I have left them disabled by default because I am not yet satisfied with their real-world performance, but they can be enabled in the settings.
This is not the launch post. It is the closing chapter of a project.
The end of the experiment
The idea started as a question. The project became the experiment. The app is the result.
I don’t know whether this architectural approach will become more common over the next decade. I don’t know which parts of the system will survive unchanged. I don’t know what the model will look like after another year of training data and real-world use.
What I do know is the approach I would use again. Build small specialised pieces that do specific jobs well. Check whether their signals match reality. Make the interface show uncertainty to the person using the product. That is less tidy than one confident answer, but it is more useful.
What I do know is that the thing I set out to build now exists. It runs on the device. It works without a connection. It identifies plants. It helps people care for them. And when it isn’t sure, it says so.
That was the thing I couldn’t find when I took a photograph of annual mercury more than a year ago.
Now it exists.