Skip to content

Agentic AI Best Practices

Production-ready patterns and best practices for building robust, scalable, and maintainable AI agent systems.

Agent Design Principles

1. Single Responsibility Principle

Each agent should have a clear, focused purpose:

php
// ✅ Good: Focused agent
class EmailAgent extends Agent
{
    public function sendEmail(EmailRequest $request): EmailResult
    {
        // Single responsibility: email operations only
    }
}

// ❌ Bad: Too many responsibilities
class CommunicationAgent extends Agent
{
    public function sendEmail() { /* ... */ }
    public function sendSMS() { /* ... */ }
    public function makePhoneCall() { /* ... */ }
    public function postToSocialMedia() { /* ... */ }
}

2. Fail-Safe Design

Design agents to fail gracefully and recover automatically:

php
class ResilientAgent extends Agent
{
    private CircuitBreaker $circuitBreaker;
    private RetryPolicy $retryPolicy;
    private FallbackHandler $fallbackHandler;

    public function executeTask(Task $task): TaskResult
    {
        try {
            return $this->circuitBreaker->call(function () use ($task) {
                return $this->retryPolicy->execute(function () use ($task) {
                    return $this->performTask($task);
                });
            });

        } catch (\Exception $e) {
            // Use fallback mechanism
            return $this->fallbackHandler->handle($task, $e);
        }
    }
}

3. Observable Behavior

Make agent behavior transparent and monitorable:

php
class ObservableAgent extends Agent
{
    private MetricsCollector $metrics;
    private EventEmitter $events;
    private Logger $logger;

    protected function executeAction(AgentAction $action): ActionResult
    {
        $startTime = microtime(true);

        // Log action start
        $this->logger->info('Agent action started', [
            'agent_id' => $this->getId(),
            'action' => $action->name,
            'parameters' => $action->parameters
        ]);

        // Emit event
        $this->events->emit('action.started', $action);

        try {
            $result = parent::executeAction($action);

            // Record metrics
            $duration = microtime(true) - $startTime;
            $this->metrics->record('action.duration', $duration, [
                'action' => $action->name,
                'success' => $result->success
            ]);

            // Log success
            $this->logger->info('Agent action completed', [
                'agent_id' => $this->getId(),
                'action' => $action->name,
                'duration' => $duration,
                'success' => $result->success
            ]);

            $this->events->emit('action.completed', $action, $result);

            return $result;

        } catch (\Exception $e) {
            // Record failure metrics
            $duration = microtime(true) - $startTime;
            $this->metrics->record('action.error', 1, [
                'action' => $action->name,
                'error_type' => get_class($e)
            ]);

            // Log error
            $this->logger->error('Agent action failed', [
                'agent_id' => $this->getId(),
                'action' => $action->name,
                'error' => $e->getMessage(),
                'duration' => $duration
            ]);

            $this->events->emit('action.failed', $action, $e);

            throw $e;
        }
    }
}

State Management

Stateless Agent Design

Prefer stateless agents for better scalability:

php
class StatelessAgent extends Agent
{
    // ✅ Good: External state storage
    public function processRequest(AgentRequest $request): AgentResponse
    {
        // Load state from external storage
        $state = $this->stateStore->load($request->sessionId);

        // Process request with loaded state
        $response = $this->process($request, $state);

        // Save updated state
        $this->stateStore->save($request->sessionId, $response->newState);

        return $response;
    }
}

// ❌ Avoid: Internal state that doesn't scale
class StatefulAgent extends Agent
{
    private array $internalState = []; // This doesn't scale across instances
}

Context Management

php
class ContextAwareAgent extends Agent
{
    private ContextManager $contextManager;

    public function processWithContext(string $request, string $sessionId): AgentResponse
    {
        // Load conversation context
        $context = $this->contextManager->getContext($sessionId);

        // Add current request to context
        $context->addMessage('user', $request);

        // Process with full context
        $response = $this->processRequest($request, $context);

        // Update context with response
        $context->addMessage('assistant', $response->message);

        // Save updated context
        $this->contextManager->saveContext($sessionId, $context);

        return $response;
    }
}

Error Handling Patterns

Comprehensive Error Recovery

