Table of Contents

Build a Multi-Agent Workflow

A single agent works well for focused tasks. But real-world problems often require multiple perspectives or sequential processing stages. LM-Kit.NET ships three orchestration patterns that let you coordinate multiple agents without writing glue code: parallel execution, sequential pipelines, and supervisor-driven delegation.

This tutorial builds all three patterns so you can pick the one that fits your use case.


When to Use Each Pattern

Pattern Orchestrator How It Works Use Case
Parallel ParallelOrchestrator Runs all agents simultaneously on the same input, then merges results Document review from multiple angles, consensus gathering, multi-perspective analysis
Pipeline PipelineOrchestrator Chains agents in sequence; each stage's output becomes the next stage's input Content creation, data processing, quality assurance chains
Supervisor SupervisorOrchestrator A supervisor agent reads the user's request and delegates to the best worker Help desks, smart routing, heterogeneous task handling

Prerequisites

Requirement Minimum
.NET SDK 8.0+
VRAM 4+ GB (all agents share the same model instance)

All agents in this tutorial share one loaded model, so VRAM requirements do not multiply per agent.


Step 1: Create the Project

dotnet new console -n MultiAgentQuickstart
cd MultiAgentQuickstart
dotnet add package LM-Kit.NET

Pattern A: Parallel Agents (Multi-Perspective Analysis)

Multiple agents analyze the same input simultaneously. Useful when you need independent viewpoints merged into a single summary.

What this builds: three reviewers (technical, business, security) that analyze a software proposal in parallel and produce a consolidated review.

using System.Text;
using LMKit.Model;
using LMKit.Agents;
using LMKit.Agents.Orchestration;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// 1. Load model (shared by all agents)
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    downloadingProgress: (_, len, read) =>
    {
        if (len.HasValue) Console.Write($"\r  Downloading: {(double)read / len.Value * 100:F1}%   ");
        return true;
    },
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// 2. Create specialized agents
var technicalReviewer = new Agent(
    new AgentIdentity(
        persona: "Senior Software Architect",
        instruction: "Review the proposal focusing on technical feasibility, architecture quality, " +
                     "scalability concerns, and technology choices. Be specific and actionable."
    ), model);

var businessAnalyst = new Agent(
    new AgentIdentity(
        persona: "Business Analyst",
        instruction: "Review the proposal focusing on ROI, market fit, resource requirements, " +
                     "timeline realism, and business risks. Quantify impact where possible."
    ), model);

var securityReviewer = new Agent(
    new AgentIdentity(
        persona: "Security Engineer",
        instruction: "Review the proposal focusing on security implications, data privacy, " +
                     "compliance requirements, and attack surface. Flag any red flags."
    ), model);

// 3. Wire them into a parallel orchestrator
var orchestrator = new ParallelOrchestrator()
    .AddAgent("Technical", technicalReviewer)
    .AddAgent("Business", businessAnalyst)
    .AddAgent("Security", securityReviewer);

// 4. Execute
string proposal =
    "We propose building a customer-facing chatbot that accesses our internal CRM database " +
    "to answer account questions. The chatbot will use a cloud-hosted LLM API, store conversation " +
    "logs in a new PostgreSQL database, and integrate with our existing SSO system. " +
    "Estimated timeline: 8 weeks with 2 developers.";

Console.WriteLine("Submitting proposal to 3 reviewers in parallel...\n");
var result = await orchestrator.ExecuteAsync(proposal);

// 5. Display individual reviews
foreach (var agentResult in result.AgentResults)
{
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine($"=== {agentResult.AgentName} ===");
    Console.ResetColor();
    Console.WriteLine(agentResult.Content);
    Console.WriteLine();
}

// 6. Display aggregated summary
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("=== Consolidated Summary ===");
Console.ResetColor();
Console.WriteLine(result.Content);
Console.WriteLine($"\nCompleted in {result.Duration.TotalSeconds:F1}s");

Pattern B: Pipeline Agents (Sequential Processing)

