Independent AI engineering · agentic homelab
Agents I run for myself
The homelab is not only infrastructure my coding agents build — some of it is agents doing recurring personal work on their own schedule. Each one reads from systems I already own, decides what (if anything) is worth saying, and stays quiet otherwise. This page walks through them.
The shape they share
A schedule, not a prompt
Each system is a named session with a cron and a written brief. The brief is a file in a git repo, so changing the behaviour is a reviewed commit rather than a config change in a UI.
Home Assistant decides when
Home automation triggers a run on an event — a long stretch without steps, a low battery, a new scan — while the agent decides what to say. Automation never composes the message: a template cannot adjust to the day, and mine once claimed 23.5 hours of daylight remained at 17:30.
Silence is the default
A run with nothing to report replies a single sentinel word, and the orchestrator withdraws the message from the channel entirely — header included. "Nothing to report" is never itself reported.
It can ask
When the right action has a choice in it, a run asks with multiple-choice buttons and acts on the answer — including a late one, which arrives after the run has ended and becomes a calendar entry.
A health coach with standing briefs
A Xiaomi Smart Band sends metrics to Home Assistant through the vendor app's integration. Home Assistant mirrors them into stable helper entities; a scheduled session then reads those helpers and writes one short Discord message per slot — morning, evening, weekly review — or nothing at all.
The design constraints are the interesting part, because each came from a specific failure:
Nudges, not a dashboard
One message per run. A metric appears only as the reason for an action, never as a tally — the headline is the instruction ("walk at 11:30, before lunch") with the reading behind it, not a status report.
Refresh before reading
The phone stops syncing the band after a reboot or an app update until the app is opened, so every run first triggers a script that opens it, then waits for the sync burst. Without this the data looked current while silently frozen.
0means "no reading yet"Not zero. A metric whose freshness cannot be established is treated as absent rather than quoted. Sleep is the worst case: written once each morning and then left alone.
A night can be split
The app closes a sleep session at the first wake and does not reliably push what follows, so a reading ending before 06:00 is unknown rather than short, and the missing hours are never guessed.
The retro scores itself
The weekly run is the only one that looks backwards: it checks what the coach actually said against the week's data, and files at most one change to a brief — or none, which is the commoner answer.
Triage: photos, mail, paper
The same pattern applied to the three inboxes that accumulate without asking.
Photos
Sorts each night's new photos into needs-you, to-file, keep and noise; tags them; files documents to a scan archive; and posts a short digest that asks about each one that needs a decision. Unclear cases are judged by a vision model rather than guessed from metadata.
Mail
Sorts personal mail into action and noise, proposes the next step for each item that needs one, and unsubscribes from bulk senders that are never engaged with — mailing the unsubscribe address in the header rather than clicking through a page.
Paper
Asks what to do about each scanned document that has something outstanding — a bill, a form to return, a deadline — then sets the reminder, opens an issue, or marks it settled.
Captures
A message from a tablet capture app becomes a triaged issue in the right repository, with the screenshot attached.
A backlog that works itself
Issues labelled as agent-workable are picked oldest-first across every repository: each is claimed, implemented in a fresh clone, opened as a pull request and landed through the review loop. Anything that genuinely needs a person becomes a blocked label and a request, instead of a guess. Runs work to a time budget.
The reviewer that gates the rest
Every change those agents open is read first by a self-hosted LLM reviewer: a sandboxed run against a contained checkout, three parallel review lenses, then a coordinator and a verifier. Findings arrive as inline comments with fingerprints, so the same issue is recognised across passes rather than re-litigated, and a by-design finding is dismissed with a recorded reason on the line it concerns.
The reviewer is described from the CV's Agentic code reviewer card; this page covers the personal systems around it.
What running these taught me
Instrument the silence
The most valuable changes were to how a system behaves when it has nothing to say. Every "why didn't it tell me" turned out to be a quiet path nobody had defined.
Write the brief as code
Coaching rules that live in a prompt are unreviewable. Rules that live in a versioned file get argued with, tested against a week's data, and reverted when they are wrong.
Trust is a freshness question
Almost every wrong output traced back to a value that was stale but plausible. Asking "when was this written" before "what does it say" fixed more than any prompt rewrite.
Built with
- Python
- Home Assistant
- Discord
- MCP
- Docker
- Forgejo
- LLM tool-use loops
- Vision models
Part of the same homelab as the CV's AI engineering work; the guest-isolation design is on its own page.