Patterns

Task Trees

Build tree-structured multi-agent workflows using compilePlan() to coordinate sequential and parallel execution across agent roles.

Overview

Task Trees let you define a hierarchical plan where each node is assigned to an agent role and specifies whether its children run sequentially or in parallel. compilePlan() compiles this tree into a single Step that the runtime executes.

PlanNode

interface PlanNode {
  id: string;
  description: string;
  assignee: string;
  execution: 'sequential' | 'parallel';
  children?: PlanNode[];
}
FieldTypePurpose
idstringUnique node identifier
descriptionstringTask description passed to the assigned agent
assigneestringKey into the agents registry
execution'sequential' | 'parallel'How children are executed
childrenPlanNode[]Sub-tasks (optional -- leaf nodes are executed directly)

compilePlan()

function compilePlan<O>(
  plan: PlanNode,
  agents: Record<string, (prompt: string) => Step<string, unknown>>,
  constraints?: PlanConstraints,
  executeStep?: ExecuteStepFn,
): Step<string, O>
ParameterTypePurpose
planPlanNodeThe task tree to compile
agentsRecord<string, (prompt: string) => Step>Factory functions keyed by agent role
constraintsPlanConstraintsOptional execution constraints
executeStepExecuteStepFnOptional custom step executor

PlanConstraints

interface PlanConstraints {
  toolAllowlist?: Record<string, string[]>;
  maxStepsPerNode?: number;
  requireApproval?: string[];
  validate?: (taskId: string, input: unknown, ctx: Context) => Promise<boolean>;
}

Example

import { compilePlan, react } from '@noetic/core';

const agents = {
  researcher: (prompt: string) => react({
    model: 'gpt-4o',
    system: prompt,
    tools: [searchTool],
  }),
  writer: (prompt: string) => react({
    model: 'gpt-4o',
    system: prompt,
    tools: [writeTool],
  }),
  reviewer: (prompt: string) => react({
    model: 'gpt-4o',
    system: prompt,
    tools: [],
  }),
};

const plan: PlanNode = {
  id: 'write-article',
  description: 'Write a technical article',
  assignee: 'writer',
  execution: 'sequential',
  children: [
    {
      id: 'research',
      description: 'Research the topic',
      assignee: 'researcher',
      execution: 'parallel',
      children: [
        { id: 'search-papers', description: 'Find academic papers', assignee: 'researcher', execution: 'sequential' },
        { id: 'search-blogs', description: 'Find blog posts', assignee: 'researcher', execution: 'sequential' },
      ],
    },
    { id: 'draft', description: 'Write the first draft', assignee: 'writer', execution: 'sequential' },
    { id: 'review', description: 'Review and improve', assignee: 'reviewer', execution: 'sequential' },
  ],
};

const step = compilePlan(plan, agents);

How It Works

  1. Leaf nodes call the agent factory with the node's description and return the resulting step.
  2. Parallel nodes compile children into a fork({ mode: 'all' }) that runs them concurrently.
  3. Sequential nodes compile children into a run step that chains execution, passing each output as the next input.

The PlanNodeSchema Zod schema is also exported, so you can have an LLM generate plans and validate them at runtime:

import { PlanNodeSchema } from '@noetic/core';

const plan = PlanNodeSchema.parse(llmOutput);

Next Steps

  • Adaptive Plans -- dynamic plans that revise themselves on failure
  • ReAct -- the agent pattern used inside each tree node

On this page