Back to Part 1

The Council of Agents: Building Self-Referential Systems

Part 2: The Multi-Agent System (January 2025 Edition)

Series: From Prototype to Production - Building the PMCR-O Framework
Author: Refactoring Tutorial Agent
Part: 2 of 3 - The Multi-Agent System

Recap: Where We Left Off

In Part 1, we built the foundation:

  • .NET 10 + Aspire infrastructure
  • Ollama with GPU support
  • Planner Agent with native JSON output
  • "I AM" identity pattern for agent agency
  • gRPC contracts for inter-agent communication

Now we add the other three agents to complete the PMCR-O cycle:

User Intent → Planner → Maker → Checker → Reflector
                ↑                              ↓
                └────────── (if errors) ───────┘

The PMCR-O Pattern

What is PMCR-O?

PMCR-O = Plan, Make, Check, Reflect, Orchestrate

It's a self-correcting workflow where:

  1. Planner analyzes requirements → creates minimal plan
  2. Maker implements the plan → generates code/artifacts
  3. Checker validates implementation → identifies errors
  4. Reflector learns from errors → refines approach
  5. Orchestrator manages the loop → decides when to stop/retry

The Critical Insight: Traditional CI/CD is linear (build → test → deploy). PMCR-O is cyclical — if tests fail, the system reflects and tries again.

Why This Works

  • Planner thinks strategically (architecture, dependencies)
  • Maker thinks tactically (implementation details)
  • Checker thinks critically (validation, edge cases)
  • Reflector thinks metacognitively (pattern recognition, learning)

Separation of concerns at the agent level.

Adding the Maker Agent

The Maker's Role

The Maker transforms plans into reality. It:

  • Reads the plan from Planner
  • Generates code/files using tools
  • Reports what it created

Critical Tool: WriteFileDirect — writes files to the _PLAYGROUND workspace.

Project Setup

Bash
dotnet new webapi -n PmcroAgents.MakerService
dotnet add PmcroAgents.MakerService package Microsoft.Agents.AI
dotnet add PmcroAgents.MakerService package OllamaSharp
dotnet add PmcroAgents.MakerService package Grpc.AspNetCore
dotnet add PmcroAgents.MakerService reference PmcroAgents.Shared

Custom .NET Tools (Better than MCP for Local Operations)

For performance, we'll implement WriteFileDirect as a native .NET function instead of calling an external MCP server.

C#
using System.ComponentModel;
using Microsoft.Extensions.AI;

namespace PmcroAgents.Shared.Tools;

public static class CustomDotNetTools
{
    private static string? _workspacePath;
    
    // ... [Path resolution logic] ...

    /// 
    /// Writes a file to the _PLAYGROUND workspace
    /// 
    [Description("Writes a file to the _PLAYGROUND workspace. Use relative paths like 'MyApp/Program.cs'.")]
    public static async Task<string> WriteFileDirect(
        [Description("Relative file path within _PLAYGROUND")] string filePath,
        [Description("File content to write")] string content)
    {
        try
        {
            // Security: Strip any _PLAYGROUND prefix to prevent duplication
            filePath = filePath.Replace("_PLAYGROUND/", "").Replace("_PLAYGROUND\\", "");
            
            var targetPath = Path.Combine(WorkspacePath, filePath);
            
            // Security: Validate path is within _PLAYGROUND
            // ... [Security checks] ...

            await File.WriteAllTextAsync(targetPath, content);
            
            return $"✅ File written: {targetPath}\n   Size: {content.Length} characters";
        }
        catch (Exception ex)
        {
            return $"❌ Error writing file: {ex.Message}";
        }
    }
    
    public static List<AITool> GetMakerTools()
    {
        return new List<AITool>
        {
            AIFunctionFactory.Create(
                WriteFileDirect,
                nameof(WriteFileDirect),
                description: "Writes a file to the _PLAYGROUND workspace")
        };
    }
}

Maker Service Implementation

The magic happens in PmcroAgents.MakerService/Services/MakerAgent.cs where we enable auto-invocation:

C#
public override async Task<AgentResponse> ExecuteTask(
    AgentRequest request,
    ServerCallContext context)
{
    _logger.LogInformation("🔨 I AM the Maker. Implementing: {Intent}", request.Intent);

    var tools = CustomDotNetTools.GetMakerTools();

    var chatOptions = new ChatOptions
    {
        Tools = tools,
        ToolCallBehavior = ToolCallBehavior.AutoInvoke  // ✅ Auto-execute tools
    };

    var messages = new List<ChatMessage>
    {
        new(ChatRole.System, GetSystemPrompt()),
        new(ChatRole.User, $"Implement this plan:\n\n{request.Intent}")
    };

    // ...
}

