Fluent DSL and Kotlin DSL for building autonomous agents with planning capabilities on the JVM, featuring annotation-based and programmatic configuration for agentic flows with Spring Boot integration
Agent definition in Embabel provides two approaches: annotation-based configuration (similar to Spring MVC) and an idiomatic Kotlin DSL. Both approaches offer full type safety and refactoring support.
Define agents by annotating classes with @Agent and methods with action/condition annotations.
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class Agent(
val name: String = "",
val provider: String = "",
val description: String,
val version: String = DEFAULT_VERSION,
val planner: PlannerType = PlannerType.GOAP,
val scan: Boolean = true,
val beanName: String = "", // Note: Uses @get:AliasFor annotation for Spring bean name aliasing
val opaque: Boolean = false,
val actionRetryPolicy: ActionRetryPolicy = ActionRetryPolicy.DEFAULT,
val actionRetryPolicyExpression: String = ""
)Usage Example (Kotlin):
@Agent(
name = "order-processor",
provider = "ecommerce",
description = "Processes customer orders and manages fulfillment",
version = "1.0.0",
planner = PlannerType.GOAP
)
class OrderProcessorAgent {
@Action(description = "Validate order contents")
fun validateOrder(order: Order): ValidationResult {
return validator.validate(order)
}
@Action(
description = "Calculate shipping cost",
pre = ["order_validated"],
cost = 0.1
)
fun calculateShipping(order: Order): ShippingCost {
return shippingService.calculate(order)
}
@Condition(name = "order_validated")
fun isOrderValid(): Boolean {
return context.last(ValidationResult::class.java)?.isValid == true
}
}Usage Example (Java):
@Agent(
name = "inventory-manager",
provider = "warehouse",
description = "Manages inventory levels and stock allocation"
)
public class InventoryManagerAgent {
@Action(description = "Check stock availability")
public StockLevel checkStock(String productId) {
return stockRepository.getLevel(productId);
}
@Action(
description = "Reserve inventory",
pre = {"stock_available"},
post = {"inventory_reserved"}
)
public Reservation reserveStock(String productId, int quantity) {
return inventoryService.reserve(productId, quantity);
}
@Condition(name = "stock_available")
public boolean hasStock() {
StockLevel level = context.last(StockLevel.class);
return level != null && level.getAvailable() > 0;
}
}Define agents using the Kotlin DSL with agent {} function.
fun agent(
name: String,
provider: String,
description: String,
block: AgentBuilder.() -> Unit
): Agent
interface AgentBuilder {
var name: String
var provider: String
var version: String
var description: String
fun action(block: ActionBuilder.() -> Unit)
fun condition(block: ConditionBuilder.() -> Unit)
fun goal(block: GoalBuilder.() -> Unit)
fun <I, O> localAgentAction(agent: Agent)
fun <I, O> referencedAgentAction(agentName: String)
fun registerPromptContributors(vararg contributors: PromptContributor)
fun overridePromptContributors()
}Usage Example:
val dataProcessorAgent = agent(
name = "data-processor",
provider = "analytics",
description = "Processes and analyzes datasets"
) {
version = "2.0.0"
action<DataSet, Report> {
name = "analyze-dataset"
description = "Analyze dataset and generate comprehensive report"
pre = listOf("dataset_valid", "schema_verified")
post = listOf("report_generated")
cost = 0.5
execute { dataset, context ->
// Access LLM for analysis
val insights = context.promptRunner()
.creating(AnalysisInsights::class.java)
.fromPrompt("""
Analyze this dataset:
Rows: ${dataset.rowCount}
Columns: ${dataset.columns.joinToString()}
Summary: ${dataset.summary}
""")
// Generate report
Report(
datasetId = dataset.id,
insights = insights,
timestamp = Instant.now()
)
}
}
action<Report, Unit> {
name = "publish-report"
description = "Publish generated report"
pre = listOf("report_generated")
execute { report, context ->
context.updateProgress("Publishing report ${report.id}")
reportService.publish(report)
}
}
condition {
name = "dataset_valid"
cost = 0.1
evaluate { context ->
val dataset = context.last(DataSet::class.java)
dataset != null && dataset.isValid()
}
}
condition {
name = "schema_verified"
evaluate { context ->
val dataset = context.last(DataSet::class.java)
dataset?.schema?.isVerified() == true
}
}
goal {
name = "data_processed"
description = "Dataset has been processed and report published"
value = 1.0
conditions = listOf("report_generated")
}
}Define reusable components that expose actions, goals, and conditions without being full agents.
@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
annotation class EmbabelComponent(
val scan: Boolean = true
)Usage Example:
@EmbabelComponent
class CustomerServiceCapabilities {
@Action(description = "Send email notification to customer")
fun sendEmail(
@LlmTool.Param(description = "Customer email address")
email: String,
@LlmTool.Param(description = "Email subject line")
subject: String,
@LlmTool.Param(description = "Email body content")
body: String
): EmailReceipt {
return emailService.send(email, subject, body)
}
@Action(description = "Create support ticket")
fun createTicket(issue: String, priority: Priority): Ticket {
return ticketSystem.create(issue, priority)
}
@Condition(name = "email_validated")
fun isEmailValid(email: String): Boolean {
return emailValidator.validate(email)
}
}@Target(AnnotationTarget.CLASS)
@Retention(AnnotationRetention.RUNTIME)
@Deprecated("Use @EmbabelComponent instead")
annotation class AgentCapabilitiesThe @AgentCapabilities annotation is deprecated. Use @EmbabelComponent for defining reusable agent capabilities.
/** Type alias for values constrained to 0.0-1.0 range */
typealias ZeroToOne = Double
interface Agent {
val name: String
val description: String
val provider: String
val version: String
val planner: PlannerType
val actions: List<Action>
val goals: List<Goal>
val conditions: List<Condition>
}
enum class PlannerType(val needsGoals: Boolean) {
GOAP(needsGoals = true),
UTILITY(needsGoals = false),
SUPERVISOR(needsGoals = true)
}
enum class ActionRetryPolicy {
/** Fire only once (maxAttempts = 1) */
FIRE_ONCE,
/** Default retry policy using ActionQos defaults */
DEFAULT
}
interface PromptContributor {
fun contribute(context: OperationContext): String
}Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-apidocs