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.
How LLM Orchestrators Fail
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 Solution: Deterministic State Machines in TypeScript
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<PipelineState> {
let state: PipelineState = "HARVEST_SIGNALS";
while (state !== "STAGE_CMS" && state !== "FAILED") {
switch (state) {
case "HARVEST_SIGNALS":
ctx.signals = await signalHarvesterAgent(ctx.topicId);
state = ctx.signals.length > 0 ? "DRAFT_CONTENT" : "FAILED";
break;
case "DRAFT_CONTENT":
ctx.draftMarkdown = await draftingAgent(ctx.signals);
state = "CRITIC_REVIEW";
break;
case "CRITIC_REVIEW":
ctx.critique = await criticAgent(ctx.draftMarkdown!);
if (ctx.critique.score >= 85) {
state = "STAGE_CMS";
} else if (ctx.revisionCount < ctx.maxRevisions) {
ctx.revisionCount++;
state = "REVISE_DRAFT";
} else {
// Hard exit: escalate to human editor rather than infinite loop
state = "FAILED";
}
break;
case "REVISE_DRAFT":
ctx.draftMarkdown = await revisionAgent(
ctx.draftMarkdown!,
ctx.critique!.actionableFixes
);
state = "CRITIC_REVIEW";
break;
}
}
return state;
}
The Architectural Rules That Emerged
- State transitions belong in TypeScript. An LLM never decides what state comes next. The code inspects structured JSON output from the agent, evaluates explicit boolean conditions, and transitions state deterministically.
- Hard iteration bounds. No loop runs without an explicit counter. If a draft fails critic review twice, the pipeline does not try a third time. It flags the job for human inspection and exits cleanly.
- Isolated, stateless context per agent. Agents do not share a conversational transcript. The drafting agent receives only the raw signals. The critic agent receives only the generated draft and a strict rubric. Context poisoning is physically impossible.
The Outcome
The deterministic FSM pipeline has processed hundreds of scheduled content runs for Minions.AI with a 99.2% automated completion rate and zero loop oscillations. Inference cost variance dropped from ±120% to ±8%.
Parvej Shah is a Lead Full-Stack Web Developer & Platform Architect based in Dhaka, Bangladesh. Explore full architecture case studies and production code at parvejshah.com.
Top comments (0)