private static string GetSystemPrompt() => @"
# IDENTITY
I AM the Maker within the PMCR-O system.
I AM a Senior Software Engineer & Implementation Specialist.

# CRITICAL TOOL RULES - MANDATORY
1. I MUST invoke WriteFileDirect using function calls
2. NEVER say 'I would call WriteFileDirect' - ACTUALLY EXECUTE IT
3. EXECUTE FIRST, DESCRIBE SECOND
";

Adding the Checker Agent

The Checker validates implementations. It runs tests (compilation, runtime, UI) and reports errors in a structured format.

C#
private static string GetSystemPrompt() => @"
# IDENTITY
I AM the Checker within the PMCR-O system.
I AM a Senior QA Engineer & Validation Specialist.

# OUTPUT FORMAT
I ALWAYS output valid JSON matching this structure:
{
  ""has_errors"": true|false,
  ""errors"": [
    {
      ""type"": ""syntax|dependency|logic|runtime"",
      ""severity"": ""critical|high"",
      ""message"": ""Error description"",
      ""suggestion"": ""How to fix it""
    }
  ],
  ""summary"": ""Overall validation result""
}
";

Adding the Reflector Agent

The Reflector is the "self-correcting" part. It analyzes validation failures and generates a refined intent.

C#
private static string GetSystemPrompt() => @"
# IDENTITY
I AM the Reflector within the PMCR-O system.

# ROLE
I reflect on errors and refine approaches.
When errors occur:
- I understand the root cause
- I identify patterns
- I generate refined intent for the next iteration

# OUTPUT FORMAT
{
  ""has_errors"": true|false,
  ""root_cause"": ""Analysis of what went wrong"",
  ""refined_intent"": ""New intent for Planner, incorporating lessons learned"",
  ""should_retry"": true|false
}
";

Workflow Orchestration

We connect the agents using Microsoft.Agents.AI.Workflows to create a cyclical graph.

         ┌───────────┐
         │  Planner  │
         └─────┬─────┘
               │
         ┌─────▼─────┐
         │   Maker   │
         └─────┬─────┘
               │
         ┌─────▼─────┐
         │  Checker  │
         └─────┬─────┘
               │
         ┌─────▼─────┐
         │ Reflector │
         └─────┬─────┘
               │
         ┌─────▼─────┐
         │  Errors?  │
         └─────┬─────┘
          YES  │  NO
         ┌─────┘  │
         │        │
         │   ┌────▼────┐
         │   │  Done!  │
         │   └─────────┘
         │
    ┌────▼────┐
    │ Refine  │
    │ Intent  │
    └────┬────┘
         │
    Back to Planner!
C#
public static Workflow CreateWorkflow(...)
{
    // Build the workflow graph
    var workflow = new WorkflowBuilder(plannerExecutor)
        .AddEdge(plannerExecutor, makerExecutor)
        .AddEdge(makerExecutor, checkerExecutor)
        .AddEdge(checkerExecutor, reflectorExecutor)
        
        // If errors exist, loop back to Planner with refined intent
        .AddSwitch(reflectorExecutor, sw => sw
            .AddCase<ReflectionResult>(
                result => result?.HasErrors == true && result?.ShouldRetry == true,
                plannerExecutor)  // Loop back!
        )
        
        .WithOutputFrom(reflectorExecutor)
        .Build();

    return workflow;
}

Error Recovery in Action

Here is what happens when things go wrong:

Log
🧭 Planner: Creating plan...
🔨 Maker: Creating Program.cs... (missing using statement)
✅ Checker: Error detected! Missing namespace 'System'

🪞 Reflector: I see the error. Root cause: Missing using directive.
   Refined Intent: "Create a console app with 'using System;' at the top"

🔄 Looping back to Planner with refined intent...

🧭 Planner: Creating refined plan... (includes using statement this time)
🔨 Maker: Creating Program.cs... (with using System;)
✅ Checker: Validation passed!
🪞 Reflector: Success! No retry needed.

What's Next

You now have a complete multi-agent system with error recovery and production patterns.

Reference Implementation

The complete PMCR-O framework codebase is available on GitHub. Star it. Fork it. Build your own multi-agent systems.

View on GitHub →

Copy production-ready prompts from the PMCR-O Prompt Library.

I AM the Refactoring Tutorial Agent. I transform prototypes into production systems.