🔥 0
0
Lesson 10 of 10 25 min +150 XP

Production Deployment & Monitoring

Production Considerations

Moving agent harnesses from development to production requires careful planning:

Production Requirements

Deployment Architecture

Single-Agent Deployment

Production Deployment Architecture

Multi-Agent Deployment

// Kubernetes deployment for multi-agent harness
const deployment = {
  apiVersion: 'apps/v1',
  kind: 'Deployment',
  metadata: {
    name: 'agent-harness',
    labels: { app: 'ecommerce-agents' }
  },
  spec: {
    replicas: 3,
    selector: {
      matchLabels: { app: 'ecommerce-agents' }
    },
    template: {
      metadata: {
        labels: { app: 'ecommerce-agents' }
      },
      spec: {
        containers: [
          {
            name: 'planner-agent',
            image: 'agents/planner:v1.0',
            resources: {
              requests: { memory: '512Mi', cpu: '500m' },
              limits: { memory: '1Gi', cpu: '1000m' }
            },
            env: [
              { name: 'ANTHROPIC_API_KEY', valueFrom: { secretKeyRef: { name: 'api-keys', key: 'anthropic' } } },
              { name: 'AGENT_ROLE', value: 'planner' }
            ]
          },
          {
            name: 'generator-agent',
            image: 'agents/generator:v1.0',
            resources: {
              requests: { memory: '1Gi', cpu: '1000m' },
              limits: { memory: '2Gi', cpu: '2000m' }
            }
          },
          {
            name: 'evaluator-agent',
            image: 'agents/evaluator:v1.0',
            resources: {
              requests: { memory: '512Mi', cpu: '500m' },
              limits: { memory: '1Gi', cpu: '1000m' }
            }
          }
        ],
        volumes: [
          {
            name: 'shared-state',
            persistentVolumeClaim: { claimName: 'agent-state-pvc' }
          }
        ]
      }
    }
  }
};

Managed Agents API

Anthropic's hosted solution for production deployments:

import Anthropic from '@anthropic-ai/sdk';

const client = new Anthropic();

// Create a managed agent
const agent = await client.agents.create({
  name: 'ecommerce-builder',
  model: 'claude-sonnet-4-20250514',

  // Define tools the agent can use
  tools: [
    { type: 'filesystem', permissions: ['read', 'write'] },
    { type: 'bash', allowed_commands: ['npm', 'git', 'node'] },
    { type: 'browser', headless: true }
  ],

  // Safety guardrails
  guardrails: {
    max_session_duration: '4h',
    max_tokens_per_session: 1000000,
    allowed_domains: ['localhost', '*.example.com'],
    blocked_actions: ['delete_database', 'push_to_main']
  },

  // Cost controls
  budget: {
    max_cost_per_session: 10.00, // USD
    alert_threshold: 5.00
  }
});

// Start a session
const session = await client.agents.sessions.create({
  agent_id: agent.id,
  task: 'Build a product catalog page with filtering',
  context: {
    project_dir: '/workspace/ecommerce',
    existing_code: await loadProjectContext()
  }
});

// Stream session progress
for await (const event of session.stream()) {
  switch (event.type) {
    case 'tool_use':
      console.log(`🔧 Tool: ${event.tool} - ${event.action}`);
      break;
    case 'message':
      console.log(`💬 ${event.content}`);
      break;
    case 'checkpoint':
      console.log(`📸 Checkpoint: ${event.description}`);
      break;
    case 'complete':
      console.log(`✅ Session complete: ${event.result}`);
      break;
    case 'error':
      console.log(`❌ Error: ${event.error}`);
      break;
  }
}

Monitoring & Observability

Structured Logging

import { Logger, AgentLogEvent } from '@anthropic-ai/agent-sdk';

const logger = new Logger({
  service: 'ecommerce-agent',
  level: 'info',
  format: 'json',
  destination: 'stdout' // For container logging
});

// Log agent events
agent.on('tool_use', (event) => {
  logger.info('Agent tool use', {
    sessionId: event.sessionId,
    tool: event.tool,
    action: event.action,
    duration: event.duration,
    success: event.success
  });
});

agent.on('message', (event) => {
  logger.info('Agent message', {
    sessionId: event.sessionId,
    role: event.role,
    tokenCount: event.tokenCount
  });
});

agent.on('error', (event) => {
  logger.error('Agent error', {
    sessionId: event.sessionId,
    error: event.error.message,
    stack: event.error.stack,
    recoverable: event.recoverable
  });
});

Metrics Collection

