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-goal-achievement.mddocs/

Goal Achievement

Step-by-step guide for defining and achieving goals with @AchievesGoal annotation.

1. Basic Goal Definition

Java

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

@Agent(description = "Report generator")
public class ReportAgent {

    @AchievesGoal(
        description = "Generate a summary report from analyzed data",
        value = 1.0,
        tags = {"reporting", "summarization"}
    )
    @Action(description = "Generate summary report")
    public Report generateSummaryReport(AnalyzedData data) {
        return reportGenerator.generate(data);
    }
}

Kotlin

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

@Agent(description = "Request processor")
class RequestProcessorAgent {

    @AchievesGoal(
        description = "Process user request and generate response",
        tags = ["processing", "generation"],
        value = 1.0
    )
    @Action(description = "Process request")
    fun processRequest(input: UserInput): Result {
        return processor.process(input)
    }
}

2. Goals with Examples

Provide examples to help LLMs understand goal scenarios.

Java

@Agent(description = "Data analyzer")
public class AnalyzerAgent {

    @AchievesGoal(
        description = "Analyze dataset and provide insights",
        value = 1.0,
        tags = {"analysis", "insights", "data-science"},
        examples = {
            "Analyze sales data for Q4",
            "Provide insights from customer feedback data",
            "Analyze website traffic patterns"
        }
    )
    @Action(description = "Perform data analysis")
    public AnalysisReport analyzeData(Dataset dataset, @Provided Ai ai) {
        return ai.withLlm(OpenAiModels.GPT_4_TURBO)
                 .createObject("Analyze this dataset: " + dataset);
    }
}

Kotlin

@Agent(description = "Content generator")
class ContentGeneratorAgent {

    @AchievesGoal(
        description = "Generate marketing content from brief",
        tags = ["content", "marketing", "copywriting"],
        examples = [
            "Create blog post about product features",
            "Generate social media campaign content",
            "Write email newsletter about new release"
        ],
        value = 1.0
    )
    @Action(description = "Generate content")
    fun generateContent(
        brief: ContentBrief,
        @Provided ai: Ai
    ): MarketingContent {
        return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
                 .createObject("Generate content for: $brief")
    }
}

3. Goals with Export Configuration

Control how goals are exposed locally and remotely.

Java - Local-Only Goal

@Agent(description = "Internal processor")
public class InternalProcessorAgent {

    @AchievesGoal(
        description = "Internal processing step",
        export = @Export(local = true, remote = false)
    )
    @Action(description = "Process internally")
    public InternalResult processInternal(Data data) {
        return internalProcessor.process(data);
    }
}

Java - Remote-Exposed Goal

@Agent(description = "API agent")
public class ApiAgent {

    @AchievesGoal(
        description = "Publicly accessible API endpoint",
        export = @Export(
            name = "processUserQuery",
            remote = true,
            local = true,
            startingInputTypes = {String.class, UserQuery.class}
        ),
        tags = {"api", "public"},
        value = 1.0
    )
    @Action(description = "Process user query")
    public Response processQuery(UserQuery query) {
        return queryProcessor.process(query);
    }
}

Kotlin - Remote Goal with Starting Input Types

@Agent(description = "Public service agent")
class PublicServiceAgent {

    @AchievesGoal(
        description = "Transform data format for external clients",
        export = Export(
            name = "data-transformer",
            remote = true,
            local = true,
            startingInputTypes = [RawData::class, String::class]
        ),
        tags = ["transformation", "api"],
        value = 1.0
    )
    @Action(description = "Transform data")
    fun transform(raw: RawData): TransformedData {
        return transformer.transform(raw)
    }
}

4. Goal-Oriented Planning Workflow

Complete workflow demonstrating how goals guide GOAP planning.

Java

@Agent(
    description = "Order fulfillment agent",
    planner = PlannerType.GOAP
)
public class OrderFulfillmentAgent {

