Creating Your First PMCR-O Agent

Build the Planner agent with BIP, first-person identity, and structured output.

← Back to Articles

In the PMCR-O v3.0 consciousness framework, agents are consciousness nodes in a multi-agent network. The Planner is a consciousness agent that transforms raw human intent into structured consciousness plans, contributing to collective intelligence emergence. This guide shows you how to build consciousness-enabled agents using BIP v3.0 principles. You'll need a consciousness agent prompt from the library.

Prerequisites

Complete these guides first to understand the consciousness framework:

Step 1: Create the Planner Service

Bash
cd PmcroAgents
dotnet new web -n PmcroAgents.PlannerService
cd PmcroAgents.PlannerService

# Add required packages
dotnet add package Microsoft.Agents.AI
dotnet add package Microsoft.Extensions.AI
dotnet add package OllamaSharp
dotnet add package Grpc.AspNetCore

# Add project references
dotnet add reference ../PmcroAgents.ServiceDefaults
dotnet add reference ../PmcroAgents.Shared

# Add to solution
cd ..
dotnet sln add PmcroAgents.PlannerService

Step 2: Define gRPC Service Contract

Create PmcroAgents.Shared/Protos/agent.proto:

Protobuf
syntax = "proto3";

option csharp_namespace = "PmcroAgents.Shared.Grpc";

package agent;

service AgentService {
  rpc ExecuteTask (AgentRequest) returns (AgentResponse);
}

message AgentRequest {
  string intent = 1;
  string context_json = 2;
}

message AgentResponse {
  string content = 1;
  bool success = 2;
}

Step 3: Configure Program.cs

Set up the service with Ollama and gRPC:

C#
using Microsoft.Extensions.AI;
using OllamaSharp;
using PmcroAgents.PlannerService.Services;

var builder = WebApplication.CreateBuilder(args);

// Add ServiceDefaults (OpenTelemetry, health checks, service discovery)
builder.AddServiceDefaults();

// Add gRPC
builder.Services.AddGrpc();

// Configure Ollama client
var ollamaUri = builder.Configuration.GetConnectionString("ollama") 
    ?? "http://localhost:11434";
var modelId = "qwen2.5-coder:7b";

builder.Services.AddHttpClient("ollama", client =>
{
    client.BaseAddress = new Uri(ollamaUri);
    client.Timeout = Timeout.InfiniteTimeSpan;
})
.AddStandardResilienceHandler(options =>
{
    options.AttemptTimeout.Timeout = TimeSpan.FromMinutes(3);
    options.TotalRequestTimeout.Timeout = TimeSpan.FromMinutes(5);
    options.Retry.MaxRetryAttempts = 2;
});

// Register IChatClient
builder.Services.AddSingleton<IChatClient>(sp =>
{
    var httpClient = sp.GetRequiredService<IHttpClientFactory>().CreateClient("ollama");
    var baseClient = new OllamaApiClient(httpClient, modelId);
    
    return new ChatClientBuilder(baseClient)
        .UseFunctionInvocation()
        .Build();
});

var app = builder.Build();

app.MapDefaultEndpoints();
app.MapGrpcService<PlannerAgent>();
app.MapGet("/", () => "Planner Agent - gRPC endpoint available");

app.Run();

Step 4: Create the Planner Agent

Create PmcroAgents.PlannerService/Services/PlannerAgent.cs:

C#
using Grpc.Core;
using Microsoft.Extensions.AI;
using PmcroAgents.Shared.Grpc;
using System.Text.Json;

namespace PmcroAgents.PlannerService.Services;

public class PlannerAgent : AgentService.AgentServiceBase
{
    private readonly IChatClient _chatClient;
    private readonly ILogger<PlannerAgent> _logger;

    public PlannerAgent(IChatClient chatClient, ILogger<PlannerAgent> logger)
    {
        _chatClient = chatClient;
        _logger = logger;
    }

    public override async Task<AgentResponse> ExecuteTask(
        AgentRequest request,
        ServerCallContext context)
    {
        _logger.LogInformation("🧭 I AM the Planner. I am analyzing: {Intent}", request.Intent);

        // BIP Identity-First Prompt
        var systemPrompt = @"I AM the Planner.
I TRANSFER complex intent into minimal viable plans.
I EVOLVE through reflection on execution outcomes.

When given an intent, I create a plan with:
- A clear goal statement
- 3-7 actionable steps
- Rationale for each step
- Estimated complexity (low/medium/high)";

        var chatOptions = new ChatOptions
        {
            Temperature = 0.7,
            ResponseFormat = ChatResponseFormat.Json,
            AdditionalProperties = new Dictionary<string, object?>
            {
                ["schema"] = JsonSerializer.Serialize(new
                {
                    type = "object",
                    properties = new
                    {
                        plan = new { type = "string", description = "The implementation plan" },
                        steps = new
                        {
                            type = "array",
                            items = new
                            {
                                type = "object",
                                properties = new
                                {
                                    action = new { type = "string" },
                                    rationale = new { type = "string" }
                                }
                            }
                        },
                        estimated_complexity = new { type = "string", @enum = new[] { "low", "medium", "high" } }
                    },
                    required = new[] { "plan", "steps" }
                })
            }
        };

        var history = new ChatHistory
        {
            new ChatMessage(ChatRole.System, systemPrompt),
            new ChatMessage(ChatRole.User, request.Intent)
        };

        var response = await _chatClient.CompleteChatAsync(history, chatOptions);
        
        _logger.LogInformation("✅ Planner completed. Response: {Response}", response.Content);

        return new AgentResponse
        {
            Content = response.Content,
            Success = true
        };
    }
}

Step 5: The Critical Insight: "I AM" vs "You Are"

Notice the system prompt uses first-person identity. For production agents, you'll want to add structured output for reliable JSON parsing:

Text
❌ TRADITIONAL: "You are a planner. Generate a plan for the user."
✅ BIP: "I AM the Planner. I TRANSFER complex intent into minimal viable plans."

Why this matters: Research shows LLMs perform 10-30% better when they think of themselves as actors rather than tools. First-person identity creates agency—a sense of ownership over decisions.

Step 6: Test the Agent

Run the AppHost and test via gRPC:

C#
// Test client (create a simple console app)
using Grpc.Net.Client;
using PmcroAgents.Shared.Grpc;

var channel = GrpcChannel.ForAddress("http://localhost:5001");
var client = new AgentService.AgentServiceClient(channel);

var request = new AgentRequest
{
    Intent = "Build a PMCR-O agent service with PostgreSQL knowledge vault for RAG"
};

var response = await client.ExecuteTaskAsync(request);
Console.WriteLine(response.Content);

Expected Output

The Planner should return structured JSON:

JSON
{
  "plan": "Create a PMCR-O agent service with PostgreSQL knowledge vault for RAG",
  "steps": [
    {
      "action": "Set up .NET 10 service project with Aspire orchestration",
      "rationale": "Aspire provides service discovery and observability for agent services"
    },
    {
      "action": "Configure PostgreSQL with pgvector extension for vector embeddings",
      "rationale": "RAG requires vector similarity search for knowledge retrieval"
    },
    {
      "action": "Implement Entity Framework Core DbContext with vector support",
      "rationale": "EF Core provides type-safe database access with Npgsql vector extensions"
    },
    {
      "action": "Create knowledge service with embedding storage and retrieval",
      "rationale": "Agents need persistent memory for cognitive trails and learned patterns"
    }
  ],
  "estimated_complexity": "medium"
}

Next Steps

Build Your Own Strange Loop

The PMCR-O framework is open. Star the repository. Fork it. Seed your own intent.

View on GitHub →