Skip to main content

Quickstart: Microsoft Agent Framework Multi-Agent Communication

Build two agents using Microsoft Agent Framework that communicate through Dapr pub/sub. One agent performs code review and publishes findings, the other subscribes and generates fix suggestions. This pattern enables decoupled, scalable multi-agent architectures.


What You'll Build

  • Reviewer Agent: Analyzes code and publishes review findings to a topic
  • Fixer Agent: Subscribes to findings and generates code fix suggestions

Both agents run as separate ASP.NET Core services, communicating asynchronously through Dapr's pub/sub building block.


Why Async Agent Communication?

Asynchronous pub/sub messaging makes your multi-agent systems production-ready:

  • Resiliency — If an agent crashes or restarts, messages wait in the broker until it recovers. No lost work.
  • Independent scaling — Scale reviewer agents and fixer agents separately based on load.
  • Loose coupling — Agents only know about topics, not each other. Add, remove, or replace agents without breaking the system.
  • Guaranteed delivery — The message broker handles retries, dead-letter queues, and at-least-once delivery.

Bring Your Own Broker

This quickstart uses Redis, but Dapr supports 40+ pub/sub brokers including Kafka, RabbitMQ, AWS SNS/SQS, Azure Service Bus, and GCP Pub/Sub. Switch brokers by changing a YAML file — your agent code stays the same.


Prerequisites

Before you begin, ensure you have:


Setup

1

Create Project Structure

Create a new solution with two projects:

mkdir microsoft-agents-pubsub && cd microsoft-agents-pubsub
mkdir -p components

dotnet new sln -n AgentsPubSub
dotnet new webapi -n ReviewerAgent -o src/ReviewerAgent
dotnet new webapi -n FixerAgent -o src/FixerAgent

dotnet sln add src/ReviewerAgent
dotnet sln add src/FixerAgent

Your project will have this structure:

microsoft-agents-pubsub/
├── components/
│ └── pubsub.yaml
├── src/
│ ├── ReviewerAgent/
│ └── FixerAgent/
└── AgentsPubSub.sln
2

Configure Dapr Pub/Sub Component

Create components/pubsub.yaml:

apiVersion: dapr.io/v1alpha1
kind: Component
metadata:
name: pubsub
spec:
type: pubsub.redis
version: v1
metadata:
- name: redisHost
value: localhost:6379
- name: redisPassword
value: ""

This uses Redis as the message broker. Dapr initializes Redis automatically with dapr init.

3

Install Dependencies

Add the required packages to both projects:

cd src/ReviewerAgent
dotnet add package Microsoft.Agents.AI --prerelease
dotnet add package Dapr.AspNetCore

cd ../FixerAgent
dotnet add package Microsoft.Agents.AI --prerelease
dotnet add package Dapr.AspNetCore
4

Set Your OpenAI API Key

export OPENAI_API_KEY="your-api-key-here"

How It Works

┌─────────────────┐      Dapr Pub/Sub       ┌─────────────────┐
│ Reviewer Agent │ ───────────────────▶ │ Fixer Agent │
│ (MS Agent │ "code-review-findings" │ (MS Agent │
│ Framework) │ │ Framework) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
Reviews code Subscribes to topic
Publishes findings Receives findings
Generates fixes
  1. Reviewer Agent receives code via HTTP, uses Microsoft Agent Framework to analyze it, and publishes review findings
  2. Dapr handles message routing through the configured pub/sub component (Redis)
  3. Fixer Agent subscribes to the topic, receives findings, and uses Microsoft Agent Framework to generate fix suggestions

Build the Agents

1

Create the Reviewer Agent

Replace src/ReviewerAgent/Program.cs:

using Dapr.Client;
using Microsoft.Agents.AI;
using OpenAI;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDaprClient();

var app = builder.Build();

