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-human-in-loop.mddocs/

Human-in-the-Loop

Step-by-step guide for implementing human-in-the-loop patterns with WaitFor utility class.

1. Basic Form Submission

Request structured form input from users.

Java

import com.embabel.agent.api.annotation.WaitFor;
import com.embabel.agent.api.hitl.FormBindingRequest;
import com.embabel.agent.api.annotation.Action;

// Define data class for form
public class UserDetails {
    private String name;
    private String email;
    private int age;
    // Getters and setters
}

@Agent(description = "User registration agent")
public class RegistrationAgent {

    @Action(description = "Gather user details")
    public Result gatherUserDetails() {
        // Request form from user
        FormBindingRequest<UserDetails> request =
            WaitFor.formSubmission("Enter Your Details", UserDetails.class);

        // Wait for user to submit the form
        UserDetails details = request.await();

        // Process the submitted details
        return processDetails(details);
    }
}

Kotlin

import com.embabel.agent.api.annotation.WaitFor
import com.embabel.agent.api.annotation.Action

// Data class for form
data class UserDetails(
    val name: String,
    val email: String,
    val age: Int
)

@Agent(description = "Registration agent")
class RegistrationAgent {

    @Action(description = "Gather user details")
    fun gatherUserDetails(): Result {
        val request = WaitFor.formSubmission(
            "Enter Your Details",
            UserDetails::class.java
        )

        val details = request.await()
        return processDetails(details)
    }
}

2. Basic Confirmation Request

Request yes/no confirmation from users.

Java

import com.embabel.agent.api.annotation.WaitFor;
import com.embabel.agent.api.hitl.ConfirmationRequest;

@Agent(description = "Resource manager")
public class ResourceAgent {

    @Action(description = "Delete resource")
    public Result deleteResource(Resource resource) {
        // Request confirmation
        ConfirmationRequest<Resource> request =
            WaitFor.confirmation(resource, "Delete this resource permanently?");

        // Wait for user confirmation
        boolean confirmed = request.await();

        if (confirmed) {
            // Proceed with deletion
            return performDeletion(resource);
        } else {
            // User cancelled
            return Result.cancelled("Deletion cancelled by user");
        }
    }
}

Kotlin

import com.embabel.agent.api.annotation.WaitFor

@Agent(description = "Resource manager")
class ResourceAgent {

    @Action(description = "Delete resource")
    fun deleteResource(resource: Resource): Result {
        val request = WaitFor.confirmation(
            resource,
            "Delete this resource permanently?"
        )

        val confirmed = request.await()

        return if (confirmed) {
            performDeletion(resource)
        } else {
            Result.cancelled("Deletion cancelled by user")
        }
    }
}

3. Form with Validation

Use validation annotations for form data classes.

Java

import javax.validation.constraints.*;

public class RegistrationForm {
    @NotBlank
    @Size(min = 3, max = 50)
    private String username;

    @Email
    @NotBlank
    private String email;

    @Min(18)
    @Max(120)
    private int age;

    @Pattern(regexp = "^\\+?[0-9]{10,15}$")
    private String phone;

    // Getters and setters
}

@Agent(description = "User registration")
public class UserRegistrationAgent {

    @Action(description = "Register new user")
    public User registerUser() {
        FormBindingRequest<RegistrationForm> request =
            WaitFor.formSubmission("User Registration", RegistrationForm.class);

        RegistrationForm form = request.await();

        // Validation happens automatically
        // If validation fails, user is prompted to correct errors

        return createUser(form);
    }
}

Kotlin

import javax.validation.constraints.*

data class RegistrationForm(
    @field:NotBlank
    @field:Size(min = 3, max = 50)
    val username: String,

    @field:Email
    @field:NotBlank
    val email: String,

    @field:Min(18)
    @field:Max(120)
    val age: Int,

    @field:Pattern(regexp = "^\\+?[0-9]{10,15}$")
    val phone: String
)

@Agent(description = "User registration")
class UserRegistrationAgent {

    @Action(description = "Register new user")
    fun registerUser(): User {
        val request = WaitFor.formSubmission(
            "User Registration",
            RegistrationForm::class.java
        )

        val form = request.await()
        return createUser(form)
    }
}

4. Multi-Step HITL Workflow

