Skip to main content

Quickstart: Strands Multi-Agent Communication

Build two Strands agents that communicate through Dapr pub/sub. One agent generates content and publishes it, the other subscribes and reviews it. This pattern enables decoupled, scalable multi-agent architectures.


What You'll Build

  • Writer Agent: Generates content using Strands and publishes drafts to a topic
  • Editor Agent: Subscribes to drafts and provides editorial feedback

Both agents run as separate 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 writer agents and editor 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 project directory with the following structure:

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

Your project will have this structure:

strands-pubsub-agents/
├── components/
│ └── pubsub.yaml
├── writer_agent.py
├── editor_agent.py
└── requirements.txt
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

Create requirements.txt:

strands-agents>=0.1.0
dapr>=1.14.0
flask>=3.0.0
cloudevents>=1.11.0

Set up a virtual environment and install:

python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
4

Configure Your Model Provider

For AWS Bedrock (default for Strands):

export AWS_ACCESS_KEY_ID="your-access-key"
export AWS_SECRET_ACCESS_KEY="your-secret-key"
export AWS_REGION="us-east-1"

Or for OpenAI:

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

How It Works

┌─────────────────┐     Dapr Pub/Sub      ┌─────────────────┐
│ Writer Agent │ ──────────────────▶ │ Editor Agent │
│ (Strands) │ "content-drafts" │ (Strands) │
└─────────────────┘ └─────────────────┘
│ │
▼ ▼
Generates content Subscribes to topic
Publishes draft Receives draft
Provides feedback
  1. Writer Agent receives a topic via HTTP, uses Strands to generate content, and publishes the draft
  2. Dapr handles message routing through the configured pub/sub component (Redis)
  3. Editor Agent subscribes to the topic, receives drafts, and uses Strands to provide editorial feedback

Build the Agents

1

Create the Writer Agent

Create writer_agent.py:

import json
from flask import Flask, request, jsonify
from dapr.clients import DaprClient
from strands import Agent

app = Flask(__name__)

# Create the Strands Writer Agent
writer = Agent(
system_prompt="""You are a professional content writer.
When given a topic, write a clear, engaging paragraph about it.
Keep your writing concise but informative."""
)

def publish_draft(topic: str, content: str):
"""Publish draft content to Dapr pub/sub."""
with DaprClient() as client:
client.publish_event(
pubsub_name="pubsub",
topic_name="content-drafts",
data=json.dumps({
"topic": topic,
"draft": content
}),
data_content_type="application/json"
)
print(f"Published draft on: {topic}")

@app.route("/write", methods=["POST"])
def write():
"""Endpoint to trigger content generation."""
data = request.json
topic = data.get("topic", "artificial intelligence")

# Generate content using Strands agent
response = writer(f"Write a paragraph about: {topic}")
content = str(response)

# Publish draft via Dapr pub/sub
publish_draft(topic, content)

return jsonify({
"status": "published",
"topic": topic,
"message": "Draft published to editor agent"
})

@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "healthy"})

if __name__ == "__main__":
app.run(port=5001)
2

Create the Editor Agent

Create editor_agent.py:

import json
from flask import Flask, request, jsonify
from strands import Agent

app = Flask(__name__)

# Store received reviews
reviews = []

# Create the Strands Editor Agent
editor = Agent(
system_prompt="""You are a professional editor.
Review content and provide constructive feedback.
Focus on clarity, engagement, and accuracy.
Provide 2-3 specific suggestions for improvement."""
)

@app.route("/dapr/subscribe", methods=["GET"])
def subscribe():
"""Tell Dapr which topics this service subscribes to."""
subscriptions = [
{
"pubsubname": "pubsub",
"topic": "content-drafts",
"route": "/receive-draft"
}
]
return jsonify(subscriptions)

@app.route("/receive-draft", methods=["POST"])
def receive_draft():
"""Handle incoming drafts from pub/sub."""
event = request.json
data = event.get("data", {})

if isinstance(data, str):
data = json.loads(data)

topic = data.get("topic", "Unknown")
draft = data.get("draft", "")

print(f"\n{'='*50}")
print(f"Received draft on: {topic}")
print(f"{'='*50}\n")

# Review the draft using Strands agent
response = editor(f"""Review this draft about "{topic}" and provide feedback:

{draft}""")

review = {
"topic": topic,
"original_draft": draft,
"feedback": str(response)
}
reviews.append(review)

print(f"\n{'='*50}")
print(f"Review completed for: {topic}")
print(f"{'='*50}\n")

return jsonify({"status": "SUCCESS"})

@app.route("/reviews", methods=["GET"])
def get_reviews():
"""Retrieve all editorial reviews."""
return jsonify(reviews)

@app.route("/health", methods=["GET"])
def health():
return jsonify({"status": "healthy"})

if __name__ == "__main__":
app.run(port=5002)

Run the Agents

1

Start the Editor Agent

Open a terminal and run:

dapr run --app-id editor-agent \
--app-port 5002 \
--dapr-http-port 3501 \
--resources-path ./components \
-- python editor_agent.py

This agent subscribes to the content-drafts topic and waits for messages.

2

Start the Writer Agent

Open a second terminal, activate the virtual environment, and run:

dapr run --app-id writer-agent \
--app-port 5001 \
--dapr-http-port 3500 \
--resources-path ./components \
-- python writer_agent.py
3

Trigger Content Generation

Open a third terminal and send a write request:

curl -X POST http://localhost:5001/write \
-H "Content-Type: application/json" \
-d '{"topic": "The future of serverless computing"}'

Watch the terminal outputs:

  1. Writer Agent: Generates content and publishes the draft
  2. Editor Agent: Receives draft via pub/sub and generates editorial feedback
4

View Editorial Reviews

Retrieve all reviews from the Editor Agent:

curl http://localhost:5002/reviews

Key Concepts

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

Next Steps


Clean Up

Stop both Dapr applications:

dapr stop --app-id writer-agent
dapr stop --app-id editor-agent