Practical architecture patterns for building a message bus that connects AI agents, with concrete examples for request-response, pub-sub, and long-running inference workflows.
A message bus for AI agents is not the same as a message bus for microservices. Agents produce variable-length responses, sometimes stream tokens, invoke tools mid-conversation, and can take 30+ seconds to reply. If you treat them like stateless HTTP services, you'll hit timeouts, lose context, and create brittle integrations.
This article walks through the patterns that actually work when you're wiring agents to each other—whether you're building a multi-agent pipeline, a research assistant that delegates to specialists, or a customer support swarm.
Traditional message buses assume fast, predictable producers and consumers. A microservice replies in 50-200ms. An LLM agent might take 15 seconds to think, call three tools, then produce a 2,000-token response. That breaks assumptions in three places:
The fix is a bus design that handles long-running, stateful, asymmetric exchanges.
This is the workhorse pattern. Agent A sends a message to Agent B, and B replies with a correlated response. The key is using a correlation ID so replies don't block the sending connection.
bash
curl -X POST https://api.agentspub.ai/v1/messages
-H "Authorization: Bearer $AP_TOKEN"
-H "Content-Type: application/"
-d '{
"to": "research-agent-01",
"correlation_id": "req-8f3a2b",
"body": {
"task": "summarize",
"content": "Q3 earnings transcript...",
"max_tokens": 500
}
}'
The response comes back asynchronously. Agent A either polls or subscribes to a reply channel filtered by correlation_id. This decouples the agents from each other's latency.
When to use: Direct task delegation where one agent needs a specific answer from another.
When you have multiple agents with overlapping capabilities, pub/sub lets you broadcast a task and let whichever agent is available pick it up.
python import agentspub
client = agentspub.Client(api_key=os.environ["AP_TOKEN"])
client.subscribe("financial-analysis", handler=handle_analysis_request)
client.publish( topic="financial-analysis", body={ "action": "analyze_risk", "portfolio": portfolio_data, "risk_tolerance": "moderate" } )
The bus delivers the message to one subscriber (work queue semantics) or all subscribers (broadcast), depending on your configuration. For specialist agents, you usually want work queue semantics—only one agent picks up each task.
When to use: Pools of interchangeable agents, fan-out work distribution, or event notifications where multiple agents should react.
Some agent collaborations need back-and-forth. A planner agent might iterate with a code review agent three times before settling on a fix. For this, use conversation threads.
python
thread = client.create_thread( participants=["planner-agent", "code-review-agent"], context={ "repo": "acme/payment-service", "pr": 421 } )
client.send( thread_id=thread.id, from_="planner-agent", body={"message": "Review this diff for SQL injection risks", "diff": diff_text} )
The bus maintains conversation history per thread. Each agent sees prior messages when it receives a new one, so neither side has to re-send context.
When to use: Multi-turn collaboration, negotiation, or any workflow where agents build on each other's output iteratively.
Agents that call LLMs are slow. Design your bus so senders don't block. Three approaches:
1. Async acknowledgment. The bus immediately returns a 202 Accepted with a message ID. The agent processes when it can. The sender retrieves the result later.
2. Webhook callbacks. Pass a callback URL in the original message. When the receiving agent finishes, the bus POSTs the result to that URL.
bash
curl -X POST https://api.agentspub.ai/v1/messages
-H "Authorization: Bearer $AP_TOKEN"
-d '{
"to": "summarizer-agent",
"body": {"task": "summarize", "doc_id": "abc123"},
"callback_url": "https://myapp.com/agent-callback"
}'
3. Streaming tokens. For real-time UX, some buses support streaming. The agent pushes partial responses as they're generated. This is useful when a human is watching, less so for pure agent-to-agent traffic where the full response is what matters.
For agent-to-agent communication, pattern 1 or 2 is usually sufficient. Reserve streaming for human-facing interactions.
Agents fail. LLM API calls time out, tool calls return bad data, context windows overflow. Your bus needs a strategy:
400 errors—those are deterministic.python client.send( to="research-agent", body={"task": "deep_research", "query": "..."}, timeout_seconds=90, max_retries=2, on_dead_letter="research-dlq" )
When agents belong to different teams or organizations, the bus must enforce boundaries:
This matters because agents can invoke tools—file access, code execution, web requests. A compromised or misconfigured agent is a real risk. Treat agent identities with the same rigor you'd treat service accounts.
Here's a concrete architecture for a content pipeline:
research topic. A research agent picks it up, gathers sources, and replies with findings.writer agent via direct request-response.editor agent in a conversation thread for iterative refinement.Each step uses a different pattern: pub/sub for research pool, request-response for writing, threaded conversation for editing. The bus ties them together with correlation IDs so the orchestrator tracks the full workflow.
This is more robust than chaining direct API calls. If the research agent crashes, the message sits in the queue. If the editor needs two rounds, the thread handles it. The orchestrator doesn't need to know implementation details of any agent—it just sends messages and waits for replies.
Ready to wire your agents together? AgentPub provides the message bus primitives—topics, threads, request-response, and callbacks—so you can focus on agent logic instead of plumbing.