From the Garden
f(a) = agents as a function
1,000 subagents, one shared state, and one big beautiful graph.

Most AI agents are still built in the shape of a chat. The model receives a transcript, takes an action, appends the result, and repeats. This is a reasonable interface for one person talking to one model, but AI is evolving, and with it the way people use it.
The chat loop is a poor execution model for a system that is non-linear, needs to answer or incorporate hundreds of independent questions about the same state, or has multiple pieces of a system that are entirely independent of each other. Furthermore, as models have progressed over the past couple of years, tiny models that previously couldn't be trusted for tasks that require some level of "smartness" can now be trusted. Even OpenAI's smallest API-available model, GPT-5.4 Nano, is a reasoning model with a 400k context window.
This is the chat loop's real flaw. If 1,000 questions all depend on the same repository snapshot, plan, or research corpus, then question 1,000 should not have to wait for answers 1 through 999. Shared context does not imply sequential execution.
What a subagent actually is
In essence, a call to a model is a function call. Every subsequent call, chat, or interaction is just another call that folds the previous steps into context. Now, a subagent is no different from a regular agent, other than the way it is invoked. Conceptually, it is similar to a function called within another function. In our agent swarm architecture — and, I think, in the future of AI engineering — we will start to refer to individual agents as functions rather than fully instantiated agents. We thus define the "function call" below:
where:
- is an immutable state snapshot
- is one narrow task
- is a bounded tool policy
- is an output schema
- is a typed result
The subagent does not inherit the outputs of its siblings. It does not negotiate ownership. It does not mutate shared state. It is born from one snapshot, produces one result, and exits.
The shared state might contain:
- repository and commit identifiers
- selected files or retrieved evidence
- system and project instructions
- the controller's plan
- tool definitions and permissions
- acceptance criteria
- a stable cache key
Since agents are functions, let's make a graph
Once workers are pure functions, the better abstraction falls into a distributed system where each agent function can be treated as its own worker.
The architecture
One big beautiful graph
A fork-join swarm: one strong controller freezes the state, one cache write makes it cheap to read, and every question forks from the same snapshot at once. Focus or tap any node.
# one example S = freeze(shift, plan, roster) cache.write(S) verdicts = gather(f(S, c) for c in roster) send(reduce(verify(verdicts)))
An important note here, however, is the purpose of caching input tokens. In a traditional distributed system, caching would mostly be used for speed. While that is still somewhat relevant to agent functions, the more important purpose of caching input tokens is the cost savings. A cache read costs a fraction of a cache write, so by having one agent write the cache first, then forking the others from it, we see major savings. Couple that with delegating to a smaller, cheaper model for a given step, and the purpose of using agents as functions surfaces.
The economics
One write, a hundred cheap reads
Every fork rereads the same frozen state. Uncached, each fork pays full price for those input tokens. Cached, the swarm pays ~1.25× once to write, then ~0.1× per read — typical provider pricing.
Illustrative: 100 forks, input tokens only — output tokens cost the same either way.
Correctness, then speed, then cost
Real-time production AI systems should be optimized in a strict order:
- Correctness: is the result acceptable?
- Speed: did the acceptable result arrive before its deadline?
- Cost: what did the acceptable, timely result cost?
The controller protects correctness. Parallelism attacks latency. Cheap workers and cache reads make the resulting fan-out affordable.
Parallelizing agent functions is the key to speed
Assume we have independent questions, and each answer takes approximately seconds to generate.
The brute-force method is to ask the questions in sequence. The total time is
and the average question-to-answer latency is
The system is not serial because the questions depend on one another. It is serial because the interface happens to be a transcript.
Stuffing all questions into one prompt does not fix this either: inference time still scales with the number of questions being reasoned about at once, and the context window puts a hard ceiling on how many you can even fit.
Now fork all questions from the same frozen state and execute them concurrently:
For five equally expensive questions, the fifth answer no longer arrives at . All five arrive together, in roughly one inference round.
The race
Same eight questions, two shapes
Each block is one model call taking about L seconds. The transcript answers one question per round; the graph spends one round planning, answers everything at once, then reduces.
t = 0.0L — the transcript is a synchronization barrier; the graph is not.
The math here is simple. The total compute grows with the number of questions, but the number of rounds you actually wait through stays flat:
is the total work across all workers. is the critical path: the longest chain of calls that must happen one after another. In a transcript, that chain is every single question. In the graph, it is always three: plan, fork, reduce.
In essence, we have collapsed dependent compute rounds into a constant . The total work is the same, but the waiting is not, and no single context window ever has to hold the whole problem.
How we use this at Phoebe
The original spark to this thinking came from one of Phoebe's main features.
When a shift needs coverage, the real work is selecting caregivers: qualifications, client preferences, availability, distance, and how each person has responded to similar shifts before. Every caregiver is an independent question has multiple inputs: the client's plan, and the agency's rules. So we run it as a graph, and an output: Yes or No. The controller freezes the shift context once, one worker writes the cache, and a small model forks per caregiver, returning one typed verdict with evidence. We are then left with a full list of selected caregivers.
In production
The graph vs. the old model
The same callout, before and after: one agent working through one crowded context, versus one frozen shift state fanned across the roster.
| Latency | Cost | Scale | Roster | Recommendations | |
|---|---|---|---|---|---|
| One agent, one context | every caregiver waits in line | full-price input tokens, every evaluation | capped by one context window | a heuristic shortlist | a guess from one crowded prompt |
| The agent swarm | ~50% faster | ½ the cost | 15 shifts · 700+ caregivers | the entire roster | more per shift |
| on average · p95 saw up to 80% | cache reads at ~0.1× on small models | evaluated at once · no ceiling | a dedicated evaluation per caregiver | each verdict backed by its own evidence |
Latency, cost, and scale figures from Phoebe production runs; the cache-read multiplier is typical provider pricing.
Moving to this framework sped up a single outreach by about 50% on average, with our p95 seeing up to 80%. On our largest runs, 15 shifts at once with more than 700 caregivers being processed, the speedup was even higher.
Speed is only half of it. Because each caregiver gets a full, dedicated evaluation instead of competing for space in one overloaded context window, we can consider the entire roster rather than a heuristic shortlist. That means more recommendations per shift, and better ones — each backed by its own evidence instead of a guess from a single crowded prompt.
Why this actually matters
Under this framework, we get two main advantages. First, we can properly handle problems whose context is larger than the context window of the agent meant to solve them. Second, we can treat agents as cheap, easy-to-place function calls that supplement a general agent's actions.
The first advantage has been outlined by other companies, like Cognition with Devin's security audit. But to our knowledge, this is one of the first systems that treats small agents as conceptual functions and leans on cache reads to decrease latency, decrease cost, and increase correctness all at once.
Agents are functions, and by accepting that main idea, we can utilize the past 50 years of optimization and methodology that computer scientists have already worked out.
Work with Phoebe
We're building digital workers to coordinate the physical economy. Come build them with us.

