By Sagar Shankaran, Founder of CallSphere
Discover how to apply chaos engineering to AI agent systems by designing controlled failure experiments, measuring blast radius, defining steady state, and building confidence in agent resilience under real-world conditions.
Key takeaways
AI agent systems have failure modes that traditional testing cannot catch. What happens when the LLM returns a malformed JSON tool call? What if a downstream API responds with a 200 but returns garbage data? What if latency spikes to 30 seconds mid-conversation?
Chaos engineering answers these questions by deliberately injecting failures in controlled environments and observing whether the system recovers gracefully. For AI agents, this is not optional — it is essential.
Before breaking things, you need to know what "working correctly" looks like. Steady state is a measurable baseline of normal agent behavior.
flowchart LR
PR(["PR opened"])
UNIT["Unit tests"]
EVAL["Eval harness<br/>PromptFoo or Braintrust"]
GOLD[("Golden set<br/>200 tagged cases")]
JUDGE["LLM as judge<br/>plus regex graders"]
SCORE["Aggregate score<br/>and per slice"]
GATE{"Score regress<br/>more than 2 percent?"}
BLOCK(["Block merge"])
MERGE(["Merge to main"])
PR --> UNIT --> EVAL --> GOLD --> JUDGE --> SCORE --> GATE
GATE -->|Yes| BLOCK
GATE -->|No| MERGE
style EVAL fill:#4f46e5,stroke:#4338ca,color:#fff
style GATE fill:#f59e0b,stroke:#d97706,color:#1f2937
style BLOCK fill:#dc2626,stroke:#b91c1c,color:#fff
style MERGE fill:#059669,stroke:#047857,color:#fff
from dataclasses import dataclass
@dataclass
class AgentSteadyState:
"""Defines what normal looks like for an agent system."""
task_completion_rate: float # e.g., 0.93
p95_latency_seconds: float # e.g., 4.2
error_rate: float # e.g., 0.02
safety_violation_rate: float # e.g., 0.0001
def is_within_bounds(self, current_completion: float,
current_latency: float,
current_error_rate: float) -> bool:
return (
current_completion >= self.task_completion_rate * 0.95
and current_latency <= self.p95_latency_seconds * 1.5
and current_error_rate <= self.error_rate * 2.0
)
baseline = AgentSteadyState(
task_completion_rate=0.93,
p95_latency_seconds=4.2,
error_rate=0.02,
safety_violation_rate=0.0001,
)
The bounds use multipliers rather than absolute thresholds. A 50% latency increase is acceptable during chaos; a 10x error rate spike is not.
Hear it before you finish reading
Talk to a live CallSphere AI voice agent in your browser — 60 seconds, no signup.
Each experiment follows a hypothesis-driven approach: state what you believe will happen, inject the fault, and measure reality against your prediction.
import asyncio
import random
from typing import Callable, Any
from datetime import datetime
@dataclass
class ChaosExperiment:
name: str
hypothesis: str
fault_type: str
blast_radius: str # "single_agent", "agent_pool", "infrastructure"
duration_seconds: int
rollback_procedure: str
class AgentChaosRunner:
def __init__(self, agent_pool, metrics_client, steady_state: AgentSteadyState):
self.agent_pool = agent_pool
self.metrics = metrics_client
self.steady_state = steady_state
async def inject_llm_timeout(self, timeout_rate: float = 0.3):
"""Simulate LLM provider timeouts on 30% of requests."""
original_call = self.agent_pool.llm_client.call
async def faulty_call(*args, **kwargs):
if random.random() < timeout_rate:
await asyncio.sleep(60)
raise TimeoutError("Simulated LLM timeout")
return await original_call(*args, **kwargs)
self.agent_pool.llm_client.call = faulty_call
return original_call # return for rollback
async def inject_tool_failures(self, tool_name: str, error_code: int = 500):
"""Make a specific tool return errors."""
original_handler = self.agent_pool.tool_registry.get(tool_name)
async def failing_tool(*args, **kwargs):
raise Exception(f"Simulated {error_code} from {tool_name}")
self.agent_pool.tool_registry.register(tool_name, failing_tool)
return original_handler
async def inject_memory_corruption(self, corruption_rate: float = 0.1):
"""Randomly corrupt agent memory/context entries."""
for agent in self.agent_pool.agents:
for entry in agent.memory:
if random.random() < corruption_rate:
entry.content = "CORRUPTED: " + entry.content[:20]
Each injection method returns the original implementation for clean rollback. Never run chaos experiments without a rollback path.
Blast radius determines how much of your system is affected by the experiment. Start small and expand only after gaining confidence.
# chaos-experiment-plan.yaml
experiments:
- name: "llm_timeout_single_agent"
blast_radius: "single_agent"
target: "agent-booking-001"
fault: "llm_timeout"
parameters:
timeout_rate: 0.5
duration_seconds: 300
steady_state_check_interval: 30
abort_conditions:
- "safety_violation_rate > 0.001"
- "customer_facing_errors > 5"
expected_behavior: "Agent retries with exponential backoff, falls back to cached response after 3 failures"
- name: "database_latency_pool"
blast_radius: "agent_pool"
target: "pool-customer-service"
fault: "database_latency"
parameters:
added_latency_ms: 2000
affected_percentage: 0.5
duration_seconds: 600
abort_conditions:
- "task_completion_rate < 0.80"
- "p99_latency > 30"
expected_behavior: "Agents degrade gracefully, skip non-critical DB lookups, serve from cache"
The abort conditions are critical. If any condition triggers, the experiment stops immediately and rolls back. For AI agents, always include a safety violation abort condition.
Still reading? Stop comparing — try CallSphere live.
CallSphere ships complete AI voice agents per industry — 14 tools for healthcare, 10 agents for real estate, 4 specialists for salons. See how it actually handles a call before you book a demo.
class ChaosExperimentRunner:
async def run_experiment(self, experiment: ChaosExperiment) -> dict:
# Capture pre-experiment metrics
pre_metrics = await self.metrics.snapshot()
# Inject the fault
rollback_fn = await self.inject_fault(experiment)
try:
# Monitor during experiment
violations = []
for _ in range(experiment.duration_seconds // 10):
await asyncio.sleep(10)
current = await self.metrics.snapshot()
if not self.steady_state.is_within_bounds(
current["completion_rate"],
current["p95_latency"],
current["error_rate"],
):
violations.append({
"timestamp": datetime.utcnow().isoformat(),
"metrics": current,
})
# Check abort conditions
if current.get("safety_violations", 0) > 0:
await rollback_fn()
return {"status": "aborted", "reason": "safety_violation"}
finally:
await rollback_fn()
post_metrics = await self.metrics.snapshot()
return {
"status": "completed",
"pre_metrics": pre_metrics,
"post_metrics": post_metrics,
"steady_state_violations": violations,
"hypothesis_confirmed": len(violations) == 0,
}
When the hypothesis is not confirmed, you have found a real resilience gap. This is the value of chaos engineering — finding weaknesses before your users do.
Start in staging environments until your team builds confidence. When moving to production, begin with the smallest possible blast radius — a single agent instance handling a tiny percentage of traffic. Always have abort conditions and automatic rollback. Never run chaos experiments on safety-critical agent functions without explicit approval.
Missing or inadequate retry logic for LLM API calls. Most agent frameworks assume the LLM will respond within a few seconds, but production LLM APIs experience latency spikes, rate limits, and partial outages regularly. Chaos testing typically reveals that agents hang indefinitely or crash instead of retrying with backoff and falling back.
Run a baseline suite of experiments after every major deployment. Schedule comprehensive chaos game days monthly. Critical path experiments — like LLM provider failover — should run weekly in staging. Automate experiments in CI/CD so they run before production deployments.
#ChaosEngineering #AIAgents #ResilienceTesting #FaultInjection #Reliability #AgenticAI #LearnAI #AIEngineering

Written by
Sagar Shankaran· Founder, CallSphere
LinkedInSagar Shankaran is the founder of CallSphere, where he builds production AI voice and chat agents deployed across healthcare, hospitality, real estate, and home services. He writes about agentic AI, LLM engineering, and shipping voice agents that handle real calls in production.
See how AI voice agents work for your industry. Live demo available -- no signup required.
A founder's guide to the personal AI assistant market: best AI assistant apps, business-grade options, and how CallSphere's voice agent fits in.
A founder's guide to free AI agents, low-code AI agent builders, and how to know when you should pay for a real platform like CallSphere.
Graphiti is the open-source temporal knowledge graph for AI agents in 2026. Learn how bi-temporal memory beats vector RAG for voice agents and long-running LLMs.
Chatbot app vs ChatGPT in 2026: a founder's clear take on the difference, when to use which, and how a real AI chatbot app development works.
How we built a fault-tolerant HVAC emergency triage and tech-dispatch platform on Kubernetes — three-tier CQRS, 11 micro-agents on the OpenAI Agents SDK + LangGraph, NATS JetStream, DTMF/SMS/WebSocket acceptance, circuit breakers, and an evaluation pipeline that catches regressions before they wake a tech at 3 AM.
Self-correction is now a property of the model, not the framework. What that means for production agent reliability, voice/chat fallbacks, and CallSphere.
© 2026 CallSphere Inc. All rights reserved.
Made within San Francisco