php
class ErrorRecoveryAgent extends Agent
{
    private ErrorClassifier $classifier;
    private RecoveryStrategyFactory $strategyFactory;

    public function handleError(\Exception $error, AgentContext $context): RecoveryResult
    {
        // Classify the error
        $classification = $this->classifier->classify($error);

        // Select appropriate recovery strategy
        $strategy = $this->strategyFactory->getStrategy($classification);

        // Attempt recovery
        $recoveryResult = $strategy->recover($error, $context);

        // Log recovery attempt
        $this->logRecoveryAttempt($error, $classification, $recoveryResult);

        return $recoveryResult;
    }

    private function logRecoveryAttempt(
        \Exception $error,
        ErrorClassification $classification,
        RecoveryResult $result
    ): void {
        $this->logger->info('Error recovery attempted', [
            'error_type' => get_class($error),
            'error_message' => $error->getMessage(),
            'classification' => $classification->type,
            'recovery_strategy' => $result->strategyUsed,
            'recovery_success' => $result->success
        ]);
    }
}

Graceful Degradation

php
class GracefulAgent extends Agent
{
    private array $fallbackStrategies = [];

    public function executeWithFallback(AgentAction $action): ActionResult
    {
        $strategies = $this->getFallbackStrategies($action);

        foreach ($strategies as $strategy) {
            try {
                return $strategy->execute($action);
            } catch (\Exception $e) {
                $this->logger->warning("Strategy failed, trying next", [
                    'strategy' => get_class($strategy),
                    'error' => $e->getMessage()
                ]);

                // Continue to next strategy
            }
        }

        throw new AgentException("All fallback strategies failed for action: {$action->name}");
    }

    private function getFallbackStrategies(AgentAction $action): array
    {
        return [
            new PrimaryStrategy($action),
            new CachedResultStrategy($action),
            new SimplifiedStrategy($action),
            new ManualInterventionStrategy($action)
        ];
    }
}

Performance Optimization

Caching Strategies

php
class CachingAgent extends Agent
{
    private CacheManager $cache;
    private array $cacheStrategies;

    public function processRequest(AgentRequest $request): AgentResponse
    {
        $cacheKey = $this->generateCacheKey($request);
        $strategy = $this->selectCacheStrategy($request);

        // Check cache first
        if ($strategy->shouldUseCache($request)) {
            $cached = $this->cache->get($cacheKey);
            if ($cached && !$this->isCacheExpired($cached, $strategy)) {
                return $cached['response'];
            }
        }

        // Process request
        $response = $this->processRequestInternal($request);

        // Cache response if appropriate
        if ($strategy->shouldCache($request, $response)) {
            $this->cache->set($cacheKey, [
                'response' => $response,
                'timestamp' => time(),
                'ttl' => $strategy->getTtl($request)
            ]);
        }

        return $response;
    }

    private function selectCacheStrategy(AgentRequest $request): CacheStrategy
    {
        // Select caching strategy based on request type
        return match($request->type) {
            'data_retrieval' => new AggressiveCacheStrategy(),
            'computation' => new ModerateeCacheStrategy(),
            'real_time' => new NoCacheStrategy(),
            default => new DefaultCacheStrategy()
        };
    }
}

Parallel Processing

php
class ParallelProcessingAgent extends Agent
{
    private TaskPartitioner $partitioner;
    private ResultAggregator $aggregator;

    public function processLargeTask(LargeTask $task): TaskResult
    {
        // Partition task into smaller chunks
        $chunks = $this->partitioner->partition($task);

        echo "📊 Processing {$task->name} in " . count($chunks) . " parallel chunks\n";

        // Process chunks in parallel
        $promises = [];
        foreach ($chunks as $index => $chunk) {
            $promises[$index] = async(function () use ($chunk, $index) {
                echo "  ⚙️ Processing chunk {$index}...\n";

                $result = $this->processChunk($chunk);

                echo "  ✅ Chunk {$index} completed\n";

                return $result;
            });
        }

        // Wait for all chunks to complete
        $results = Promise::all($promises)->await();

        // Aggregate results
        $aggregatedResult = $this->aggregator->aggregate($results);

        echo "🎉 Large task {$task->name} completed successfully\n";

        return $aggregatedResult;
    }

