AI applications are increasingly moving from a single agent to multiple agents that work together.

One agent may handle customer questions, another may search internal documentation, and another may perform an action in a business system. Once these agents start communicating with each other, the application needs a reliable way to exchange messages, maintain state, and coordinate work.

This is where an agent mesh can be useful.

A Redis-based agent mesh provides an infrastructure layer through which multiple AI agents can communicate without being tightly coupled to each other. Redis can help with message delivery, shared state, task coordination, and short-lived agent information.

The important idea is that agents should not need to know the internal implementation of every other agent. They can communicate through well-defined messages and routing rules.

What Is an Agent Mesh?

An agent mesh is a communication layer connecting multiple AI agents.

Consider a customer-support application with these agents:

Code
User
  |
  v
Orchestrator Agent
  |
  +----> Knowledge Agent
  |
  +----> Order Agent
  |
  +----> Billing Agent
  |
  +----> Notification Agent

The orchestrator does not need to implement every capability itself.

Instead, it determines which agent should handle each task.

For example:

Code
Customer asks about an order
        |
        v
Orchestrator Agent
        |
        v
Order Agent
        |
        v
Order Service

The response can then return through the same communication layer.

Redis can act as part of this communication infrastructure.

Why Use Redis for Agent Communication?

AI agents often need communication that is faster and more dynamic than traditional request-response APIs.

Redis is useful for this type of workload because applications can use it for:

  • Message queues
  • Pub/Sub communication
  • Shared state
  • Task coordination
  • Temporary context
  • Event notifications
  • Rate limiting
  • Agent status
  • Session information

The exact Redis feature used should depend on the communication pattern.

For example, Pub/Sub is useful when multiple consumers need to receive an event, while a queue-oriented design is better when a task should be processed by a worker.

A Simple Agent Mesh Architecture

A basic architecture can look like this:

SQL
                         +----------------+
                         |  API / Client  |
                         +-------+--------+
                                 |
                                 v
                       +-------------------+
                       | Orchestrator Agent|
                       +---------+---------+
                                 |
                       +---------v---------+
                       |   Redis Agent     |
                       |   Communication   |
                       +----+----+----+----+
                            |    |    |
                            v    v    v
                         Agent  Agent Agent
                           A      B     C

Each agent can have its own responsibility.

For example:

Code
Agent A → Customer Support
Agent B → Order Processing
Agent C → Knowledge Search
Agent D → Notification

The agents do not need direct connections to every other agent.

This reduces the number of dependencies between services.

Defining Agent Messages

The first step in building an agent mesh is defining the message format.

A message should contain enough information for the receiving agent to understand the request.

For example:

JSON
{
  "messageId": "msg-1024",
  "sourceAgent": "support-agent",
  "targetAgent": "order-agent",
  "task": "get-order-status",
  "conversationId": "conv-501",
  "payload": {
    "orderId": "ORD-10045"
  }
}

The message contains several important fields.

The messageId helps identify a particular message.

The sourceAgent identifies the sender.

The targetAgent identifies the intended receiver.

The task describes the requested operation.

The conversationId allows related messages to be associated with the same interaction.

The payload contains task-specific information.

A consistent message contract becomes especially important when the number of agents increases.

Sending Agent Tasks Through Redis

A simplified Node.js example can use Redis to publish an agent message.

JavaScript
const message = {
  messageId: crypto.randomUUID(),
  sourceAgent: "support-agent",
  targetAgent: "order-agent",
  task: "get-order-status",
  conversationId: "conv-501",
  payload: {
    orderId: "ORD-10045"
  }
};

await redis.publish(
  "agent:order",
  JSON.stringify(message)
);

The support agent does not need to know how the order agent retrieves the order.

It only needs to know the communication contract.

The order agent can subscribe to its communication channel and process incoming messages.

Receiving Messages in an Agent

A receiving agent can subscribe to its designated channel.

For example:

JavaScript
await subscriber.subscribe("agent:order");