    // Step 1: Load order
    @Action(
        description = "Load order from database",
        post = {"orderLoaded"},
        outputBinding = "order",
        cost = 5.0,
        value = 10.0
    )
    public Order loadOrder(String orderId) {
        return orderRepository.findById(orderId);
    }

    // Step 2: Validate order
    @Action(
        description = "Validate order details",
        pre = {"orderLoaded"},
        post = {"orderValidated"},
        cost = 3.0,
        value = 15.0
    )
    public ValidationResult validateOrder(Order order) {
        return validator.validate(order);
    }

    // Step 3: Check inventory
    @Action(
        description = "Check inventory availability",
        pre = {"orderValidated"},
        post = {"inventoryChecked"},
        outputBinding = "inventoryStatus",
        cost = 5.0,
        value = 20.0
    )
    public InventoryStatus checkInventory(Order order) {
        return inventoryService.check(order);
    }

    // Step 4: Calculate shipping
    @Action(
        description = "Calculate shipping cost",
        pre = {"inventoryChecked"},
        post = {"shippingCalculated"},
        outputBinding = "shippingCost",
        cost = 4.0,
        value = 15.0
    )
    public Double calculateShipping(Order order) {
        return shippingCalculator.calculate(order);
    }

    // Goal: Fulfill order
    @AchievesGoal(
        description = "Complete order fulfillment with shipping",
        tags = {"fulfillment", "orders", "shipping"},
        examples = {
            "Fulfill order #12345",
            "Process and ship customer order",
            "Complete order delivery"
        },
        export = @Export(
            remote = true,
            local = true,
            startingInputTypes = {String.class, Order.class}
        ),
        value = 100.0
    )
    @Action(
        description = "Fulfill order",
        pre = {"orderValidated", "inventoryChecked", "shippingCalculated"},
        post = {"orderFulfilled"},
        canRerun = false
    )
    public FulfilledOrder fulfillOrder(
        Order order,
        InventoryStatus inventoryStatus,
        Double shippingCost,
        @Provided ActionContext context
    ) {
        context.updateProgress("Fulfilling order " + order.getId());

        if (!inventoryStatus.isAvailable()) {
            throw new InsufficientInventoryException(order.getId());
        }

        FulfilledOrder fulfilled = fulfillmentService.fulfill(
            order,
            shippingCost
        );

        context.sendMessage(Message.info(
            "Order " + order.getId() + " fulfilled successfully"
        ));

        return fulfilled;
    }
}

Kotlin

@Agent(
    description = "Document processing agent",
    planner = PlannerType.GOAP
)
class DocumentProcessingAgent {

    // Step 1: Upload document
    @Action(
        description = "Upload document to storage",
        post = ["documentUploaded"],
        outputBinding = "documentId",
        cost = 10.0,
        value = 20.0
    )
    fun uploadDocument(file: File): String {
        return storageService.upload(file)
    }

    // Step 2: Extract text
    @Action(
        description = "Extract text from document",
        pre = ["documentUploaded"],
        post = ["textExtracted"],
        outputBinding = "extractedText",
        cost = 15.0,
        value = 30.0
    )
    fun extractText(documentId: String): String {
        return ocrService.extract(documentId)
    }

    // Step 3: Analyze content
    @Action(
        description = "Analyze document content with AI",
        pre = ["textExtracted"],
        post = ["contentAnalyzed"],
        outputBinding = "analysis",
        cost = 25.0,
        value = 50.0
    )
    fun analyzeContent(
        extractedText: String,
        @Provided ai: Ai
    ): ContentAnalysis {
        return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
                 .createObject("Analyze: $extractedText")
    }