// Create the Microsoft Agent Framework reviewer
var openAiClient = new OpenAIClient(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
var reviewer = openAiClient
.GetOpenAIResponseClient("gpt-4o-mini")
.CreateAIAgent(
name: "CodeReviewer",
instructions: """
You are an expert code reviewer.
Analyze code for bugs, security issues, and best practices.
Provide findings as a JSON array with 'issue' and 'severity' fields.
Severity should be: 'critical', 'warning', or 'suggestion'.
"""
);

app.MapPost("/review", async (ReviewRequest request, DaprClient daprClient) =>
{
var code = request.Code ?? "print('hello world')";

// Run the agent to review the code
var result = await reviewer.RunAsync(
$"Review this code and provide findings:\n\n```\n{code}\n```"
);
var findings = result.ToString();

// Publish findings via Dapr pub/sub
await daprClient.PublishEventAsync(
"pubsub",
"code-review-findings",
new CodeReviewFindings(code, findings)
);

Console.WriteLine("Published code review findings");

return Results.Ok(new
{
Status = "published",
Message = "Review findings published to fixer agent"
});
});

app.MapGet("/health", () => Results.Ok(new { Status = "healthy" }));

app.Run();

record ReviewRequest(string? Code);
record CodeReviewFindings(string Code, string Findings);
2

Create the Fixer Agent

Replace src/FixerAgent/Program.cs:

using Dapr;
using Microsoft.Agents.AI;
using OpenAI;

var builder = WebApplication.CreateBuilder(args);
builder.Services.AddDaprClient();

var app = builder.Build();

// Store generated fixes
var fixes = new List<FixResult>();

// Create the Microsoft Agent Framework fixer
var openAiClient = new OpenAIClient(Environment.GetEnvironmentVariable("OPENAI_API_KEY"));
var fixer = openAiClient
.GetOpenAIResponseClient("gpt-4o-mini")
.CreateAIAgent(
name: "CodeFixer",
instructions: """
You are an expert software engineer.
Given code and review findings, provide specific fixes.
Show the corrected code and explain each change.
"""
);

// Subscribe to code review findings
app.MapPost("/receive-findings", [Topic("pubsub", "code-review-findings")] async (CodeReviewFindings findings) =>
{
Console.WriteLine(new string('=', 50));
Console.WriteLine("Received code review findings");
Console.WriteLine(new string('=', 50));

// Run the agent to generate fixes
var result = await fixer.RunAsync(
$"""
Fix the following code based on review findings:

Original code:
```
{findings.Code}
```

Review findings:
{findings.Findings}

Provide the fixed code and explain your changes.
"""
);

var fix = new FixResult(
findings.Code,
findings.Findings,
result.ToString()
);
fixes.Add(fix);

Console.WriteLine(new string('=', 50));
Console.WriteLine("Generated fix suggestions");
Console.WriteLine(new string('=', 50));

return Results.Ok();
});

app.MapGet("/fixes", () => Results.Ok(fixes));

app.MapGet("/health", () => Results.Ok(new { Status = "healthy" }));

app.Run();

record CodeReviewFindings(string Code, string Findings);
record FixResult(string OriginalCode, string Findings, string SuggestedFixes);
3

Configure the Ports

Update src/ReviewerAgent/Properties/launchSettings.json:

{
"profiles": {
"http": {
"commandName": "Project",
"applicationUrl": "http://localhost:5001"
}
}
}

Update src/FixerAgent/Properties/launchSettings.json:

{
"profiles": {
"http": {
"commandName": "Project",
"applicationUrl": "http://localhost:5002"
}
}
}

Run the Agents

1

Start the Fixer Agent

Open a terminal and run:

dapr run --app-id fixer-agent \
--app-port 5002 \
--dapr-http-port 3501 \
--resources-path ./components \
-- dotnet run --project src/FixerAgent

This agent subscribes to the code-review-findings topic and waits for messages.

2

Start the Reviewer Agent

Open a second terminal and run:

dapr run --app-id reviewer-agent \
--app-port 5001 \
--dapr-http-port 3500 \
--resources-path ./components \
-- dotnet run --project src/ReviewerAgent
3

Trigger Code Review

Open a third terminal and send code for review:

curl -X POST http://localhost:5001/review \
-H "Content-Type: application/json" \
-d '{
"code": "public string GetUser(string id) {\n var query = \"SELECT * FROM users WHERE id = \" + id;\n return db.Execute(query);\n}"
}'

Watch the terminal outputs:

  1. Reviewer Agent: Analyzes the code and publishes findings (should catch the SQL injection!)
  2. Fixer Agent: Receives findings via pub/sub and generates fix suggestions
4

View Generated Fixes

Retrieve all fixes from the Fixer Agent:

curl http://localhost:5002/fixes

Key Concepts

ConceptDescription
Pub/Sub DecouplingAgents don't need to know each other's addresses. They communicate through topics.
Async CommunicationReviewer agent publishes and returns immediately. Fixer agent processes asynchronously.
ScalabilityMultiple fixer agents can subscribe to the same topic for parallel processing.
ReliabilityDapr handles message delivery, retries, and dead-letter queues.

Next Steps

  • Add more agents for a complete code review pipeline (security scanner, performance analyzer)
  • Use Dapr state management to persist agent memory
  • Deploy to Kubernetes with Catalyst
  • Explore Dapr workflows for orchestrated agent coordination

Clean Up

Stop both Dapr applications:

dapr stop --app-id reviewer-agent
dapr stop --app-id fixer-agent