Engineering

Models are feedforward. Brains are not.

A language model is a stateless function: unstructured in, structured out, one pass, no memory of the last one. Everything that makes an assistant feel like a colleague is wiring we added around it. Here's the whole shape, and why ours came out different.

← All postsDaniel Lenton15 July 202512 min read

Strip away the interface and a language model is a remarkably narrow thing. It maps unstructured input to structured output. You hand it a pile of text and images with no schema and no guarantees, and it hands back something shaped — a sentence, a JSON object, a tool call, a Python program. That is the entire contract.

Two properties of that contract matter more than anything else about the model, and both get glossed over because they're boring. It is stateless: the call has no idea it ever ran before. And it is feedforward: input goes in one end, output comes out the other, and nothing about the run can be redirected once it's underway. Every apparent memory an assistant has is something a harness put back into the prompt. Every apparent change of mind mid-task is a harness cancelling a call and starting another one.

The analogy I keep coming back to — and I want to be upfront that it's an analogy, not a claim about neuroscience — is that a model call is something like a single feedforward sweep through a cluster of neurons. It's a lot of computation and it does something genuinely impressive with an unstructured stimulus. But a sweep through a patch of cortex is not a brain. A brain has regions that specialise, feedback paths that run backwards, recurrent loops that let activity persist, and state that outlives any particular sweep. Take those away and you don't have a slower brain. You have a reflex.

Which reframes what an agent framework is. It isn't a wrapper that makes a model convenient to call. It's the wiring: which regions exist, what they own, how they talk to each other, what persists between sweeps, and where a signal can travel backwards. Models improve on a schedule none of us control. The wiring is the part we actually design, and it's where I think the interesting disagreements live.

Three stateless LLM calls — ConversationManager, CodeActActor, and a state manager — each drawn as a small feedforward network, connected left to right by act() and primitives.* dispatch arrows. All three read and write a shared persistent state layer below them, and a dashed steering path reaches into a call that is already running. The whole picture is enclosed in a dashed frame labelled 'the harness'.
Stateless calls in the boxes. Everything that makes them a brain is outside them.

Three ways to wire it

The open-source projects worth comparing against are making different bets about where the wiring investment belongs, and I'd rather state theirs plainly than argue against a straw man.

OpenClawinvests on the input side. Many messaging platforms, a gateway tier that maps platform messages onto agent runs, a broad plugin ecosystem. In the anatomy metaphor it's the sensory periphery: an enormous amount of care spent on getting the world into the system through as many surfaces as possible. If you want a personal assistant that reaches you everywhere and has a marketplace behind it, that is the project I'd point you at, and I don't think we compete with it so much as care about a different layer.

Hermes Agentinvests in one region done extremely well: a single agent core you can read end to end, text-injection steering, a polished skills library, mature cron and webhook automation. The bet is that one legible loop beats many opaque ones, and it's a good bet. The cost of many loops is real, and I'll come back to it.

Our bet is that the specialisation and the feedback paths are the product. Unify is a back office of many tool loops, each owning one slice of persistent state, each returning the same steerable handle, nesting arbitrarily deep. What follows is the whole shape in one pass. Each part has since had a post of its own, linked as it comes up.

Regions on different clocks

The first thing biology does that a single sweep can't is run different functions at different speeds. Keeping your balance and deciding where to go for dinner are not the same job and don't happen on the same clock.

Most agent frameworks have exactly one loop, so presence and work share a clock. Send a message while the agent is busy and the framework has to pick a policy: queue it, abort and restart, or splice your words into the worker's next tool result. None of those are bugs — they're reasonable choices when there is only one region. But notice what can't happen: nothing in the system is thinking about the conversation while the work runs, because the only thing that could is occupied.

So we split it. A ConversationManager owns presence and judgment and has no tools that do work; when work is needed it calls act(...), gets a handle back immediately, and carries on thinking. That's the conversation layer post. On a live phone call the same split happens again, one level down and far more aggressively, because three seconds of silence is a UX failure: a fast brain welded to the audio pipeline holds the floor while a slow brain composes anything with consequences. That's the dual-brain post. Same reasoning, two latency budgets.

Feedback: signals that run backwards

A feedforward sweep can't be redirected halfway. Brains solve this with feedback paths — top-down signals that reach a computation already in progress and change what it does. This is the part I'd defend hardest, because it's where the shape of the system does work no prompt could.

Every public method in the runtime returns the same type: a handle with interject, ask, pause, resume, stop. Those handles nest. A correction you type in chat propagates down through the dispatched task, into whatever manager loop is running inside it, into whatever that spawned. In the other direction, a loop three levels deep can call request_clarification, block at exactly the point of ambiguity, and have the question bubble all the way up to you — and the answer routes back down to the blocked call, which resumes from where it stopped rather than starting over.

Recurrence is the other half. A task that finishes doesn't have to end: with persist=Truethe loop surfaces its result and then waits, transcript and Python sandbox intact, so "now do March" is one line into a warm context rather than a cold start that re-derives everything. Both of those are the steering post.

