Overview

Plugin components are the building blocks that give agents their capabilities. Each component type serves a specific purpose in the agent’s decision-making and interaction flow. For system architecture, see Plugin Architecture.

Component Types

ComponentPurposeWhen Executed
ActionsTasks agents can performWhen agent decides to take action
ProvidersSupply contextual dataBefore actions/decisions
EvaluatorsProcess and extract from responsesAfter agent generates response
ServicesManage stateful connectionsThroughout agent lifecycle

Actions

Actions are discrete tasks agents can perform. They represent the agent’s capabilities - what it can DO.

Action Interface

Actions define discrete tasks that agents can perform. Each action has:
  • name: Unique identifier for the action
  • description: Clear explanation of what the action does
  • similes: Alternative names or aliases for fuzzy matching
  • examples: Training examples showing when to use the action
  • validate: Function to check if the action can run in the current context
  • handler: The execution logic that returns an ActionResult
The handler receives the runtime, message, state, options (for action chaining), an optional callback for intermediate responses, and previous responses. Important: All action handlers must return an ActionResult with a success field indicating whether the action completed successfully. For complete interface definitions, see Plugin Reference.

Core Actions (Bootstrap Plugin)

The bootstrap plugin provides 13 essential actions:

Communication Actions

ActionDescriptionExample Trigger
REPLYGenerate response”Tell me about…”
SEND_MESSAGESend to specific room”Message the team…”
NONEAcknowledge silently”Thanks!”
IGNORESkip messageSpam/irrelevant

Room Management

ActionDescriptionExample Trigger
FOLLOW_ROOMSubscribe to updates”Join #general”
UNFOLLOW_ROOMUnsubscribe”Leave #general”
MUTE_ROOMMute notifications”Mute this channel”
UNMUTE_ROOMUnmute”Unmute #general”

Data & Configuration

ActionDescriptionExample Trigger
UPDATE_CONTACTUpdate contact info”Remember that I…”
UPDATE_ROLEChange roles”Make me admin”
UPDATE_SETTINGSModify settings”Set model to gpt-4”

Media & Utilities

ActionDescriptionExample Trigger
GENERATE_IMAGECreate AI images”Draw a cat”
CHOICEPresent options”Should I A or B?”

Creating Actions

For advanced patterns, see Plugin Patterns.

Minimal Action

const action: Action = {
  name: 'MY_ACTION',
  description: 'Does something',
  validate: async () => true,
  handler: async (runtime, message) => {
    return { 
      success: true,  // REQUIRED
      text: "Done!" 
    };
  }
};

With Validation

const sendTokenAction: Action = {
  name: 'SEND_TOKEN',
  description: 'Send tokens to address',
  
  validate: async (runtime, message) => {
    return message.content.includes('send') && 
           message.content.includes('0x');
  },
  
  handler: async (runtime, message) => {
    const address = extractAddress(message.content);
    const amount = extractAmount(message.content);
    await sendToken(address, amount);
    return {
      success: true,
      text: `Sent ${amount} tokens to ${address}`
    };
  }
};

With Examples

const action: Action = {
  name: 'WEATHER',
  description: 'Get weather info',
  examples: [[
    { name: "user", content: { text: "What's the weather?" } },
    { name: "agent", content: { text: "Let me check the weather for you." } }
  ]],
  validate: async (runtime, message) => {
    return message.content.toLowerCase().includes('weather');
  },
  handler: async (runtime, message) => {
    const weather = await fetchWeather();
    return { 
      success: true,
      text: `It's ${weather.temp}°C and ${weather.condition}` 
    };
  }
};

Handler Patterns

// Using callbacks
handler: async (runtime, message, state, options, callback) => {
  const result = await doWork();
  if (callback) {
    await callback({ text: result }, []);
  }
  return { success: true, text: result };
}

// Using services
handler: async (runtime, message) => {
  const service = runtime.getService<TwitterService>('twitter');
  return service.post(message.content);
}

// Using database
handler: async (runtime, message) => {
  const memories = await runtime.databaseAdapter.searchMemories({
    query: message.content,
    limit: 5
  });
  return { success: true, data: { memories } };
}

Best Practices for Actions

  • Name actions clearly (VERB_NOUN format)
  • Always return ActionResult with success field
  • Validate before executing
  • Return consistent response format
  • Use similes for alternative triggers
  • Provide diverse examples
  • Handle errors gracefully

Providers

Providers supply contextual information to the agent’s state before it makes decisions. They act as the agent’s “senses”, gathering relevant data.

Provider Interface

Providers supply contextual data to enhance agent decision-making. Each provider has:
  • name: Unique identifier for the provider
  • description: Optional explanation of what data it provides
  • dynamic: If true, data is re-fetched each time (not cached)
  • position: Execution order priority (-100 to 100, lower runs first)
  • private: If true, hidden from the default provider list
  • get: Function that returns a ProviderResult with text, values, and data