    private function processChunk(TaskChunk $chunk): ChunkResult
    {
        // Use MCP tools to process individual chunk
        $result = $this->act(new AgentAction(
            'mcp_tool',
            'data_processor:process_chunk',
            $chunk->getData()
        ));

        return new ChunkResult(
            $result->success,
            $result->data,
            $chunk->id
        );
    }
}

Security Best Practices

Secure Agent Communication

php
class SecureAgent extends Agent
{
    private EncryptionService $encryption;
    private AuthenticationService $auth;
    private AuditLogger $auditLogger;

    public function sendSecureMessage(string $targetAgent, Message $message): void
    {
        // Authenticate sender
        $authToken = $this->auth->authenticate($this->getId());

        // Encrypt message
        $encryptedMessage = $this->encryption->encrypt($message->toJson());

        // Sign message for integrity
        $signature = $this->encryption->sign($encryptedMessage, $this->getPrivateKey());

        // Send secure message
        $secureMessage = new SecureMessage(
            $encryptedMessage,
            $signature,
            $authToken,
            $this->getId(),
            $targetAgent
        );

        $this->sendMessage($secureMessage);

        // Audit log
        $this->auditLogger->logSecureMessage($this->getId(), $targetAgent, $message->type);
    }

    public function receiveSecureMessage(SecureMessage $message): Message
    {
        // Verify authentication
        if (!$this->auth->verifyToken($message->authToken)) {
            throw new SecurityException('Invalid authentication token');
        }

        // Verify signature
        if (!$this->encryption->verifySignature(
            $message->encryptedContent,
            $message->signature,
            $this->getPublicKey($message->fromAgent)
        )) {
            throw new SecurityException('Message signature verification failed');
        }

        // Decrypt message
        $decryptedContent = $this->encryption->decrypt($message->encryptedContent);

        // Audit log
        $this->auditLogger->logSecureMessageReceived($message->fromAgent, $this->getId());

        return Message::fromJson($decryptedContent);
    }
}

Input Validation and Sanitization

php
class ValidatingAgent extends Agent
{
    private InputValidator $validator;
    private InputSanitizer $sanitizer;

    public function processRequest(AgentRequest $request): AgentResponse
    {
        // Validate input structure
        $validationResult = $this->validator->validate($request);

        if (!$validationResult->isValid) {
            throw new ValidationException(
                'Invalid request: ' . implode(', ', $validationResult->errors)
            );
        }

        // Sanitize inputs
        $sanitizedRequest = $this->sanitizer->sanitize($request);

        // Process sanitized request
        return $this->processValidatedRequest($sanitizedRequest);
    }

    private function processValidatedRequest(AgentRequest $request): AgentResponse
    {
        // Safe to process - inputs are validated and sanitized
        return parent::processRequest($request);
    }
}

Testing Strategies

Agent Unit Testing

php
class AgentTestCase extends TestCase
{
    protected function createMockMcpClient(array $toolResponses = []): MockMcpClient
    {
        $client = new MockMcpClient();

        foreach ($toolResponses as $toolName => $response) {
            $client->addToolResponse($toolName, $response);
        }

        return $client;
    }

    protected function createTestAgent(array $mcpClients = []): Agent
    {
        return new TestAgent('test-api-key', $mcpClients);
    }

    public function testAgentBehavior(): void
    {
        $mockClient = $this->createMockMcpClient([
            'get_weather' => ['content' => [['type' => 'text', 'text' => 'Sunny, 25°C']]]
        ]);

        $agent = $this->createTestAgent(['weather' => $mockClient]);

        $response = $agent->processRequest('What\'s the weather like?');

        $this->assertStringContains('Sunny', $response->message);
        $this->assertTrue($response->success);
    }
}

Integration Testing

php
class MultiAgentIntegrationTest extends TestCase
{
    private AgentOrchestrator $orchestrator;
    private array $testAgents = [];

    protected function setUp(): void
    {
        $this->orchestrator = new AgentOrchestrator();

        // Create test agents with mock MCP servers
        $this->testAgents['inventory'] = $this->createInventoryAgent();
        $this->testAgents['payment'] = $this->createPaymentAgent();

        foreach ($this->testAgents as $id => $agent) {
            $this->orchestrator->registerAgent($id, $agent, $agent->getCapabilities());
        }
    }

