CtrlK
CommunityDocumentationLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-starter

Base starter module for the Embabel Agent Framework providing core dependencies for building agentic flows on the JVM with Spring Boot integration and GOAP-based intelligent path finding.

Overview
Eval results
Files

guides-creating-agents.mddocs/

Creating Agents

Step-by-step guide for creating agents with GOAP planning.

1. Basic Agent Creation

Java

import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.annotation.PlannerType;
import org.springframework.stereotype.Component;

@Agent(
    description = "Customer support agent for handling inquiries",
    provider = "customer-service",
    version = "1.0.0",
    planner = PlannerType.GOAP,
    scan = true
)
public class CustomerSupportAgent {
    // Agent actions and conditions defined here
}

Kotlin

import com.embabel.agent.api.annotation.Agent
import com.embabel.agent.api.annotation.PlannerType
import org.springframework.stereotype.Component

@Agent(
    description = "Data processing agent for ETL operations",
    provider = "data-platform",
    version = "2.1.0",
    planner = PlannerType.GOAP,
    scan = true,
    opaque = false
)
class DataProcessingAgent {
    // Agent actions and conditions defined here
}

2. Agent with Custom Retry Policy

Agents support configurable retry policies for resilient action execution.

Java

import com.embabel.agent.api.ActionRetryPolicy;
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.annotation.PlannerType;

@Agent(
    description = "Resilient agent with exponential backoff",
    planner = PlannerType.GOAP,
    actionRetryPolicy = ActionRetryPolicy.exponential(3, 1000)
)
public class ResilientAgent {

    @Action(description = "Fetch data with retries")
    public Data fetchData(String source) {
        return dataService.fetch(source);
    }

    @Action(
        description = "Critical operation - no retries",
        actionRetryPolicy = ActionRetryPolicy.NO_RETRY
    )
    public void criticalOperation() {
        // Must succeed first time
    }
}

Kotlin

import com.embabel.agent.api.ActionRetryPolicy
import com.embabel.agent.api.annotation.Agent
import com.embabel.agent.api.annotation.Action

@Agent(
    description = "Data processor with fixed retry",
    actionRetryPolicy = ActionRetryPolicy.fixed(3, 2000)
)
class DataProcessor {

    @Action(
        description = "Process with exponential backoff",
        actionRetryPolicy = ActionRetryPolicy.exponential(10, 100)
    )
    fun processData(data: Input): Output {
        return processor.process(data)
    }
}

Retry Policy Options:

  • ActionRetryPolicy.DEFAULT - Sensible defaults for most scenarios
  • ActionRetryPolicy.exponential(attempts, initialBackoffMs) - Exponential backoff between retries
  • ActionRetryPolicy.fixed(attempts, backoffMs) - Constant wait time between retries
  • ActionRetryPolicy.NO_RETRY - Disable retries completely

3. Creating Capability Components

Use @EmbabelComponent to create reusable capability providers that aren't full agents.

Java

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

@EmbabelComponent(scan = true)
public class FileOperations {

    @Action(description = "Read file contents")
    public String readFile(String path) {
        return Files.readString(Path.of(path));
    }

    @Action(description = "Write file contents")
    public void writeFile(String path, String content) {
        Files.writeString(Path.of(path), content);
    }

    @Action(description = "List directory contents")
    public List<String> listDirectory(String path) {
        return Arrays.asList(new File(path).list());
    }
}

Kotlin

import com.embabel.agent.api.annotation.EmbabelComponent
import com.embabel.agent.api.annotation.Action
import java.nio.file.Files
import java.nio.file.Paths

@EmbabelComponent(scan = true)
class DatabaseOperations {

    @Action(description = "Query database records")
    fun query(sql: String): List<Record> {
        return executeQuery(sql)
    }

    @Action(description = "Insert database record")
    fun insert(record: Record) {
        executeInsert(record)
    }

    @Action(description = "Update database record")
    fun update(id: String, record: Record) {
        executeUpdate(id, record)
    }
}

4. Agent with Opaque Internal Actions

Use opaque = true to hide internal implementation details when the agent is used as a subagent.

Java

@Agent(
    description = "Internal processing agent",
    opaque = true,
    scan = true
)
public class InternalProcessorAgent {

    // These actions are hidden from parent agents
    @Action(description = "Internal validation step")
    private ValidationResult validate(Input input) {
        return validator.validate(input);
    }

    // Public interface for parent agents
    @AchievesGoal(
        description = "Process input and return result",
        export = @Export(local = true, remote = false)
    )
    @Action(description = "Process input")
    public ProcessedOutput process(Input input) {
        ValidationResult validated = validate(input);
        return transform(validated);
    }
}

Kotlin

@Agent(
    description = "Background worker agent",
    opaque = true
)
class BackgroundWorkerAgent {