The get function receives the runtime, current message, and state, returning data that will be composed into the agent’s context. For complete interface definitions, see the Provider Interface in the Reference.

Core Providers (Bootstrap Plugin)

ProviderReturnsExample Use
characterProviderAgent personalityName, bio, traits
timeProviderCurrent date/time”What time is it?”
knowledgeProviderKnowledge baseDocumentation, facts
recentMessagesProviderChat historyContext awareness
actionsProviderAvailable actions”What can you do?”
factsProviderStored factsUser preferences
settingsProviderConfigurationModel settings

Creating Providers

Basic Provider

const provider: Provider = {
  name: 'MY_DATA',
  get: async (runtime, message, state) => {
    return {
      text: "Contextual information",
      data: { key: "value" }
    };
  }
};

Dynamic Provider

const dynamicProvider: Provider = {
  name: 'LIVE_DATA',
  dynamic: true,  // Re-fetched each time
  get: async (runtime) => {
    const data = await fetchLatestData();
    return { data };
  }
};

Private Provider

const secretProvider: Provider = {
  name: 'INTERNAL_STATE',
  private: true,  // Not shown in provider list
  get: async (runtime) => {
    return runtime.getInternalState();
  }
};

Provider Priority

// Lower numbers = higher priority
position: -100  // Loads first
position: 0     // Default
position: 100   // Loads last

Provider Execution Flow

  1. Providers are executed during runtime.composeState()
  2. By default, all non-private, non-dynamic providers are included
  3. Providers are sorted by position and executed in order
  4. Results are aggregated into a unified state object
  5. The composed state is passed to actions and the LLM for decision-making

Best Practices for Providers

  • Return consistent data structures
  • Handle errors gracefully
  • Cache when appropriate
  • Keep data fetching fast
  • Document what data is provided
  • Use position to control execution order

Evaluators

Evaluators are post-processors that analyze and extract information from conversations.

Evaluator Interface

Evaluators process and extract information from agent responses. Each evaluator has:
  • name: Unique identifier for the evaluator
  • description: Explanation of what it evaluates or extracts
  • similes: Alternative names for matching
  • alwaysRun: If true, runs on every agent response
  • examples: Training examples for the evaluator
  • validate: Function to determine if evaluator should run
  • handler: Processing logic that analyzes the response
Evaluators run after an agent generates a response, allowing for fact extraction, sentiment analysis, or content filtering. For complete interface definitions, see the Evaluator Interface in the Reference.

Core Evaluators (Bootstrap Plugin)

EvaluatorPurposeExtracts
reflectionEvaluatorSelf-awarenessInsights about interactions
factEvaluatorFact extractionImportant information
goalEvaluatorGoal trackingUser objectives

Evaluator Flow

Common Use Cases

Memory Building

  • Extract facts from conversations
  • Track user preferences
  • Update relationship status
  • Record important events

Content Filtering

  • Remove sensitive data
  • Filter profanity
  • Ensure compliance
  • Validate accuracy

Analytics

  • Track sentiment
  • Measure engagement
  • Monitor topics
  • Analyze patterns

Creating Evaluators

Basic Evaluator

const evaluator: Evaluator = {
  name: 'my-evaluator',
  description: 'Processes responses',
  examples: [],  // Training examples
  
  validate: async (runtime, message) => {
    return true;  // Run on all messages
  },
  
  handler: async (runtime, message) => {
    // Process and extract
    const result = await analyze(message);
    // Store findings
    await storeResult(result);
    return result;
  }
};

With Examples

const evaluator: Evaluator = {
  name: 'fact-extractor',
  description: 'Extracts facts from conversations',
  examples: [{
    prompt: 'Extract facts from this conversation',
    messages: [
      { name: 'user', content: { text: 'I live in NYC' } },
      { name: 'agent', content: { text: 'NYC is a great city!' } }
    ],
    outcome: 'User lives in New York City'
  }],
  validate: async () => true,
  handler: async (runtime, message, state) => {
    const facts = await extractFacts(state);
    for (const fact of facts) {
      await runtime.factsManager.addFact(fact);
    }
    return facts;
  }
};

Best Practices for Evaluators

  • Run evaluators async (don’t block responses)
  • Store extracted data for future context
  • Use alwaysRun: true sparingly
  • Provide clear examples for training
  • Keep handlers lightweight

Services

Services manage stateful connections and provide core functionality. They are singleton instances that persist throughout the agent’s lifecycle.

Service Abstract Class

Services are singleton instances that manage stateful connections and provide persistent functionality throughout the agent’s lifecycle. Services extend an abstract class with:
  • serviceType: Static property identifying the service type
  • capabilityDescription: Description of what the service provides
  • start(): Static method to initialize and start the service
  • stop(): Method to clean up resources when shutting down
  • config: Optional configuration metadata