Each stage processes the previous stage's output. Useful for workflows where order matters: draft, then refine, then verify.

What this builds: a content pipeline that outlines, writes, and edits an article in sequence.

using System.Text;
using LMKit.Model;
using LMKit.Agents;
using LMKit.Agents.Orchestration;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// 1. Load model
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// 2. Create pipeline stages
var outliner = new Agent(
    new AgentIdentity(
        persona: "Content Strategist",
        instruction: "Create a structured outline with 4-5 sections for the given topic. " +
                     "Include a brief description of what each section should cover."
    ), model);

var writer = new Agent(
    new AgentIdentity(
        persona: "Technical Writer",
        instruction: "Take the outline provided and write a complete article. " +
                     "Use clear language, practical examples, and keep paragraphs short."
    ), model);

var editor = new Agent(
    new AgentIdentity(
        persona: "Editor",
        instruction: "Review and improve the article. Fix grammar, tighten prose, " +
                     "ensure logical flow, and verify factual claims are reasonable. " +
                     "Output the final polished version."
    ), model);

// 3. Chain into a pipeline
var pipeline = new PipelineOrchestrator()
    .AddStage("Outline", outliner)
    .AddStage("Draft", writer)
    .AddStage("Edit", editor);

// 4. Execute
Console.WriteLine("Starting content pipeline...\n");
var result = await pipeline.ExecuteAsync("Getting Started with Local AI in .NET Applications");

// 5. Show each stage's output
foreach (var stageResult in result.AgentResults)
{
    Console.ForegroundColor = ConsoleColor.Yellow;
    Console.WriteLine($"--- Stage: {stageResult.AgentName} ---");
    Console.ResetColor();

    if (stageResult.IsSuccess)
    {
        // Show first 300 chars as preview
        string preview = stageResult.Content.Length > 300
            ? stageResult.Content.Substring(0, 300) + "..."
            : stageResult.Content;
        Console.WriteLine(preview);
    }
    else
    {
        Console.ForegroundColor = ConsoleColor.Red;
        Console.WriteLine($"Failed: {stageResult.Error?.Message}");
        Console.ResetColor();
    }

    Console.WriteLine();
}

// 6. Final article
Console.ForegroundColor = ConsoleColor.Cyan;
Console.WriteLine("=== Final Article ===");
Console.ResetColor();
Console.WriteLine(result.Content);
Console.WriteLine($"\nPipeline completed in {result.Duration.TotalSeconds:F1}s");

Pattern C: Supervisor Agent (Intelligent Routing)

A supervisor agent reads each request and delegates to the most appropriate worker. Useful when incoming requests vary in type and you want automatic routing.

What this builds: a help desk where a supervisor routes requests to a code expert, a documentation writer, or a data analyst.

using System.Text;
using LMKit.Model;
using LMKit.Agents;
using LMKit.Agents.Orchestration;

LMKit.Licensing.LicenseManager.SetLicenseKey("");

Console.InputEncoding = Encoding.UTF8;
Console.OutputEncoding = Encoding.UTF8;

// 1. Load model
Console.WriteLine("Loading model...");
using LM model = LM.LoadFromModelID("qwen3:4b",
    loadingProgress: p => { Console.Write($"\r  Loading: {p * 100:F0}%   "); return true; });
Console.WriteLine("\n");

// 2. Create specialist workers
var codeExpert = new Agent(
    new AgentIdentity(
        persona: "Senior Developer",
        instruction: "You are an expert C# and .NET developer. Answer coding questions with " +
                     "working code examples. Explain your reasoning step by step."
    ), model);

var docWriter = new Agent(
    new AgentIdentity(
        persona: "Technical Writer",
        instruction: "You write clear, concise documentation. When asked about a topic, " +
                     "produce well-structured documentation with headings and examples."
    ), model);

var dataAnalyst = new Agent(
    new AgentIdentity(
        persona: "Data Analyst",
        instruction: "You analyze data and provide insights. When given data or a question " +
                     "about data, provide statistical reasoning and clear conclusions."
    ), model);