Combine multiple HITL interactions in a workflow.

Java

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

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

    @Action(description = "Gather customer details")
    public CustomerDetails gatherDetails(UserInput input) {
        // Request customer details via form
        FormBindingRequest<CustomerDetails> request =
            WaitFor.formSubmission("Customer Information", CustomerDetails.class);

        return request.await();
    }

    @Action(description = "Propose service plan")
    public ServicePlan proposePlan(CustomerDetails details) {
        // Analyze and propose a service plan
        ServicePlan plan = analyzAndPropose(details);

        // Request approval for additional features
        ConfirmationRequest<String> premiumRequest =
            WaitFor.confirmation(
                "Premium Support",
                "Include premium support package (+$50/month)?"
            );

        if (premiumRequest.await()) {
            plan.addPremiumSupport();
        }

        return plan;
    }

    @AchievesGoal(
        description = "Finalize customer service plan with approval",
        tags = {"finalization", "approval"},
        export = @Export(remote = true, local = true)
    )
    @Action(description = "Finalize plan")
    public FinalizedPlan finalizePlan(ServicePlan plan) {
        // Request final user confirmation
        ConfirmationRequest<ServicePlan> request =
            WaitFor.confirmation(plan, "Approve and activate this service plan?");

        boolean approved = request.await();

        if (approved) {
            return activatePlan(plan);
        } else {
            throw new PlanRejectionException("User rejected the service plan");
        }
    }

    private ServicePlan analyzAndPropose(CustomerDetails details) {
        // Business logic
        return new ServicePlan();
    }

    private FinalizedPlan activatePlan(ServicePlan plan) {
        // Activation logic
        return new FinalizedPlan(plan, true);
    }
}

Kotlin

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

@Agent(description = "Data processing agent with HITL interactions")
class DataProcessingAgent {

    @Action(description = "Gather input data")
    fun gatherInputData(request: ProcessRequest): InputData {
        val formRequest = WaitFor.formSubmission(
            "Provide Input Data",
            InputData::class.java
        )
        return formRequest.await()
    }

    @Action(description = "Process data")
    fun processData(input: InputData): ProcessedData {
        val data = process(input)

        // Confirm if uncertain results
        if (data.confidence < 0.8) {
            val confirmRequest = WaitFor.confirmation(
                data,
                "Processing confidence is ${data.confidence}. Proceed anyway?"
            )

            if (!confirmRequest.await()) {
                throw ProcessingCancelledException("User cancelled low-confidence processing")
            }
        }

        return data
    }

    @AchievesGoal(
        description = "Generate and deliver final report",
        tags = ["reporting", "delivery"],
        export = Export(
            name = "generate-report",
            remote = true,
            local = true,
            startingInputTypes = [ProcessRequest::class]
        )
    )
    @Action(description = "Generate report")
    fun generateReport(processed: ProcessedData): Report {
        val report = createReport(processed)

        // Confirm delivery
        val confirmRequest = WaitFor.confirmation(
            report,
            "Send report to stakeholders?"
        )

        if (confirmRequest.await()) {
            deliverReport(report)
        }

        return report
    }

    private fun process(input: InputData) = ProcessedData(/* ... */)
    private fun createReport(data: ProcessedData) = Report(/* ... */)
    private fun deliverReport(report: Report) { /* ... */ }
}

5. Conditional Confirmation

Request confirmation based on conditions.

Java

@Agent(description = "Transaction processor")
public class TransactionAgent {

    private static final double HIGH_VALUE_THRESHOLD = 10000.0;

    @Action(description = "Process transaction")
    public TransactionResult processTransaction(Transaction transaction) {
        // Only require confirmation for high-value transactions
        if (transaction.getAmount() > HIGH_VALUE_THRESHOLD) {
            ConfirmationRequest<Transaction> request = WaitFor.confirmation(
                transaction,
                String.format(
                    "Confirm high-value transaction of $%.2f?",
                    transaction.getAmount()
                )
            );

            if (!request.await()) {
                return TransactionResult.cancelled();
            }
        }

        // Process transaction
        return paymentProcessor.process(transaction);
    }
}

Kotlin

@Agent(description = "Approval workflow")
class ApprovalAgent {