await subscriber.on("message", async (channel, rawMessage) => {
  const message = JSON.parse(rawMessage);

  if (message.targetAgent !== "order-agent") {
    return;
  }

  await processOrderTask(message);
});

The processOrderTask function can then perform the actual business operation.

A simplified implementation might look like this:

JavaScript
async function processOrderTask(message) {
  if (message.task === "get-order-status") {
    const order = await getOrder(message.payload.orderId);

    await redis.publish(
      "agent:responses",
      JSON.stringify({
        messageId: crypto.randomUUID(),
        sourceAgent: "order-agent",
        targetAgent: message.sourceAgent,
        conversationId: message.conversationId,
        task: "order-status-result",
        payload: order
      })
    );
  }
}

This creates a simple request-and-response flow between agents.

Pub/Sub Is Not the Same as a Task Queue

One common design mistake is treating every Redis communication pattern as a queue.

Pub/Sub works well for broadcasting events.

For example:

Code
Order Updated
      |
      +----> Support Agent
      +----> Analytics Agent
      +----> Notification Agent

Each subscriber can receive the event.

However, a task-processing system may require a different pattern.

For example:

Code
Generate Report
      |
      v
Task Queue
      |
      +----> Worker Agent

The goal here is to have a worker process the task rather than broadcast it to every subscriber.

The communication mechanism should therefore match the required delivery behavior.

Adding an Orchestrator Agent

A multi-agent system usually benefits from an orchestrator.

The orchestrator determines which agent should handle a task.

For example:

Code
User Request
     |
     v
Orchestrator
     |
     +---- Customer question → Support Agent
     |
     +---- Order question → Order Agent
     |
     +---- Documentation → Knowledge Agent

The routing decision can be based on application rules, an AI model, or a combination of both.

For predictable operations, deterministic rules are often easier to maintain.

For example:

JavaScript
function selectAgent(task) {
  switch (task.type) {
    case "order":
      return "order-agent";

    case "knowledge":
      return "knowledge-agent";

    case "support":
      return "support-agent";

    default:
      return "support-agent";
  }
}

An AI model can then be used where the classification requires natural-language understanding.

Sharing Conversation Identity

Agents often need to understand that multiple messages belong to the same user interaction.

A conversationId can provide this connection.

For example:

Code
conversationId = conv-501

Message 1 → Support Agent
Message 2 → Order Agent
Message 3 → Notification Agent
Message 4 → Support Agent

Each agent can attach the same conversation identifier to messages related to the interaction.

This makes it easier to trace the flow across the mesh.

It also helps applications separate one user's workflow from another.

Handling Agent Failures

Distributed agent systems must assume that an agent can fail.

For example:

Code
Orchestrator
     |
     v
Order Agent
     X
   Failed

The orchestrator should not wait indefinitely.

A production design should consider:

  • Timeouts
  • Retry policies
  • Maximum retry counts
  • Failure messages
  • Dead-letter handling
  • Agent health status
  • Correlation IDs
  • Logging and tracing

A retry should also be designed carefully.

If an operation creates an external side effect, blindly retrying it can create duplicate actions.

For example, retrying a read operation is usually easier than retrying a payment operation.

Making Agent Tasks Idempotent

An agent may receive the same message more than once depending on the delivery architecture.

The application should therefore consider idempotency.

For example:

Code
messageId = msg-1024

Before processing the message, the agent can check whether that message has already been handled.

A simplified approach could be:

JavaScript
const processed = await redis.get(
  `processed:${message.messageId}`
);

if (processed) {
  return;
}

await processTask(message);

await redis.set(
  `processed:${message.messageId}`,
  "1",
  { EX: 3600 }
);

The expiration prevents processed-message records from remaining indefinitely.

The exact retention period should depend on the application's retry and processing model.

Controlling Agent Access

Not every agent should be allowed to communicate with every other agent.

For example:

Code
Support Agent
    |
    +----> Order Agent
    |
    +----> Knowledge Agent

Billing Agent
    |
    +----> Payment Service

The application should define which communication paths are allowed.

