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
The invocation API provides a fluent interface for programmatically executing agents with typed inputs and outputs. It supports synchronous and asynchronous execution, configuration options, and result handling.
Main interface for invoking agents programmatically.
interface AgentInvocation<T> : TypedInvocation<T, AgentInvocation<T>> {
/** Set the agent platform */
fun withAgentPlatform(agentPlatform: AgentPlatform): AgentInvocation<T>
/** Change return type */
fun <U> returning(resultType: Class<U>): AgentInvocation<U>
companion object {
/** Create invocation with result type */
fun <T> create(
agentPlatform: AgentPlatform,
resultType: Class<T>
): AgentInvocation<T>
/** Create invocation with reified type (Kotlin) */
inline fun <reified T> create(
agentPlatform: AgentPlatform
): AgentInvocation<T>
/** Create untyped invocation */
fun on(agentPlatform: AgentPlatform): AgentInvocation<Any>
/** Get builder for invocation */
fun builder(agentPlatform: AgentPlatform): Builder
}
}Basic Invocation Example:
@Service
class OrderService(
private val agentPlatform: AgentPlatform
) {
fun processOrder(order: Order): OrderResult {
return AgentInvocation.create<OrderResult>(agentPlatform)
.withAgentName("order-processor")
.withInput(order)
.execute()
}
fun processOrderAsync(order: Order): CompletableFuture<OrderResult> {
return AgentInvocation.create<OrderResult>(agentPlatform)
.withAgent(orderProcessorAgent)
.withInput(order)
.executeAsync()
}
}Generic invocation interface with configuration methods.
interface TypedInvocation<T, SELF> {
/** Set agent by reference */
fun withAgent(agent: Agent): SELF
/** Set agent by name */
fun withAgentName(name: String): SELF
/** Add input object */
fun withInput(input: Any): SELF
/** Add multiple inputs */
fun withInputs(inputs: List<Any>): SELF
/** Set blackboard state */
fun withBlackboard(blackboard: Blackboard): SELF
/** Add goal to achieve */
fun withGoal(goal: Goal): SELF
/** Add goals to achieve */
fun withGoals(goals: List<Goal>): SELF
/** Set output channel */
fun withOutputChannel(channel: OutputChannel): SELF
/** Add event listener */
fun withEventListener(listener: AgenticEventListener): SELF
/** Set process options */
fun withProcessOptions(options: ProcessOptions): SELF
/** Set autonomy level */
fun withAutonomy(autonomy: Autonomy): SELF
/** Set user context */
fun withUser(user: User): SELF
/** Set timeout */
fun withTimeout(timeout: Duration): SELF
/** Execute synchronously */
fun execute(): T
/** Execute asynchronously */
fun executeAsync(): CompletableFuture<T>
}Configuration Example:
fun processWithFullConfig(
data: Dataset,
agentPlatform: AgentPlatform
): Report {
val eventLogger = object : AgenticEventListener {
override fun onEvent(event: AgenticEvent) {
logger.info("Event: ${event::class.simpleName}")
}
}
return AgentInvocation.create<Report>(agentPlatform)
.withAgentName("data-analyzer")
.withInput(data)
.withGoal(Goal.of("data_analyzed"))
.withEventListener(eventLogger)
.withOutputChannel(consoleOutputChannel)
.withAutonomy(Autonomy.HUMAN_IN_LOOP)
.withTimeout(Duration.ofMinutes(5))
.withProcessOptions(ProcessOptions.builder()
.maxPlanningIterations(10)
.earlyTerminationPolicy(EarlyTerminationPolicy.ON_GOAL_ACHIEVED)
.build())
.execute()
}Use builder for complex invocations.
val result = AgentInvocation.builder(agentPlatform)
.agent(myAgent)
.input(inputData)
.goal("process_complete")
.autonomy(Autonomy.FULLY_AUTONOMOUS)
.build()
.execute()Invocation within an agent scope.
interface ScopedInvocation<T> : TypedInvocation<T, ScopedInvocation<T>> {
/** Set agent scope */
fun withScope(scope: AgentScope): ScopedInvocation<T>
}Scoped Invocation Example:
fun executeInScope(
scope: AgentScope,
input: Data
): Result {
return ScopedInvocation.create<Result>(scope)
.withInput(input)
.execute()
}Invoke supervisor agents that coordinate multiple subagents.
interface SupervisorInvocation<T> : TypedInvocation<T, SupervisorInvocation<T>> {
/** Add subagent to supervise */
fun withSubagent(subagent: Subagent): SupervisorInvocation<T>
/** Add multiple subagents */
fun withSubagents(subagents: List<Subagent>): SupervisorInvocation<T>
}Supervisor Example:
fun coordinateWorkflow(
task: ComplexTask,
agentPlatform: AgentPlatform
): WorkflowResult {
return SupervisorInvocation.create<WorkflowResult>(agentPlatform)
.withAgentName("workflow-supervisor")
.withInput(task)
.withSubagent(Subagent(dataProcessorAgent, Dataset::class.java))
.withSubagent(Subagent(reportGeneratorAgent, Report::class.java))
.withSubagent(Subagent(notificationAgent, Notification::class.java))
.execute()
}Invoke utility agents for specific tasks.
interface UtilityInvocation<T> : TypedInvocation<T, UtilityInvocation<T>>Provide initial state via blackboard.
fun processWithInitialState(
agentPlatform: AgentPlatform
): Result {
val blackboard = Blackboard.create()
blackboard.put("config", AppConfig(mode = "production"))
blackboard.put("timestamp", Instant.now())
return AgentInvocation.create<Result>(agentPlatform)
.withAgentName("processor")
.withBlackboard(blackboard)
.execute()
}Monitor agent execution with event listeners.
class ExecutionMonitor : AgenticEventListener {
private val events = mutableListOf<AgenticEvent>()
override fun onEvent(event: AgenticEvent) {
events.add(event)
when (event) {
is ActionExecutionStartEvent ->
logger.info("Action started: ${event.action.name}")
is ActionExecutionResultEvent ->
logger.info("Action completed: ${event.result}")
is LlmRequestEvent<*> ->
logger.info("LLM called with ${event.messages.size} messages")
is AgentProcessCompletedEvent ->
logger.info("Agent completed: ${event.result}")
is AgentProcessFailedEvent ->
logger.error("Agent failed", event.cause)
}
}
fun getEventSummary(): EventSummary {
return EventSummary(
totalEvents = events.size,
actionCount = events.filterIsInstance<ActionExecutionStartEvent>().size,
llmCallCount = events.filterIsInstance<LlmRequestEvent<*>>().size
)
}
}
// Usage
val monitor = ExecutionMonitor()
val result = AgentInvocation.create<Report>(agentPlatform)
.withAgentName("analyzer")
.withInput(data)
.withEventListener(monitor)
.execute()
val summary = monitor.getEventSummary()
logger.info("Execution used ${summary.llmCallCount} LLM calls")Execute agents asynchronously with CompletableFuture.
fun processMultipleAsync(
orders: List<Order>,
agentPlatform: AgentPlatform
): List<OrderResult> {
val futures = orders.map { order ->
AgentInvocation.create<OrderResult>(agentPlatform)
.withAgentName("order-processor")
.withInput(order)
.executeAsync()
}
// Wait for all to complete
return CompletableFuture.allOf(*futures.toTypedArray())
.thenApply { futures.map { it.join() } }
.join()
}
fun processWithCallback(
order: Order,
agentPlatform: AgentPlatform
) {
AgentInvocation.create<OrderResult>(agentPlatform)
.withAgentName("order-processor")
.withInput(order)
.executeAsync()
.thenAccept { result ->
logger.info("Order processed: ${result.orderId}")
notificationService.notify(result)
}
.exceptionally { error ->
logger.error("Order processing failed", error)
null
}
}Specify goals for the agent to achieve.
fun achieveGoals(
data: Data,
agentPlatform: AgentPlatform
): Result {
val primaryGoal = Goal.of("data_processed", value = 1.0)
val secondaryGoal = Goal.of("report_generated", value = 0.8)
return AgentInvocation.create<Result>(agentPlatform)
.withAgentName("data-processor")
.withInput(data)
.withGoals(listOf(primaryGoal, secondaryGoal))
.execute()
}Configure agent execution behavior.
fun processWithOptions(
task: Task,
agentPlatform: AgentPlatform
): Result {
val options = ProcessOptions.builder()
.maxPlanningIterations(15)
.maxExecutionTime(Duration.ofMinutes(10))
.earlyTerminationPolicy(EarlyTerminationPolicy.ON_GOAL_ACHIEVED)
.enableReplanning(true)
.build()
return AgentInvocation.create<Result>(agentPlatform)
.withAgentName("task-processor")
.withInput(task)
.withProcessOptions(options)
.execute()
}Set execution timeouts.
fun processWithTimeout(
data: Data,
agentPlatform: AgentPlatform
): Result {
return try {
AgentInvocation.create<Result>(agentPlatform)
.withAgentName("processor")
.withInput(data)
.withTimeout(Duration.ofSeconds(30))
.execute()
} catch (e: TimeoutException) {
logger.warn("Processing timed out")
Result.timeout()
}
}Provide user context for personalization and permissions.
fun processForUser(
request: UserRequest,
user: User,
agentPlatform: AgentPlatform
): Response {
return AgentInvocation.create<Response>(agentPlatform)
.withAgentName("request-processor")
.withInput(request)
.withUser(user)
.execute()
}interface AgentPlatform {
fun getAgent(name: String): Agent?
fun <T> executeAgent(agent: Agent, input: Any?, resultType: Class<T>): T
}
interface BaseInvocation<T> : TypedInvocation<T, BaseInvocation<T>>
interface ProcessOptions {
val maxPlanningIterations: Int
val maxExecutionTime: Duration
val earlyTerminationPolicy: EarlyTerminationPolicy
val enableReplanning: Boolean
companion object {
fun builder(): Builder
}
}
enum class EarlyTerminationPolicy {
NEVER,
ON_GOAL_ACHIEVED,
ON_NO_PROGRESS
}
enum class Autonomy {
FULLY_AUTONOMOUS,
HUMAN_IN_LOOP,
HUMAN_APPROVAL_REQUIRED
}
interface Goal {
val name: String
val description: String
val value: Double
companion object {
fun of(name: String, value: Double = 1.0): Goal
}
}Install with Tessl CLI
npx tessl i tessl/maven-com-embabel-agent--embabel-agent-apidocs