When multiple agents work together, three challenges emerge: how do requests reach the right agent (routing), how does information flow between agents (data flow), and how do agents maintain a consistent view of the world (state coordination). In this post, I’ll explore patterns for managing these critical aspects of multi-agent systems.
Routing in Multi-Agent Systems
Beyond simple orchestration, real systems face unpredictable streams of diverse requests. Routing ensures each request reaches the appropriate specialist, like a sophisticated mail sorting facility directing letters to the right destination.
flowchart LR
R[Requests] --> RT{Router}
RT -->|Type A| A1[Agent Pool 1]
RT -->|Type B| A2[Agent Pool 2]
RT -->|Urgent| A3[Priority Agent]
classDef orangeClass fill:#F39C12,stroke:#333,stroke-width:2px,color:#fff
class RT orangeClass
Three Core Routing Patterns
1. Content-Based Routing
Inspect the message content to decide where it goes:
# Keyword-based routing ifany(word in request_lower for word in ["account", "balance", "transfer"]): returnself.banking_agent.handle(request) elifany(word in request_lower for word in ["package", "mail", "shipping"]): returnself.postal_agent.handle(request) else: returnself.general_agent.handle(request)
As data moves between agents, it may need transformation, enhancement, or filtering. Like passing a baton in a relay race - the handoff needs to be clean.
Three Data Operations
flowchart LR
D[Data] --> E[Enhance]
E --> F[Filter]
F --> T[Transform]
T --> N[Next Agent]
classDef blueClass fill:#4A90E2,stroke:#333,stroke-width:2px,color:#fff
classDef greenClass fill:#27AE60,stroke:#333,stroke-width:2px,color:#fff
classDef pinkClass fill:#E74C3C,stroke:#333,stroke-width:2px,color:#fff
class E blueClass
class F greenClass
class T pinkClass
deffilter_for_agent(self, data: dict, agent_type: str) -> dict: if agent_type == "billing": # Billing agent doesn't need personal details return { "order_id": data["order_id"], "amount": data["amount"], "payment_method": data["payment_method"] } elif agent_type == "shipping": # Shipping agent doesn't need payment info return { "order_id": data["order_id"], "address": data["address"], "items": data["items"] } return data
Transformation - Changing structure or format:
1 2 3 4 5 6 7 8 9 10 11 12 13
deftransform_for_api(self, internal_data: dict) -> dict: # Convert internal format to external API format return { "orderId": internal_data["order_id"], # camelCase for API "customerInfo": { "name": internal_data["customer_name"], "email": internal_data["customer_email"] }, "lineItems": [ {"sku": item["id"], "qty": item["quantity"]} for item in internal_data["items"] ] }
State Management in Multi-Agent Systems
When multiple agents operate, they need a consistent understanding of the world. State management is the challenge of keeping everyone synchronized.
defread(self, key: str) -> tuple: """Return value and version number""" returnself.data.get(key), self.versions.get(key, 0)
defwrite(self, key: str, value, expected_version: int) -> bool: """ Write only if version matches (no one else changed it). Returns True if successful, False if conflict detected. """ current_version = self.versions.get(key, 0)
if current_version != expected_version: returnFalse# Conflict - another agent updated
self.data[key] = value self.versions[key] = current_version + 1 returnTrue
# Usage state = OptimisticState()
defupdate_with_retry(state, key, update_fn, max_retries=3): for _ inrange(max_retries): value, version = state.read(key) new_value = update_fn(value)
if state.write(key, new_value, version): returnTrue# Success
defexecute_with_rollback(self, actions: list): try: for action in actions: result = action.execute() self.completed_actions.append((action, result))
except Exception as e: # Rollback all completed actions in reverse order for action, result inreversed(self.completed_actions): action.rollback(result) raise
defcompensate(self): """Undo all actions if later step fails""" for action, result inreversed(self.completed_actions): action.rollback(result)
defminimal_response(self, request: str) -> str: return"We're experiencing issues. A support agent will contact you shortly."
Key Takeaways
Route intelligently: Content-based for specialization, round-robin for load balancing, priority-based for urgency
Manage data flow: Enhance, filter, and transform data between agents
Share state carefully: Use thread-safe patterns and pass only needed context
Handle conflicts: Predefined rules, optimistic locking, or human escalation
Plan for failure: Retry with backoff, fallback paths, and compensating actions
With proper routing, data flow, and state coordination, multi-agent systems can handle complex, dynamic workloads reliably. In the next post, I’ll explore Multi-Agent RAG and how to build complete end-to-end systems.
This is Part 13 of my series on building intelligent AI systems. Next: Multi-Agent RAG and building complete systems.
Comments