OrchestratorAgent¶
Multi-step task planner and executor. Uses a Gemini model to decompose a user request into a structured plan of steps, then executes each step by delegating to atomic agents (DocumentAnalysisAgent, QAChatAgent) or registered tools. Supports dependency ordering, parallel execution, retries, and automatic result aggregation.
Import¶
import { OrchestratorAgent } from "@modernpath/agent-framework";
import type { OrchestratorAgentConfig } from "@modernpath/agent-framework";
Constructor¶
class OrchestratorAgent extends BaseAgent {
constructor(
toolRegistry: ToolRegistry,
gemini: GeminiClient,
prompts: PromptTemplate,
atomicAgents: {
documentAnalysis: BaseAgent;
qaChat: BaseAgent;
},
cfg?: OrchestratorAgentConfig,
)
}
| Parameter | Type | Required | Description |
|---|---|---|---|
toolRegistry | ToolRegistry | Yes | Tool registry. When allowToolSteps is enabled, registered tools can be invoked as plan steps. |
gemini | GeminiClient | Yes | Gemini model client for plan generation. |
prompts | PromptTemplate | Yes | Prompt template engine. Expects orchestrator.planning.* templates. |
atomicAgents | object | Yes | The atomic agents available for plan steps. |
atomicAgents.documentAnalysis | BaseAgent | Yes | Agent for document operations (typically DocumentAnalysisAgent). |
atomicAgents.qaChat | BaseAgent | Yes | Agent for knowledge-base Q&A (typically QAChatAgent). |
cfg | OrchestratorAgentConfig | No | Optional configuration. |
Configuration¶
OrchestratorAgentConfig¶
interface OrchestratorAgentConfig {
parallel?: boolean;
maxRetries?: number;
defaultSiteId?: string;
allowToolSteps?: boolean;
exposeToolsToPlanner?: boolean;
toolCatalogMode?: "externalOnly" | "all";
}
| Name | Type | Required | Default | Description |
|---|---|---|---|---|
parallel | boolean | No | false | If true, executes dependency-free steps in parallel using Promise.allSettled. Disable for safety in serverless environments with rate limits. |
maxRetries | number | No | 1 | Maximum number of retries for a retryable step after the first failure (so a step runs at most maxRetries + 1 times). |
defaultSiteId | string | No | undefined | Default SharePoint site ID for documentAnalysis steps when the planner omits it. Also tries context.parameters.siteId, context.parameters.sharepoint.siteId, and context.metadata.siteId. |
allowToolSteps | boolean | No | true | When true, plan steps can invoke ToolRegistry tools directly (in addition to atomic agents). The step's agent field is set to the tool name. |
exposeToolsToPlanner | boolean | No | false | When true, includes registered tool names and parameter schemas in the planning prompt so the LLM can generate tool steps. |
toolCatalogMode | "externalOnly" \| "all" | No | "externalOnly" | Controls which tools appear in the planner's tool catalog. "externalOnly" shows only tools with category "external" or tags "external" / "client". "all" shows every registered tool. |
Return Value¶
On success, AgentResult.data contains:
TaskPlan¶
interface TaskPlan {
taskType: TaskType;
steps: PlanStep[];
estimatedTime: string;
requiredDocuments: string[];
metadata?: Record<string, any>;
}
| Field | Type | Description |
|---|---|---|
taskType | TaskType | Category of the task. One of: reconciliation, substantive_testing, analytical_procedures, checklist, report_generation, company_info. |
steps | PlanStep[] | Ordered list of steps to execute. |
estimatedTime | string | Human-readable time estimate (e.g. "5-10 minutes"). |
requiredDocuments | string[] | Document names the plan expects to be available. |
PlanStep¶
interface PlanStep {
stepNumber: number;
description: string;
agent: AgentName;
inputs: Record<string, any>;
expectedOutput: string;
dependencies: number[];
timeout?: number;
retryable?: boolean;
critical?: boolean;
}
| Field | Type | Description |
|---|---|---|
stepNumber | number | Unique step identifier within the plan. |
description | string | Human-readable description of what this step does. |
agent | string | Name of the agent or tool to execute. For atomic agents: "documentAnalysis" or "qaChat". For tools: the tool's registered name. |
inputs | Record<string, any> | Parameters passed to the agent or tool. For agents, these become context.parameters. |
expectedOutput | string | Description of the expected result. |
dependencies | number[] | Step numbers that must complete successfully before this step runs. |
timeout | number | Optional step-level timeout in milliseconds. |
retryable | boolean | Whether the step can be retried on failure. Default: true. |
critical | boolean | If true and the step fails, the entire plan execution is aborted with an error. Default: false. |
StepResult¶
interface StepResult {
stepNumber: number;
success: boolean;
data?: any;
error?: string;
executionTime: number;
agent: string;
timestamp: Date;
}
AggregatedResult¶
interface AggregatedResult {
summary: string;
data: {
resultsByStep: Record<string, StepResult>;
};
executionSummary: {
totalSteps: number;
successfulSteps: number;
failedSteps: number;
totalExecutionTime: number;
};
}
Execution Flow¶
sequenceDiagram
participant Caller
participant Orchestrator
participant Gemini
participant AtomicAgent
participant ToolRegistry
Caller->>Orchestrator: execute(context)
Orchestrator->>Gemini: Generate task plan (JSON)
Gemini-->>Orchestrator: TaskPlan
Orchestrator->>Orchestrator: Validate & normalize plan
loop For each ready step
alt Atomic agent step
Orchestrator->>AtomicAgent: execute(stepContext)
AtomicAgent-->>Orchestrator: AgentResult
else Tool step
Orchestrator->>ToolRegistry: execute(toolName, params)
ToolRegistry-->>Orchestrator: Tool result
end
end
Orchestrator->>Orchestrator: Aggregate results
Orchestrator-->>Caller: { plan, results, aggregated } Planning¶
How the Plan is Generated¶
- The orchestrator renders the planning prompt using
orchestrator.planning.systemandorchestrator.planning.user_templatetemplates - Available agents and (optionally) tool names are included in the prompt
- Gemini generates a JSON plan at temperature 0.2
- The plan is parsed, validated, and normalized:
- Agent names are normalized (e.g.,
"qa-chat"becomes"qaChat") - Missing fields get defaults (
stepNumber,dependencies,retryable) - Tool parameters are validated against their metadata schemas
- Agent names are normalized (e.g.,
- If parsing/validation fails, a second attempt is made with the error message included
- If both attempts fail, a fallback plan with a single
documentAnalysisstep is used
Plan Validation¶
The orchestrator validates:
- The plan has a
stepsarray - Each step has a valid
agent(matching an atomic agent or registered tool) - Step numbers are unique
- Tool step inputs pass parameter validation
Agent name normalization
The planner may produce agent names in different formats ("qa_chat", "QAChatAgent", "document-analysis"). The orchestrator normalizes these to the canonical names "documentAnalysis" and "qaChat".
Fallback Plan¶
If the LLM fails to produce a valid plan after two attempts, the orchestrator creates a minimal fallback plan:
{
"taskType": "analytical_procedures",
"steps": [{
"stepNumber": 1,
"description": "Analyze documents based on: <user prompt>",
"agent": "documentAnalysis",
"inputs": { "prompt": "<user prompt>" },
"dependencies": [],
"retryable": true,
"critical": false
}],
"estimatedTime": "5-10 minutes",
"requiredDocuments": []
}
Step Execution¶
Dependency Resolution¶
Steps are executed in dependency order. A step runs only when all steps in its dependencies array have completed successfully. If a dependency fails, the dependent step is skipped (unless critical causes an abort).
Parallel Execution¶
When parallel: true, all ready steps (those with satisfied dependencies) in the same "wave" run concurrently via Promise.allSettled. This can reduce total execution time but may increase API rate limit pressure.
Retry Logic¶
If a step fails and retryable is true, the orchestrator retries it up to maxRetries times. If all retries fail:
- If
critical: true, the entire plan execution throws an error - If
critical: false, the step is marked as failed and execution continues
Document ID Auto-resolution¶
For documentAnalysis steps that require a documentId but do not have one in their inputs, the orchestrator automatically:
- Resolves the
siteIdfrom step inputs, context parameters, ordefaultSiteId - Calls the
documentAnalysisagent withaction: "select"to pick the most relevant documents - Injects the selected document IDs into the step inputs
Tool Step Execution¶
When a step's agent matches a registered tool (not an atomic agent), the orchestrator:
- Injects
userIdandauditingIdfrom the context - Passes step inputs as tool parameters
- Passes orchestrator metadata via the
__metadatakey (for dynamic tools)
Prompt Templates¶
| Template Key | Required | Description |
|---|---|---|
orchestrator.planning.system | No | System prompt for plan generation. |
orchestrator.planning.user_template | No | User prompt template. Variables: task (the user prompt), agents (available agent/tool names). Falls back to a built-in prompt if not defined. |
Code Example¶
Basic Usage¶
import {
OrchestratorAgent,
DocumentAnalysisAgent,
QAChatAgent,
ToolRegistry,
} from "@modernpath/agent-framework";
const registry = new ToolRegistry();
// Register tools...
const docAgent = new DocumentAnalysisAgent(registry, gemini, prompts);
const qaAgent = new QAChatAgent(registry, retrieval, gemini, prompts);
const orchestrator = new OrchestratorAgent(
registry,
gemini,
prompts,
{ documentAnalysis: docAgent, qaChat: qaAgent },
{ defaultSiteId: "site-abc-123" },
);
const result = await orchestrator.execute({
userId: 42,
auditingId: 1001,
prompt: "Analyze the Q3 financial report and answer: what were the main cost drivers?",
parameters: {},
});
if (result.success) {
const { plan, results, aggregated } = result.data;
console.log(`Plan type: ${plan.taskType}`);
console.log(`Steps: ${plan.steps.length}`);
console.log(`Summary: ${aggregated.summary}`);
console.log(`Success rate: ${aggregated.executionSummary.successfulSteps}/${aggregated.executionSummary.totalSteps}`);
// Access individual step results
for (const step of results) {
console.log(`Step ${step.stepNumber} (${step.agent}): ${step.success ? "OK" : step.error}`);
}
}
With Tool Steps¶
import { registerDynamicTool } from "@modernpath/agent-framework";
// Register an external tool
registerDynamicTool(registry, {
name: "send-report",
description: "Sends a formatted report via email",
category: "external",
tags: ["external", "email"],
parameters: {
recipient: { type: "string", description: "Email address" },
subject: { type: "string", description: "Email subject" },
body: { type: "string", description: "Email body (HTML)" },
},
execute: async (params, ctx) => {
await emailService.send(params);
return { sent: true };
},
});
const orchestrator = new OrchestratorAgent(
registry,
gemini,
prompts,
{ documentAnalysis: docAgent, qaChat: qaAgent },
{
defaultSiteId: "site-abc-123",
exposeToolsToPlanner: true, // Let the LLM see available tools
toolCatalogMode: "externalOnly", // Only external tools
parallel: false, // Sequential for safety
maxRetries: 2, // Retry failed steps twice
},
);
// The orchestrator can now generate plans that include "send-report" steps
const result = await orchestrator.execute({
userId: 42,
auditingId: 1001,
prompt: "Analyze Q3 report and email the summary to finance@example.com",
parameters: {},
});
Parallel Execution¶
const orchestrator = new OrchestratorAgent(
registry,
gemini,
prompts,
{ documentAnalysis: docAgent, qaChat: qaAgent },
{ parallel: true },
);
// Steps with no dependencies on each other will run concurrently
Related Pages¶
- BaseAgent -- base class and execution lifecycle
- QAChatAgent -- the RAG Q&A atomic agent
- DocumentAnalysisAgent -- the document analysis atomic agent
- Dynamic Tools -- registering tools for orchestrator steps
- ToolRegistry -- the tool registry that the orchestrator queries
- Prompt Templates -- configuring planning prompts