CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/maven-com-embabel-agent--embabel-agent-api

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

Overview
Eval results
Files

subagents.mddocs/

Subagents

Subagents enable hierarchical agent composition where agents can invoke other specialized agents to handle specific tasks. This supports delegation patterns and complex multi-agent workflows.

Capabilities

RunSubagent Object

Static methods for executing subagents.

object RunSubagent {
    /** Execute agent instance as subagent */
    fun instance(agent: Agent): SubagentExecutionRequest

    /** Execute agent from annotated instance */
    fun fromAnnotatedInstance(instance: Any): SubagentExecutionRequest

    /** Execute with typed input (Kotlin) */
    inline fun <reified I> instance(agent: Agent, input: I): SubagentExecutionRequest

    /** Execute with typed input and output (Kotlin) */
    inline fun <reified I, reified O> instance(agent: Agent, input: I): SubagentExecutionRequest
}

Basic Subagent Execution:

@Agent(name = "orchestrator", provider = "workflow", description = "Orchestrates workflow")
class OrchestratorAgent(
    private val dataProcessorAgent: Agent,
    private val reportGeneratorAgent: Agent
) {

    @Action(description = "Execute complete workflow")
    fun executeWorkflow(rawData: RawData): WorkflowResult {
        // Delegate to data processor subagent
        RunSubagent.instance(dataProcessorAgent)

        // The framework handles the subagent execution
        // and returns control here after completion
        return WorkflowResult.success()
    }
}

SubagentExecutionRequest

Control flow exception that triggers subagent execution.

class SubagentExecutionRequest(
    val agent: Agent,
    val input: Any?,
    val outputType: Class<*>?
) : SpecialReturnException() {
    override fun handle(actionContext: ActionContext): Any {
        // Framework executes subagent and returns result
        return executeSubagent(agent, input, outputType, actionContext)
    }
}

asSubProcess Extension Functions

Execute subagents via context methods.

fun <O> ExecutingOperationContext.asSubProcess(
    outputClass: Class<O>,
    agent: Agent
): O

fun <O> ExecutingOperationContext.asSubProcess(
    outputClass: Class<O>,
    agentScopeBuilder: AgentScopeBuilder
): O

// Kotlin extension with reified generics
inline fun <reified O> ExecutingOperationContext.asSubProcess(
    agent: Agent
): O

asSubProcess Example:

@Agent(name = "supervisor", provider = "management", description = "Supervises processing")
class SupervisorAgent(
    private val validatorAgent: Agent,
    private val enrichmentAgent: Agent,
    private val transformerAgent: Agent
) {

    @Action(description = "Process with subagents")
    fun processData(data: RawData, context: ExecutingOperationContext): ProcessedData {
        // Step 1: Validate using subagent
        val validation = context.asSubProcess(
            outputClass = ValidationResult::class.java,
            agent = validatorAgent
        )

        if (!validation.isValid) {
            throw ValidationException(validation.errors)
        }

        // Step 2: Enrich data using subagent
        val enriched = context.asSubProcess(
            outputClass = EnrichedData::class.java,
            agent = enrichmentAgent
        )

        // Step 3: Transform using subagent
        return context.asSubProcess(
            outputClass = ProcessedData::class.java,
            agent = transformerAgent
        )
    }
}

Subagent Reference

Reference subagents for configuration and invocation.

data class Subagent(
    val agent: Agent?,
    val agentName: String?,
    val agentType: Class<*>?,
    val inputClass: Class<*>
) {
    companion object {
        /** Create from agent instance */
        operator fun invoke(agent: Agent, inputClass: Class<*>): Subagent

        /** Create from agent name */
        operator fun invoke(agentName: String, inputClass: Class<*>): Subagent

        /** Create from agent type */
        operator fun invoke(agentType: Class<*>, inputClass: Class<*>): Subagent
    }

    /** Resolve agent from platform */
    fun resolve(agentPlatform: AgentPlatform): Agent
}

Subagent Reference Example:

val dataProcessorSubagent = Subagent(
    agent = dataProcessorAgent,
    inputClass = Dataset::class.java
)

val reportGeneratorSubagent = Subagent(
    agentName = "report-generator",
    inputClass = ProcessedData::class.java
)

val analyzerSubagent = Subagent(
    agentType = AnalyzerAgent::class.java,
    inputClass = RawData::class.java
)

Specialist Delegation Pattern

Delegate specific tasks to specialized agents.

