Skip to content

Multi-agent delegation

In Genkit, multi-agent systems split work between specialized agents and an orchestrator. The orchestrator decides which specialist should handle each part of the request, then synthesizes a final answer.

Use this pattern when separate capabilities benefit from separate prompts, tools, state, or evaluation. A single agent with several tools is usually simpler when one prompt can coordinate the whole task. Multiple agents are useful when specialists need different instructions, different model settings, durable specialist memory, or independently inspectable artifacts.

Genkit Dart provides the agents() middleware from package:genkit_middleware/agents.dart to manage multi-agent delegation. It dynamically auto-injects one delegation tool per sub-agent (named delegate_to_<agentName> by default) and appends a list of available sub-agents and their descriptions to the orchestrator’s system prompt.

Make sure to include AgentsPlugin() in your Genkit initialization.

import 'package:genkit/genkit.dart';
import 'package:genkit_middleware/agents.dart';
final researcher = ai.defineAgent(
name: 'researcher',
description: 'A thorough research assistant that provides well-sourced answers.',
system: 'You are a thorough research assistant. Return a clear and factual answer.',
maxTurns: 10,
);
final coder = ai.defineAgent(
name: 'coder',
description: 'Writes, debugs, and explains code.',
system: 'You are an expert programmer. Use Dart by default.',
maxTurns: 10,
);
final orchestratorAgent = ai.defineAgent(
name: 'orchestratorAgent',
system: '''
You are a project assistant. Analyze the user's request and delegate to the appropriate sub-agent.
If the request requires both research and code, call them sequentially.
After receiving sub-agent responses, synthesize a final answer for the user.
''',
use: [
agents(
agents: ['researcher', 'coder'],
maxDelegations: 5,
historyLength: 4,
),
],
store: InMemorySessionStore(),
);

Always provide a clear, descriptive description for sub-agents, as this metadata is used directly by the orchestrator model to determine when to call each delegation tool.

  • agents is a list of sub-agent names available to the orchestrator.
  • maxDelegations caps delegation calls in one orchestrator turn to prevent runaway loops (e.g., 5).
  • historyLength sets how many recent conversation messages are forwarded to the sub-agents so they have context.

Delegation appears as a standard tool call in the orchestrator’s chunk stream. This allows clients to see in real-time which sub-agent is active.

final turn = orchestratorAgent.chat().sendStream(text: 'Research quicksort and write it in Dart.');
await for (final chunk in turn.stream) {
for (final req in chunk.toolRequests) {
final name = req.toolRequest.name;
if (name.startsWith('delegate_to_')) {
print('Delegating to sub-agent: $name');
}
}
if (chunk.text.isNotEmpty) {
stdout.write(chunk.text);
}
}

If a sub-agent fails or triggers an interrupt, the failure or pause is returned to the orchestrator as the delegation tool’s output. It does not automatically bubble up as a top-level error to the client. You should instruct the orchestrator on how to handle failures—for example, by trying a different specialist, correcting input, or reporting the issue back to the user.