HazelJS LogoHazelJS
Production Agent Runtime: Build Stateful AI Agents That Actually Work
HazelJS Blog

Production Agent Runtime: Build Stateful AI Agents That Actually Work

Author
HazelJS Team
12/16/2025
General
← Back to Blog

Stop building chatbots. Start building production-grade AI agents with stateful execution, tool orchestration, memory integration, and human-in-the-loop workflows. See real examples of support agents, sales agents, and multi-agent systems.

⚙️ Production-Grade Agent Runtime

Build stateful AI agents that actually work in production. Not chatbots—real agents with tools, memory, and workflows.

🎯 The Problem with Chatbots

Most AI applications are just chatbots—they respond to queries but forget everything, can't execute actions safely, and break under load. They're stateless, forgetful, and limited.

Production AI agents need:

  • ✅ Stateful execution that remembers context
  • ✅ Safe tool execution with approval workflows
  • ✅ Memory integration for conversation continuity
  • ✅ Pause/resume for long-running tasks
  • ✅ Observability for monitoring and debugging
  • ✅ Production features: circuit breakers, rate limiting, retries

That's exactly what HazelJS Agent Runtime provides—a production-grade execution engine for building real AI agents.

🚀 What is Agent Runtime?

Agent Runtime is a dedicated execution engine for stateful AI agents. Unlike stateless request handlers, it manages the entire agent lifecycle:

Runtime Capabilities

  • Controlled Execution Loop: Prevents infinite loops, enforces state transitions
  • State Persistence: Maintains context across steps and sessions
  • Tool Orchestration: Safe execution with approval workflows
  • Memory Integration: Automatic conversation tracking and entity memory
  • Event System: Comprehensive observability for monitoring
  • Production Features: Circuit breakers, rate limiting, retries, health checks

💼 Example 1: Customer Support Agent

Let's build a customer support agent that handles tickets, looks up orders, and processes refunds with approval:

import { Agent, Tool } from '@hazeljs/agent';
import { AgentRuntime } from '@hazeljs/agent';

@Agent({
  name: 'support-agent',
  description: 'Customer support agent for handling tickets',
  systemPrompt: 'You are a helpful customer support agent. Be friendly and professional.',
  enableMemory: true,
  enableRAG: true,
})
export class SupportAgent {
  @Tool({
    description: 'Look up order information by order ID',
    parameters: [
      {
        name: 'orderId',
        type: 'string',
        description: 'The order ID to lookup',
        required: true,
      },
    ],
  })
  async lookupOrder(input: { orderId: string }) {
    // Query your order database
    return {
      orderId: input.orderId,
      status: 'shipped',
      trackingNumber: 'TRACK123',
      items: ['Product A', 'Product B'],
    };
  }

  @Tool({
    description: 'Process a refund for an order',
    requiresApproval: true, // Requires human approval
    parameters: [
      {
        name: 'orderId',
        type: 'string',
        description: 'The order ID to refund',
        required: true,
      },
      {
        name: 'amount',
        type: 'number',
        description: 'Refund amount',
        required: true,
      },
    ],
  })
  async processRefund(input: { orderId: string; amount: number }) {
    // This will wait for approval before executing
    return {
      success: true,
      refundId: 'REF123',
      amount: input.amount,
    };
  }

  @Tool({
    description: 'Create a support ticket',
    parameters: [
      {
        name: 'subject',
        type: 'string',
        description: 'Ticket subject',
        required: true,
      },
      {
        name: 'description',
        type: 'string',
        description: 'Ticket description',
        required: true,
      },
    ],
  })
  async createTicket(input: { subject: string; description: string }) {
    // Create ticket in your system
    return {
      ticketId: 'TICKET-123',
      status: 'open',
      createdAt: new Date(),
    };
  }
}

Key Features Demonstrated:

  • ✅ Multiple tools with different capabilities
  • ✅ Approval workflow for sensitive operations (refunds)
  • ✅ Memory and RAG enabled for context-aware responses
  • ✅ Type-safe parameters with validation

💼 Example 2: Sales Agent with Approval Workflow

A sales agent that qualifies leads, schedules meetings, and creates quotes—with approval for high-value deals:

@Agent({
  name: 'sales-agent',
  description: 'Sales agent for lead qualification and deal management',
  systemPrompt: 'You are a professional sales agent. Qualify leads and create opportunities.',
  enableMemory: true,
})
export class SalesAgent {
  @Tool({
    description: 'Qualify a lead based on company size and budget',
    parameters: [
      { name: 'companyName', type: 'string', required: true },
      { name: 'companySize', type: 'string', required: true },
      { name: 'budget', type: 'number', required: true },
    ],
  })
  async qualifyLead(input: { companyName: string; companySize: string; budget: number }) {
    const score = input.budget > 100000 ? 'high' : input.budget > 50000 ? 'medium' : 'low';
    return {
      companyName: input.companyName,
      qualificationScore: score,
      recommendedAction: score === 'high' ? 'immediate_followup' : 'nurture',
    };
  }