    public function testWorkflowExecution(): void
    {
        $workflow = new Workflow('test-workflow', [
            new WorkflowStep('check_inventory', ['inventory']),
            new WorkflowStep('process_payment', ['payment'])
        ]);

        $result = $this->orchestrator->executeWorkflow($workflow);

        $this->assertTrue($result->success);
        $this->assertCount(2, $result->stepResults);
    }
}

Monitoring and Observability

Comprehensive Monitoring

php
class AgentMonitoringSystem
{
    private MetricsCollector $metrics;
    private HealthChecker $healthChecker;
    private AlertManager $alertManager;
    private PerformanceAnalyzer $analyzer;

    public function startMonitoring(array $agents): void
    {
        async(function () use ($agents) {
            while (true) {
                foreach ($agents as $agentId => $agent) {
                    $this->monitorAgent($agentId, $agent);
                }

                // Analyze overall system performance
                $this->analyzeSystemPerformance();

                delay(30000)->await(); // Monitor every 30 seconds
            }
        })->await();
    }

    private function monitorAgent(string $agentId, Agent $agent): void
    {
        try {
            // Collect agent metrics
            $metrics = $agent->getMetrics();
            $this->metrics->record("agent.{$agentId}", $metrics);

            // Health check
            $health = $this->healthChecker->check($agent);

            if (!$health->isHealthy) {
                $this->alertManager->triggerAlert("Agent {$agentId} unhealthy", $health);
            }

            // Performance analysis
            $performance = $this->analyzer->analyze($agentId, $metrics);

            if ($performance->hasIssues()) {
                $this->handlePerformanceIssues($agentId, $performance);
            }

        } catch (\Exception $e) {
            $this->alertManager->triggerAlert("Failed to monitor agent {$agentId}", [
                'error' => $e->getMessage()
            ]);
        }
    }

    private function analyzeSystemPerformance(): void
    {
        $systemMetrics = $this->metrics->getSystemMetrics();
        $analysis = $this->analyzer->analyzeSystem($systemMetrics);

        if ($analysis->needsScaling) {
            $this->triggerAutoScaling($analysis);
        }

        if ($analysis->hasBottlenecks) {
            $this->identifyAndResolveBottlenecks($analysis);
        }
    }
}

Performance Metrics

php
class AgentPerformanceTracker
{
    private array $metrics = [];

    public function trackOperation(string $operation, callable $callback): mixed
    {
        $startTime = microtime(true);
        $startMemory = memory_get_usage();

        try {
            $result = $callback();

            $this->recordSuccess($operation, $startTime, $startMemory);

            return $result;

        } catch (\Exception $e) {
            $this->recordFailure($operation, $startTime, $startMemory, $e);
            throw $e;
        }
    }

    private function recordSuccess(string $operation, float $startTime, int $startMemory): void
    {
        $duration = microtime(true) - $startTime;
        $memoryUsed = memory_get_usage() - $startMemory;

        $this->metrics[$operation][] = [
            'success' => true,
            'duration' => $duration,
            'memory_used' => $memoryUsed,
            'timestamp' => time()
        ];
    }

    public function getOperationStats(string $operation): OperationStats
    {
        $operationMetrics = $this->metrics[$operation] ?? [];

        if (empty($operationMetrics)) {
            return new OperationStats(0, 0, 0, 0);
        }

        $successCount = count(array_filter($operationMetrics, fn($m) => $m['success']));
        $totalCount = count($operationMetrics);
        $avgDuration = array_sum(array_column($operationMetrics, 'duration')) / $totalCount;
        $avgMemory = array_sum(array_column($operationMetrics, 'memory_used')) / $totalCount;

        return new OperationStats(
            $successCount / $totalCount, // Success rate
            $avgDuration,
            $avgMemory,
            $totalCount
        );
    }
}

Production Deployment

Agent Lifecycle Management

php
class AgentLifecycleManager
{
    private array $agents = [];
    private ConfigurationManager $config;
    private HealthMonitor $healthMonitor;