    // Internal implementation details
    @Action(description = "Prepare data")
    private fun prepare(raw: RawData): PreparedData {
        return preparer.prepare(raw)
    }

    @Action(description = "Transform data")
    private fun transform(prepared: PreparedData): TransformedData {
        return transformer.transform(prepared)
    }

    // Public goal for external callers
    @AchievesGoal(
        description = "Complete background processing",
        export = Export(local = true)
    )
    @Action(description = "Execute background job")
    fun execute(raw: RawData): TransformedData {
        val prepared = prepare(raw)
        return transform(prepared)
    }
}

5. Agent with Custom Bean Name

Override the default Spring bean name for an agent.

Java

@Agent(
    description = "Custom named agent",
    beanName = "myCustomAgentBean",
    scan = true
)
public class CustomNamedAgent {
    // Implementation
}

// Usage in other components
@Service
public class AgentService {

    @Autowired
    @Qualifier("myCustomAgentBean")
    private Agent customAgent;

    public void useAgent() {
        // Use the custom-named agent
    }
}

Kotlin

@Agent(
    description = "Custom bean agent",
    beanName = "specialAgentBean"
)
class CustomBeanAgent {
    // Implementation
}

// Usage with qualifier
@Service
class AgentService(
    @Qualifier("specialAgentBean")
    private val customAgent: Agent
) {
    fun useAgent() {
        // Use the custom-named agent
    }
}

6. Agent with Disabled Scanning

For performance or control, disable automatic scanning and manually register capabilities.

Java

@Agent(
    description = "Manually configured agent",
    scan = false
)
public class ManualAgent {

    // Capabilities must be registered programmatically
    // or explicitly invoked

    public void explicitMethod() {
        // This won't be auto-discovered as an action
    }
}

Kotlin

@Agent(
    description = "Manual configuration agent",
    scan = false
)
class ManualConfigAgent {

    // Manual registration required for actions
    fun customOperation() {
        // Not automatically discovered
    }
}

7. Complete Working Example

Java - Task Processing Agent

import com.embabel.agent.api.annotation.*;
import com.embabel.agent.api.ActionRetryPolicy;
import org.springframework.stereotype.Component;

@Agent(
    description = "Task processing agent with full configuration",
    provider = "task-management",
    version = "1.0.0",
    planner = PlannerType.GOAP,
    scan = true,
    opaque = false,
    actionRetryPolicy = ActionRetryPolicy.exponential(3, 1000)
)
public class TaskProcessingAgent {

    private final TaskRepository taskRepository;
    private final TaskProcessor processor;

    public TaskProcessingAgent(
        TaskRepository taskRepository,
        TaskProcessor processor
    ) {
        this.taskRepository = taskRepository;
        this.processor = processor;
    }

    @Action(
        description = "Fetch task from database",
        post = {"taskLoaded"},
        outputBinding = "task",
        cost = 5.0,
        value = 10.0
    )
    public Task fetchTask(String taskId) {
        return taskRepository.findById(taskId)
            .orElseThrow(() -> new TaskNotFoundException(taskId));
    }

    @Action(
        description = "Validate task data",
        pre = {"taskLoaded"},
        post = {"taskValidated"},
        cost = 2.0,
        value = 15.0
    )
    public ValidationResult validateTask(Task task) {
        return processor.validate(task);
    }

    @Action(
        description = "Process validated task",
        pre = {"taskValidated"},
        post = {"taskProcessed"},
        outputBinding = "result",
        cost = 20.0,
        value = 50.0,
        actionRetryPolicy = ActionRetryPolicy.exponential(5, 500)
    )
    public ProcessedResult processTask(
        Task task,
        @Provided Ai ai,
        @Provided ActionContext context
    ) {
        context.updateProgress("Processing task: " + task.getId());
        return ai.withLlm(OpenAiModels.GPT_4_TURBO)
                 .createObject("Process this task: " + task.getDescription());
    }

    @Action(
        description = "Save processed result",
        pre = {"taskProcessed"},
        post = {"taskSaved"},
        canRerun = false,
        cost = 5.0,
        value = 30.0
    )
    public void saveResult(ProcessedResult result) {
        taskRepository.saveResult(result);
    }
}

Kotlin - Data Analysis Agent

import com.embabel.agent.api.annotation.*
import com.embabel.agent.api.ActionRetryPolicy
import org.springframework.stereotype.Component