    companion object {
        const val AUTO_APPROVE_LIMIT = 1000.0
    }

    @Action(description = "Process approval request")
    fun processApproval(request: ApprovalRequest): ApprovalResult {
        // Auto-approve below threshold
        if (request.amount < AUTO_APPROVE_LIMIT) {
            return ApprovalResult.approved("Auto-approved")
        }

        // Request manual approval for high amounts
        val confirmRequest = WaitFor.confirmation(
            request,
            "Approve amount of $${request.amount}?"
        )

        return if (confirmRequest.await()) {
            ApprovalResult.approved("Manually approved")
        } else {
            ApprovalResult.rejected("User rejected")
        }
    }
}

6. Form with Optional Fields

Handle forms with optional and required fields.

Java

public class ProfileUpdateForm {
    @NotBlank
    private String name;  // Required

    private String bio;  // Optional

    @Email
    private String email;  // Optional but must be valid if provided

    private String website;  // Optional

    // Getters and setters
}

@Agent(description = "Profile manager")
public class ProfileAgent {

    @Action(description = "Update user profile")
    public Profile updateProfile(User user) {
        FormBindingRequest<ProfileUpdateForm> request =
            WaitFor.formSubmission(
                "Update Your Profile",
                ProfileUpdateForm.class
            );

        ProfileUpdateForm form = request.await();

        return profileService.update(user.getId(), form);
    }
}

Kotlin

data class ProfileUpdateForm(
    @field:NotBlank
    val name: String,  // Required
    val bio: String? = null,  // Optional
    @field:Email
    val email: String? = null,  // Optional but validated
    val website: String? = null  // Optional
)

@Agent(description = "Profile manager")
class ProfileAgent {

    @Action(description = "Update user profile")
    fun updateProfile(user: User): Profile {
        val request = WaitFor.formSubmission(
            "Update Your Profile",
            ProfileUpdateForm::class.java
        )

        val form = request.await()
        return profileService.update(user.id, form)
    }
}

7. Progress Tracking with HITL

Combine progress updates with HITL interactions.

Java

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

    @Action(description = "Generate comprehensive report")
    public Report generateReport(
        DataSet data,
        @Provided ActionContext context
    ) {
        // Update progress
        context.updateProgress("Loading data...");

        // Process data
        context.updateProgress("Analyzing data...");
        Analysis analysis = analyze(data);

        // Request user input for report format
        context.updateProgress("Waiting for user input...");
        FormBindingRequest<ReportFormat> formatRequest =
            WaitFor.formSubmission("Choose Report Format", ReportFormat.class);
        ReportFormat format = formatRequest.await();

        // Generate report
        context.updateProgress("Generating " + format.getType() + " report...");
        Report report = createReport(analysis, format);

        // Confirm before sending
        context.updateProgress("Waiting for confirmation...");
        ConfirmationRequest<Report> confirmRequest =
            WaitFor.confirmation(report, "Send report to recipients?");

        if (confirmRequest.await()) {
            context.updateProgress("Sending report...");
            emailService.send(report);
        }

        context.updateProgress("Complete!");
        return report;
    }
}

Kotlin

@Agent(description = "Batch processor with HITL")
class BatchProcessorAgent {

    @Action(description = "Process batch with confirmation")
    fun processBatch(
        batch: Batch,
        @Provided context: ActionContext
    ): BatchResult {
        context.updateProgress("Processing ${batch.items.size} items")

        val results = mutableListOf<ItemResult>()

        for ((index, item) in batch.items.withIndex()) {
            context.updateProgress("Processing item ${index + 1}/${batch.items.size}")

            val result = processItem(item)

            // Request confirmation for failed items
            if (!result.success) {
                context.updateProgress("Item ${index + 1} failed - waiting for decision")

                val confirmRequest = WaitFor.confirmation(
                    result,
                    "Item ${index + 1} failed. Continue with remaining items?"
                )

                if (!confirmRequest.await()) {
                    throw ProcessingCancelledException("User cancelled batch processing")
                }
            }

            results.add(result)
        }

        context.updateProgress("Batch processing complete")
        return BatchResult(results)
    }

    private fun processItem(item: Item): ItemResult {
        // Processing logic
        return ItemResult()
    }
}

8. Custom Awaitable Pattern