The reason this needs to be structural rather than a feature is that mid-flight interruption is exactly the thing you cannot bolt on afterwards. If your steering mechanism is "append the user's words to the next tool result", it works at depth one and nowhere else, because there is no handle to the loop three levels down to address. The many-loops cost that Hermes Agent correctly identifies is the price of having something to address at every depth.

What travels across a synapse

Once you have more than one region, every boundary between them poses the same question: how much context travels with the signal? A request string is a lossy summary of a conversation, written by a model that can't know which detail will turn out to matter.

We made it an explicit argument at every delegation boundary. Pass the parent context and the child forks the outer conversation — full rendered history at the branch point, roles rewritten so it can never confuse the parent's turns for its own, then its own timeline diverges. Omit it and the child goes in cold, a pure function of the text it was handed. Both are right sometimes, and the model dispatching the work chooses. That's the parent-context post. The failure it prevents is the quiet one: twenty messages ago you mentioned you're flying Monday, nothing in the request says "not next week", and the cold task books Tuesday and is confidently wrong.

Two kinds of memory

Statelessness is the other half of the problem, and it splits cleanly into two needs that get conflated.

Working memory is scratch space that has to survive between calls but not forever. Under a one-shot code executor an agent reads a 2 GB export, prints a summary, loses the sandbox, and reads it again — ten minutes of wall time reconstructing state that existed and was discarded. Under a permanently shared interpreter you trade amnesia for contamination. Both are right sometimes, so execute_codetakes a coordinate instead of a policy: a state mode, a backend, a named session, an environment. The mode I'd least want to give up is read_only— copy a session's globals into a throwaway sandbox, try the risky reshape, discard everything. What-if without consequences. That's the execution post.

Long-term memory is what consolidates after the work is done, and here we diverge from the convention hardest. Agent skills have settled on a folder: a prose document with scripts nested inside it. We keep two libraries instead — executable functions and prose guidance, linked many-to-many, each searchable on its own. So a tone rule lives once and five functions point at it, and a program is findable without a document wrapped around it to make it retrievable. After every task a librarian pass decides what deserves to persist, and very often stores nothing. That's the functions-and-guidance post.

The consolidation direction is what makes this more than filing. A workflow the assistant worked out once becomes ordinary Python — deterministic control flow, with focused query_llmcalls only where judgment is genuinely required. The second time that task comes around it doesn't re-derive forty thinking steps on a frontier model. It calls the function. That's most of our cost story, and it falls out of making the program the stored unit rather than the paragraph.

The test: features nobody wrote

The reason to care whether the wiring is right is that good wiring produces capabilities you didn't implement. My favourite example is teaching by demonstration. Share your screen, talk through the weekly invoice run, and the assistant learns it — and there is no record button, no capture pipeline, no demo mode anywhere in the runtime.

It works because three unrelated decisions point the same way. Images are ordinary message content, plumbed from the bottom, so a shared frame is the same kind of thing as a sentence. Task sessions stay alive, so "no, not that button" is an interjection rather than an edit to a finished recording. And functions are writable from inside a task, so what the assistant learned lands in the same library as everything else, with no flag marking it as demo-taught and no separate shelf. That's the demonstration post, and the general principle underneath it is one I'd stand behind: every mode in a system is a place where the abstractions didn't generalise. If you can count the modes, you can count the failures.

Where the analogy breaks

I should be honest about how far this metaphor carries, because it carries less far than it's fun to pretend.

Real neural feedback is continuous and simultaneous — activity flows in both directions at once, at the same time, in the same tissue. Ours is discrete and turn-based: a queue checked between steps, a generation cancelled and restarted, a sentinel forwarded to a child. That's a real difference in kind, not just degree, and it shows up as latency you can sometimes feel. Nothing in our system learns from experience the way a brain does either. Weights don't move. What we call consolidation is writing rows to a database, which is a much shallower thing than it sounds when you call it memory.

And there's a sharper objection, which is that all of this belongs in the model eventually. If a model natively maintains presence while reasoning, holds its own working state, and accepts input mid-computation, then a chunk of what I've described is scaffolding around a limitation that stopped existing. I take that seriously. For the lowest level — turn detection, barge-in, knowing whether someone is yielding or just thinking — I'd happily delete our plumbing the day it's an API call.

But I don't think the seams disappear. Even a natively interactive model needs wiring to your channels, your memory, your team's permissions, and a fleet of long-running work it can steer. A framework where the conversation is the work loop has no seat to upgrade — the better models get at holding a thread, the more it costs to have fused the thread to the work. Our bet is that the seats outlive the things we currently put in them.

Why any of this is open

The runtime is MIT-licensed because the shape of the orchestration layer is the thing worth evaluating, and a shape you can't read is a shape you have to take on faith. If the bets above are wrong, they're wrong legibly, and someone can fork them into something better. That's a trade I'm happy with.

I do wonder whether everyone converges here anyway. A presence layer over persistent working loops, steerable at every depth, distilling what it learns into code — it's a fairly natural shape once you've lived with the alternative for a while. I'd rather have written it down early and been visibly wrong than be quietly right.

Where to look

All open at github.com/unifyai/unify:

Pick the workflow that's been eating your week.

Starter credits, no card. You'll have real work running before the end of the call.