Limitations of the Current Embabel-Agent Version: Critical Constraints in 0.3.x

TLDR: The current Embabel-Agent 0.3.x release has seven documented constraints including unenforced skill permissions, isolated Docker sandboxes without network access, AOP proxy limitations in observability, lack of nested property filtering in RAG, strict LLM structured output requirements, and limited environment variable binding for collections.

The Embabel-Agent framework provides a Spring-based foundation for building LLM-augmented flows on the JVM, but developers targeting production deployments must understand the limitations of the current embabel-agent version before architecting solutions. As implemented in embabel/embabel-agent, the 0.3.x branch contains specific architectural constraints around skill execution, observability tracing, and configuration binding that impact how you design agent workflows.

Skill Execution and Permission Enforcement Limitations

Script Execution Not Yet Supported

According to embabel-agent-docs/src/main/asciidoc/reference/agent_skills/page.adoc at line 273, the framework parses skill definitions but script execution is not yet supported in the current release. While you can define skills with configuration metadata, the actual runtime execution engine for scripts remains unimplemented.

Unenforced Allowed-Tools Field

The allowed-tools front-matter field in skill definitions is parsed during startup but never enforced at runtime. As documented in the agent skills reference, this means a skill declared with restricted tool access can still invoke any available tool in the registry.

val skill = Skill(
    name = "web-search",
    description = "Runs web-search tools",
    // The front-matter lists allowedTools, but the framework does NOT enforce it yet.
    allowedTools = listOf("search", "fetch")
)

Work-around: Implement a custom SkillValidator bean or manually validate tool usage within the skill's action method until enforcement is implemented in a future release.

Docker Sandbox Isolation Constraints

The default DockerSkillScriptExecutionEngine.isolated() configuration documented in embabel-agent-skills/README.md at line 170 creates completely isolated containers with no network access and limited CPU/memory quotas.

val engine = DockerSkillScriptExecutionEngine.isolated()   // No network
val result = engine.executeScript("curl https://example.com")  
// → Fails with "Network is disabled" because the container cannot reach the internet.

This isolation prevents skills from making live web calls or accessing external APIs directly.

Work-around: Use DockerSkillScriptExecutionEngine.unrestricted() when security policies permit network access, or expose required APIs via a separate micro-service that the isolated skill calls through a defined interface.

Observability Proxy Limitations with @Tracked

The @Tracked annotation relies on Spring AOP proxies for trace interception. As noted in embabel-agent-observability/README.md at line 516, internal method calls within the same class bypass the proxy, resulting in missing trace records.

@Component
public class OrderService {

    @Tracked("validate")
    public void validate(Order order) { /* … */ }

    public void process(Order order) {
        // ❌ This internal call bypasses the proxy → no trace emitted
        validate(order);
    }
}

Work-around: Self-inject the proxy to ensure intercepted calls:

@Component
public class OrderService {
    @Autowired private OrderService self;   // Spring injects the proxy

    @Tracked("validate")
    public void validate(Order order) { /* … */ }

    public void process(Order order) {
        self.validate(order);   // ✅ goes through the proxy
    }
}

RAG Filter and PropertyFilter API Constraints

Nested Property Path Limitations

The vector-store-agnostic filter DSL in embabel-agent-api/src/main/kotlin/com/embabel/agent/filter/PropertyFilter.kt at line 29 intentionally excludes nested-property paths to maintain deterministic translation across backends (Lucene, Neo4j, etc.). As documented in embabel-agent-docs/src/main/asciidoc/reference/rag/page.adoc at line 785, filters work only on top-level properties.

val filter = PropertyFilter.eq("address.city", "London") // ❌ will never match
// Correct approach: flatten the address into top-level fields
val filter = PropertyFilter.eq("city", "London")

Work-around: Denormalize complex domain models before indexing, maintaining flattened top-level fields for filtering, or create a secondary mapping with the required keys.

LLM Structured Output Restrictions

The current release faces five specific constraints when handling structured LLM output, as detailed in embabel-agent-docs/src/main/asciidoc/reference/llms/page.adoc at line 1425:

  • Required fields must be explicit: Fields must declare @JsonProperty(required=true) or similar annotations
  • Optional values need nullable types: Use type: ["string","null"] or nullable Kotlin types
  • Arrays of objects lack full support: Many providers cannot reliably parse arrays of complex objects
  • Streaming output unavailable: No streaming support for structured responses
  • OpenAI compatibility only: Only OpenAI-compatible response_format is currently reliable
data class Review(
    @JsonProperty(required = true) val rating: Int,
    // Optional comment – must be nullable or annotated as required if you want it sent.
    val comment: String? = null
)

If comment is omitted in the LLM prompt without proper null handling, OpenAI will reject the response.

Work-around: Use createObjectIfPossible for fault-tolerant parsing or mark all optional fields explicitly nullable.

Spring Boot Configuration Binding Limitations

Environment variables cannot bind directly to Lists, Maps, or nested objects when using constructor binding, as verified in embabel-agent-api/src/test/kotlin/com/embabel/agent/config/AgentPlatformPropertiesIntegrationTest.kt at line 78.


# ❌ This will NOT bind to a List<String> property via constructor binding

MY_APP_SERVERS=server1,server2
@ConfigurationProperties(prefix = "my.app")
data class MyAppProps(
    val servers: List<String>    // ← constructor-bound list fails with env vars
)

Work-around: Switch to setter-based binding with mutable properties:

@ConfigurationProperties(prefix = "my.app")
class MyAppProps {
    var servers: List<String> = listOf()  // ✅ setter binding works with env vars
}

Or use YAML configuration files with proper list syntax instead of environment variables.

Summary

  • Skill permissions are parsed but unenforced in the current 0.3.x release, requiring manual validation
  • Docker sandboxes run in network-isolated mode by default, blocking external API calls from skills
  • @Tracked annotations miss internal method calls due to Spring AOP proxy limitations; use self-injection or extract to separate beans
  • RAG filters only support top-level properties; nested paths like address.city return no matches
  • LLM structured output requires explicit required field declarations and lacks streaming support or reliable array-of-objects handling
  • Environment variables cannot bind to constructor-bound Lists or Maps; use setter-based binding or YAML files instead

Frequently Asked Questions

Why doesn't my @Tracked annotation capture internal method calls?

The @Tracked annotation uses Spring AOP proxies to intercept method calls. Proxies only intercept public method calls made through the Spring container; direct internal calls within the same class bypass the proxy entirely. To capture these calls, self-inject the proxy instance or extract the tracked method into a separate Spring bean.

Can I use nested JSON properties in RAG vector store filters?

No. The current PropertyFilter implementation in PropertyFilter.kt and the RAG filter DSL documented in page.adoc do not support nested-property paths like address.city. You must flatten your data model before indexing or maintain separate top-level fields for filtering purposes.

How do I allow network access for skills running in Docker?

By default, DockerSkillScriptExecutionEngine.isolated() disables network access. You can switch to DockerSkillScriptExecutionEngine.unrestricted() to enable network connectivity, though this reduces security isolation. Alternatively, architect your solution so that isolated skills communicate with external services through a sidecar or API gateway pattern rather than direct network calls.

Why won't my environment variables bind to List properties?

Spring Boot's constructor binding for configuration properties has limited support for binding environment variables to collections. The test case in AgentPlatformPropertiesIntegrationTest.kt confirms this limitation. Convert your configuration class to use setter-based binding (mutable var properties) or migrate from environment variables to YAML configuration files for complex collection types.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →