Embabel Agent Framework: The Intelligent Agent Framework for the JVM
In the ever-evolving landscape of software development, artificial intelligence and agent technologies are playing an increasingly pivotal role. The Embabel Agent Framework emerges as a powerful and flexible solution for creating intelligent agent applications on the Java Virtual Machine (JVM). This comprehensive blog post delves into the framework’s core features, usage patterns, and future roadmap, providing developers with an in-depth understanding of its capabilities.
Introduction to Embabel Agent Framework
Embabel (pronounced Em-BAY-bel) is a framework designed for authoring agentic flows on the JVM, seamlessly blending large language model (LLM)-prompted interactions with code and domain models. It supports intelligent pathfinding toward goals, developed by the creator of Spring. While written in Kotlin, it offers a natural usage model for Java developers, bridging the gap between modern AI capabilities and enterprise-grade Java applications.

Key Concepts
The framework is built around several foundational concepts that define agentic behavior:
-
Actions: The discrete steps an agent takes to progress toward its objectives. For example, extracting user input data in a news-finding agent. -
Goals: The end-state objectives an agent aims to achieve, such as retrieving relevant news stories for a specific zodiac sign. -
Conditions: Predicates evaluated before executing actions or determining goal achievement, reassessed after each action to adapt to changing states. -
Domain Model: The core objects underpinning the agent flow, informing actions, goals, and conditions. Examples include person profiles, horoscopes, and news articles. -
Plan: A dynamic sequence of actions generated by the system to achieve goals, replanned after each action to incorporate new information—functioning as an OODA loop.
Differentiators from Other Agent Frameworks
Embabel stands out in the agent framework landscape due to:
-
Sophisticated Planning:超越有限状态机,引入基于GOAP(目标导向动作规划)的动态规划, enabling novel task execution through combinatorial action sequencing. -
Extensibility Without Modification: Dynamic planning allows adding domain objects, actions, and goals without altering existing code or FSM definitions. -
Strong Typing & OOP Principles: Leveraging a typed domain model with behavior, enabling clean integration of prompts and code, full refactoring support, and eliminating magic maps.
Additional Advantages
-
Platform Abstraction: Clear separation between programming model and platform internals allows local development with production-grade QoS without code changes. -
LLM Hybridization: Facilitates cost-effective LLM usage by mixing models (e.g., local models for point tasks) to balance capability and privacy. -
Spring & JVM Ecosystem Integration: Seamless access to enterprise features like dependency injection, AOP, persistence, and transaction management. -
Testability by Design: Supports unit and end-to-end testing, mirroring Spring’s testing philosophy for AI applications.
Using Embabel Agent Framework
Authoring Agent Flows
Embabel supports two primary approaches for defining agent flows:
-
Annotation-based Model: Similar to Spring MVC, using @Agent
,@Goal
,@Condition
, and@Action
annotations:
@Agent(description = "Find news based on a person's star sign")
class StarNewsFinder(
private val horoscopeService: HoroscopeService,
private val storyCount: Int = 5,
) {
@Action
fun extractPerson(userInput: UserInput): StarPerson =
PromptRunner().createObject("Create a person from this user input, extracting their name and star sign: $userInput")
@Action
fun retrieveHoroscope(starPerson: StarPerson) =
Horoscope(horoscopeService.dailyHoroscope(starPerson.sign))
@Action(toolGroups = [ToolGroup.WEB])
fun findNewsStories(person: StarPerson, horoscope: Horoscope): RelevantNewsStories =
PromptRunner().createObject(
"""
${person.name} is an astrology believer with the sign ${person.sign}.
Their horoscope for today is:
<horoscope>${horoscope.summary}</horoscope>
Given this, use web tools and generate search queries
to find $storyCount relevant news stories summarize them in a few sentences.
Include the URL for each story.
Do not look for another horoscope reading or return results directly about astrology;
find stories relevant to the reading above.
For example:
- If the horoscope says that they may
want to work on relationships, you could find news stories about
novel gifts
- If the horoscope says that they may want to work on their career,
find news stories about training courses.
""".trimIndent()
)
@AchievesGoal(
description = "Write an amusing writeup for the target person based on their horoscope and current news stories",
)
@Action
fun writeup(
person: StarPerson,
relevantNewsStories: RelevantNewsStories,
horoscope: Horoscope,
): Writeup =
PromptRunner().withTemperature(1.2).createObject(
"""
Take the following news stories and write up something
amusing for the target person.
Begin by summarizing their horoscope in a concise, amusing way, then
talk about the news. End with a surprising signoff.
${person.name} is an astrology believer with the sign ${person.sign}.
Their horoscope for today is:
<horoscope>${horoscope.summary}</horoscope>
Relevant news stories are:
${relevantNewsStories.items.joinToString("\n") { "- ${it.url}: ${it.summary}" }}
Format it as Markdown with links.
""".trimIndent()
)
}
-
Kotlin DSL: Using agent {
andaction {
blocks for a more idiomatic Kotlin experience, enhancing code readability and expressiveness.
Planning Mechanism
The planning component is pluggable, with the default implementation using Goal Oriented Action Planning (GOAP), a popular AI algorithm in gaming. GOAP enables dynamic decision-making based on the current world state and agent goals. Future planning options include integration with reasoning models like OpenAI o1 or DeepSeek R1.
Execution Modes
Agents execute via an AgentPlatform
implementation, supporting multiple modes:
-
Focused: User code invokes specific agent functionality with explicit inputs, ideal for event-driven flows. -
Closed: User intent is classified to select an agent, with actions constrained to the chosen agent’s definitions. -
Open: The platform assesses intent and uses all available resources to build a custom agent, combining relevant actions and goals. This mode allows novel, unanticipated solution paths but is less deterministic.
Future modes may include “Evolving,” where running processes can adapt by adding new goals and agents dynamically.
Quick Start Guide
Creating a New Agent Project
Begin building with Embabel using these methods:
-
GitHub Templates: Use Java or Kotlin templates by clicking “Use this template”.
-
Project Creator Tool: Run the following command to generate a project locally:
uvx --from git+https://github.com/embabel/project-creator.git project-creator
Choose Java/Kotlin, specify project details, and with an OPENAI_API_KEY
and Maven installed, your agent can be running in under a minute.
Why Embabel is Needed
The Case for Agent Frameworks
While direct LLM integration is possible, agent frameworks offer:
-
Modular LLM interactions, reducing cost and errors by using cheaper models for specific tasks. -
Robust testing support for unit and integration testing. -
Composability, enabling reusable subflows and actions. -
Workflow management for retries, state maintenance, and guardrails.
JVM-Centric Approach
Despite Python’s current dominance, JVM-based solutions excel where enterprise infrastructure, Spring ecosystems, and existing Java codebases are prevalent. Embabel bridges AI capabilities with JVM enterprise systems.
Relationship to Spring AI
Embabel builds on Spring AI, elevating it from a low-level API (like Servlet) to a higher-level framework (like Spring MVC), simplifying complex AI application development and testing.
Code Examples
Agent Implementation
The StarNewsFinder
agent demonstrates key concepts:
@Agent(description = "Find news based on a person's star sign")
class StarNewsFinder(
private val horoscopeService: HoroscopeService,
private val storyCount: Int = 5,
) {
@Action
fun extractPerson(userInput: UserInput): StarPerson =
PromptRunner().createObject("Create a person from this user input, extracting their name and star sign: $userInput")
@Action
fun retrieveHoroscope(starPerson: StarPerson) =
Horoscope(horoscopeService.dailyHoroscope(starPerson.sign))
@Action(toolGroups = [ToolGroup.WEB])
fun findNewsStories(person: StarPerson, horoscope: Horoscope): RelevantNewsStories =
PromptRunner().createObject(
"""
${person.name} is an astrology believer with the sign ${person.sign}.
Their horoscope for today is:
<horoscope>${horoscope.summary}</horoscope>
Given this, use web tools and generate search queries
to find $storyCount relevant news stories summarize them in a few sentences.
Include the URL for each story.
Do not look for another horoscope reading or return results directly about astrology;
find stories relevant to the reading above.
For example:
- If the horoscope says that they may
want to work on relationships, you could find news stories about
novel gifts
- If the horoscope says that they may want to work on their career,
find news stories about training courses.
""".trimIndent()
)
@AchievesGoal(
description = "Write an amusing writeup for the target person based on their horoscope and current news stories",
)
@Action
fun writeup(
person: StarPerson,
relevantNewsStories: RelevantNewsStories,
horoscope: Horoscope,
): Writeup =
PromptRunner().withTemperature(1.2).createObject(
"""
Take the following news stories and write up something
amusing for the target person.
Begin by summarizing their horoscope in a concise, amusing way, then
talk about the news. End with a surprising signoff.
${person.name} is an astrology believer with the sign ${person.sign}.
Their horoscope for today is:
<horoscope>${horoscope.summary}</horoscope>
Relevant news stories are:
${relevantNewsStories.items.joinToString("\n") { "- ${it.url}: ${it.summary}" }}
Format it as Markdown with links.
""".trimIndent()
)
}
Domain Models
Type-safe domain classes underpin the agent:
data class RelevantNewsStories(
val items: List<NewsStory>
)
data class NewsStory(
val url: String,
val summary: String,
)
data class Subject(
val name: String,
val sign: String,
)
data class Horoscope(
val summary: String,
)
data class FunnyWriteup(
override val text: String,
) : HasContent
Development Best Practices
Dogfood Policy
Embabel practices extreme dogfooding, using AI agents for coding, documentation, and marketing, with key principles:
-
Automate all possible tasks using AI agents, striving for maximum efficiency. -
Developers retain control, guiding agents and ensuring code quality & readability. -
Use only open-source Embabel-based agents, contributing improvements to the community. -
Prioritize agents that accelerate project development, setting an example for users.
Environment Setup
Prerequisites
-
Code Acquisition: Clone the repo or create a new Spring Boot project. -
Environment Variables: -
OPENAI_API_KEY
: Required for OpenAI integrations. -
ANTHROPIC_API_KEY
: Optional for Anthropic API, needed for coding agents.
-
-
Services: Docker Desktop with MCP extension, activating tools like Brave Search, Fetch, Puppeteer, and Wikipedia. Local Ollama setups use the ollama
profile.
Running & Testing
Execution
Use the project creator:
uvx --from git+https://github.com/embabel/project-creator.git project-creator
Or run via scripts:
cd scripts
./shell.sh
Maven execution:
export SPRING_PROFILES_ACTIVE=shell,starwars
mvn -P agent-examples-kotlin -Dmaven.test.skip=true spring-boot:run
Testing
Run tests via Maven:
mvn test
This executes unit and integration tests without requiring external services.
Spring Profiles
Profiles configure environment behavior:
-
Interaction: shell
for interactive mode. -
Models: ollama
,docker-desktop
,docker
for different model backends. -
Logging: Thematic profiles like starwars
,severance
, andcolossus
for enhanced logging experiences.
Integrating Embabel in Your Project
Maven Configuration
Add the BOM to your pom.xml
:
<dependencyManagement>
<dependencies>
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-dependencies</artifactId>
<version>1.0.0-SNAPSHOT</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Add core dependencies:
<dependencies>
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-api</artifactId>
</dependency>
</dependencies>
Gradle (Kotlin DSL)
Repositories in build.gradle.kts
:
repositories {
mavenCentral()
maven {
name = "embabel-snapshots"
url = uri("https://repo.embabel.com/artifactory/libs-snapshot")
mavenContent {
snapshotsOnly()
}
}
maven {
name = "Spring Milestones"
url = uri("https://repo.spring.io/milestone")
}
}
Dependencies:
dependencies {
implementation(platform("com.embabel.agent:embabel-agent-dependencies:1.0.0-SNAPSHOT"))
implementation("com.embabel.agent:embabel-agent-api:1.0.0-SNAPSHOT")
}
Gradle (Groovy DSL)
Repositories in build.gradle
:
repositories {
mavenCentral()
maven {
name = 'embabel-snapshots'
url = 'https://repo.embabel.com/artifactory/libs-snapshot'
mavenContent {
snapshotsOnly()
}
}
maven {
name = 'Spring Milestones'
url = 'https://repo.spring.io/milestone'
}
}
Dependencies:
dependencies {
implementation platform('com.embabel.agent:embabel-agent-dependencies:1.0.0-SNAPSHOT')
implementation 'com.embabel.agent:embabel-agent-api:1.0.0-SNAPSHOT'
}
MCP & A2A Integrations
Using Embabel as an MCP Server
Embabel can serve as an MCP (Model Context Protocol) server, though integration with UIs like Claude Desktop requires a middleware layer due to SSE/stdio differences.
Consuming MCP Servers
MCP standardizes AI tool integration. Configure in application.yml
:
spring:
ai:
mcp:
client:
enabled: true
name: embabel
version: 1.0.0
request-timeout: 30s
type: SYNC
stdio:
connections:
docker-mcp:
command: docker
args:
- run
- -i
- --rm
- alpine/socat
- STDIO
- TCP:host.docker.internal:8811
Docker Desktop integrates MCP tools via its catalog, offering sandboxed, credential-managed executions.
A2A Integration
Embabel supports the A2A protocol for connecting to Google’s AI services:
-
Enable the a2a
profile. -
Set GOOGLE_STUDIO_API_KEY
. -
Start via Docker:
docker compose --profile a2a up
Access the web interface at http://localhost:12000/
and connect to the agent at host.docker.internal:8080/a2a
.
Roadmap and Future Directions
Embabel’s roadmap focuses on:
-
JVM AI Enablement: Becoming the go-to solution for adding generative AI to Spring-based Java applications. -
Model Validation: Demonstrating extensibility, federation, budget-aware agents, and enterprise data integration. -
Cross-Platform Expansion: Extending the framework to TypeScript and Python after solidifying JVM foundations.
Conclusion
The Embabel Agent Framework represents a significant advancement in JVM-based AI application development, combining LLM capabilities with enterprise-grade software principles. Its dynamic planning, strong typing, and Spring integration make it a compelling choice for developers seeking to build robust, intelligent agents.
As the framework evolves, its focus on extensibility, testability, and cross-platform compatibility positions it to become a leading agent framework across industries. Whether you’re integrating AI into existing Spring applications or building new intelligent systems, Embabel provides the tools and abstractions to bring your agentic visions to life.
Start exploring Embabel today by creating a project from our templates, and join the community as we shape the future of intelligent agent development on the JVM.