AGENTUPDATE JOURNAL

Claude Code Dynamic Workflow Guide: Multi-Agent Orchestration with JavaScript

Claude Code Dynamic Workflow Guide: Multi-Agent Orchestration with JavaScript
Table of Contents

A comprehensive tutorial guide for beginners, summarized from real-world practices.


Table of Contents


1. What is Dynamic Workflow

Dynamic Workflow is a multi-agent orchestration engine introduced in Claude Code v2.1.154+. You write a JavaScript script, and it automatically spawns multiple sub-agents according to your logic, executing tasks in parallel or sequentially, and ultimately aggregating the results.

In short: Use code to control multiple AI agents to collaborate on tasks.

Applicable Scenarios

Scenario Example
Batch Processing Solving 8 math problems in parallel
Pipeline Solve → Verify → Score
Large-scale Review Reviewing 100 files in parallel
Multi-perspective Analysis Evaluating the same issue separately from security, performance, and readability perspectives

2. Quick Overview of Core Concepts

Concept Description
meta Script header declaration, defining name, description, and phases
agent() Spawns a sub-agent and returns the result
pipeline() Sequential pipeline: each data item passes through multiple stages in order
parallel() Parallel execution: multiple tasks run simultaneously
phase() Declares the current phase (used for UI progress display)
log() Outputs logs to the user interface
args Parameters passed from the outside
budget Token budget control for the current turn

Execution Model

mermaid graph TD A[User triggers Workflow] --> B[Parse meta] B --> C{Orchestration logic} C -->|Sequential| D[pipeline: item → stage1 → stage2 → stage3] C -->|Parallel| E[parallel: task1 | task2 | task3 run simultaneously] C -->|Mixed| F[parallel + pipeline nested] D --> G[Aggregate and return results] E --> G F --> G


3. Directory Structure

.claude/
├── workflows/              # Directory for Workflow scripts
│   └── math-problems.js    # One workflow = One .js file
├── agents/                 # Custom agent definitions (⚠️ cannot be referenced by workflows)
│   ├── math-solver.md      # Only used for automatic routing in the main session
│   └── math-verifier.md
└── settings.json           # Project configuration

Workflow File Structure

Fixed format for each .js file:

export const meta = {
  name: 'my-workflow',           // Required: Unique identifier
  description: 'Description of purpose', // Required: Description of purpose
  phases: [                      // Optional: Define phases (for UI display)
    { title: 'Analyze' },
    { title: 'Execute' },
  ],
}

// ... Your orchestration logic
// Orchestrate using agent() / pipeline() / parallel()
// Finally, return the result

4. First Workflow: Hello World

Minimal Version

export const meta = {
  name: 'hello',
  description: 'The simplest workflow',
  phases: [{ title: 'Greet' }],
}

const result = await agent('Say hello world', { phase: 'Greet' })
return result

Parameterized Version

export const meta = {
  name: 'hello-name',
  description: 'Greeting',
}

// args are passed from the outside
const name = (args && args.name) || 'World'
const result = await agent(`Say hello to ${name} and introduce yourself`)
return result

⚠️ IMPORTANT: When referencing a workflow by name, args might not be passed! It is recommended to always provide default values or use inline script calls.


5. Built-in API Details

5.1 agent() — Spawn a Sub-agent

// Basic usage
const result = await agent('Your prompt')

// Full parameters
const result = await agent('Your prompt', {
  label: 'Display Name',      // Label displayed in the UI
  phase: 'Solve',             // Assigned phase
  schema: {                   // Enforce structured output (JSON Schema)
    type: 'object',
    properties: {
      answer: { type: 'string' },
      confidence: { type: 'number' }
    }
  },
  model: 'sonnet',            // Optional model override
  agentType: 'Explore',       // ⚠️ Only supports built-in types, custom types are not supported
})

5.2 pipeline() — Sequential Pipeline

Data passes through each stage sequentially; the output of the previous stage becomes the input for the next stage:

graph LR
    A[Data item] -->|stage1| B[Intermediate result 1]
    B -->|stage2| C[Intermediate result 2]
    C -->|stage3| D[Final result]

    style A fill:#e1f5fe
    style D fill:#c8e6c9
const results = await pipeline(
  ['Problem 1', 'Problem 2', 'Problem 3'], // Data array
  (item) => agent(`Process: ${item}`),     // stage 1
  (prev, item) => agent(`Review: ${prev}`), // stage 2
)

// results[0] = Result of stage2(Problem 1)
// results[1] = Result of stage2(Problem 2)
// Each data item completes the entire pipeline independently; different items run in parallel

Key Feature: Inside a pipeline, each data item flows independently. Item A might be at stage 3 while item B is still at stage 1. Total time = the slowest single-item chain, not the sum of all stages.

5.3 parallel() — Parallel Barrier

All tasks start simultaneously and return only when all are completed:

graph TD
    A[parallel starts] --> B[Task 1]
    A --> C[Task 2]
    A --> D[Task 3]
    B --> E[Aggregate results]
    C --> E
    D --> E

    style A fill:#fff9c4
    style E fill:#c8e6c9
const results = await parallel([
  () => agent('Analyze frontend code'),
  () => agent('Analyze backend code'),
  () => agent('Analyze database'),
])

// results = [Frontend analysis, Backend analysis, Database analysis]
// Returns null if any fails; filter using .filter(Boolean)

5.4 phase() and log()

phase('Review')          // Switch to the Review phase (UI progress bar grouping)
log('Processed 3/10 files')  // Output progress information

6. Practice: Math Problem Pipeline

Design Principle: Write problems directly into the prompt. Each group of problems is treated as a whole and passed to the agent via a prompt template, allowing beginners to see the grouping structure and data flow at a glance.

Architecture Diagram