Services are ideal for managing database connections, API clients, WebSocket connections, or any long-running background tasks. For the complete Service class definition, see the Service Abstract Class in the Reference.

Service Types

The system includes predefined service types:
  • TRANSCRIPTION, VIDEO, BROWSER, PDF
  • REMOTE_FILES (AWS S3)
  • WEB_SEARCH, EMAIL, TEE
  • TASK, WALLET, LP_POOL, TOKEN_DATA
  • DATABASE_MIGRATION
  • PLUGIN_MANAGER, PLUGIN_CONFIGURATION, PLUGIN_USER_INTERACTION

Creating Services

import { Service, IAgentRuntime, logger } from '@elizaos/core';

export class MyService extends Service {
  static serviceType = 'my-service';
  capabilityDescription = 'Description of what this service provides';

  private client: any;
  private refreshInterval: NodeJS.Timer | null = null;

  constructor(protected runtime: IAgentRuntime) {
    super();
  }

  static async start(runtime: IAgentRuntime): Promise<MyService> {
    logger.info('Initializing MyService');
    const service = new MyService(runtime);

    // Initialize connections, clients, etc.
    await service.initialize();

    // Set up periodic tasks if needed
    service.refreshInterval = setInterval(
      () => service.refreshData(),
      60000 // 1 minute
    );

    return service;
  }

  async stop(): Promise<void> {
    // Cleanup resources
    if (this.refreshInterval) {
      clearInterval(this.refreshInterval);
    }
    // Close connections
    if (this.client) {
      await this.client.disconnect();
    }
    logger.info('MyService stopped');
  }

  private async initialize(): Promise<void> {
    // Service initialization logic
    const apiKey = this.runtime.getSetting('MY_API_KEY');
    if (!apiKey) {
      throw new Error('MY_API_KEY not configured');
    }

    this.client = new MyClient({ apiKey });
    await this.client.connect();
  }
}

Service Lifecycle Patterns

Delayed Initialization

Sometimes services need to wait for other services or perform startup tasks:
export class MyService extends Service {
  static serviceType = 'my-service';

  static async start(runtime: IAgentRuntime): Promise<MyService> {
    const service = new MyService(runtime);

    // Immediate initialization
    await service.initialize();

    // Delayed initialization for non-critical tasks
    setTimeout(async () => {
      try {
        await service.loadCachedData();
        await service.syncWithRemote();
        logger.info('MyService: Delayed initialization complete');
      } catch (error) {
        logger.error('MyService: Delayed init failed', error);
        // Don't throw - service is still functional
      }
    }, 5000);

    return service;
  }
}

Best Practices for Services

  • Handle missing API tokens gracefully
  • Implement proper cleanup in stop()
  • Use delayed initialization for non-critical tasks
  • Log service lifecycle events
  • Make services resilient to failures
  • Keep service instances stateless when possible

Component Interaction

Execution Flow

  1. Providers gather context → compose state
  2. Actions validate against state → execute if valid
  3. Evaluators process responses → extract information
  4. Services provide persistent functionality throughout

State Composition

// Providers contribute to state
const state = await runtime.composeState(message, [
  'RECENT_MESSAGES',
  'CHARACTER',
  'KNOWLEDGE'
]);

// Actions receive composed state
const result = await action.handler(runtime, message, state);

// Evaluators process with full context
await evaluator.handler(runtime, message, state);

Service Access

// Actions and providers can access services
const service = runtime.getService<MyService>('my-service');
const data = await service.getData();

Plugin Example with All Components

import type { Plugin } from '@elizaos/core';

export const myPlugin: Plugin = {
  name: 'my-complete-plugin',
  description: 'Example plugin with all component types',
  
  services: [MyService],
  
  actions: [{
    name: 'MY_ACTION',
    description: 'Performs an action using the service',
    validate: async (runtime) => {
      return runtime.getService('my-service') !== null;
    },
    handler: async (runtime, message) => {
      const service = runtime.getService<MyService>('my-service');
      const result = await service.doSomething();
      return { success: true, text: result };
    }
  }],
  
  providers: [{
    name: 'MY_PROVIDER',
    get: async (runtime) => {
      const service = runtime.getService<MyService>('my-service');
      const data = await service.getCurrentState();
      return {
        text: `Current state: ${JSON.stringify(data)}`,
        data
      };
    }
  }],
  
  evaluators: [{
    name: 'MY_EVALUATOR',
    description: 'Extracts relevant information',
    examples: [],
    validate: async () => true,
    handler: async (runtime, message) => {
      const extracted = await extractInfo(message);
      await runtime.storeExtracted(extracted);
      return extracted;
    }
  }]
};

What’s Next?