Implement custom awaitable operations.

Java

import com.embabel.agent.api.hitl.Awaitable;

@Agent(description = "Custom interaction agent")
public class CustomInteractionAgent {

    @Action(description = "Custom user interaction")
    public Result customInteraction() {
        // Custom awaitable implementation
        Awaitable<CustomResult, ?> customAwaitable = createCustomAwaitable();

        // Wrap for consistent handling
        Awaitable<CustomResult, ?> wrapped = WaitFor.awaitable(customAwaitable);

        // Use in action
        CustomResult result = wrapped.await();
        return processResult(result);
    }

    private Awaitable<CustomResult, ?> createCustomAwaitable() {
        // Custom implementation
        return new CustomAwaitable();
    }

    private class CustomAwaitable implements Awaitable<CustomResult, CustomAwaitable> {
        @Override
        public CustomResult await() {
            // Custom await logic
            return new CustomResult();
        }

        @Override
        public boolean isComplete() {
            return false;
        }

        @Override
        public void cancel() {
            // Cancellation logic
        }
    }
}

Kotlin

import com.embabel.agent.api.hitl.Awaitable

@Agent(description = "Custom awaitable agent")
class CustomAwaitableAgent {

    @Action(description = "Custom interaction")
    fun customInteraction(): Result {
        val customAwaitable = createCustomAwaitable()
        val wrapped = WaitFor.awaitable(customAwaitable)
        val result = wrapped.await()
        return processResult(result)
    }

    private fun createCustomAwaitable(): Awaitable<CustomResult, *> {
        return CustomAwaitable()
    }

    private class CustomAwaitable : Awaitable<CustomResult, CustomAwaitable> {
        override fun await(): CustomResult {
            // Custom await logic
            return CustomResult()
        }

        override fun isComplete(): Boolean = false

        override fun cancel() {
            // Cancellation logic
        }
    }
}

9. Asynchronous HITL Operations

Use reactive patterns for non-blocking HITL.

Java

import reactor.core.publisher.Mono;

@Agent(description = "Async HITL agent")
public class AsyncHitlAgent {

    @Action(description = "Async confirmation")
    public Mono<Result> asyncConfirmation(Input input) {
        return Mono.fromCallable(() -> {
            ConfirmationRequest<Input> request =
                WaitFor.confirmation(input, "Proceed with this action?");
            return request.await();
        }).map(confirmed -> {
            if (confirmed) {
                return processInput(input);
            } else {
                return Result.cancelled();
            }
        });
    }
}

Kotlin

import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext

@Agent(description = "Coroutine HITL agent")
class CoroutineHitlAgent {

    @Action(description = "Async confirmation")
    suspend fun asyncConfirmation(input: Input): Result {
        return withContext(Dispatchers.IO) {
            val request = WaitFor.confirmation(
                input,
                "Proceed with this action?"
            )
            val confirmed = request.await()

            if (confirmed) {
                processInput(input)
            } else {
                Result.cancelled()
            }
        }
    }
}

10. Complete Working Example

Java - Document Approval Workflow

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

@Agent(
    description = "Document approval workflow with HITL",
    planner = PlannerType.GOAP
)
public class DocumentApprovalAgent {

    private final DocumentService documentService;
    private final ValidationService validationService;
    private final ApprovalService approvalService;

    public DocumentApprovalAgent(
        DocumentService documentService,
        ValidationService validationService,
        ApprovalService approvalService
    ) {
        this.documentService = documentService;
        this.validationService = validationService;
        this.approvalService = approvalService;
    }

    @Action(
        description = "Upload document",
        post = {"documentUploaded"},
        outputBinding = "document"
    )
    public Document uploadDocument() {
        // Request document metadata via form
        FormBindingRequest<DocumentMetadata> metadataRequest =
            WaitFor.formSubmission("Document Information", DocumentMetadata.class);

        DocumentMetadata metadata = metadataRequest.await();

        // Upload document
        return documentService.upload(metadata);
    }