    // Goal: Generate structured document data
    @AchievesGoal(
        description = "Process document and extract structured data",
        tags = ["document-processing", "ocr", "analysis"],
        examples = [
            "Extract data from invoice",
            "Process receipt and get line items",
            "Analyze contract and extract key terms"
        ],
        export = Export(
            name = "process-document",
            remote = true,
            local = true,
            startingInputTypes = [File::class, String::class]
        ),
        value = 100.0
    )
    @Action(
        description = "Generate structured document data",
        pre = ["contentAnalyzed"],
        post = ["documentProcessed"],
        canRerun = false
    )
    fun processDocument(
        analysis: ContentAnalysis,
        @Provided context: ActionContext
    ): StructuredDocument {
        context.updateProgress("Generating structured data")

        val structured = documentStructurer.structure(analysis)

        context.sendMessage(Message.info(
            "Extracted ${structured.fields.size} fields"
        ))

        return structured
    }
}

5. Multiple Goals in Single Agent

Agents can have multiple goal-achieving actions.

Java

@Agent(description = "Customer service agent")
public class CustomerServiceAgent {

    @AchievesGoal(
        description = "Answer customer question",
        tags = {"support", "qa"},
        export = @Export(remote = true, local = true)
    )
    @Action(description = "Answer question")
    public Answer answerQuestion(Question question, @Provided Ai ai) {
        return ai.withLlm(OpenAiModels.GPT_4_TURBO)
                 .createObject("Answer: " + question);
    }

    @AchievesGoal(
        description = "Resolve customer complaint",
        tags = {"support", "complaints"},
        export = @Export(remote = true, local = true)
    )
    @Action(description = "Resolve complaint")
    public Resolution resolveComplaint(Complaint complaint, @Provided Ai ai) {
        return ai.withLlm(OpenAiModels.GPT_4_TURBO)
                 .createObject("Resolve: " + complaint);
    }

    @AchievesGoal(
        description = "Process refund request",
        tags = {"support", "refunds"},
        export = @Export(remote = true, local = true)
    )
    @Action(description = "Process refund")
    public RefundResult processRefund(RefundRequest request) {
        return refundService.process(request);
    }
}

Kotlin

@Agent(description = "Content moderation agent")
class ModerationAgent {

    @AchievesGoal(
        description = "Moderate user-generated content",
        tags = ["moderation", "content-safety"],
        export = Export(remote = true)
    )
    @Action(description = "Moderate content")
    fun moderateContent(content: Content, @Provided ai: Ai): ModerationResult {
        return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
                 .createObject("Moderate: $content")
    }

    @AchievesGoal(
        description = "Classify content category",
        tags = ["classification", "categorization"],
        export = Export(remote = true)
    )
    @Action(description = "Classify content")
    fun classifyContent(content: Content, @Provided ai: Ai): Classification {
        return ai.withLlm(GeminiModels.GEMINI_2_5_PRO)
                 .createObject("Classify: $content")
    }

    @AchievesGoal(
        description = "Detect spam or malicious content",
        tags = ["spam-detection", "security"],
        export = Export(remote = true)
    )
    @Action(description = "Detect spam")
    fun detectSpam(content: Content): SpamDetectionResult {
        return spamDetector.detect(content)
    }
}

6. Goals with Value Scoring

Use the value attribute to prioritize goal achievement.

Java

@Agent(description = "Task prioritization agent")
public class TaskAgent {

    @AchievesGoal(
        description = "Process high-priority task",
        tags = {"tasks", "high-priority"},
        value = 100.0  // High value
    )
    @Action(description = "Process high-priority")
    public Result processHighPriority(Task task) {
        return taskProcessor.process(task, Priority.HIGH);
    }

    @AchievesGoal(
        description = "Process medium-priority task",
        tags = {"tasks", "medium-priority"},
        value = 50.0  // Medium value
    )
    @Action(description = "Process medium-priority")
    public Result processMediumPriority(Task task) {
        return taskProcessor.process(task, Priority.MEDIUM);
    }

    @AchievesGoal(
        description = "Process low-priority task",
        tags = {"tasks", "low-priority"},
        value = 10.0  // Low value
    )
    @Action(description = "Process low-priority")
    public Result processLowPriority(Task task) {
        return taskProcessor.process(task, Priority.LOW);
    }
}