  @Tool({
    description: 'Create a sales quote',
    requiresApproval: true, // Requires manager approval
    parameters: [
      { name: 'leadId', type: 'string', required: true },
      { name: 'amount', type: 'number', required: true },
      { name: 'products', type: 'array', required: true },
    ],
  })
  async createQuote(input: { leadId: string; amount: number; products: string[] }) {
    // Waits for approval before creating quote
    return {
      quoteId: 'QUOTE-123',
      leadId: input.leadId,
      amount: input.amount,
      status: 'pending_approval',
    };
  }

  @Tool({
    description: 'Schedule a meeting with a lead',
    parameters: [
      { name: 'leadId', type: 'string', required: true },
      { name: 'date', type: 'string', required: true },
      { name: 'duration', type: 'number', required: true },
    ],
  })
  async scheduleMeeting(input: { leadId: string; date: string; duration: number }) {
    return {
      meetingId: 'MEET-123',
      leadId: input.leadId,
      scheduledAt: input.date,
      duration: input.duration,
    };
  }
}

Runtime Features in Action:

  • ✅ Approval workflow for high-value quotes
  • ✅ Memory tracks lead interactions across sessions
  • ✅ Stateful execution maintains conversation context
  • ✅ Event system for tracking sales activities

💼 Example 3: Multi-Agent System

Coordinate multiple specialized agents working together:

// Research Agent - finds information
@Agent({ name: 'research-agent', enableRAG: true })
export class ResearchAgent {
  @Tool({ description: 'Research a topic' })
  async research(topic: string) {
    // Use RAG to find relevant information
    return { findings: '...' };
  }
}

// Writing Agent - creates content
@Agent({ name: 'writing-agent', enableMemory: true })
export class WritingAgent {
  @Tool({ description: 'Write an article' })
  async writeArticle(topic: string, findings: string) {
    // Generate content based on research
    return { article: '...' };
  }
}

// Coordinator - orchestrates agents
const runtime = new AgentRuntime({ /* ... */ });

// Register all agents
runtime.registerAgent(ResearchAgent);
runtime.registerAgent(WritingAgent);

// Execute research agent
const research = await runtime.execute('research-agent', 'AI trends 2024');

// Execute writing agent with research results
const article = await runtime.execute('writing-agent', 
  `Write article about: ${research.response}`,
  { initialContext: { research: research.response } }
);

Multi-Agent Benefits:

  • ✅ Specialized agents for different tasks
  • ✅ Runtime coordinates agent communication
  • ✅ Shared context and memory across agents
  • ✅ Event system tracks multi-agent workflows

💼 Example 4: RAG-Powered Research Agent

An agent that combines RAG retrieval with tool execution for comprehensive research:

@Agent({
  name: 'research-agent',
  description: 'Research agent with RAG integration',
  enableRAG: true,
  ragTopK: 5, // Retrieve top 5 relevant documents
})
export class ResearchAgent {
  constructor(private ragService: RAGService) {}

  @Tool({
    description: 'Search knowledge base for information',
    parameters: [
      { name: 'query', type: 'string', required: true },
    ],
  })
  async searchKnowledgeBase(input: { query: string }) {
    // RAG automatically retrieves relevant context before tool execution
    const results = await this.ragService.search(input.query, { topK: 5 });
    return {
      documents: results.documents,
      totalResults: results.totalResults,
    };
  }

  @Tool({
    description: 'Summarize research findings',
    parameters: [
      { name: 'findings', type: 'array', required: true },
    ],
  })
  async summarizeFindings(input: { findings: string[] }) {
    // Agent uses RAG context + findings to create summary
    return {
      summary: '...',
      keyPoints: ['...'],
    };
  }
}

RAG Integration Benefits:

  • ✅ Automatic context retrieval before reasoning
  • ✅ Knowledge base integration
  • ✅ Context-aware responses
  • ✅ Memory of previous research queries

🔧 Runtime Features Deep Dive

1. Stateful Execution

Unlike stateless handlers, the runtime maintains state across steps:

// First interaction
const result1 = await runtime.execute('support-agent', 
  'My name is John and I need help with order #12345',
  { sessionId: 'user-123' }
);