    @Action(
        description = "Validate document",
        pre = {"documentUploaded"},
        post = {"documentValidated"},
        outputBinding = "validationResult"
    )
    public ValidationResult validateDocument(
        Document document,
        @Provided ActionContext context
    ) {
        context.updateProgress("Validating document: " + document.getName());

        ValidationResult result = validationService.validate(document);

        // If validation issues found, ask user to proceed
        if (result.hasWarnings()) {
            context.updateProgress("Validation warnings found");

            ConfirmationRequest<ValidationResult> confirmRequest =
                WaitFor.confirmation(
                    result,
                    "Document has validation warnings. Proceed anyway?"
                );

            if (!confirmRequest.await()) {
                throw ValidationCancelledException("User cancelled due to warnings");
            }
        }

        return result;
    }

    @Action(
        description = "Request approvals",
        pre = {"documentValidated"},
        post = {"approvalsRequested"},
        outputBinding = "approvalRequests"
    )
    public List<ApprovalRequest> requestApprovals(
        Document document,
        @Provided ActionContext context
    ) {
        // Request approval configuration via form
        context.updateProgress("Configuring approvals");

        FormBindingRequest<ApprovalConfiguration> configRequest =
            WaitFor.formSubmission(
                "Approval Configuration",
                ApprovalConfiguration.class
            );

        ApprovalConfiguration config = configRequest.await();

        // Create approval requests
        List<ApprovalRequest> requests = approvalService.createRequests(
            document,
            config
        );

        context.sendMessage(Message.info(
            "Created " + requests.size() + " approval requests"
        ));

        return requests;
    }

    @AchievesGoal(
        description = "Complete document approval workflow",
        tags = {"document", "approval", "workflow"},
        export = @Export(remote = true, local = true),
        value = 100.0
    )
    @Action(
        description = "Finalize approval",
        pre = {"approvalsRequested"},
        canRerun = false
    )
    public ApprovedDocument finalizeApproval(
        Document document,
        List<ApprovalRequest> approvalRequests,
        @Provided ActionContext context
    ) {
        context.updateProgress("Waiting for all approvals...");

        // Wait for all approvals
        approvalService.waitForApprovals(approvalRequests);

        context.updateProgress("All approvals received");

        // Final confirmation before publishing
        ConfirmationRequest<Document> publishRequest =
            WaitFor.confirmation(
                document,
                "All approvals received. Publish document?"
            );

        if (publishRequest.await()) {
            ApprovedDocument approved = documentService.publish(document);

            context.sendMessage(Message.info(
                "Document published: " + approved.getPublishedUrl()
            ));

            return approved;
        } else {
            throw PublishCancelledException("User cancelled publishing");
        }
    }
}

// Form data classes
public class DocumentMetadata {
    @NotBlank
    private String title;

    @NotBlank
    private String author;

    private String description;

    @NotBlank
    private String category;

    // Getters and setters
}

public class ApprovalConfiguration {
    @NotEmpty
    private List<String> approverEmails;

    @Min(1)
    private int requiredApprovals;

    private boolean requireAllApprovals;

    // Getters and setters
}

Kotlin - Expense Approval Workflow

import com.embabel.agent.api.annotation.*
import javax.validation.constraints.*