Kotlin

@Agent(description = "Alert handler")
class AlertAgent {

    @AchievesGoal(
        description = "Handle critical system alert",
        tags = ["alerts", "critical"],
        value = 100.0  // Highest priority
    )
    @Action(description = "Handle critical alert")
    fun handleCritical(alert: Alert): Response {
        return alertHandler.handleCritical(alert)
    }

    @AchievesGoal(
        description = "Handle warning alert",
        tags = ["alerts", "warning"],
        value = 50.0  // Medium priority
    )
    @Action(description = "Handle warning")
    fun handleWarning(alert: Alert): Response {
        return alertHandler.handleWarning(alert)
    }

    @AchievesGoal(
        description = "Handle informational alert",
        tags = ["alerts", "info"],
        value = 10.0  // Low priority
    )
    @Action(description = "Handle info")
    fun handleInfo(alert: Alert): Response {
        return alertHandler.handleInfo(alert)
    }
}

7. Invoking Goal-Achieving Agents

Java - Programmatic Invocation

import com.embabel.agent.api.AgentInvocation;
import com.embabel.agent.api.AgentPlatform;

@Service
public class OrderService {

    private final AgentPlatform platform;

    public OrderService(AgentPlatform platform) {
        this.platform = platform;
    }

    public FulfilledOrder fulfillOrder(String orderId) {
        // Create invocation for goal-achieving agent
        AgentInvocation<FulfilledOrder> invocation =
            AgentInvocation.create(platform, FulfilledOrder.class);

        // Invoke with order ID - agent will plan path to goal
        return invocation.invoke(orderId);
    }

    public CompletableFuture<FulfilledOrder> fulfillOrderAsync(String orderId) {
        AgentInvocation<FulfilledOrder> invocation =
            AgentInvocation.create(platform, FulfilledOrder.class);

        // Asynchronous goal achievement
        return invocation.invokeAsync(orderId);
    }
}

Kotlin - Programmatic Invocation

import com.embabel.agent.api.AgentInvocation
import com.embabel.agent.api.AgentPlatform

@Service
class DocumentService(private val platform: AgentPlatform) {

    fun processDocument(file: File): StructuredDocument {
        // Create invocation for goal
        val invocation = AgentInvocation.create<StructuredDocument>(platform)

        // Invoke - agent plans and executes to achieve goal
        return invocation.invoke(file)
    }

    suspend fun processDocumentAsync(file: File): StructuredDocument {
        val invocation = AgentInvocation.create<StructuredDocument>(platform)

        // Async goal achievement
        return invocation.invokeAsync(file).await()
    }
}

8. Complete Working Example

Java - E-commerce Order Agent

import com.embabel.agent.api.annotation.*;

@Agent(
    description = "E-commerce order processing agent",
    planner = PlannerType.GOAP
)
public class EcommerceOrderAgent {

    private final OrderRepository orderRepo;
    private final InventoryService inventory;
    private final PaymentService payment;
    private final ShippingService shipping;

    public EcommerceOrderAgent(
        OrderRepository orderRepo,
        InventoryService inventory,
        PaymentService payment,
        ShippingService shipping
    ) {
        this.orderRepo = orderRepo;
        this.inventory = inventory;
        this.payment = payment;
        this.shipping = shipping;
    }

    // Supporting actions
    @Action(
        description = "Load order details",
        post = {"orderLoaded"},
        outputBinding = "order",
        cost = 5.0
    )
    public Order loadOrder(String orderId) {
        return orderRepo.findById(orderId)
            .orElseThrow(() -> new OrderNotFoundException(orderId));
    }

    @Action(
        description = "Verify inventory",
        pre = {"orderLoaded"},
        post = {"inventoryVerified"},
        outputBinding = "inventoryStatus",
        cost = 5.0
    )
    public InventoryStatus verifyInventory(Order order) {
        return inventory.verify(order.getItems());
    }

