Advanced Orchestration Patterns
Master the orchestrator-workers pattern, evaluator-optimizer loops, and autonomous agent architectures for complex AI tasks.
What you'll learn in this module
- How the orchestrator-workers pattern delegates and synthesizes subtasks
- How evaluator-optimizer loops iteratively improve outputs
- How autonomous agents work and when they're appropriate
- Trade-offs between each advanced pattern
Pattern 4: Orchestrator-Workers
An "orchestrator" LLM dynamically breaks a task into subtasks, delegates each to a "worker" LLM (or tool), and then synthesizes the results.
How it differs from parallelization
| Aspect | Parallelization | Orchestrator-Workers |
|---|---|---|
| Subtasks | Predefined by developer | Dynamically determined by LLM |
| Number of subtasks | Fixed | Variable per input |
| Worker specialization | All identical or predefined | Orchestrator chooses per subtask |
When to use it
- The task can't be decomposed in advance — the subtasks depend on the input
- Different parts of the task require different approaches
- You need the flexibility of agents with more structure than a free-form loop
Example: Codebase Refactoring Agent
The orchestrator prompt
The orchestrator needs to output a structured plan:
You are a task planner. Given a complex request, break it into
independent subtasks that can be executed separately.
For each subtask, provide:
- task_id: a unique identifier
- description: what needs to be done
- dependencies: list of task_ids this depends on (can be empty)
- tools_needed: which tools the worker should use
Output as JSON array. Do not execute the tasks, only plan them.Worker execution
Each worker receives:
- The subtask description from the orchestrator
- Access to the specific tools it needs
- Results from any dependency subtasks
The orchestrator then collects all worker outputs and makes a final synthesis call.
Orchestrator mapping: This pattern maps to a workflow where the first AI action dynamically generates a plan, followed by a loop or fan-out of action nodes that execute each subtask. The Orchestrator's template variable system passes the plan and results between steps.
Pattern 5: Evaluator-Optimizer
A generator LLM produces output, then an evaluator LLM reviews it and provides feedback. The loop continues until the evaluator passes the output or a maximum iteration count is reached.
Two-LLM design
| Role | Responsibility | Prompt style |
|---|---|---|
| Generator | Produce the best possible output given the task + feedback | Creative, detailed, follow instructions carefully |
| Evaluator | Assess output against criteria, provide actionable feedback | Critical, specific, reference concrete issues |
When to use it
- Output quality matters more than latency
- There are clear, articulable evaluation criteria
- Human review is too slow or expensive for every output
- The task benefits from iteration (writing, code, design)
Example: Code Review Loop
import OpenAI from "openai";
const client = new OpenAI({
apiKey: process.env.SKYTELLS_API_KEY,
baseURL: "https://api.skytells.ai/v1",
});
async function codeReviewLoop(spec: string, maxIterations = 3) {
let code = "";
let feedback = "No prior attempt.";
for (let i = 0; i < maxIterations; i++) {
// Generate
const generated = await client.chat.completions.create({
model: "deepbrain-router",
messages: [
{
role: "user",
content: `Write code that meets this specification:
${spec}
Previous feedback to address:
${feedback}
Return only the code, no explanation.`,
},
],
});
code = generated.choices[0].message.content!;
// Evaluate
const evaluation = await client.chat.completions.create({
model: "deepbrain-router",
messages: [
{
role: "user",
content: `Review this code against the specification.
Specification: ${spec}
Code:
${code}
If the code fully meets the spec, respond with exactly "APPROVED".
Otherwise, list specific issues to fix. Be concrete — reference line numbers and suggest corrections.`,
},
],
});
const evalText = evaluation.choices[0].message.content!;
if (evalText.trim() === "APPROVED") {
return { code, iterations: i + 1 };
}
feedback = evalText;
}
return { code, iterations: maxIterations, warning: "Max iterations reached" };
}Designing effective evaluators
The evaluator is often more important than the generator. A good evaluator:
- Has explicit criteria — don't ask "is this good?" Ask "does this meet criteria X, Y, Z?"
- Provides actionable feedback — "The function on line 12 doesn't handle the null case" > "Needs improvement"
- Can approve — must have a clear "done" signal to exit the loop
- Has iteration limits — always cap the loop to prevent runaway costs
Orchestrator mapping: The evaluator-optimizer maps to a workflow with a loop structure. In Orchestrator, you can model this with a Condition node that checks the evaluator output — if not approved, route back to the generator action, passing the feedback via template variables. Set a counter variable as a safety limit.
Pattern 6: Autonomous Agents
The most flexible and the most complex pattern. The LLM operates in a loop, choosing which tools to call, processing results, and deciding when the task is complete.
The agent loop
Every autonomous agent follows this core loop:
- Think — assess the current state and decide the next action
- Act — execute the chosen tool or API call
- Observe — process the result
- Repeat until done or stuck
This is sometimes called the ReAct (Reasoning + Acting) pattern.
When to use autonomous agents
| Good fit | Poor fit |
|---|---|
| Research tasks with unknown scope | Tasks with clear, fixed steps |
| Exploratory data analysis | High-stakes financial transactions |
| Complex debugging | Simple CRUD operations |
| Multi-tool tasks where sequence can't be predetermined | Tasks requiring audit trails |
The critical guardrails
Autonomous agents need constraints to prevent runaway behavior:
| Guardrail | Implementation |
|---|---|
| Iteration limit | Cap the think-act-observe loop (e.g., 15 iterations max) |
| Tool restrictions | Only expose tools the agent needs; don't give write access if read-only suffices |
| Cost budget | Track token usage; terminate if exceeding budget |
| Timeout | Hard wall-clock limit (e.g., 5 minutes) |
| Scope boundaries | Instruct the agent what it should NOT do |
| Human escalation | If confidence drops below threshold, escalate to human |
Example: Research Agent
import json
from openai import OpenAI
client = OpenAI(
api_key="YOUR_SKYTELLS_API_KEY",
base_url="https://api.skytells.ai/v1",
)
TOOLS = {
"web_search": web_search_fn,
"read_page": read_page_fn,
"take_notes": take_notes_fn,
"final_answer": final_answer_fn,
}
def research_agent(question: str, max_iterations: int = 10):
notes = []
history = []
for i in range(max_iterations):
response = client.chat.completions.create(
model="deepbrain-router",
messages=[
{
"role": "system",
"content": "You are a research agent. Use the available tools to answer questions.",
},
{
"role": "user",
"content": f"""Answer this question: {question}
Your notes so far: {json.dumps(notes)}
Your action history: {json.dumps(history[-5:])}
Available tools: web_search(query), read_page(url), take_notes(text), final_answer(answer)
Respond with a JSON object: {{"tool": "tool_name", "args": {{...}}}}
If you have enough information, use final_answer.""",
},
],
)
action = json.loads(response.choices[0].message.content)
tool_name = action["tool"]
if tool_name == "final_answer":
return action["args"]["answer"]
result = TOOLS[tool_name](**action["args"])
history.append({"tool": tool_name, "result": str(result)[:500]})
if tool_name == "take_notes":
notes.append(action["args"]["text"])
return f"Reached max iterations. Current notes: {notes}"Autonomous agents are powerful but expensive and unpredictable. In production, always combine them with the guardrails above. Monitor executions closely, especially in the first weeks after deployment.
Comparing the Advanced Patterns
| Pattern | LLM Control | Predictability | Cost | Best for |
|---|---|---|---|---|
| Orchestrator-Workers | Plans subtasks | Medium — plan varies, execution is structured | Medium | Complex tasks needing dynamic decomposition |
| Evaluator-Optimizer | Reviews and iterates | Medium — loop count varies | Medium-High | Quality-critical outputs |
| Autonomous Agent | Full control | Low — path is entirely dynamic | High | Open-ended research and exploration |
Decision tree
What you now understand
| Pattern | Architecture | Use when |
|---|---|---|
| Orchestrator-Workers | LLM planner + dynamic workers | Task decomposition depends on input |
| Evaluator-Optimizer | Generator + evaluator in a loop | Output quality is paramount |
| Autonomous Agent | Full LLM-controlled loop with tools | Task is open-ended and exploratory |
| Guardrails | Iteration limits, cost budgets, scope boundaries | Always — for any agent-like system |
Up next: Tool Use & Function Calling — design tools that LLMs can use reliably and build robust function-calling interfaces.
Workflow Patterns
Master the three foundational workflow patterns — prompt chaining, routing, and parallelization — that form the building blocks of every AI system.
Tool Use & Function Calling
Design effective tools for AI agents, implement function calling with structured outputs, and connect agents to external APIs safely.