# How to Define Actions Using the @Action Annotation in Embabel

> Define executable operations in Embabel using the @Action annotation. Control cost, triggers, and retries for your agent's goals effortlessly.

- Repository: [Embabel/embabel-agent](https://github.com/embabel/embabel-agent)
- Tags: how-to-guide
- Published: 2026-08-09

---

**Use the `@Action` annotation to mark Java or Kotlin methods as executable operations that the Embabel planner invokes to achieve goals, with optional metadata controlling cost, triggers, and retry behavior.**

The embabel/embabel-agent framework implements a goal-driven planning system where agents assemble workflows from discrete, callable units. You define these units using the `@Action` annotation, which exposes methods to the planner along with metadata describing their behavior, constraints, and execution characteristics.

## Understanding the @Action Annotation

Located in [`embabel-agent-api/src/main/java/com/embabel/agent/api/annotation/Action.java`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-api/src/main/java/com/embabel/agent/api/annotation/Action.java), the `@Action` annotation transforms ordinary methods into **action catalog entries**. When the framework scans a class, every method marked with `@Action` becomes available for plan generation. During execution, the planner evaluates these actions based on their metadata to determine the optimal sequence for achieving a declared goal.

## Core Attributes of @Action

The annotation supports several attributes that control how the planner treats each action:

- **`description`** — Human-readable text explaining the action's purpose. The planner uses this to select sensible actions during workflow generation.
- **`cost`** — Static numerical value representing the action's expense. The planner prefers cheaper actions when multiple alternatives exist.
- **`costMethod`** — Name of a method within the same class that returns a dynamic `double` cost at runtime, enabling context-sensitive pricing.
- **`trigger`** — Event class (extending `Event`) that automatically fires the action when the specified event occurs.
- **`actionRetryPolicy`** — Enum value (`FIRE_ONCE`, `FIRE_ALWAYS`, etc.) controlling retry behavior when the action fails.
- **`actionRetryPolicyExpression`** — SpEL expression that resolves to a retry policy at runtime for dynamic control.
- **`pre`** — Array of method names that must return `true` before the action executes, serving as pre-conditions.
- **`clearBlackboard`** — Boolean flag that, when `true`, clears the agent's blackboard after execution, forcing a fresh planning cycle.

## Implementing Actions in Java

### Basic Action Definition

Define a simple action by annotating a method with a description:

```java
package com.embabel.example;

import com.embabel.agent.api.annotation.Action;
import java.util.concurrent.ThreadLocalRandom;

public class RandomNumberGenerator {

    @Action(description = "Pick a random number between 1 and 100")
    public int pickRandomNumber() {
        return ThreadLocalRandom.current().nextInt(1, 101);
    }
}

```

### Cost-Aware Actions

Control planning decisions by specifying static or dynamic costs. The [`TestStarNewsFinder.java`](https://github.com/embabel/embabel-agent/blob/main/TestStarNewsFinder.java) file demonstrates both approaches:

```java
// Static cost discourages use unless necessary
@Action(cost = 500.0, description = "Send a bulk email")
public void sendBulkEmail(String subject, String body) {
    // email-sending logic
}

// Dynamic cost computed at runtime via method reference
@Action(costMethod = "dynamicCost",
        description = "Perform a data-intensive operation")
public void heavyComputation() {
    // computation logic
}

private double dynamicCost() {
    // Return cost based on current system load
    return SystemCpuLoad.getCurrentLoad() * 1000;
}

```

### Event-Triggered Actions

Use the `trigger` attribute to automatically execute actions when specific events occur:

```java
import com.embabel.agent.api.annotation.Action;

@Action(trigger = IncomingMessage.class,
        description = "Echo the received message")
public String echoMessage(IncomingMessage msg) {
    return "You said: " + msg.getText();
}

```

### Retry Policies and Pre-conditions

Configure failure handling and execution guards using attributes tested in [`RetryActionAnnotationJavaTest.java`](https://github.com/embabel/embabel-agent/blob/main/RetryActionAnnotationJavaTest.java):

```java
// Attempt only once regardless of failure
@Action(actionRetryPolicy = ActionRetryPolicy.FIRE_ONCE,
        description = "Call external API with one retry")
public String callExternalApi() {
    // API call logic
}

// Require authentication before execution
@Action(pre = {"userAuthenticated"},
        description = "Access user profile")
public UserProfile getProfile() {
    // retrieval logic
}

// Clear blackboard after execution to reset state
@Action(clearBlackboard = true,
        description = "Reset the workflow state")
public void resetWorkflow() {
    // reset logic
}

```

## Connecting Actions to Goals

Actions become powerful when combined with the `@AchievesGoal` annotation from [`embabel-agent-api/src/main/java/com/embabel/agent/api/annotation/AchievesGoal.java`](https://github.com/embabel/embabel-agent/blob/main/embabel-agent-api/src/main/java/com/embabel/agent/api/annotation/AchievesGoal.java). The [`MealPreparationStages.java`](https://github.com/embabel/embabel-agent/blob/main/MealPreparationStages.java) test fixture demonstrates how the planner assembles multiple actions to satisfy a goal:

```java
package com.embabel.example;

import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.AchievesGoal;

public class MealPreparation {

    @Action(description = "Choose a cook from the user input")
    public String chooseCook(String userChoice) {
        return userChoice;
    }

    @Action(description = "Take a food order from user input")
    public Order takeOrder(String userInput) {
        return new Order(userInput);
    }

    @Action(description = "Prepare the final meal")
    public Meal prepareMeal(String cook, Order order) {
        return new Meal(cook, order);
    }

    @AchievesGoal
    public Meal prepareDinner(String userChoice, String userInput) {
        // The planner generates a workflow using the actions above
        // rather than executing this method body directly
        return null;
    }
}

```

In this pattern, the `@AchievesGoal` method declares the target state, while the planner evaluates the three `@Action` methods—considering their descriptions, costs, and pre-conditions—to build an execution sequence: choose cook → take order → prepare meal.

## Summary

- The `@Action` annotation in [`Action.java`](https://github.com/embabel/embabel-agent/blob/main/Action.java) marks methods as callable units in the Embabel planning framework.
- Metadata attributes like `cost`, `costMethod`, `trigger`, and `actionRetryPolicy` control how the planner selects and executes actions.
- Use `pre` conditions to guard execution and `clearBlackboard` to reset agent state between planning cycles.
- Combine `@Action` methods with `@AchievesGoal` methods to create goal-driven workflows where the planner automatically orchestrates execution sequences.

## Frequently Asked Questions

### What is the difference between @Action and @AchievesGoal?

`@Action` marks methods that perform specific operations and may be used by the planner, while `@AchievesGoal` marks a method that represents the target state the planner attempts to reach. The planner analyzes `@AchievesGoal` methods to determine which `@Action` methods to invoke in sequence.

### How does the planner choose between multiple actions that could achieve the same goal?

The planner evaluates the `description` for semantic relevance and the `cost` or `costMethod` for efficiency. Actions with lower cost values are preferred when multiple alternatives exist, and descriptions help the planner determine which action best fits the current context.

### Can I use @Action with Kotlin classes?

Yes, the annotation works with Kotlin. Since Embabel supports both Java and Kotlin, you can annotate Kotlin functions with `@Action` using the same syntax, and the framework will include them in the action catalog during component scanning.

### How do dynamic costs work with the costMethod attribute?

The `costMethod` specifies the name of a parameterless method in the same class that returns a `double`. At planning time, the framework invokes this method to determine the current cost of the action, allowing costs to vary based on runtime conditions like system load, time of day, or external API pricing.