This prevents accidental coupling and reduces the impact of a compromised or incorrectly configured agent.

Message validation should also be performed by the receiving agent.

Never assume that because a message arrived on a Redis channel, its contents are automatically trustworthy.

Monitoring an Agent Mesh

As the number of agents grows, debugging becomes more difficult.

A request might travel through several agents:

Code
User
  |
  v
Orchestrator
  |
  v
Knowledge Agent
  |
  v
Tool Agent
  |
  v
Database

Without tracing information, finding a slow or failed step can become difficult.

Useful fields include:

Code
messageId
conversationId
sourceAgent
targetAgent
task
createdAt
completedAt
status

Logging these identifiers consistently allows developers to reconstruct the workflow.

Metrics can also track:

  • Messages processed
  • Failed tasks
  • Retry counts
  • Processing duration
  • Queue depth
  • Agent availability
  • Timeout frequency

Common Mistakes

Creating Direct Connections Between Every Agent

If every agent communicates directly with every other agent, the architecture becomes difficult to maintain.

A communication layer provides a cleaner boundary.

Using One Redis Channel for Everything

A single communication channel can become difficult to manage.

Use clear naming and routing conventions.

For example:

Code
agent:order
agent:knowledge
agent:support
agent:notifications

Sending Large Agent Context Through Messages

Do not place an entire conversation history into every message.

Large context increases network traffic and processing requirements.

Store suitable shared state separately and pass references when appropriate.

Ignoring Duplicate Messages

Distributed systems can encounter retries and duplicate delivery.

Message handling should account for this possibility.

Giving Every Agent Full Access

Agents should only have access to the communication paths and resources they actually require.

Best Practices

  1. Define a consistent message contract before connecting multiple agents.
  2. Give every message a unique message ID.
  3. Use a conversation or correlation ID to connect related operations.
  4. Keep agent responsibilities clearly separated.
  5. Use Pub/Sub for broadcast-style events and queue-oriented mechanisms for task processing.
  6. Make important agent operations idempotent.
  7. Set reasonable timeouts and retry limits.
  8. Validate every incoming message.
  9. Avoid sending unnecessarily large context between agents.
  10. Monitor message latency, failures, retries, and agent health.
  11. Restrict which agents can communicate with each other.
  12. Keep business logic inside the appropriate agent rather than inside the communication layer.

Advantages and Disadvantages

AreaAdvantagesDisadvantages
Agent communicationReduces direct dependenciesAdds another infrastructure component
Shared stateAgents can coordinate through shared dataPoorly designed state can create coupling
ScalingAgents can scale independentlyDistributed scaling is more complex
Fault handlingRetries and routing can be centralizedDuplicate processing must be handled
DevelopmentAgents can remain focused on specific tasksMessage contracts require careful design
ObservabilityCentral communication provides useful tracing pointsDebugging still requires distributed tracing

When an Agent Mesh Makes Sense

An agent mesh is useful when an application has multiple specialized agents that need to collaborate.

It may be unnecessary for a small application where one agent handles the entire workflow.

For example:

Code
Simple Application
User → Single Agent → Database

does not necessarily need an agent mesh.

A more complex system may benefit from:

Code
User
  |
  v
Orchestrator
  |
  +----> Support Agent
  +----> Order Agent
  +----> Knowledge Agent
  +----> Notification Agent

The architecture should therefore grow with the application's requirements rather than introducing distributed infrastructure too early.

Summary

Redis can provide useful building blocks for connecting multiple AI agents into a coordinated system.

An agent mesh separates agent responsibilities from agent communication. The orchestrator can route tasks, specialized agents can perform focused operations, and Redis can provide communication and shared infrastructure between them.

A reliable design requires more than simply publishing messages. Message contracts, correlation IDs, retries, idempotency, access control, stable routing, and monitoring should all be considered as the system grows.

The most important design principle is to keep agents loosely coupled. Each agent should know what task it is responsible for and how to communicate with the rest of the system without needing to understand every internal implementation detail.