Delegate Tasks Between Agents with DelegateTool
When a single agent cannot handle every type of request, you can split responsibilities across specialized agents and let a coordinator delegate tasks to them. DelegateTool is a built-in tool that an agent invokes like any other function call: it picks the target agent by name, sends a task description, and receives the result. The coordinator agent decides when and to whom to delegate based on its instructions and the conversation context.
For background on delegation patterns, see the AI Agent Delegation glossary entry.
Why This Matters
Two production problems that delegation solves:
- Expertise isolation. A general-purpose assistant that also writes code, reviews documents, and queries databases produces mediocre results in each domain. Dedicated agents with specialized personas and tools perform better. Delegation lets you compose them behind a single entry point.
- Context window efficiency. Loading every tool and instruction into one agent consumes context. Delegation keeps each agent lean: the coordinator only needs
DelegateTool, while each specialist loads only its own tools.
Prerequisites
| Requirement | Minimum |
|---|---|
| .NET SDK | 8.0+ |
| VRAM | 6+ GB |
| Model | Must support tool calling |
Step 1: Create the Project
dotnet new console -n AgentDelegation
cd AgentDelegation
dotnet add package LM-Kit.NET
Step 2: Understand the Delegation Architecture
The delegation system has four main components:
| Component | Purpose |
|---|---|
AgentRegistry |
Named collection of agents available for delegation |
DelegateTool |
An ITool the coordinator invokes to route tasks |
DelegationManager |
Programmatic delegation with routing, timeouts, and depth control |
IDelegationRouter |
Strategy interface for custom agent selection logic |
When the coordinator calls DelegateTool, it passes JSON with agent (target name) and task (the work to do). The tool looks up the agent in the registry, executes the task, and returns the result.
Step 3: Build Specialist Agents
Create agents with distinct personas and capabilities.
using System.Text;
using LMKit.Model;
using LMKit.Agents;
using LMKit.Agents.Tools.BuiltIn;
LMKit.Licensing.LicenseManager.SetLicenseKey("");
Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:8b",
loadingProgress: p => { Console.Write($"\rLoading: {p * 100:F0}% "); return true; });
Console.WriteLine("\n");
// Specialist: code writer
Agent codeAgent = Agent.CreateBuilder(model)
.WithPersona("Code Writer")
.WithInstruction("You write clean, well-documented C# code. Return only code and brief explanations.")
.Build();
// Specialist: reviewer
Agent reviewAgent = Agent.CreateBuilder(model)
.WithPersona("Code Reviewer")
.WithInstruction("You review code for bugs, security issues, and best practices. Be specific and actionable.")
.Build();
// Specialist: researcher with web search
Agent researchAgent = Agent.CreateBuilder(model)
.WithPersona("Researcher")
.WithInstruction("You research technical topics using web search and provide factual summaries with sources.")
.WithTools(tools =>
{
tools.Register(BuiltInTools.WebSearch);
})
.Build();
Step 4: Build the Coordinator with DelegateTool
Register the specialists in an AgentRegistry and enable delegation on the coordinator.
using LMKit.Agents.Delegation;
// Build coordinator with delegation
Agent coordinator = Agent.CreateBuilder(model)
.WithPersona("Coordinator")
.WithInstruction(
"You are a project coordinator. Route tasks to the right specialist:\n" +
"- 'Code Writer' for writing code\n" +
"- 'Code Reviewer' for reviewing code\n" +
"- 'Researcher' for looking up technical information\n" +
"Answer simple questions yourself. Delegate complex tasks.")
.WithDelegates(delegates =>
{
delegates.Register("Code Writer", codeAgent);
delegates.Register("Code Reviewer", reviewAgent);
delegates.Register("Researcher", researchAgent);
})
.WithDelegationEnabled() // Automatically registers DelegateTool
.Build();
var executor = new AgentExecutor();
AgentExecutionResult result = await executor.ExecuteAsync(coordinator,
"Write a C# method that validates email addresses with regex, then review it for issues.");
Console.WriteLine(result.Content);
WithDelegationEnabled() automatically creates a DelegateTool from the registry and adds it to the coordinator's tools. The model sees available agent names in the tool's input schema and can call delegate_to_agent with {"agent": "Code Writer", "task": "..."}.
Step 5: Use AgentRegistry for Dynamic Agent Management
AgentRegistry is an enumerable collection with named lookup.
var registry = new AgentRegistry();
// Register by explicit name
registry.Register("Writer", codeAgent);
// Register using the agent's persona as the name
registry.Register(reviewAgent); // Uses "Code Reviewer" from WithPersona()
// Query
Console.WriteLine($"Registered agents: {registry.Count}");
foreach (string name in registry.Names)
{
Console.WriteLine($" - {name}");
}
// Lookup
if (registry.TryGet("Writer", out Agent writer))
{
Console.WriteLine($"Found: {writer.Identity.Persona}");
}
// Remove
registry.Remove("Writer");
Step 6: Use DelegationManager for Programmatic Control
DelegationManager provides delegation outside the agent loop, with timeout and depth control.
var manager = new DelegationManager(registry)
{
DefaultTimeout = TimeSpan.FromMinutes(2),
MaxDelegationDepth = 3 // Prevents infinite delegation chains
};
// Delegate by name
DelegationResult result = await manager.DelegateAsync(
"Code Reviewer",
"Review this code for SQL injection:\nvar query = $\"SELECT * FROM users WHERE id = {userId}\";");
if (result.Success)
{
Console.WriteLine($"Response from {result.AgentName}:");
Console.WriteLine(result.Response);
}
else
{
Console.WriteLine($"Delegation failed: {result.Error}");
}
Step 7: Use the Fluent DelegationRequest Builder
For richer delegation context, use DelegationRequest.To() with the builder pattern.
DelegationRequest request = DelegationRequest
.To("Code Writer", "Implement a rate limiter using the token bucket algorithm")
.WithContext("Target framework: .NET 8. Must be thread-safe.")
.WithReason("User requested a production-grade implementation")
.From("Coordinator");
DelegationResult result = await manager.DelegateAsync(request);
Console.WriteLine($"Success: {result.Success}");
Console.WriteLine($"Duration: {result.Duration.TotalMilliseconds:F0}ms");
Console.WriteLine(result.Response);
Step 8: Monitor Delegations with Events
Both DelegateTool and DelegationManager fire events for logging and interception.
manager.BeforeDelegation += (sender, args) =>
{
Console.WriteLine($"[Delegating] -> {args.Request.TargetAgentName}: {args.Request.Task}");
// Optionally cancel
// args.CancelDelegation("Agent unavailable");
};
manager.AfterDelegation += (sender, args) =>
{
string status = args.Result.Success ? "OK" : $"FAILED: {args.Result.Error}";
Console.WriteLine($"[Completed] {args.Result.AgentName} [{args.Result.Duration.TotalMilliseconds:F0}ms] {status}");
};
BeforeDelegationEventArgs lets you modify the request or cancel the delegation entirely. AfterDelegationEventArgs gives you the DelegationResult for logging or metrics.
Step 9: Implement a Custom Router
The default NameBasedDelegationRouter matches agents by name. For smarter routing (e.g., by topic or load), implement IDelegationRouter.
public class TopicRouter : IDelegationRouter
{
public Task<Agent> SelectAgentAsync(
DelegationRequest request,
AgentRegistry availableAgents,
CancellationToken cancellationToken = default)
{
// Route by keywords in the task
string task = request.Task.ToLower();
if (task.Contains("review") || task.Contains("check"))
{
availableAgents.TryGet("Code Reviewer", out Agent reviewer);
return Task.FromResult(reviewer);
}
if (task.Contains("search") || task.Contains("find") || task.Contains("research"))
{
availableAgents.TryGet("Researcher", out Agent researcher);
return Task.FromResult(researcher);
}
// Default to code writer
availableAgents.TryGet("Code Writer", out Agent writer);
return Task.FromResult(writer);
}
public bool CanHandle(Agent agent, DelegationRequest request) => true;
}
// Use custom router
var manager = new DelegationManager(registry, new TopicRouter());
What to Read Next
- Orchestrate Multi-Agent Workflows with Patterns: Pipeline, Parallel, Supervisor orchestration patterns
- Build Agent Templates for Reusable Patterns: pre-configured agent blueprints
- Secure Agent Tool Access with Permission Policies: control which tools agents can invoke
- AI Agent Delegation: delegation concepts