Quick wins for a faster PC:
Clear out junk files and repair common Windows errorsFree Scan →Fix the driver behind crashes, sound loss and screen glitchesFind Drivers →Repair Windows errors before they cause bigger problemsFix Now →You can make a multi-agent workflow deterministic at the application level by having TypeScript own its state, routing rules, validation, retry limits, and stopping conditions. That does not make an LLM’s reasoning or output deterministic: it makes the path your application takes in response to validated outputs explicit and testable.
The practical design is to keep agents inside narrow contracts, choose deliberately who owns each branch, and persist enough state to resume or inspect a run. For work that must survive worker restarts, a durable workflow engine may be appropriate; for shorter runs, the Agents SDK’s run loop and a chosen continuation strategy may be sufficient.
What “deterministic” means in a multi-agent workflow
There are two broad ways to control a workflow: let a model choose what should happen next, or have application code determine the steps and use model output only where judgment is needed. The OpenAI Agents SDK orchestration guide describes code orchestration as making tasks more deterministic and predictable in speed, cost, and performance. That is a claim about workflow behavior, not a guarantee that model responses will repeat identically.
In a code-owned workflow, your application decides which step is legal, what input an agent receives, how its output is checked, and what happens after success, failure, timeout, or a request for approval. The model can still classify, summarize, research, or recommend; its response is treated as an input to your program, not as permission to change the workflow arbitrarily.
Do these 3 things before closing this tab:
1Scan for outdated or missing drivers - takes under a minute2Clear out junk files and repair common Windows errors3Fix the driver behind crashes, sound loss and screen glitches#1 Best Overall
- Application-controlled: state transitions, required steps, routing policy, retry caps, approval gates, and terminal outcomes.
- Model-influenced: decisions that genuinely need language understanding or domain judgment, provided the result is constrained and validated before it affects state.
- Still variable: model text, tool results, external services, timing, and failures. A state machine cannot make those dependencies deterministic.
Define the state and legal transitions first
Before choosing an agent framework, write down the states, events, and permitted next steps. Keep transitions in ordinary TypeScript where practical so they can be tested without making model calls. The following small example models an intake, research, review, approval pause, completion, and failure path. It is an illustrative application design, not a tested SDK implementation.
type Status = "intake" | "research" | "review" | "waiting_approval" | "done" | "failed";
type State = {
runId: string;
status: Status;
topic: string;
findings?: string[];
draft?: string;
revision: number;
attempts: Partial<Record<"research" | "review", number>>;
lastError?: string;
};
type Event =
| { type: "INTAKE_ACCEPTED" }
| { type: "RESEARCH_COMPLETED"; findings: string[] }
| { type: "REVIEW_APPROVED"; draft: string }
| { type: "REVIEW_NEEDS_CHANGES"; reason: string }
| { type: "APPROVAL_GRANTED" }
| { type: "STEP_FAILED"; step: "research" | "review"; message: string }
| { type: "RETRY_ALLOWED"; step: "research" | "review" }
| { type: "RETRY_EXHAUSTED"; message: string };
function transition(state: State, event: Event): State {
switch (state.status) {
case "intake":
if (event.type === "INTAKE_ACCEPTED") {
return { ...state, status: "research", revision: state.revision + 1 };
}
break;
case "research":
if (event.type === "RESEARCH_COMPLETED") {
return { ...state, status: "review", findings: event.findings, revision: state.revision + 1 };
}
if (event.type === "STEP_FAILED" && event.step === "research") {
return { ...state, lastError: event.message, revision: state.revision + 1 };
}
if (event.type === "RETRY_EXHAUSTED") {
return { ...state, status: "failed", lastError: event.message, revision: state.revision + 1 };
}
break;
case "review":
if (event.type === "REVIEW_APPROVED") {
return { ...state, status: "waiting_approval", draft: event.draft, revision: state.revision + 1 };
}
if (event.type === "REVIEW_NEEDS_CHANGES") {
return { ...state, status: "research", lastError: event.reason, revision: state.revision + 1 };
}
if (event.type === "STEP_FAILED" && event.step === "review") {
return { ...state, lastError: event.message, revision: state.revision + 1 };
}
if (event.type === "RETRY_EXHAUSTED") {
return { ...state, status: "failed", lastError: event.message, revision: state.revision + 1 };
}
break;
case "waiting_approval":
if (event.type === "APPROVAL_GRANTED") {
return { ...state, status: "done", revision: state.revision + 1 };
}
break;
}
throw new Error(`Illegal event ${event.type} while in ${state.status}`);
}
The reducer deliberately rejects events that do not fit the current state. Production code should also validate persisted state when loading it and validate agent responses at the boundary before constructing events. For example, a research agent should return a schema-checked list of findings rather than an unconstrained string that the router then interprets as instructions.
Make every outcome explicit
For each step, define what counts as success, a malformed response, a tool or runtime failure, a timeout, a retryable error, exhausted retries, or a human-approval pause. Decide whether a review rejection returns to research, requests a bounded revision, or fails. Put a maximum on any cycle that can send work back to an earlier state; otherwise a “review again” path can become an unbounded loop.
The transition function above illustrates one possible policy, not the only correct policy. In particular, a real system should model separate error classes if they need different handling, and should not turn every failure into an automatic retry.
The Tool Desk
Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Rank #2
- TypeScript implements a superset of syntax for strictly typed development, facilitating deep static analysis and enhanced development environment integration. The compiler translates source into standard script formats, ensuring parity across any runtime.
- TypeScript is ideal for front-end developers, full-stack engineers, and software architects who build large-scale web applications. It serves those looking to improve code excellence, reduce bugs through static checking, and maintain complex projects more.
- Lightweight, Classic fit, Double-needle sleeve and bottom hem
Keep model decisions inside narrow boundaries
Use code to select required steps and enforce policy. Use a model for the parts that benefit from judgment, such as classifying an intake or summarizing research. The SDK orchestration guide describes structured outputs as a way to return data that application code can inspect before choosing the next agent.
A useful boundary has three parts: a specific task, an explicit output contract, and a validator that runs before the result changes workflow state. Treat both model responses and tool results as variable inputs. If validation fails, route to a defined failure or retry path; do not silently coerce an invalid answer into a successful transition.
type ResearchResult = { findings: string[] };
interface AgentRunner {
research(input: { topic: string }): Promise<unknown>;
review(input: { topic: string; findings: string[] }): Promise<unknown>;
}
interface Validators {
research(value: unknown): ResearchResult;
review(value: unknown): { decision: "approved" | "needs_changes"; text: string };
}
async function runResearch(
agents: AgentRunner,
validators: Validators,
state: State,
): Promise<Event> {
const raw = await agents.research({ topic: state.topic });
const result = validators.research(raw);
return { type: "RESEARCH_COMPLETED", findings: result.findings };
}
The validator implementation depends on your chosen schema library or hand-written checks; the example intentionally does not imply a particular one. Keep prompts and tools aligned with each role’s narrow responsibility. Add a specialist when it materially improves capability, policy isolation, prompt clarity, or trace legibility. Creating agents merely to divide every small task can add prompts, traces, and approval surfaces without improving the workflow.
Choose who owns each branch: handoff or agent-as-tool
The key distinction is who remains responsible for the answer after a specialist is involved. In a handoff, control passes to the specialist. When a manager calls a specialist as a tool, the manager retains responsibility for synthesizing and returning the final response. The OpenAI orchestration and handoffs guide frames this as an ownership choice; the patterns can also be combined where a workflow needs both.
| Pattern | Who owns the branch and response? | Useful when |
|---|---|---|
| Handoff | The specialist takes over the interaction or response. | A branch belongs to a specialist with its own task, instructions, or policy boundary. |
| Agent as a tool | The manager remains responsible for synthesis and the final response. | A specialist performs a bounded job, such as summarization or classification, and returns a result to the manager. |
In a code-owned state machine, you can make that ownership even clearer: your router dispatches the specialist for the current state, validates the result, and advances the state itself. If routing is model-led instead, use concrete routing descriptions and guard the transitions in application code so the model cannot bypass required checks.
Choose one continuation model for each conversation
State continuation determines what context is available on the next run and who stores it. The OpenAI guide to running agents documents several approaches. Choose one as the primary conversation strategy unless your application has a deliberate reconciliation design; combining local history with server-managed state without a clear rule can duplicate context.
| Continuation approach | What your application keeps or supplies | Best fit |
|---|---|---|
| Application-managed replay history | Your application stores and replays the relevant history. | Maximum control over the context sent on each step. |
| SDK session | A session backed by storage holds resumable state. | Runs that need session continuity using your chosen storage setup. |
| Conversations API | A conversation ID refers to server-managed conversation state. | Services that need shared server-managed conversation context. |
| Responses API continuation | A previous-response ID links the next response to a prior one. | A lighter response-to-response continuation pattern. |
The table describes the documented approaches, not a claim that one is universally preferable. Application-managed history gives your code the most direct say over replayed context. A session is appropriate when resumable state should live in your storage. Server-managed continuation can reduce the need to carry the full history yourself, but it changes which layer owns that context. Make that ownership explicit in your persistence design.
Persist workflow state separately from conversation context
Conversation history is not necessarily the same thing as the workflow’s business state. A resumable run may also need its current status, validated findings, retry counts, approval state, and enough provenance to explain how it reached its present step. Persist these at a clear checkpoint boundary, together with a run identifier and revision or equivalent concurrency control if multiple workers could update the same run.
Before retrying a step after a crash, consider whether the step may already have performed an external side effect. Use idempotency or a record of completed work where needed, so recovery does not accidentally repeat an action such as sending a message or creating a duplicate record. This is an application design precaution, not a guarantee supplied by an agent SDK.
Independent reader supportYour contribution helps us test, update, and keep practical guides available for everyone.Decide whether the workflow needs durable execution
A basic agent run can iterate over model calls, tool calls, and handoffs until it reaches a stopping point. That can be adequate for bounded work where the process is expected to finish within one execution window and your application handles pauses and failures. The SDK’s running-agents guide also documents pauses and failures as outcomes to handle rather than treating every run as a simple success.
For work that must continue through worker restarts or long waits, evaluate a durable workflow engine. Temporal’s OpenAI Agents SDK integration for TypeScript documents running orchestration in a Workflow and model calls as Activities. Its integration guide says model calls retry durably and are not repeated during workflow replay. That is a specific integration’s documented behavior, not a blanket property of every agent runtime or every external side effect.
- Use an in-process loop when runs are short-lived and your application can own their continuation and failure handling.
- Consider durable workflow execution when a run must outlive a worker, wait for approval, or recover across process restarts.
- Keep human approval as an explicit paused state with a defined resume event, rather than treating a pause as completion.
- Verify the durability semantics of the model-call and tool-call activities you actually use; durable orchestration alone does not make arbitrary side effects safe to repeat.
Make transitions inspectable and recoverable
A useful trace should let an operator explain what happened without reconstructing it from a final answer. Record the run and state identifiers, transition event, input and validated output references, agent or tool invoked, retry count, timestamps, validation result, approval pause or resolution, and terminal reason. Apply your privacy and retention rules to prompts, outputs, and tool data; observability should not mean retaining sensitive content without a reason.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Best Value
Build evaluations around the behavior your code promises, not only the quality of a generated answer. Test legal and illegal transitions directly, and exercise malformed outputs, retry exhaustion, repeated review loops, approval pauses, tool failures, and recovery from saved checkpoints. The Agents SDK orchestration guide recommends monitoring, iteration, and investing in evaluations; the application-specific route expectations still need to come from your own workflow contract.
Choose a framework after clarifying the requirements
Framework choice should follow the control, persistence, customization, latency, and operational requirements of the workflow. LangGraph’s reference documentation describes LangGraph as a low-level orchestration framework for long-running, stateful agents and points JavaScript and TypeScript users to LangGraph.js. That reference page has redirected, so confirm the current JavaScript documentation and implementation details before adopting a specific API.
The available documentation does not establish an across-framework performance winner. Compare the actual workflow constraints instead of treating framework feature descriptions as benchmark results.
Quick Recap
- Control ownership: Should code route every step, should a model choose some branches, or do you need a mixture?
- Branch ownership: Does the specialist take over, or does a manager synthesize the specialist’s result?
- State: Will your application replay history, use an SDK session, or continue through server-managed identifiers?
- Recovery: Is in-process continuation enough, or must execution survive worker restarts?
- Operational complexity: How many distinct prompts, traces, approval points, and state boundaries will the design introduce?
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.