graph TD
    subgraph "Problem Grouping (written directly in prompt)"
        G1["Group 1 (Basic Math)
12×15, √144+3², 2^10, ..."] G2["Group 2 (Advanced Math)
Trailing zeros of 100!, sin30°×cos60°, ..."] end subgraph "Pipeline (processed as a whole per group)" S1["🧮 Solve
Entire group of problems in prompt"] V1["✅ Verify
Problems + solutions in prompt"] SC1["📊 Score
Problems + verification results in prompt"] S1 --> V1 --> SC1 end G1 -->|"prompt contains Group 1 problems"| S1 G2 -->|"prompt contains Group 2 problems"| S1 SC1 --> R["Aggregate results by group"] style G1 fill:#e3f2fd style G2 fill:#fce4ec style S1 fill:#bbdefb style V1 fill:#c8e6c9 style SC1 fill:#fff9c4 style R fill:#f8bbd0

Data Flow Details

Data flow per group (using Group 1 as an example):

┌─────────────────────────────────────────────────┐
│ Stage 1: Solver                                 │
│ prompt = SOLVER_PROMPT + all Group 1 problems   │
│ output = detailed solutions for 5 problems      │
└──────────────────────┬──────────────────────────┘
                       │ solution (output from previous stage)
┌──────────────────────▼──────────────────────────┐
│ Stage 2: Verifier                               │
│ prompt = VERIFIER_PROMPT + Group 1 problems + solution │
│ output = step-by-step verification results (✅/❌) │
└──────────────────────┬──────────────────────────┘
                       │ verification (output from previous stage)
┌──────────────────────▼──────────────────────────┐
│ Stage 3: Scorer                                 │
│ prompt = SCORER_PROMPT + Group 1 problems + verification │
│ output = score and comments for each problem    │
└─────────────────────────────────────────────────┘

Actual Prompt Examples (After Template Replacement)

Below is the complete prompt actually sent to the agent after executing .replace('{problems_block}', ...).

Stage 1 — Prompt received by Solver (Group 1)

You are a math problem-solving expert. Please solve the following math problems one by one.

1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?

Output format (per problem):
- Problem: {Original problem}
- Solution process: {Step-by-step derivation}
- Answer: {Final result}

Stage 1 — Prompt received by Solver (Group 2)

You are a math problem-solving expert. Please solve the following math problems one by one.

6. How many trailing zeros are in 100!?
7. sin30° × cos60° = ?
8. log₂1024 = ?
9. The circumference of a circle is 31.4cm, find its area (π≈3.14)
10. Solve the equation: 2x + 5 = 17

Output format (per problem):
- Problem: {Original problem}
- Solution process: {Step-by-step derivation}
- Answer: {Final result}

Stage 2 — Prompt received by Verifier (Group 1, with solutions)

You are a math verification expert. Independently re-solve the problems and compare them with the solutions below.

1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?

=== Solutions to Verify ===
[Solver's complete output will be inserted here]

=== Requirements ===
- Calculate independently; do not trust the provided solutions.
- Compare step-by-step and mark as correct or incorrect.
- If there is an error, pinpoint its exact location.

Output format:
- Judgment: ✅ Correct / ❌ Incorrect
- Verification process: {Independent derivation}
- Reason for error: {If any}

Stage 3 — Prompt received by Scorer (Group 1, with verification results)

You are a math scoring expert. Score based on the solutions and verification results.

1. 12 × 15 = ?
2. √144 + 3² = ?
3. 2^10 = ?
4. 1 + 2 + 3 + ... + 100 = ?
5. 1/2 + 1/3 + 1/6 = ?

=== Verification Results ===
[Verifier's complete output will be inserted here]

Scoring: 10=Perfect | 7-9=Correct but improvable | 4-6=Partially correct | 0-3=Incorrect

Output format:
- Score: {0-10}
- Comment: {One sentence}

Key Point: {problems_block} is replaced by the same group of problems at each stage, while {solution} / {verification} are replaced by the output of the previous stage. Each agent can see the full context and does not need to guess what the problems are.

Complete Code

export const meta = {
  name: 'math-problems',
  description: 'Math problem workflow — problems written into prompt by group, 3-stage pipeline (Solve→Verify→Score)',
  phases: [
    { title: 'Solve', detail: 'Solve problems in parallel per group' },
    { title: 'Verify', detail: 'Independently verify solutions per group' },
    { title: 'Score', detail: 'Comprehensive scoring' },
  ],
}

// ─── Prompt Templates (Defined at the top for clarity) ──────────────
// {problems_block}  → All problems in the group
// {solution}        → Solver's output
// {verification}    → Verifier's output

const SOLVER_PROMPT = `You are a math problem-solving expert. Please solve the following math problems one by one.

{problems_block}

Output format (per problem):
- Problem: {Original problem}
- Solution process: {Step-by-step derivation}
- Answer: {Final result}`

const VERIFIER_PROMPT = `You are a math verification expert. Independently re-solve the problems and compare them with the solutions below.

{problems_block}

=== Solutions to Verify ===
{solution}

=== Requirements ===
- Calculate independently; do not trust the provided solutions.
- Compare step-by-step and mark as correct or incorrect.
- If there is an error, pinpoint its exact location.

Output format:
- Judgment: ✅ Correct / ❌ Incorrect
- Verification process: {Independent derivation}
- Reason for error: {If any}`

const SCORER_PROMPT = `You are a math scoring expert. Score based on the solutions and verification results.

{problems_block}

=== Verification Results ===
{verification}

Scoring: 10=Perfect | 7-9=Correct but improvable | 4-6=Partially correct | 0-3=Incorrect

Output format:
- Score: {0-10}
- Comment: {One sentence}`

// ─── Problem Grouping (Written directly here, easy for beginners to modify) ──────────
const DEFAULT_GROUPS = [
  {
    name: 'Group 1 (Basic Math)',
    problems: [
      '1. 12 × 15 = ?',
      '2. √144 + 3² = ?',
      '3. 2^10 = ?',
      '4. 1 + 2 + 3 + ... + 100 = ?',
      '5. 1/2 + 1/3 + 1/6 = ?',
    ],
  },
  {
    name: 'Group 2 (Advanced Math)',
    problems: [
      '6. How many trailing zeros are in 100!?',
      '7. sin30° × cos60° = ?',
      '8. log₂1024 = ?',
      '9. The circumference of a circle is 31.4cm, find its area (π≈3.14)',
      '10. Solve the equation: 2x + 5 = 17',
    ],
  },
]

// ─── Main Flow ─────────────────────────────────────────
const groups = (args && args.groups && args.groups.length)
  ? args.groups
  : DEFAULT_GROUPS

log(`Total ${groups.reduce((n, g) => n + g.problems.length, 0)} problems, ${groups.length} groups`)
groups.forEach((g) => log(`  ${g.name}: ${g.problems.join(', ')}`))

// Run pipeline in parallel for each group, problems written directly in prompt
const results = await parallel(
  groups.map((group) => () => {
    const problemsBlock = group.problems.join('\n')
    return pipeline(
      [group],  // The entire group as a single unit
      // Stage 1: Solve
      (grp) => agent(
        SOLVER_PROMPT.replace('{problems_block}', grp.problems.join('\n')),
        { label: `${grp.name}-Solve`, phase: 'Solve' }
      ),
      // Stage 2: Verify
      (solution, grp) => agent(
        VERIFIER_PROMPT
          .replace('{problems_block}', grp.problems.join('\n'))
          .replace('{solution}', solution),
        { label: `${grp.name}-Verify`, phase: 'Verify' }
      ),
      // Stage 3: Score
      (verification, grp) => agent(
        SCORER_PROMPT
          .replace('{problems_block}', grp.problems.join('\n'))
          .replace('{verification}', verification),
        { label: `${grp.name}-Score`, phase: 'Score' }
      ),
    )
  })
)

// Return by group
const output = {}
groups.forEach((g, i) => output[g.name] = results[i][0])
return output

Invocation Methods

Just say "run workflow math-problems" in Claude Code. Claude will call the Workflow tool to execute it.

Method 1: Use Default Problems (10 problems in 2 groups)

Ask Claude to run it directly:

run workflow math-problems

Claude executes:

Workflow({ name: 'math-problems' })

Method 2: Custom Problem Grouping (Passed via args)

Tell Claude the specific problems and groupings:

run workflow math-problems, 8 problems divided into 4 groups:
12×15, √144+3², 2^10, trailing zeros of 100!,
1+2+...+100, sin30°×cos60°, log₂1024, 1/2+1/3+1/6

Claude executes:

Workflow({
  name: 'math-problems',
  args: {
    groups: [
      { name: 'Group 1', problems: ['1. 12×15', '2. √144+3²'] },
      { name: 'Group 2', problems: ['3. 2^10', '4. trailing zeros of 100!'] },
      { name: 'Group 3', problems: ['5. 1+2+...+100', '6. sin30°×cos60°'] },
      { name: 'Group 4', problems: ['7. log₂1024', '8. 1/2+1/3+1/6'] },
    ]
  }
})

Method 3: Custom Grouping (Arbitrary Problems)

run workflow math-problems, divided into 2 groups:
Geometry: Find the area of a circle with radius 5, find the volume of a cube with side length 3
Algebra: Solve x²-4=0, find x for 3x+2=14

Claude executes:

Workflow({
  name: 'math-problems',
  args: {
    groups: [
      { name: 'Geometry', problems: ['Find the area of a circle with radius 5', 'Find the volume of a cube with side length 3'] },
      { name: 'Algebra', problems: ['Solve x²-4=0', 'Find x for 3x+2=14'] },
    ]
  }
})

Tip: Use /workflows to view real-time progress. After the workflow finishes, the results are automatically returned to the conversation.

Why write problems in the prompt?

Method Beginner Experience Maintainability
Dynamic Round-Robin Grouping Hard to tell which problem is in which group; requires mentally mapping the grouping logic Modifying problems requires understanding round-robin
Problems written in prompt (✅ Current) DEFAULT_GROUPS directly shows the grouping; modifying problems means just modifying the array Intuitive to modify problems and groupings

7. Defining Agents within Workflows

The agentType in Workflows does not support custom agents from .claude/agents/. Solution: Define agent roles within the script using Prompt Templates.

Write the complete instructions for each agent as a string template, using {placeholder} to mark data insertion points:

// ─── Prompt Templates (Defined at the top, clear view of all agents) ──────────

const SOLVER_PROMPT = `You are a math problem-solving expert. Please solve the following math problems one by one.

{problems_block}

Output format (per problem):
- Problem: {Original problem}
- Solution process: {Step-by-step derivation}
- Answer: {Final result}`

const VERIFIER_PROMPT = `You are a math verification expert. Independently re-solve the problems and compare them with the solutions below.

{problems_block}

=== Solutions to Verify ===
{solution}

=== Requirements ===
- Calculate independently; do not trust the provided solutions.
- Compare step-by-step and mark as correct or incorrect.

Output format:
- Judgment: ✅ Correct / ❌ Incorrect
- Reason for error: {If any}`

const SCORER_PROMPT = `You are a math scoring expert. Score based on the solutions and verification results.

{problems_block}

=== Verification Results ===
{verification}

Scoring: 10=Perfect | 7-9=Correct but improvable | 4-6=Partially correct | 0-3=Incorrect

Output format:
- Score: {0-10}
- Comment: {One sentence}`
// ─── End of Templates ─────────────────────────────────────────

Usage: Fill data using `.replace()`

// Use .replace() in the pipeline to fill data into templates
pipeline(
  [group],  // Entire group of data
  // Stage 1: Solve
  (grp) => agent(
    SOLVER_PROMPT.replace('{problems_block}', grp.problems.join('\n')),
    { label: `${grp.name}-Solve`, phase: 'Solve' }
  ),
  // Stage 2: Verify (solution is the output of the previous stage)
  (solution, grp) => agent(
    VERIFIER_PROMPT
      .replace('{problems_block}', grp.problems.join('\n'))
      .replace('{solution}', solution),
    { label: `${grp.name}-Verify`, phase: 'Verify' }
  ),
  // Stage 3: Score (verification is the output of the previous stage)
  (verification, grp) => agent(
    SCORER_PROMPT
      .replace('{problems_block}', grp.problems.join('\n'))
      .replace('{verification}', verification),
    { label: `${grp.name}-Score`, phase: 'Score' }
  ),
)

Comparison of Three Agent Definition Methods

Method Pros Cons Applicable Scenarios
Prompt Template (✅ Recommended) Clear separation of instructions and data; .replace() is easy to understand Templates can get long with multiple stages Pipelines with ≥2 stages
Agent Object + buildPrompt() Well-encapsulated, reusable Requires understanding this binding Reusing the same agent in multiple places
Inline Strings Simplest Prompts and logic are mixed Single stage, demos

Recommendation: Beginners should use the Prompt Template pattern. Having templates at the top of the file provides a clear view of all agent roles and data flows at a glance.


8. ⚠️ Limitations and Pitfalls

Known Limitations

Issue Description Solution
Custom agents unavailable agentType only supports built-in types; definitions in .claude/agents/ are ignored Define Agent objects within the script
args might be lost args might be undefined when referencing a workflow by name Use inline scripts or provide default values
TypeScript not supported Scripts are pure JS; type annotations will cause errors Use pure JS, replace types with comments
No Date/Math.random Date.now(), Math.random(), etc., are unavailable (affects caching) Pass timestamps via args
Concurrency limit Simultaneous running agents ≈ min(16, CPU cores - 2) Automatically queues, no handling needed
Total limit Max 1000 agents per workflow Group reasonably
7-day auto-expiration Looping tasks stop automatically after 7 days Recreate when needed

Supported agentType List (Built-in)

claude, claude-code-guide, Explore, general-purpose, Plan
gsd-*, glm-plan-usage:usage-query-agent, statusline-setup

❌ Not supported: Custom names in .claude/agents/


9. Architecture Comparison

Workflow vs. Direct Agent Tool Usage

graph TB
    subgraph "Workflow Method"
        W1[JS Script Control Flow] --> W2[agent]
        W1 --> W3[agent]
        W1 --> W4[agent]
        W2 --> W5[pipeline/parallel orchestration]
        W3 --> W5
        W4 --> W5
        W5 --> W6[Structured return]
    end

    subgraph "Agent Tool Method"
        A1[Main session prompt] --> A2[Agent subagent_type]
        A1 --> A3[Agent subagent_type]
        A2 --> A4[Manual aggregation]
        A3 --> A4
    end

    style W1 fill:#bbdefb
    style A1 fill:#fff9c4
Dimension Workflow Agent Tool
Orchestration Method JS code control flow Prompt-driven
Parallel Capability Natively supported by parallel() Requires manually spawning multiple
Pipeline Done in one line with pipeline() Requires manual chaining
Custom Agents ❌ Built-in types only subagent_type supports custom
args Passing ⚠️ Might be lost when referenced by name ✅ Passed via prompt
Applicable Scenarios Batch, multi-stage, structured Flexible, small number of agents
Complexity Requires writing JS scripts Simple prompts

Choosing between pipeline and parallel

graph LR
    Q{Are there dependencies
between data items?} -->|Yes| P[Use pipeline] Q -->|No| PR[Use parallel] Q{Do all results need to be
processed together?} -->|Yes| PR Q -->|No| P style Q fill:#fff9c4
Pattern When to Use Analogy
pipeline Each data item goes through multiple stages, items are independent Factory assembly line
parallel Multiple tasks run simultaneously, aggregated after all complete Parallel engineering team
pipeline + parallel Parallel grouping, pipeline within groups Multiple assembly lines operating simultaneously

10. Summary

Core Takeaways

  1. Dynamic Workflow = JS script orchestration of multiple agents
  2. Three primitives: agent(), pipeline(), parallel()
  3. Custom agents must be defined within the script; cannot reference .claude/agents/
  4. args passing is unstable; provide default values or use inline scripts
  5. Scripts are pure JS; TS syntax, Date.now(), and Math.random() are not supported
export const meta = {
  name: 'my-workflow',
  description: 'Description',
  phases: [{ title: 'Phase1' }, { title: 'Phase2' }],
}

// 1. Prompt Templates (Defined at the top, {placeholder} marks data positions)
const AGENT_PROMPT = `You are an expert.

{data_block}

Please process the above data as required.`

// 2. Data Grouping (Defined directly, easy for beginners to understand at a glance)
const DEFAULT_GROUPS = [
  { name: 'Group A', items: ['Data 1', 'Data 2'] },
  { name: 'Group B', items: ['Data 3', 'Data 4'] },
]

const groups = (args && args.groups) || DEFAULT_GROUPS

// 3. Orchestration: Parallel per group, pipeline within group
const results = await parallel(
  groups.map(group => () => pipeline(
    [group],
    (grp) => agent(
      AGENT_PROMPT.replace('{data_block}', grp.items.join('\n')),
      { label: `${grp.name}-Stage 1`, phase: 'Phase1' }
    ),
    (prev, grp) => agent(
      'Further process based on the result:\n' + prev,
      { label: `${grp.name}-Stage 2`, phase: 'Phase2' }
    ),
  ))
)

// 4. Return by group
const output = {}
groups.forEach((g, i) => output[g.name] = results[i][0])
return output

Further Learning

Official Documentation:

Community Resources:


📝 This guide is summarized from practical experience with Claude Code Dynamic Workflow. All code has been verified through actual execution. Official documentation reference: Dynamic Workflows.