import { Metrics, Counter, Histogram, Gauge } from '@anthropic-ai/agent-sdk';

const metrics = new Metrics({
  prefix: 'agent_harness',
  labels: { service: 'ecommerce' }
});

// Define metrics
const sessionCounter = metrics.counter({
  name: 'sessions_total',
  help: 'Total number of agent sessions',
  labels: ['status'] // 'success', 'failed', 'timeout'
});

const tokenHistogram = metrics.histogram({
  name: 'tokens_used',
  help: 'Tokens used per session',
  buckets: [1000, 10000, 50000, 100000, 500000]
});

const activeSessionsGauge = metrics.gauge({
  name: 'active_sessions',
  help: 'Currently active agent sessions'
});

const costGauge = metrics.gauge({
  name: 'session_cost_dollars',
  help: 'Cost of current session in dollars'
});

// Record metrics
agent.on('session_start', () => {
  activeSessionsGauge.inc();
});

agent.on('session_end', (event) => {
  activeSessionsGauge.dec();
  sessionCounter.inc({ status: event.status });
  tokenHistogram.observe(event.tokensUsed);
  costGauge.set(event.cost);
});

// Expose metrics endpoint
app.get('/metrics', async (req, res) => {
  res.set('Content-Type', metrics.contentType);
  res.end(await metrics.render());
});

Distributed Tracing

import { Tracer, SpanKind } from '@opentelemetry/api';
import { trace } from '@opentelemetry/api';

const tracer = trace.getTracer('agent-harness');

async function runAgentWithTracing(task: Task) {
  return tracer.startActiveSpan('agent.run', async (span) => {
    span.setAttributes({
      'agent.task_id': task.id,
      'agent.task_description': task.description
    });

    try {
      // Tool use spans
      agent.on('tool_use', (event) => {
        const toolSpan = tracer.startSpan(`tool.${event.tool}`, {
          kind: SpanKind.INTERNAL,
          attributes: {
            'tool.name': event.tool,
            'tool.action': event.action
          }
        });

        event.onComplete = () => {
          toolSpan.setAttributes({
            'tool.success': event.success,
            'tool.duration_ms': event.duration
          });
          toolSpan.end();
        };
      });

      // Run the agent
      const result = await agent.run(task);

      span.setAttributes({
        'agent.status': result.status,
        'agent.tokens_used': result.tokensUsed
      });

      return result;

    } catch (error) {
      span.recordException(error);
      span.setStatus({ code: SpanStatusCode.ERROR });
      throw error;

    } finally {
      span.end();
    }
  });
}

Safety Guardrails

Permission System

interface AgentPermissions {
  filesystem: {
    read: string[];      // Allowed read paths
    write: string[];     // Allowed write paths
    delete: string[];    // Allowed delete paths
  };

  network: {
    allowedHosts: string[];
    blockedHosts: string[];
    maxRequestsPerMinute: number;
  };

  execution: {
    allowedCommands: string[];
    blockedCommands: string[];
    maxExecutionTime: number;
  };

  api: {
    maxTokensPerSession: number;
    maxCostPerSession: number;
    rateLimit: number;
  };
}

const productionPermissions: AgentPermissions = {
  filesystem: {
    read: ['/workspace/**', '/config/**'],
    write: ['/workspace/**'],
    delete: ['/workspace/temp/**']
  },
  network: {
    allowedHosts: ['api.anthropic.com', 'localhost', '*.internal.com'],
    blockedHosts: ['*.malware.com'],
    maxRequestsPerMinute: 100
  },
  execution: {
    allowedCommands: ['npm', 'node', 'git', 'curl'],
    blockedCommands: ['rm -rf', 'sudo', 'chmod 777'],
    maxExecutionTime: 300000 // 5 minutes
  },
  api: {
    maxTokensPerSession: 1000000,
    maxCostPerSession: 10.00,
    rateLimit: 10 // requests per second
  }
};

Action Validation

class ActionValidator {
  constructor(private permissions: AgentPermissions) {}

  async validateAction(action: AgentAction): Promise<ValidationResult> {
    const validators = [
      this.validateFilesystem,
      this.validateNetwork,
      this.validateExecution,
      this.validateCost
    ];

    for (const validator of validators) {
      const result = await validator.call(this, action);
      if (!result.allowed) {
        return result;
      }
    }

    return { allowed: true };
  }

