Why We Stopped Using LLM Agents to Control LLM Agents (Deterministic Multi-Agent FSM) Parvej Shah, lead full-stack engineer at Minions.AI, detailed in a blog post why the company replaced an LLM orchestration agent with a deterministic finite state machine (FSM) for its automated content pipeline. The FSM, written in TypeScript, achieved a 99.2% automated completion rate and reduced inference cost variance from ±120% to ±8%, eliminating loop oscillations and context poisoning. Originally published at parvejshah.com/blog/deterministic-multi-agent-systems-production by Parvej Shah . The standard architecture pattern for multi-agent systems right now is an orchestration agent: a central LLM that receives a goal, decides which specialized agents to invoke, passes messages between them, and decides when the task is complete. In early 2025, we built this exact pattern for Minions.AI 's automated technical content pipeline. An orchestrator LLM coordinated a research agent, a draft writer, a critic agent, and a formatting specialist. It worked in 70% of runs. In the other 30%, it failed in creative, unpredictable ways. The failure modes weren't bugs in the traditional sense. The individual prompts were well-engineered. The tool definitions were clean. The failures came from the non-deterministic nature of the control plane. Loop oscillation. The critic agent would reject a draft for lacking specific technical details. The writer agent would add details, but slightly change the tone. The critic agent would then reject the new draft for tone issues, causing the writer to revert the details. The orchestrator would watch this tennis match loop until hitting the maximum turn limit. Context poisoning. As agents passed conversational turns back and forth, the shared context window accumulated conversational residue — conversational filler, apologies, retry explanations. By turn 6, 40% of the token budget was spent on orchestration chatter rather than the actual task domain. Non-deterministic convergence. The same topic with the same source signals would sometimes produce a crisp, 1,200-word technical deep dive in 4 turns, and other times produce a rambling 3,000-word overview in 14 turns costing 4x the inference budget. The fix was conceptually simple: remove all control flow decisions from LLMs and put them in typed code. LLMs are exceptional at content transformation, extraction, synthesis, and evaluation against specific criteria. They are terrible at state machine transitions, termination detection, and error routing. We redesigned the multi-agent pipeline as a formal Finite State Machine FSM written in TypeScript: type PipelineState = | "HARVEST SIGNALS" | "DRAFT CONTENT" | "CRITIC REVIEW" | "REVISE DRAFT" | "STAGE CMS" | "FAILED"; interface PipelineContext { topicId: string; signals: IndustrySignal ; draftMarkdown?: string; critique?: CritiqueResult; revisionCount: number; maxRevisions: 2; // Strict deterministic limit } export async function runContentPipeline ctx: PipelineContext : Promise