DEV Community

Kasi Yaswanth
Kasi Yaswanth

Posted on

Cascading Failures in Multi-Agent Systems

I still remember the day our support bot, powered by a LangGraph agent workflow, started behaving erratically. Customers would ask for help with their orders, and the bot would respond with irrelevant information or, worse, loop indefinitely. After digging into the logs, we discovered that the issue was caused by a cascading failure in our multi-agent system. One of the downstream services, responsible for fetching order data, had gone offline, and our agent workflow didn't know how to handle it.

As we investigated further, we realized that our agents were tightly coupled to the availability of these downstream services. If one service failed, the entire workflow would come to a grinding halt. We needed a way to detect when a service was down and recover from it gracefully. That's when we turned to MCP's Tool primitive.

The Tool primitive in MCP allows us to define a reusable piece of functionality that can be used across multiple agents. In our case, we created a Tool that would check the status of our downstream services and notify the agent workflow if any of them were unavailable. We could then use this information to recover from the failure and provide a better experience for our customers.

Here's an example of how we implemented this using Python and the MCP API:

import langgraph as lg
from mcp import Tool, ToolRequest

# Define the Tool that checks the status of our downstream services
class ServiceChecker(Tool):
    def __init__(self, service_urls):
        self.service_urls = service_urls

    def check_services(self):
        available_services = []
        for url in self.service_urls:
            try:
                # Simulate a request to the service
                response = requests.head(url)
                if response.status_code == 200:
                    available_services.append(url)
            except requests.RequestException:
                pass
        return available_services

# Create a LangGraph agent workflow that uses the ServiceChecker Tool
def create_workflow():
    graph = lg.StateGraph()
    service_checker = ServiceChecker(["https://service1.example.com", "https://service2.example.com"])

    # Add a node to the graph that checks the status of the services
    graph.add_node("check_services", service_checker.check_services)

    # Add a conditional edge to the graph that depends on the result of the service check
    graph.add_conditional_edges("check_services", [
        (lambda result: len(result) == 2, "proceed_with_workflow"),
        (lambda result: len(result) < 2, "handle_service_outage")
    ])

    # Add a node to the graph that handles the service outage
    graph.add_node("handle_service_outage", lambda: "Service outage detected. Please try again later.")

    return graph

# Create the workflow and execute it
workflow = create_workflow()
result = workflow.execute()
print(result)
Enter fullscreen mode Exit fullscreen mode

In this example, we define a ServiceChecker Tool that takes a list of service URLs and checks their status. We then create a LangGraph agent workflow that uses this Tool to check the status of the services and recover from any outages.

One practical gotcha we learned from this experience is that it's essential to consider the latency of the service checks when designing the agent workflow. If the service checks take too long, the workflow may timeout or become unresponsive. To mitigate this, we can use techniques like caching or asynchronous service checks to reduce the latency.

As we continue to build and deploy more complex multi-agent systems, we'll need to develop strategies for handling failures and exceptions. Tomorrow, we'll explore another critical aspect of building robust agentic AI systems, and I'm excited to share our learnings and insights with you.

Top comments (0)