@Agent(
    description = "Expense approval workflow with HITL",
    planner = PlannerType.GOAP
)
class ExpenseApprovalAgent(
    private val expenseService: ExpenseService,
    private val policyChecker: PolicyChecker,
    private val approvalService: ApprovalService
) {

    @Action(
        description = "Submit expense",
        post = ["expenseSubmitted"],
        outputBinding = "expense"
    )
    fun submitExpense(): Expense {
        // Request expense details via form
        val request = WaitFor.formSubmission(
            "Submit Expense",
            ExpenseForm::class.java
        )

        val form = request.await()
        return expenseService.create(form)
    }

    @Action(
        description = "Check policy compliance",
        pre = ["expenseSubmitted"],
        post = ["policyChecked"],
        outputBinding = "policyResult"
    )
    fun checkPolicy(
        expense: Expense,
        @Provided context: ActionContext
    ): PolicyResult {
        context.updateProgress("Checking policy compliance")

        val result = policyChecker.check(expense)

        // If policy violations, ask user to justify
        if (result.hasViolations()) {
            context.updateProgress("Policy violations detected")

            val justificationRequest = WaitFor.formSubmission(
                "Provide Justification",
                JustificationForm::class.java
            )

            val justification = justificationRequest.await()
            result.addJustification(justification)
        }

        return result
    }

    @Action(
        description = "Request manager approval",
        pre = ["policyChecked"],
        post = ["approvalRequested"],
        outputBinding = "approvalStatus"
    )
    fun requestApproval(
        expense: Expense,
        policyResult: PolicyResult,
        @Provided context: ActionContext
    ): ApprovalStatus {
        // Determine approval routing
        val routing = if (expense.amount > 1000.0) {
            // High amount - request approval routing config
            context.updateProgress("High amount expense - configure approval")

            val routingRequest = WaitFor.formSubmission(
                "Approval Routing",
                ApprovalRoutingForm::class.java
            )

            routingRequest.await()
        } else {
            // Standard routing
            ApprovalRoutingForm.standard()
        }

        return approvalService.requestApproval(expense, routing)
    }

    @AchievesGoal(
        description = "Complete expense approval and reimbursement",
        tags = ["expense", "approval", "reimbursement"],
        export = Export(remote = true, local = true),
        value = 100.0
    )
    @Action(
        description = "Process reimbursement",
        pre = ["approvalRequested"],
        canRerun = false
    )
    fun processReimbursement(
        expense: Expense,
        approvalStatus: ApprovalStatus,
        @Provided context: ActionContext
    ): Reimbursement {
        context.updateProgress("Waiting for approval")

        // Wait for approval decision
        approvalService.waitForDecision(approvalStatus)

        if (!approvalStatus.isApproved) {
            throw ExpenseRejectedException("Expense rejected by approver")
        }

        context.updateProgress("Expense approved")

        // Confirm reimbursement method
        val methodRequest = WaitFor.formSubmission(
            "Reimbursement Method",
            ReimbursementMethodForm::class.java
        )

        val method = methodRequest.await()

        // Process reimbursement
        val reimbursement = expenseService.reimburse(expense, method)

        context.sendMessage(Message.info(
            "Reimbursement processed: $${reimbursement.amount}"
        ))

        return reimbursement
    }
}

// Form data classes
data class ExpenseForm(
    @field:NotBlank
    val description: String,

    @field:Min(0)
    val amount: Double,

    @field:NotBlank
    val category: String,

    @field:NotNull
    val date: LocalDate,

    val receiptUrl: String? = null
)

data class JustificationForm(
    @field:NotBlank
    @field:Size(min = 50)
    val justification: String,

    val supportingDocuments: List<String> = emptyList()
)

data class ApprovalRoutingForm(
    @field:NotEmpty
    val approvers: List<String>,

    val requireAllApprovals: Boolean = false
) {
    companion object {
        fun standard() = ApprovalRoutingForm(
            approvers = listOf("manager"),
            requireAllApprovals = false
        )
    }
}

data class ReimbursementMethodForm(
    @field:NotBlank
    val method: String,  // "direct_deposit", "check", "payroll"

    val accountNumber: String? = null
)

Key Concepts

WaitFor Methods

  • formSubmission(title, class) - Request structured form input
  • confirmation(object, description) - Request yes/no confirmation
  • awaitable(awaitable) - Wrap custom awaitable for consistent handling

Form Binding

  • Uses strongly-typed data classes
  • Supports validation annotations
  • Automatically handles validation errors

Confirmation Patterns

  • Simple yes/no decisions
  • Conditional confirmations based on logic
  • Multi-step approval workflows

Best Practices

  1. Use Sparingly - Only use HITL when human judgment is truly needed
  2. Clear Descriptions - Write clear, actionable confirmation messages
  3. Type Safety - Use strongly-typed classes for form binding
  4. Validate Forms - Add validation annotations to form fields
  5. Progress Updates - Show progress to users during HITL operations
  6. Handle Cancellations - Handle rejections and cancellations gracefully
  7. Keep Forms Simple - Focus forms on specific data collection needs
  8. Provide Context - Include sufficient context in confirmation messages
  9. Error Handling - Catch and handle HITL-related exceptions
  10. Test Workflows - Test complete HITL workflows end-to-end

See Also

  • Goal Achievement - Combine HITL with goal-achieving actions
  • Defining Actions - Create actions with HITL
  • Creating Agents - Build agents with HITL workflows
  • Multimodal - Use multimodal content in HITL interactions
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