@Agent(name = "customer-service", provider = "support", description = "Customer service")
class CustomerServiceAgent(
    private val technicalSupportAgent: Agent,
    private val billingAgent: Agent,
    private val generalSupportAgent: Agent
) {

    @Action(description = "Handle customer inquiry")
    fun handleInquiry(
        inquiry: CustomerInquiry,
        context: ExecutingOperationContext
    ): InquiryResponse {
        // Route to appropriate specialist agent
        return when (inquiry.category) {
            InquiryCategory.TECHNICAL -> {
                context.asSubProcess(
                    outputClass = InquiryResponse::class.java,
                    agent = technicalSupportAgent
                )
            }

            InquiryCategory.BILLING -> {
                context.asSubProcess(
                    outputClass = InquiryResponse::class.java,
                    agent = billingAgent
                )
            }

            else -> {
                context.asSubProcess(
                    outputClass = InquiryResponse::class.java,
                    agent = generalSupportAgent
                )
            }
        }
    }
}

Pipeline Pattern

Chain multiple subagents in a processing pipeline.

@Agent(name = "pipeline-coordinator", provider = "processing", description = "Coordinates pipeline")
class PipelineCoordinatorAgent(
    private val extractorAgent: Agent,
    private val transformerAgent: Agent,
    private val loaderAgent: Agent
) {

    @Action(description = "Execute ETL pipeline")
    fun executePipeline(
        source: DataSource,
        context: ExecutingOperationContext
    ): LoadResult {
        context.updateProgress("Starting ETL pipeline")

        // Extract
        context.updateProgress("Extracting data...")
        val extracted = context.asSubProcess<ExtractedData>(extractorAgent)

        // Transform
        context.updateProgress("Transforming data...")
        val transformed = context.asSubProcess<TransformedData>(transformerAgent)

        // Load
        context.updateProgress("Loading data...")
        return context.asSubProcess<LoadResult>(loaderAgent)
    }
}

Parallel Subagent Execution

Execute multiple subagents in parallel.

@Agent(name = "parallel-processor", provider = "processing", description = "Parallel processor")
class ParallelProcessorAgent(
    private val agentPlatform: AgentPlatform
) {

    @Action(description = "Process with parallel subagents")
    fun processParallel(
        data: ComplexData,
        context: OperationContext
    ): AggregatedResult {
        // Define subagents
        val analysisAgent = agentPlatform.getAgent("analysis-agent")!!
        val validationAgent = agentPlatform.getAgent("validation-agent")!!
        val enrichmentAgent = agentPlatform.getAgent("enrichment-agent")!!

        // Execute in parallel using fireAgent
        val analysisFuture = context.fireAgent(data, AnalysisResult::class.java)
        val validationFuture = context.fireAgent(data, ValidationResult::class.java)
        val enrichmentFuture = context.fireAgent(data, EnrichmentResult::class.java)

        // Wait for all to complete
        val analysis = analysisFuture?.join()
        val validation = validationFuture?.join()
        val enrichment = enrichmentFuture?.join()

        return AggregatedResult(analysis, validation, enrichment)
    }
}

AgentScopeBuilder

Build agent scopes for dynamic subagent creation.

interface AgentScopeBuilder {
    fun withAction(action: Action): AgentScopeBuilder
    fun withGoal(goal: Goal): AgentScopeBuilder
    fun withCondition(condition: Condition): AgentScopeBuilder
    fun build(): AgentScope
}

interface AgentScope {
    val actions: List<Action>
    val goals: List<Goal>
    val conditions: List<Condition>
}

Dynamic Subagent Example:

@Action(description = "Execute dynamic subagent")
fun executeDynamic(
    task: Task,
    context: ExecutingOperationContext
): TaskResult {
    // Build agent scope dynamically
    val scopeBuilder = AgentScopeBuilder()
        .withAction(createValidationAction(task))
        .withAction(createProcessingAction(task))
        .withGoal(createCompletionGoal(task))

    // Execute as subprocess
    return context.asSubProcess(
        outputClass = TaskResult::class.java,
        agentScopeBuilder = scopeBuilder
    )
}

Subagent with Context Passing

Pass context and state to subagents.

@Agent(name = "context-aware-coordinator", provider = "coordination", description = "Context-aware coordinator")
class ContextAwareCoordinatorAgent(
    private val processorAgent: Agent
) {

    @Action(description = "Process with context")
    fun processWithContext(
        data: Data,
        context: ExecutingOperationContext
    ): ProcessedData {
        // Add context to blackboard
        context.put("processing_mode", "strict")
        context.put("validation_level", "high")
        context.put("timestamp", Instant.now())

        // Subagent will have access to blackboard context
        return context.asSubProcess(
            outputClass = ProcessedData::class.java,
            agent = processorAgent
        )
    }
}

