Time: 60 minutes | Difficulty: Intermediate
You've built agents that work in ideal conditions. But production systems need to handle errors, failures, retries, and persistent state. In this tutorial, we'll transform your agent into a robust, production-grade system.
By the end of this tutorial, you'll be able to:
- Implement comprehensive error handling
- Add retry logic with exponential backoff
- Handle tool execution failures gracefully
- Integrate server-side tools (Web Search)
- Implement logging and monitoring
- Build graceful degradation strategies
- Test error scenarios
We'll enhance our multi-tool agent with:
- Error Handling - Catch and handle all failure modes
- Retry Logic - Automatically retry transient failures
- Server-Side Tools - Integrate tools like Web Search
- Logging - Track agent behavior and issues
- Graceful Degradation - Continue working when tools fail
-
API Errors
- Rate limiting (429)
- Temporary outages (503)
- Authentication issues (401)
- Timeout errors
-
Tool Execution Errors
- Invalid input
- External API failures
- Calculation errors
- Resource unavailable
-
Agent Errors
- Infinite loops
- Context window overflow
- Malformed responses
- Unexpected stop reasons
-
State Management
- Lost conversation history
- Memory inconsistencies
- Token limit exceeded
try {
$response = $client->messages()->create([...]);
} catch (\ClaudePhp\Exceptions\RateLimitError $e) {
// Handle rate limiting
$retryAfter = $e->response->getHeaderLine('retry-after');
sleep($retryAfter);
// Retry...
} catch (\ClaudePhp\Exceptions\APIConnectionError $e) {
// Network/timeout issue
log_error("Connection failed: " . $e->getMessage());
// Retry with backoff...
} catch (\ClaudePhp\Exceptions\AuthenticationError $e) {
// Invalid API key - don't retry
log_error("Authentication failed");
throw $e;
} catch (\ClaudePhp\Exceptions\APIStatusError $e) {
// Other API errors
log_error("API error {$e->status_code}: {$e->message}");
// Retry if 5xx, fail if 4xx
} catch (Exception $e) {
// Unexpected error
log_error("Unexpected: " . $e->getMessage());
throw $e;
}function retryWithBackoff(callable $fn, $maxAttempts = 3) {
$attempt = 0;
$delay = 1000; // Start with 1 second
while ($attempt < $maxAttempts) {
try {
return $fn();
} catch (\ClaudePhp\Exceptions\RateLimitError $e) {
$attempt++;
if ($attempt >= $maxAttempts) throw $e;
$retryAfter = $e->response->getHeaderLine('retry-after');
$waitTime = $retryAfter ?: ($delay / 1000);
echo "Rate limited. Waiting {$waitTime}s...\n";
sleep($waitTime);
$delay *= 2; // Exponential backoff
} catch (\ClaudePhp\Exceptions\APIConnectionError $e) {
$attempt++;
if ($attempt >= $maxAttempts) throw $e;
echo "Connection error. Retrying in {$delay}ms...\n";
usleep($delay * 1000);
$delay *= 2;
}
}
}function executeToolSafely($toolName, $input) {
try {
$result = executeTool($toolName, $input);
return [
'success' => true,
'content' => $result
];
} catch (Exception $e) {
log_error("Tool {$toolName} failed: " . $e->getMessage());
return [
'success' => false,
'content' => "Error: " . $e->getMessage(),
'is_error' => true
];
}
}
// Use in ReAct loop
foreach ($response->content as $block) {
if ($block['type'] === 'tool_use') {
$result = executeToolSafely($block['name'], $block['input']);
$toolResults[] = [
'type' => 'tool_result',
'tool_use_id' => $block['id'],
'content' => $result['content'],
'is_error' => !$result['success']
];
}
}The Web Search Tool gives Claude access to real-time information from the web. Unlike custom tools, web search is executed server-side by Claude automatically!
$webSearchTool = [
'type' => 'web_search_20250305',
'name' => 'web_search',
'max_uses' => 3 // Limit searches per request
];
// Add to your tools
$tools = [$calculator, $weather, $webSearchTool];Note: Web search must be enabled in your organization's Claude Console.
Claude can:
- Search the web for current information
- Cite sources automatically from search results
- Answer questions beyond its knowledge cutoff
- Find real-time data like weather, prices, or news
// User: "What is the current version of PHP?"
// Claude will:
// - Decide to search based on the query
// - Execute web search automatically (server-side)
// - Provide answer with cited sources
// User: "What are the latest developments in quantum computing?"
// Claude will:
// - Perform web search
// - Synthesize information from multiple sources
// - Include citations in the responseclass AgentLogger {
public function logIteration($iteration, $response) {
$log = [
'timestamp' => date('Y-m-d H:i:s'),
'iteration' => $iteration,
'stop_reason' => $response->stop_reason,
'tokens' => [
'input' => $response->usage->input_tokens,
'output' => $response->usage->output_tokens
],
'tools_used' => $this->extractToolsUsed($response)
];
file_put_contents(
'agent.log',
json_encode($log) . "\n",
FILE_APPEND
);
}
private function extractToolsUsed($response) {
$tools = [];
foreach ($response->content as $block) {
if ($block['type'] === 'tool_use') {
$tools[] = $block['name'];
}
}
return $tools;
}
}Track these metrics:
- Success Rate: % of tasks completed successfully
- Average Iterations: How many loops per task
- Token Usage: Total tokens consumed
- Error Rate: % of requests that fail
- Tool Usage: Which tools are used most
- Latency: Time per request
When tools fail, continue working with reduced functionality:
function executeToolWithFallback($toolName, $input) {
try {
return executeTool($toolName, $input);
} catch (Exception $e) {
// Log the failure
log_error("Tool {$toolName} failed, using fallback");
// Return a fallback response
return "Tool temporarily unavailable. Please try again later.";
}
}
// Or disable failed tools
if ($toolFailureCount > 3) {
echo "Disabling unreliable tool: {$toolName}\n";
$tools = array_filter($tools, fn($t) => $t['name'] !== $toolName);
}// 1. Test rate limiting
function testRateLimiting() {
// Make many requests quickly
// Should trigger rate limit and retry
}
// 2. Test tool failure
function testToolFailure() {
// Force a tool to fail
// Agent should handle gracefully
}
// 3. Test max iterations
function testMaxIterations() {
// Give a task that requires many steps
// Should stop at limit without crashing
}
// 4. Test invalid input
function testInvalidInput() {
// Provide malformed data
// Should validate and reject cleanly
}
// 5. Test memory persistence
function testWebSearch() {
// Test search functionality
// Verify citations included
}Before deploying your agent:
- All errors caught and handled
- Retry logic implemented for transient failures
- Tool execution wrapped in try-catch
- Logging configured and tested
- Server-side tools configured (if needed)
- Iteration limits set appropriately
- Token usage monitored
- Graceful degradation strategies in place
- Error scenarios tested
- Documentation for operators
$client = new ClaudePhp(
apiKey: $apiKey,
timeout: 30.0, // 30 seconds
maxRetries: 2
);function validateCalculatorInput($expression) {
// Only allow safe characters
if (!preg_match('/^[0-9+\-*\/().\s]+$/', $expression)) {
throw new Exception("Invalid expression");
}
return true;
}class RateLimiter {
private $lastRequest = 0;
private $minInterval = 100; // ms
public function throttle() {
$now = microtime(true) * 1000;
$elapsed = $now - $this->lastRequest;
if ($elapsed < $this->minInterval) {
usleep(($this->minInterval - $elapsed) * 1000);
}
$this->lastRequest = microtime(true) * 1000;
}
}class CircuitBreaker {
private $failures = 0;
private $threshold = 5;
private $timeout = 60; // seconds
private $openedAt = null;
public function call(callable $fn) {
if ($this->isOpen()) {
throw new Exception("Circuit breaker is open");
}
try {
$result = $fn();
$this->recordSuccess();
return $result;
} catch (Exception $e) {
$this->recordFailure();
throw $e;
}
}
private function isOpen() {
if ($this->openedAt === null) return false;
if (time() - $this->openedAt > $this->timeout) {
$this->openedAt = null;
$this->failures = 0;
return false;
}
return true;
}
private function recordFailure() {
$this->failures++;
if ($this->failures >= $this->threshold) {
$this->openedAt = time();
}
}
private function recordSuccess() {
$this->failures = 0;
$this->openedAt = null;
}
}Before moving on, make sure you understand:
- Common failure modes in production
- How to catch and handle different exception types
- Retry logic with exponential backoff
- Tool error handling with is_error flag
- Using server-side tools like Web Search
- Logging and monitoring strategies
- Graceful degradation patterns
Your agent is now production-ready! But we can make it even smarter with planning, reflection, and extended thinking.
Learn advanced agentic patterns with planning and reflection!
Run the complete working example:
php tutorials/04-production-ready/production_agent.phpThe script demonstrates:
- ✅ Comprehensive error handling
- ✅ Retry logic with backoff
- ✅ Tool error handling
- ✅ Server-side tool integration
- ✅ Logging and monitoring
- ✅ Error scenario testing
- ✅ Graceful degradation