ctx.call()

Cross-agent communication with automatic workflow DAG building

The ctx.call() method enables cross-agent communication, allowing reasoners to invoke reasoners and skills on other agents while automatically building workflow DAGs.

Basic Usage

agent.reasoner('orchestrate', async (ctx) => {
  // Call another agent's reasoner
  const sentiment = await ctx.call('sentiment-agent.analyze', {
    text: ctx.input.text
  });

  return { sentiment };
});

Target Format

The target string follows the format: {agent-id}.{reasoner-or-skill-name}

// Call a reasoner
await ctx.call('analysis-agent.deep_analyze', { data });

// Call a skill
await ctx.call('formatter-agent.format_json', { data });

// Call on same agent
await ctx.call('my-agent.other_reasoner', { data });

Parameters

Prop

Type

Automatic Context Propagation

When you call another agent, the execution context is automatically propagated:

  • executionId - Unique ID for the child execution
  • parentExecutionId - Links to the calling execution
  • workflowId - Shared across the entire workflow
  • runId - Groups related executions
  • sessionId - User session tracking

The Agentfield control plane uses this context to build workflow DAGs showing the complete execution flow across all agents.

Multi-Agent Orchestration

agent.reasoner('process_ticket', async (ctx) => {
  const { ticketId, content } = ctx.input;

  // Parallel calls to multiple agents
  const [sentiment, category, priority] = await Promise.all([
    ctx.call('sentiment-agent.analyze', { text: content }),
    ctx.call('classifier-agent.categorize', { text: content }),
    ctx.call('priority-agent.assess', { text: content })
  ]);

  // Sequential call based on results
  let handler;
  if (priority.level === 'critical') {
    handler = await ctx.call('escalation-agent.escalate', {
      ticketId,
      sentiment,
      category,
      priority
    });
  } else {
    handler = await ctx.call('routing-agent.assign', {
      ticketId,
      category
    });
  }

  return {
    ticketId,
    analysis: { sentiment, category, priority },
    handler
  };
});

Agent-Level Call

You can also call from the agent instance directly:

// Outside of a reasoner context
const result = await agent.call('other-agent.function', {
  data: 'input'
});

Error Handling

agent.reasoner('safe_call', async (ctx) => {
  try {
    const result = await ctx.call('external-agent.process', ctx.input);
    return { success: true, result };
  } catch (error) {
    // Log error and provide fallback
    console.error('External agent failed:', error);

    // Try fallback agent
    const fallback = await ctx.call('fallback-agent.process', ctx.input);
    return { success: true, result: fallback, usedFallback: true };
  }
});

Workflow DAG Example

When agents call each other, the control plane builds a DAG:

orchestrator.process_request
├── sentiment-agent.analyze
├── classifier-agent.categorize
│   └── enrichment-agent.add_context
└── routing-agent.assign
    └── notification-agent.notify

This DAG is visible in the Agentfield UI and can be exported for auditing.

Examples

Research Pipeline

agent.reasoner('research', async (ctx) => {
  const { topic } = ctx.input;

  // Step 1: Gather sources
  const sources = await ctx.call('search-agent.find_sources', { topic });

  // Step 2: Extract information from each source
  const extractions = await Promise.all(
    sources.map(source =>
      ctx.call('extraction-agent.extract', { url: source.url })
    )
  );

  // Step 3: Synthesize findings
  const synthesis = await ctx.call('synthesis-agent.combine', {
    topic,
    extractions
  });

  // Step 4: Generate report
  const report = await ctx.call('report-agent.generate', {
    topic,
    synthesis
  });

  return report;
});

Conditional Routing

agent.reasoner('smart_router', async (ctx) => {
  const { request } = ctx.input;

  // Classify the request
  const classification = await ctx.call('classifier-agent.classify', {
    text: request.content
  });

  // Route based on classification
  switch (classification.type) {
    case 'technical':
      return await ctx.call('tech-support-agent.handle', request);

    case 'billing':
      return await ctx.call('billing-agent.handle', request);

    case 'sales':
      return await ctx.call('sales-agent.handle', request);

    default:
      return await ctx.call('general-agent.handle', request);
  }
});

Fan-Out / Fan-In

agent.reasoner('batch_process', async (ctx) => {
  const { items } = ctx.input;

  // Fan-out: Process all items in parallel
  const results = await Promise.all(
    items.map((item, index) =>
      ctx.call('processor-agent.process', { item, index })
    )
  );

  // Fan-in: Aggregate results
  const aggregated = await ctx.call('aggregator-agent.combine', {
    results
  });

  return aggregated;
});