Error Handling in Subagents

Handle subagent failures gracefully.

@Agent(name = "resilient-coordinator", provider = "coordination", description = "Resilient coordinator")
class ResilientCoordinatorAgent(
    private val primaryAgent: Agent,
    private val fallbackAgent: Agent
) {

    @Action(description = "Process with fallback")
    fun processWithFallback(
        data: Data,
        context: ExecutingOperationContext
    ): ProcessResult {
        return try {
            // Try primary agent
            context.asSubProcess(
                outputClass = ProcessResult::class.java,
                agent = primaryAgent
            )
        } catch (e: Exception) {
            logger.warn("Primary agent failed, using fallback", e)

            // Fall back to alternative agent
            context.asSubProcess(
                outputClass = ProcessResult::class.java,
                agent = fallbackAgent
            )
        }
    }
}

Nested Subagent Hierarchy

Create hierarchical agent structures.

@Agent(name = "top-level-coordinator", provider = "coordination", description = "Top coordinator")
class TopLevelCoordinatorAgent(
    private val midLevelAgent: Agent
) {

    @Action(description = "Execute hierarchical workflow")
    fun executeHierarchy(
        request: WorkflowRequest,
        context: ExecutingOperationContext
    ): WorkflowResult {
        // Top level delegates to mid level
        // Mid level may delegate to specialists
        return context.asSubProcess(
            outputClass = WorkflowResult::class.java,
            agent = midLevelAgent
        )
    }
}

@Agent(name = "mid-level-coordinator", provider = "coordination", description = "Mid coordinator")
class MidLevelCoordinatorAgent(
    private val specialistAgent1: Agent,
    private val specialistAgent2: Agent
) {

    @Action(description = "Coordinate specialists")
    fun coordinateSpecialists(
        request: WorkflowRequest,
        context: ExecutingOperationContext
    ): WorkflowResult {
        // Execute specialist 1
        val result1 = context.asSubProcess<SpecialistResult1>(specialistAgent1)

        // Execute specialist 2
        val result2 = context.asSubProcess<SpecialistResult2>(specialistAgent2)

        // Combine results
        return WorkflowResult(result1, result2)
    }
}

Subagent Communication

Share data between parent and subagents via blackboard.

@Agent(name = "parent-agent", provider = "coordination", description = "Parent agent")
class ParentAgent(
    private val childAgent: Agent
) {

    @Action(description = "Communicate with subagent")
    fun communicateWithSubagent(
        data: Data,
        context: ExecutingOperationContext
    ): Result {
        // Set up shared state
        context.put("parent_context", ParentContext(mode = "production"))
        context.put("configuration", Config(strict = true))

        // Child agent can access these values from its context
        val childResult = context.asSubProcess<ChildResult>(childAgent)

        // Child agent may have added values to blackboard
        val childMetadata = context.last(ChildMetadata::class.java)

        return Result(childResult, childMetadata)
    }
}

@Agent(name = "child-agent", provider = "processing", description = "Child agent")
class ChildAgent {

    @Action(description = "Process with parent context")
    fun process(data: Data, context: ActionContext): ChildResult {
        // Access parent's context
        val parentContext = context.get("parent_context", ParentContext::class.java)
        val config = context.get("configuration", Config::class.java)

        // Process using parent's configuration
        val result = processor.process(data, config)

        // Add metadata for parent
        context.put("child_metadata", ChildMetadata(processingTime = 1000))

        return result
    }
}

Types

sealed class SpecialReturnException : RuntimeException() {
    abstract fun handle(actionContext: ActionContext): Any
}

interface Agent {
    val name: String
    val description: String
}

interface AgentPlatform {
    fun getAgent(name: String): Agent?
}

interface AgentScope {
    val actions: List<Action>
    val goals: List<Goal>
    val conditions: List<Condition>
}

Install with Tessl CLI

npx tessl i tessl/maven-com-embabel-agent--embabel-agent-api@0.3.0

docs

actions-goals.md

agent-definition.md

builtin-tools.md

chat.md

conditions.md

events.md

human-in-the-loop.md

index.md

invocation.md

io-binding.md

llm-interaction.md

models.md

planning-workflows.md

platform-management.md

runtime-context.md

state-management.md

streaming.md

subagents.md

tools.md

type-system.md

typed-operations.md

validation.md

tile.json