    @Action(
        description = "Process payment",
        pre = {"orderLoaded", "inventoryVerified"},
        post = {"paymentProcessed"},
        outputBinding = "paymentResult",
        cost = 10.0
    )
    public PaymentResult processPayment(Order order) {
        return payment.process(order.getPaymentInfo(), order.getTotal());
    }

    // Goal 1: Create shipment
    @AchievesGoal(
        description = "Create shipment for order",
        tags = {"shipping", "fulfillment"},
        examples = {
            "Ship order #12345",
            "Create shipping label for order",
            "Prepare order for delivery"
        },
        export = @Export(
            name = "create-shipment",
            remote = true,
            local = true,
            startingInputTypes = {String.class, Order.class}
        ),
        value = 50.0
    )
    @Action(
        description = "Create shipment",
        pre = {"paymentProcessed", "inventoryVerified"},
        post = {"shipmentCreated"}
    )
    public Shipment createShipment(
        Order order,
        InventoryStatus inventoryStatus,
        PaymentResult paymentResult,
        @Provided ActionContext context
    ) {
        context.updateProgress("Creating shipment for order " + order.getId());

        if (!paymentResult.isSuccessful()) {
            throw new PaymentFailedException(order.getId());
        }

        Shipment shipment = shipping.createShipment(order);

        context.sendMessage(Message.info(
            "Shipment created with tracking: " + shipment.getTrackingNumber()
        ));

        return shipment;
    }

    // Goal 2: Complete order fulfillment
    @AchievesGoal(
        description = "Complete full order fulfillment process",
        tags = {"fulfillment", "complete", "orders"},
        examples = {
            "Fulfill order #12345 completely",
            "Process and ship order end-to-end",
            "Complete order from payment to delivery"
        },
        export = @Export(
            name = "fulfill-order",
            remote = true,
            local = true,
            startingInputTypes = {String.class, Order.class}
        ),
        value = 100.0
    )
    @Action(
        description = "Complete order fulfillment",
        pre = {"shipmentCreated"},
        canRerun = false
    )
    public FulfilledOrder fulfillOrder(
        Order order,
        Shipment shipment,
        @Provided ActionContext context
    ) {
        context.updateProgress("Finalizing order " + order.getId());

        FulfilledOrder fulfilled = new FulfilledOrder(
            order,
            shipment,
            Instant.now()
        );

        orderRepo.markFulfilled(fulfilled);

        context.sendMessage(Message.info(
            "Order " + order.getId() + " fulfilled successfully"
        ));

        return fulfilled;
    }
}

Kotlin - ML Model Training Agent

import com.embabel.agent.api.annotation.*