@Agent(
    description = "Data analysis agent with GOAP planning",
    provider = "analytics",
    version = "2.0.0",
    planner = PlannerType.GOAP,
    scan = true,
    actionRetryPolicy = ActionRetryPolicy.fixed(3, 2000)
)
class DataAnalysisAgent(
    private val dataSource: DataSource,
    private val analyzer: DataAnalyzer,
    private val reportGenerator: ReportGenerator
) {

    @Action(
        description = "Load dataset from source",
        post = ["dataLoaded"],
        outputBinding = "dataset",
        cost = 10.0,
        value = 20.0
    )
    fun loadData(query: String): Dataset {
        return dataSource.load(query)
    }

    @Action(
        description = "Clean and prepare data",
        pre = ["dataLoaded"],
        post = ["dataCleaned"],
        outputBinding = "cleanedData",
        cost = 5.0,
        value = 25.0
    )
    fun cleanData(dataset: Dataset): CleanedData {
        return analyzer.clean(dataset)
    }

    @Action(
        description = "Perform statistical analysis",
        pre = ["dataCleaned"],
        post = ["dataAnalyzed"],
        outputBinding = "analysis",
        cost = 15.0,
        value = 50.0,
        actionRetryPolicy = ActionRetryPolicy.exponential(5, 1000)
    )
    fun analyzeData(
        cleanedData: CleanedData,
        @Provided ai: Ai,
        @Provided context: ActionContext
    ): Analysis {
        context.updateProgress("Analyzing ${cleanedData.size} records")
        return analyzer.analyze(cleanedData)
    }

    @Action(
        description = "Generate final report",
        pre = ["dataAnalyzed"],
        post = ["reportGenerated"],
        canRerun = false,
        cost = 8.0,
        value = 40.0
    )
    fun generateReport(
        analysis: Analysis,
        @Provided context: ActionContext
    ): Report {
        context.updateProgress("Generating report")
        return reportGenerator.generate(analysis)
    }
}

Key Annotation Attributes

@Agent

  • description (required) - Human-readable description of agent's purpose
  • provider - Namespace for organizing agents (default: empty)
  • version - Agent version (default: DEFAULT_VERSION)
  • planner - Planning algorithm (default: GOAP)
  • scan - Enable classpath scanning (default: true)
  • beanName - Override Spring bean name (default: auto-generated)
  • opaque - Hide internal actions from introspection (default: false)
  • actionRetryPolicy - Default retry strategy (default: DEFAULT)

@EmbabelComponent

  • scan - Enable scanning for annotated methods (default: true)

Best Practices

  1. Clear Descriptions - Write descriptive agent descriptions that explain purpose
  2. Version Your Agents - Use semantic versioning for tracking changes
  3. Use Providers - Organize agents by provider/namespace for large systems
  4. Configure Retry Policies - Set appropriate retry policies based on operation criticality
  5. Use Opaque for Subagents - Hide implementation details when composing agents
  6. Enable Scanning - Keep scanning enabled unless you have specific performance needs
  7. Inject Dependencies - Use constructor injection for testability
  8. Document Capabilities - Use clear action descriptions for GOAP planning

Common Patterns

Simple Utility Agent

@Agent(description = "Simple file processor")
public class FileProcessorAgent {
    @Action(description = "Process file")
    public Result processFile(String path) {
        // Implementation
    }
}

Multi-step Workflow Agent

@Agent(
    description = "Complex workflow orchestrator",
    planner = PlannerType.GOAP
)
public class WorkflowAgent {
    @Action(pre = {}, post = {"initialized"})
    public void initialize() { }

    @Action(pre = {"initialized"}, post = {"processed"})
    public void process() { }

    @Action(pre = {"processed"}, post = {"completed"})
    public void finalize() { }
}

Resilient Remote Service Agent

@Agent(
    description = "Remote API client",
    actionRetryPolicy = ActionRetryPolicy.exponential(5, 1000)
)
public class RemoteApiAgent {
    @Action(description = "Call remote service")
    public ApiResponse callService(Request request) {
        return apiClient.call(request);
    }
}

See Also

  • Defining Actions - Define agent actions with preconditions/postconditions
  • Goal Achievement - Mark actions that achieve goals
  • Creating Tools - Expose tools to LLMs
  • Human-in-the-Loop - Implement HITL patterns
tessl i tessl/maven-com-embabel-agent--embabel-agent-starter@0.3.1

docs

api-annotations.md

api-domain-model.md

api-invocation.md

api-tools.md

concepts-actions.md

concepts-agents.md

concepts-goals.md

concepts-invocation.md

concepts-tools.md

guides-creating-agents.md

guides-creating-tools.md

guides-defining-actions.md

guides-goal-achievement.md

guides-human-in-loop.md

guides-multimodal.md

index.md

integration-mcp.md

integration-model-providers.md

integration-spring-boot.md

LlmTool.md

quickstart.md

reference-component-scanning.md

reference-configuration-properties.md

reference-installation.md

reference-logging.md

reference-resilience.md

reference-streaming.md

tile.json