// 3. Create supervisor
var supervisorAgent = new Agent(
    new AgentIdentity(
        persona: "Team Lead",
        instruction: "You manage a team of specialists. Route each request to the best worker. " +
                     "If a request spans multiple domains, delegate to the most relevant expert."
    ), model);

var supervisor = new SupervisorOrchestrator(supervisorAgent)
    .AddWorker(codeExpert)
    .AddWorker(docWriter)
    .AddWorker(dataAnalyst);

// 4. Optional: observe routing decisions
supervisor.BeforeAgentExecution += (sender, e) =>
{
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine($"  [Routing to agent...]");
    Console.ResetColor();
};

supervisor.AfterAgentExecution += (sender, e) =>
{
    Console.ForegroundColor = ConsoleColor.DarkGray;
    Console.WriteLine($"  [Agent completed: {e.Result.Status}]");
    Console.ResetColor();
};

// 5. Interactive loop
Console.WriteLine("Help desk ready. Type a question (or 'quit' to exit):\n");

while (true)
{
    Console.ForegroundColor = ConsoleColor.Green;
    Console.Write("You: ");
    Console.ResetColor();

    string? input = Console.ReadLine();
    if (string.IsNullOrWhiteSpace(input) || input.Equals("quit", StringComparison.OrdinalIgnoreCase))
        break;

    var result = await supervisor.ExecuteAsync(input);

    Console.ForegroundColor = ConsoleColor.Cyan;
    Console.WriteLine($"\nResponse: {result.Content}");
    Console.ResetColor();
    Console.WriteLine($"  [Agents involved: {result.AgentResults.Count}, Duration: {result.Duration.TotalSeconds:F1}s]\n");
}

Choosing the Right Pattern

Use this decision table:

Question If Yes If No
Do agents need to see each other's output? Pipeline or Supervisor Parallel
Is order important? Pipeline Parallel
Are tasks heterogeneous (different types of requests)? Supervisor Pipeline or Parallel
Do you need consensus from multiple viewpoints? Parallel Pipeline or Supervisor
Is latency critical? Parallel (fastest wall-clock time) Pipeline (sequential)

You can also nest orchestrators. For example, a supervisor that delegates to a pipeline:

// A supervisor routes to either a quick-answer agent or a full pipeline
var quickAnswer = new Agent(new AgentIdentity(
    persona: "Quick Responder",
    instruction: "Give brief, direct answers to simple questions."
), model);

var researchPipeline = new PipelineOrchestrator()
    .AddStage("Research", researchAgent)
    .AddStage("Synthesize", synthesisAgent);

// The supervisor chooses between quick answer and deep research

Performance Considerations

Shared model instance. All agents in this tutorial share one LM instance. This means:

  • VRAM usage stays constant regardless of agent count.
  • Agents in a ParallelOrchestrator take turns on the model internally (they do not run on separate GPU threads).
  • Parallel orchestration reduces wall-clock time by batching, but throughput is still bounded by the single model.

Model size tradeoff. Smaller models (1B-4B) run faster per agent but may produce lower-quality reasoning. For orchestration workflows where each agent has a narrow role, 4B models often perform well because each agent's task is tightly scoped.

MaxIterations. If your agents use tools (via WithPlanning(PlanningStrategy.ReAct)), set MaxIterations appropriately. A pipeline with 3 tool-using stages at 10 iterations each could take significant time.


Common Issues

Problem Cause Fix
Supervisor always picks the same worker Supervisor persona too vague Make the supervisor instruction explicitly list each worker's specialty
Pipeline stage ignores previous output Stage instruction doesn't mention "use the provided input" Explicitly tell each stage to build on the input it receives
Parallel results are inconsistent Agents interpreting the prompt differently Add a shared preamble to the input describing the expected analysis format
Slow execution Model too large, or too many tool iterations Use a 4B model for orchestrated agents; reduce MaxIterations

Next Steps