ed@edheltzel: ~/log/agent-does-not-own-the-workflow
ed@edheltzel:~/log$ cat agent-does-not-own-the-workflow.md

Your agent does not need to own the workflow

Most of the agents I see billed as autonomous are a language model sitting on top of a switch statement. Dex Horthy’s 12-factor agents notes put that in writing, and it matches what I keep running into. This is a reading of that guide (content CC BY-SA 4.0), not a dump of the twelve headings.

The useful move is smaller than the marketing. The model translates a sentence into a structured next step. Your code decides whether that step runs, how it runs, and what happens after.

Natural language in, a typed object out

Horthy’s first factor is the whole trick, shown with a payment-link request. Someone asks for a $750 Stripe link. The model does not “use Stripe.” It emits something your code can switch on: create this link, for this customer, with this memo. Deterministic code picks up the payload. Mailcrew is the worked example; the Stripe API still needs real customer and price ids, which you either look up in code or put in context on purpose.

Anthropic’s Building effective agents note draws the same line from the other side. Workflows are code-directed. Agents are model-directed loops. A lot of products called agents are workflows with one LLM step in the right place. That is not a failure. That is the design.

A tool call is JSON you chose to honor

Factor 4 is the unglamorous version of “tools.” CreateIssue and SearchIssues are not magic bindings. They are shapes. The model prints JSON that parses into one of those shapes. Your switch statement may call Linear, or wait, or refuse. A tool call is a request, not a promise that the matching function will run.

That is why the JSON-mode vs function-calling vs constrained-decoding argument is secondary. Boundary’s writeup on schema-aligned parsing and Vellum’s comparison are worth reading if you are picking an API. The product decision is still: the model names an intent, your code owns the side effect.

Keep the model on a short leash

Factor 10 is the reason not to hand the whole DAG to one loop. Longer tasks mean longer context. Longer context is where models lose the plot. Horthy’s range is roughly a handful of steps, maybe twenty. Treat that as a smell test, not a law. NotebookLM’s team said the interesting work sits near the edge of what the model can do consistently. Past that edge you get fluent wandering.

If models get better at long jobs, you still want small agents. You grow the slice of the DAG they own. You do not throw away the surrounding program. Same lesson as refactoring a large deterministic codebase: expand the module that earned it.

What I actually keep

  • The model proposes. The program disposes.
  • One agent, one job, short enough that you can read the thread.
  • The rest of the workflow stays in code you can test without an LLM.

Next: the context window is an application interface.