// Later interaction - agent remembers
const result2 = await runtime.execute('support-agent',
  'What was my order status?',
  { sessionId: 'user-123' }
);
// Agent remembers: "John's order #12345 status is shipped"

2. Tool Approval Workflow

Human-in-the-loop for sensitive operations:

// Subscribe to approval requests
runtime.on(AgentEventType.TOOL_APPROVAL_REQUESTED, async (event) => {
  const { requestId, toolName, input } = event.data;
  
  console.log(`Approval needed for ${toolName}:`, input);
  
  // Your approval logic
  if (shouldApprove(input)) {
    runtime.approveToolExecution(requestId, 'admin-user');
  } else {
    runtime.rejectToolExecution(requestId);
  }
});

// Agent execution pauses until approval
const result = await runtime.execute('sales-agent', 
  'Create a quote for $500,000 deal'
);
// Execution pauses, waits for approval, then continues

3. Pause and Resume

Support for long-running workflows:

// Execute agent
const result = await runtime.execute('agent', 'Start complex task');

// Agent might pause for user input
if (result.state === 'waiting_for_input') {
  // Get user response
  const userResponse = await getUserInput();
  
  // Resume execution
  const resumed = await runtime.resume(result.executionId, userResponse);
}

4. Comprehensive Observability

Monitor everything with the event system:

// Track execution lifecycle
runtime.on(AgentEventType.EXECUTION_STARTED, (event) => {
  logger.info('Agent started', { executionId: event.executionId });
});

runtime.on(AgentEventType.STEP_COMPLETED, (event) => {
  logger.info('Step completed', { 
    step: event.data.stepNumber,
    action: event.data.action 
  });
});

runtime.on(AgentEventType.TOOL_EXECUTION_COMPLETED, (event) => {
  metrics.trackToolUsage(event.data.toolName, event.data.duration);
});

// Get metrics
const metrics = runtime.getMetrics();
console.log('Success rate:', metrics.executions.successRate);
console.log('Average latency:', metrics.performance.averageDuration);

5. Production Features

Built-in resilience and reliability:

const runtime = new AgentRuntime({
  // Circuit breaker - prevents cascading failures
  enableCircuitBreaker: true,
  circuitBreakerConfig: {
    failureThreshold: 5,
    resetTimeout: 30000,
  },
  
  // Rate limiting - control resource usage
  enableRateLimiting: true,
  rateLimitConfig: {
    tokensPerMinute: 60,
    burstSize: 10,
  },
  
  // Metrics - track performance
  enableMetrics: true,
  
  // Health checks
  healthCheckConfig: {
    enabled: true,
    interval: 60000,
  },
});

📊 Real-World Use Cases

Customer Support

  • • Handle support tickets
  • • Look up orders and accounts
  • • Process refunds with approval
  • • Maintain conversation history
  • • Escalate to human agents

Sales Automation

  • • Qualify leads automatically
  • • Schedule meetings
  • • Create quotes with approval
  • • Track deal progression
  • • Generate sales reports

Research & Analysis

  • • Research topics with RAG
  • • Generate reports
  • • Analyze data
  • • Multi-hop reasoning
  • • Source verification

Workflow Automation

  • • Multi-step business processes
  • • Pause/resume long tasks
  • • Human-in-the-loop approvals
  • • State persistence
  • • Error recovery

🎨 Getting Started

Ready to build your first production agent? Here's how:

# 1. Install packages
npm install @hazeljs/agent @hazeljs/rag @hazeljs/ai

# 2. Generate your first agent
npx @hazeljs/cli generate agent support-agent

# 3. Customize the generated agent
# 4. Set up the runtime
# 5. Execute your agent!

🚀 Why Agent Runtime Matters

Building production AI agents without a runtime means:

  • ❌ Manual state management (error-prone)
  • ❌ No tool orchestration (unsafe)
  • ❌ No memory integration (forgetful)
  • ❌ No observability (hard to debug)
  • ❌ No production features (unreliable)

With Agent Runtime:

  • ✅ Automatic state management
  • ✅ Safe tool orchestration with approval
  • ✅ Automatic memory integration
  • ✅ Comprehensive observability
  • ✅ Production-ready features built-in

Build Production Agents Today

Start building stateful AI agents with HazelJS Agent Runtime

🎯 Conclusion

Agent Runtime is more than a library—it's a production-grade execution engine that handles everything your agents need: state management, tool orchestration, memory integration, and observability.

Whether you're building customer support agents, sales automation, research assistants, or workflow automation, Agent Runtime provides the foundation for building real, production-ready AI agents.

Stop building chatbots. Start building agents.