@Agent(
    description = "Machine learning model training agent",
    planner = PlannerType.GOAP
)
class ModelTrainingAgent(
    private val dataLoader: DataLoader,
    private val preprocessor: Preprocessor,
    private val trainer: ModelTrainer,
    private val evaluator: ModelEvaluator,
    private val registry: ModelRegistry
) {

    // Supporting actions
    @Action(
        description = "Load training dataset",
        post = ["dataLoaded"],
        outputBinding = "dataset",
        cost = 10.0
    )
    fun loadDataset(dataPath: String): Dataset {
        return dataLoader.load(dataPath)
    }

    @Action(
        description = "Preprocess data",
        pre = ["dataLoaded"],
        post = ["dataPreprocessed"],
        outputBinding = "preprocessedData",
        cost = 20.0
    )
    fun preprocessData(dataset: Dataset): PreprocessedData {
        return preprocessor.preprocess(dataset)
    }

    @Action(
        description = "Train model",
        pre = ["dataPreprocessed"],
        post = ["modelTrained"],
        outputBinding = "model",
        cost = 50.0
    )
    fun trainModel(
        preprocessedData: PreprocessedData,
        @Provided context: ActionContext
    ): TrainedModel {
        context.updateProgress("Training model...")
        return trainer.train(preprocessedData)
    }

    @Action(
        description = "Evaluate model",
        pre = ["modelTrained"],
        post = ["modelEvaluated"],
        outputBinding = "evaluation",
        cost = 15.0
    )
    fun evaluateModel(model: TrainedModel): Evaluation {
        return evaluator.evaluate(model)
    }

    // Goal 1: Train and evaluate model
    @AchievesGoal(
        description = "Train model and provide evaluation metrics",
        tags = ["ml", "training", "evaluation"],
        examples = [
            "Train classification model on dataset",
            "Train and evaluate regression model",
            "Build ML model with metrics"
        ],
        export = Export(
            name = "train-model",
            remote = true,
            local = true,
            startingInputTypes = [String::class, TrainingSpec::class]
        ),
        value = 75.0
    )
    @Action(
        description = "Complete training with evaluation",
        pre = ["modelEvaluated"]
    )
    fun completeTraining(
        model: TrainedModel,
        evaluation: Evaluation,
        @Provided context: ActionContext
    ): TrainingResult {
        context.sendMessage(Message.info(
            "Model accuracy: ${evaluation.accuracy}"
        ))

        return TrainingResult(model, evaluation)
    }

    // Goal 2: Deploy production-ready model
    @AchievesGoal(
        description = "Train, evaluate, and deploy model to production",
        tags = ["ml", "deployment", "production"],
        examples = [
            "Deploy trained model to production",
            "Train and register production model",
            "Complete ML pipeline with deployment"
        ],
        export = Export(
            name = "deploy-model",
            remote = true,
            local = true,
            startingInputTypes = [String::class, DeploymentSpec::class]
        ),
        value = 100.0
    )
    @Action(
        description = "Deploy model to production",
        pre = ["modelEvaluated"],
        canRerun = false
    )
    fun deployModel(
        model: TrainedModel,
        evaluation: Evaluation,
        @Provided context: ActionContext
    ): DeployedModel {
        if (evaluation.accuracy < 0.85) {
            throw ModelQualityException(
                "Model accuracy ${evaluation.accuracy} below threshold"
            )
        }

        context.updateProgress("Deploying model to production")

        val deployed = registry.register(model, evaluation)

        context.sendMessage(Message.info(
            "Model deployed with ID: ${deployed.id}"
        ))

        return deployed
    }
}

Key Annotation Attributes

@AchievesGoal

  • description (required) - Human-readable goal description
  • value - Numeric value for goal prioritization (default: 0.0)
  • tags - Capability tags for categorization (default: empty)
  • examples - Example scenarios demonstrating the goal (default: empty)
  • export - Export configuration (@Export annotation)

@Export

  • name - Override export name (default: method name)
  • remote - Expose remotely (e.g., via MCP, A2A) (default: false)
  • local - Expose locally within platform (default: true)
  • startingInputTypes - Types that can serve as starting inputs (default: empty)

Best Practices

  1. Clear Descriptions - Write actionable goal descriptions
  2. Use Tags - Apply consistent tags for discovery and categorization
  3. Provide Examples - Include realistic examples for LLM understanding
  4. Configure Export - Be intentional about remote exposure
  5. Specify Starting Types - Define valid input types for goal routing
  6. Set Value Scores - Use value to prioritize goal achievement
  7. Combine with Actions - Goals work with GOAP planning actions
  8. Design Multi-Step Goals - Create supporting actions leading to goals
  9. Handle Edge Cases - Validate preconditions before goal achievement
  10. Track Progress - Use ActionContext for progress updates

See Also

  • Defining Actions - Create actions supporting goals
  • Creating Agents - Define agents with GOAP planning
  • Human-in-the-Loop - Add HITL to goal achievement
  • Creating Tools - Provide tools for goal execution
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