    public function deployAgent(string $agentId, AgentConfiguration $config): void
    {
        echo "🚀 Deploying agent: {$agentId}\n";

        try {
            // Create agent instance
            $agent = $this->createAgent($config);

            // Initialize agent
            $agent->initialize();

            // Health check before activation
            $health = $this->healthMonitor->check($agent);

            if (!$health->isHealthy) {
                throw new DeploymentException("Agent failed health check: {$health->issues}");
            }

            // Activate agent
            $this->agents[$agentId] = $agent;
            $agent->activate();

            echo "✅ Agent {$agentId} deployed successfully\n";

        } catch (\Exception $e) {
            echo "❌ Failed to deploy agent {$agentId}: {$e->getMessage()}\n";
            throw $e;
        }
    }

    public function updateAgent(string $agentId, AgentConfiguration $newConfig): void
    {
        echo "🔄 Updating agent: {$agentId}\n";

        if (!isset($this->agents[$agentId])) {
            throw new \InvalidArgumentException("Agent {$agentId} not found");
        }

        $currentAgent = $this->agents[$agentId];

        try {
            // Create new agent instance
            $newAgent = $this->createAgent($newConfig);
            $newAgent->initialize();

            // Graceful transition
            $currentAgent->prepareForShutdown();

            // Health check new agent
            $health = $this->healthMonitor->check($newAgent);

            if (!$health->isHealthy) {
                throw new UpdateException("New agent failed health check");
            }

            // Switch agents
            $this->agents[$agentId] = $newAgent;
            $newAgent->activate();

            // Shutdown old agent
            $currentAgent->shutdown();

            echo "✅ Agent {$agentId} updated successfully\n";

        } catch (\Exception $e) {
            echo "❌ Failed to update agent {$agentId}: {$e->getMessage()}\n";

            // Rollback if possible
            if (isset($newAgent)) {
                $newAgent->shutdown();
            }

            throw $e;
        }
    }
}

Configuration Management

php
class AgentConfigurationManager
{
    private array $configurations = [];
    private ConfigValidator $validator;
    private VersionManager $versionManager;

    public function loadConfiguration(string $configPath): AgentConfiguration
    {
        $configData = $this->loadConfigFile($configPath);

        // Validate configuration
        $validation = $this->validator->validate($configData);

        if (!$validation->isValid) {
            throw new ConfigurationException(
                'Invalid configuration: ' . implode(', ', $validation->errors)
            );
        }

        // Create configuration object
        $config = new AgentConfiguration($configData);

        // Version tracking
        $this->versionManager->trackConfiguration($config);

        return $config;
    }

    public function updateConfiguration(string $agentId, array $updates): void
    {
        $currentConfig = $this->configurations[$agentId];
        $newConfig = $currentConfig->merge($updates);

        // Validate updated configuration
        $validation = $this->validator->validate($newConfig->toArray());

        if (!$validation->isValid) {
            throw new ConfigurationException('Invalid configuration update');
        }

        // Apply configuration
        $this->configurations[$agentId] = $newConfig;

        // Notify agent of configuration change
        $this->notifyConfigurationChange($agentId, $newConfig);
    }
}

Documentation and Maintenance

Self-Documenting Agents

php
class DocumentedAgent extends Agent
{
    public function getCapabilityDocumentation(): array
    {
        return [
            'capabilities' => $this->getCapabilities(),
            'tools' => $this->getAvailableTools(),
            'performance_characteristics' => $this->getPerformanceProfile(),
            'dependencies' => $this->getDependencies(),
            'configuration_options' => $this->getConfigurationSchema(),
            'examples' => $this->getUsageExamples()
        ];
    }

    public function generateApiDocumentation(): string
    {
        $doc = "# {$this->getName()} Agent API\n\n";
        $doc .= "## Description\n{$this->getDescription()}\n\n";

        $doc .= "## Capabilities\n";
        foreach ($this->getCapabilities() as $capability) {
            $doc .= "- {$capability}\n";
        }

        $doc .= "\n## Available Operations\n";
        foreach ($this->getAvailableOperations() as $operation) {
            $doc .= "### {$operation->name}\n";
            $doc .= "{$operation->description}\n\n";
            $doc .= "**Parameters:**\n";
            foreach ($operation->parameters as $param) {
                $doc .= "- `{$param->name}` ({$param->type}): {$param->description}\n";
            }
            $doc .= "\n";
        }

        return $doc;
    }
}

See Also

Released under the MIT License.