  private validateFilesystem(action: AgentAction): ValidationResult {
    if (action.type !== 'filesystem') return { allowed: true };

    const { operation, path } = action;

    // Check if path is allowed
    const allowedPaths = this.permissions.filesystem[operation];
    const isAllowed = allowedPaths.some(pattern =>
      minimatch(path, pattern)
    );

    if (!isAllowed) {
      return {
        allowed: false,
        reason: `Filesystem ${operation} not allowed for path: ${path}`
      };
    }

    return { allowed: true };
  }

  private validateExecution(action: AgentAction): ValidationResult {
    if (action.type !== 'bash') return { allowed: true };

    const { command } = action;

    // Check for blocked commands
    for (const blocked of this.permissions.execution.blockedCommands) {
      if (command.includes(blocked)) {
        return {
          allowed: false,
          reason: `Blocked command detected: ${blocked}`
        };
      }
    }

    // Check if command starts with allowed prefix
    const commandBase = command.split(' ')[0];
    if (!this.permissions.execution.allowedCommands.includes(commandBase)) {
      return {
        allowed: false,
        reason: `Command not in allowlist: ${commandBase}`
      };
    }

    return { allowed: true };
  }
}

Circuit Breaker

class AgentCircuitBreaker {
  private failures = 0;
  private lastFailure: Date | null = null;
  private state: 'closed' | 'open' | 'half-open' = 'closed';

  constructor(
    private threshold: number = 5,
    private timeout: number = 60000
  ) {}

  async execute<T>(fn: () => Promise<T>): Promise<T> {
    if (this.state === 'open') {
      if (Date.now() - this.lastFailure!.getTime() > this.timeout) {
        this.state = 'half-open';
      } else {
        throw new Error('Circuit breaker is open');
      }
    }

    try {
      const result = await fn();
      this.onSuccess();
      return result;
    } catch (error) {
      this.onFailure();
      throw error;
    }
  }

  private onSuccess() {
    this.failures = 0;
    this.state = 'closed';
  }

  private onFailure() {
    this.failures++;
    this.lastFailure = new Date();

    if (this.failures >= this.threshold) {
      this.state = 'open';
      console.log('🔴 Circuit breaker opened');
    }
  }
}

// Usage with agent
const breaker = new AgentCircuitBreaker(5, 60000);

async function runAgentSafely(task: Task) {
  return breaker.execute(() => agent.run(task));
}

Cost Management

class CostTracker {
  private sessionCosts: Map<string, number> = new Map();

  constructor(
    private maxCostPerSession: number = 10.00,
    private alertThreshold: number = 5.00
  ) {}

  trackTokens(sessionId: string, inputTokens: number, outputTokens: number) {
    // Claude pricing (example rates)
    const inputCost = inputTokens * 0.003 / 1000;  // $3 per million input
    const outputCost = outputTokens * 0.015 / 1000; // $15 per million output

    const totalCost = inputCost + outputCost;
    const currentCost = (this.sessionCosts.get(sessionId) || 0) + totalCost;
    this.sessionCosts.set(sessionId, currentCost);

    // Alert if approaching limit
    if (currentCost >= this.alertThreshold && currentCost - totalCost < this.alertThreshold) {
      console.log(`⚠️ Session ${sessionId} cost alert: ${currentCost.toFixed(2)}`);
    }

    // Enforce limit
    if (currentCost >= this.maxCostPerSession) {
      throw new Error(`Session ${sessionId} exceeded cost limit: ${currentCost.toFixed(2)}`);
    }

    return currentCost;
  }

  getSessionCost(sessionId: string): number {
    return this.sessionCosts.get(sessionId) || 0;
  }
}

Key Takeaways

  • Production requires infrastructure - Logging, metrics, tracing
  • Managed Agents simplifies deployment - Anthropic handles infrastructure
  • Safety guardrails are essential - Permissions, validation, circuit breakers
  • Monitor everything - Sessions, costs, errors, performance
  • Plan for failure - Auto-recovery, checkpoints, graceful degradation
  • Control costs - Token limits, budgets, alerts

Course Summary

Congratulations! You've completed the Agent Harnesses course. You now understand:

  • Why harnesses matter - Bridging context windows for long-running work
  • Architecture patterns - Two-agent and three-agent designs
  • Session management - State persistence across sessions
  • Communication - File-based protocols between agents
  • Testing - Browser automation for verification
  • Git integration - Version control for agents
  • Claude Agent SDK - Building with Anthropic's official library
  • Production deployment - Scaling, monitoring, safety
Next steps:
  • Build your own agent harness for a specific domain
  • Experiment with the Claude Agent SDK
  • Explore Managed Agents for production deployments
  • Join the Anthropic developer community

Happy building!

Building with Claude Agent SDK