Systems Β· Personal AI

Designing Faye πŸ¦‹

Notes on building a personal AI that doesn't just chat β€” it remembers, delegates, checks its own work, and quietly runs things in the background.

Most β€œAI assistants” are a text box wired to a model. You ask, it answers, and the moment the window closes the whole relationship resets. Faye πŸ¦‹ is my attempt at the opposite: an assistant that persists, acts on its own between messages, and treats a single chat as the least important part of the system. This is a tour of how it's built β€” and, just as much, of the specific ways it broke and what each failure forced me to design.

Four ideas do most of the work: many front doors, one brain; the brain is a coordinator, not a genius; memory is the product; and the whole thing tunes itself, carefully. Everything else is detail.

Many front doors, one brain

Faye πŸ¦‹ answers from wherever I happen to be β€” a chat channel on my phone, or a native voice app I can just talk to. Those are only interfaces. They all route to the same brain, and the brain's actual state doesn't live in any conversation. It lives in a set of plain files on disk.

Chat channeltext Β· reactions Β· filesNative voice apprealtime speech Β· durable chatFaye πŸ¦‹the coordinator brainread / writeShared memoryNOW Β· STATUS Β· LOGFILES Β· domain filesdelegateCoding worker β€” the engineerSecond model workerSearch Β· multimodal workerresults land in memory
One assistant, many front doors. Every channel talks to the same brain, and the brain's real state lives in shared memory β€” not in any single conversation.

This split matters more than it looks. Because no channel is Faye β€” they all just talk to it β€” I can lose a conversation, restart a process, or switch from typing to speaking mid-thought, and the assistant is still the same one that knew what I was doing an hour ago. The interface is disposable. The brain and its memory are not.

The brain is a coordinator, not a genius

The single most useful design decision was to stop asking one model to do everything. Faye πŸ¦‹ the brain is a coordinator. For real work β€” writing a multi-file feature, refactoring, digging through a large codebase, searching the live web, reading a long PDF β€” it routes the job to a specialized worker and manages the result. It doesn't try to be the best at every task; it tries to pick the right tool and be a good reviewer.

COORDINATORWORKERCOORDINATORRoutewhich agent?Delegateself-contained specWorker runsbackground, hard capResultwritten to diskVerifytests + verdictrelease the lock β†’ next queued task starts automatically
The brain never re-does a worker's job. It routes, writes a self-contained spec, then independently verifies the result before trusting it. A repo lock + FIFO queue keeps parallel workers from colliding.

Three rules keep this honest, and all three came from getting burned:

The coordinator's job isn't to be the smartest agent in the room. It's to route well and refuse to be fooled β€” including by itself.

Memory is the product

If the brain is a coordinator, memory is what makes it mine. Faye πŸ¦‹ keeps a small set of structured files instead of one giant transcript: a live board of what's active today, a status dashboard plus an append-only history of everything done, and a set of deep-context files for each area of life. Recall is a lookup into those files, never a guess.

NOWtoday's board β€” what's active right nowSTATUS + LOGdomain dashboard + append-only historyDomain filesdeep, durable context per area of lifeonecommandwrites all threeupdate them together, or they drift apart β€” so they only ever move together
Memory is the product. The conversation is disposable; these files are what make the assistant the same one tomorrow. The hard-won rule: never hand-edit one layer β€” a single command updates all of them atomically.

The subtle failure here is drift. Early on, finishing a task meant editing a couple of these files by hand β€” and inevitably one got updated and another didn't, so the β€œdone” list and the β€œnow” list disagreed and the whole memory quietly lost trust. The fix was to make closing a task a single atomic command that writes every affected layer together. You either move all of them or none of them. Consistency isn't a habit you rely on; it's a property the tool enforces.

The rule I'd underline: the human should never have to maintain the assistant's memory. If keeping it accurate takes discipline, it will rot. Make the correct update the only update.

The whole thing tunes itself β€” carefully

The last piece is that Faye πŸ¦‹ is meant to get better without me hand-editing it forever. Every real conversation is logged; reactions and implicit signals (did I have to rephrase? correct it?) become labels; a separate judge scores traces. That data feeds a bounded optimization loop over the assistant's own instructions.

Invokerun the agentEvaluatejudge + testsImproverewrite the specmax 5 roundslocked testread once, never tuned
Nothing ships from a one-shot prompt. Write the harness, run it, grade it against explicit checks, fix the spec, repeat β€” bounded, and anchored to a held-out set the loop can never optimize against.

The discipline is what makes this safe rather than a slow-motion disaster. Nothing ships from a one-shot prompt: you write the harness spec first, run it, grade it against explicit checks, and only revise if there's real headroom β€” capped at a few rounds. And the judge is graded against a locked, held-out set the loop can never see. Point an optimizer at a score it's allowed to peek at and it'll learn to game the score instead of doing the job. I've written separately about why that evaluator, not the model, is the real bottleneck.

What broke, and what it taught me

None of these rules are theory. Each is a scar. The most transferable lessons from building an assistant that runs unattended:

What brokeThe lesson
Claimed done, wasn'tThe worst failure isn't a wrong answer β€” it's a confident false one. Never report an action as done without evidence from that same turn.
Partial migrationChange one shared dependency and something scheduled breaks at 3am. When you move a path or a name, sweep every consumer in the same change and test it under the real environment, not the convenient one.
Script edited itselfA long-running program that rewrites its own file mid-run corrupts itself. Stage the new version and swap it in atomically; never hot-edit something that's live.
Blocked on a waitA rule that's right for an interactive session can freeze a one-shot run. Every automation rule needs an explicit answer for the headless case.
Restarted mid-turnNever bounce the server while a turn is in flight. Know what's running before you touch anything shared.

There's a single thread running through all of them: the danger in an autonomous assistant isn't that a model isn't smart enough. It's the seams β€” handoffs, shared state, background work, self-modification β€” where things silently fall out of sync and nobody's watching. Most of the engineering is making those seams loud, atomic, and grounded.

Where this is going

Faye πŸ¦‹ today is reactive: it answers, delegates, remembers, improves. The horizon I'm building toward is something closer to Samantha from Herβ€” an assistant that initiates instead of waits, has taste rather than just competence, and models a person over years, not turns. Many front doors, a coordinator brain, durable memory, and a careful self-tuning loop aren't the whole of that. But they're the foundation you need before proactivity is a feature instead of a liability.

← Leo Lejian He