Overview
Agentic AI is the use of large language (and other multi-modal) models not just to generate text, but to act as reasoning, goal-driven agents that can plan, call tools, and adapt their actions to deliver outcomes.
The JVM is a compelling platform for this because its strong type safety provides guardrails for integrating LLM-driven behaviors with real systems. Because so many production applications already run on the JVM it is the natural place to embed AI.
While Agentic AI has been hyped, much of it has lived in academic demos with little practical value; by integrating directly into legacy and enterprise JVM applications, we can unlock AI capabilities without rewriting core systems or tearing down a factory to install new machinery.
Glossary
Before we begin, in this glossary we’ll explain some terms that may be new if you’re taking your first steps as an applied AI software developer. It is assumed that you already know what a large language model (LLM) is from an end-user’s point of view.
You may skim or skip this section if you’re already a seasoned agentic AI engineer.
-
Agent
An Agent in the Embabel framework is a self-contained component that bundles together domain logic, AI capabilities, and tool usage to achieve a specific goal on behalf of the user.Inside, it exposes multiple
@Actionmethods, each representing discrete steps the agent can take. Actions depend on typically structured (sometimes natural language) input. The input is used to perform tasks on behalf of the user - executing domain code, calling AI models or even calling other agents as a sub-process.When an AI model is called it may be given access to tools that expand its capabilities in order to achieve a goal. The output is a new type, representing a transformation of the input, however during execution one or more side-effects can occur. An example of side effects might be new records stored in a database, orders placed on an e-commerce site and so on.
-
Tools
Tools extend the raw capabilities of an LLM by letting it interact with the outside world. On its own, a language model can only generate responses from its training data and context window, which risks producing inaccurate or “hallucinated” answers.While tool usage is inspired by an technique known as ReAct (Reason + Act), which itself builds on Chain of Thought reasoning, most recent LLMs allow specifying tools specifically instead of relying on prompt engineering techniques.
When tools are present, the LLM interprets the user request, plans steps, and then delegates certain tasks to tools in a loop. This lets the model alternate between reasoning (“what needs to be done?”) and acting (“which tool can do it?”).
Benefits of tools include:
-
The ability to answer questions or perform tasks beyond what the LLM was trained on, by delegating to domain-specific or external systems.
-
Producing useful side effects, such as creating database records, generating visualizations, booking flights, or invoking any process the system designer provides.
In short, tools are one way to bridge the gap between text prediction and real-world action, turning an LLM into a practical agent capable of both reasoning and execution. In Embabel many tools are bound domain objects.
-
-
MCP
Model Context Protocol (MCP) is a standardized way of hosting and sharing tools. Unlike plain tools, which are usually wired directly into one agent or app, an MCP Server makes tools discoverable and reusable across models and runtimes they can be registered system-wide or at runtime, and invoked through a common protocol. Embabel can both consume and publish such tools for systems integration.
- Domain Integrated Context Engineering (DICE)
Enhances context engineering by grounding both LLM inputs and outputs in typed domain objects. Instead of untyped prompts, context is structured with business-aware models that provide precision, testability, and seamless integration with existing systems. DICE transforms context into a re-usable, inspectable, and reliably manipulable artifact.
Why do we need an Agent Framework?
Aren’t LLMs smart enough to solve our problems directly? Aren’t MCP tools all we need to allow them to solve complex problems? LLMs seem to get more capable by the day and MCPs can give LLMs access to a lot of empowering tools, making them even more capable.
But there are still many reasons that a higher level orchestration technology is needed, especially for business applications. Here are some of the most important:
- Explainability: Why were choices made in solving a problem?
- Discoverability: How do we find the right tools at each point, and ensure that models aren’t confused in choosing between them?
- Ability to mix models, so that we are not reliant only on the largest models but can use local, cheaper, private models for many tasks
- Ability to inject guardrails at any point in a flow
- Ability to manage flow execution and introduce greater resilience
- Composability of flows at scale. We’ll soon be seeing not just agents running on one system, but federations of agents.
- Safer integration with sensitive existing systems such as databases, where it is dangerous to allow even the best LLM write access.
Agent frameworks break complex tasks into smaller, manageable components, offering greater control and predictability.
Agent frameworks offer "code agency" as well as "LLM agency." This division is well described in this paper from NVIDIA Research.
Further reading:
Embabel Differentiators
So how does Embabel differ from other agent frameworks? We like to believe the Embabel agent framework is to be the best fit for developing agentic AI in the enterprise.
Sophisticated Planning
Goes beyond a finite state machine or sequential execution with nesting by introducing a true planning step, using a non-LLM AI algorithm. This enables the system to perform tasks it wasn’t programmed to do by combining known steps in a novel order, as well as make decisions about parallelization and other runtime behavior.
Superior Extensibility and Reuse
Because of dynamic planning, adding more domain objects, actions, goals and conditions can extend the capability of the system, without editing FSM definitions or existing code.
Strong Typing and Object Orientation
Actions, goals and conditions are informed by a domain model, which can include behavior. Everything is strongly typed and prompts and manually authored code interact cleanly. No more magic maps. Enjoy full refactoring support.
Platform Abstraction
Clean separation between programming model and platform internals allows running locally while potentially offering higher QoS in production without changing application code.
LLM Mixing
It is easy to build applications that mix LLMs, ensuring the most cost-effective yet capable solution. This enables the system to leverage the strengths of different models for different tasks. In particular, it facilitates the use of local models for point tasks. This can be important for cost and privacy.
Spring and JVM Integration
Built on Spring and the JVM, making it easy to access existing enterprise functionality and capabilities. For example:
- Spring can inject and manage agents, including using Spring AOP to decorate functions.
- Robust persistence and transaction management solutions are available.
Designed for Testability
Both unit testing and agent end-to-end testing are easy from the ground up.
Core Concepts
Agent frameworks break up tasks into separate smaller interactions, making LLM use more predictable and focused.
Embabel models agentic flows in terms of:
- Actions: Steps an agent takes. These are the building blocks of agent behavior.
- Goals: What an agent is trying to achieve.
- *** Conditions***: Conditions to do evaluations while planning. Conditions are reassessed after each action is executed.
- Domain Model: Objects underpinning the flow and informing Actions, Goals and Conditions.
This enables Embabel to create a plan: A sequence of actions to achieve a goal. Plans are dynamically formulated by the system, not the programmer. The system replans after the completion of each action, allowing it to adapt to new information as well as observe the effects of the previous action. This is effectively an OODA loop.
Application developers don’t usually have to deal with conditions and planning directly, as most conditions result from data flow defined in code, allowing the system to infer pre and post conditions to (re-)evaluate the plan.
Complete Example
Let’s look at a complete example that demonstrates how Embabel infers conditions from input/output types and manages data flow between actions. This example comes from the Embabel Agent Examples repository:
@Agent(description = "Find news based on a person's star sign") // ①
public class StarNewsFinder {
private final HoroscopeService horoscopeService; // ②
private final int storyCount;
public StarNewsFinder(
HoroscopeService horoscopeService, // ③
@Value("$\{star-news-finder.story.count:5}") int storyCount) {
this.horoscopeService = horoscopeService;
this.storyCount = storyCount;
}
@Action // ④
public StarPerson extractStarPerson(UserInput userInput, OperationContext context) { // ⑤
return context.ai()
.withLlm(OpenAiModels.GPT_41)
.createObject("""
Create a person from this user input, extracting their name and star sign:
%s""".formatted(userInput.getContent()), StarPerson.class); // ⑥
}
@Action // ⑦
public Horoscope retrieveHoroscope(StarPerson starPerson) { // ⑧
// Uses regular injected Spring service - not LLM
return new Horoscope(horoscopeService.dailyHoroscope(starPerson.sign())); // ⑨
}
@Action // ⑩
public RelevantNewsStories findNewsStories(
StarPerson person, Horoscope horoscope, OperationContext context) { // ⑪
var prompt = """
%s is an astrology believer with the sign %s.
Their horoscope for today is: %s
Given this, use web tools to find %d relevant news stories.
""".formatted(person.name(), person.sign(), horoscope.summary(), storyCount);
return context.ai().withDefaultLlm()
.withToolGroup(CoreToolGroups.WEB) // ⑫
.createObject(prompt, RelevantNewsStories.class);
}
@AchievesGoal(description = "Write an amusing writeup based on horoscope and news") // ⑬
@Action
public Writeup writeup(
StarPerson person, RelevantNewsStories stories, Horoscope horoscope,
OperationContext context) { // ⑭
var llm = LlmOptions.fromCriteria(ModelSelectionCriteria.getAuto())
.withTemperature(0.9); // ⑮
var storiesFormatted = stories.items().stream()
.map(s -> "- " + s.url() + ": " + s.summary())
.collect(Collectors.joining("\n"));
var prompt = """
Write something amusing for %s based on their horoscope and news stories.
Format as Markdown with links.
<horoscope>%s</horoscope>
<news_stories>
%s
</news_stories>
""".formatted(person.name(), horoscope.summary(), storiesFormatted); // ⑯
return context.ai().withLlm(llm).createObject(prompt, Writeup.class); // ⑰
}
}
- Agent Declaration: The
@Agentannotation defines this as an agent capable of a multi-step flow. - Spring Integration: Regular Spring dependency injection - the agent uses both LLM services and traditional business services.
- Service Injection:
HoroscopeServiceis injected like any Spring bean - agents can mix AI and non-AI operations seamlessly. - Action Definition:
@Actionmarks methods as steps the agent can take. Each action represents a capability. - Input Condition Inference: The method signature
extractStarPerson(UserInput userInput, ...)tells Embabel:- Precondition: "A UserInput object must be available"
- Required Data: The agent needs user input to proceed
- Capability: This action can extract structured data from unstructured input
- Output Condition Creation: Returning
StarPersoncreates:- Postcondition: "A StarPerson object is now available in the world state"
- Data Availability: This output becomes input for subsequent actions
- Type Safety: The domain model enforces structure
- Non-LLM Action: Not all actions use LLMs - this demonstrates hybrid AI/traditional programming.
- Data Flow Chain: The method signature
retrieveHoroscope(StarPerson starPerson)creates:- Precondition: "A StarPerson object must exist" (from previous action)
- Dependency: This action can only execute after
extractStarPersoncompletes - Service Integration: Uses the injected
horoscopeServicerather than an LLM
- Regular Service Call: This action calls a traditional Spring service - demonstrating how agents blend AI and conventional operations.
- Another Action: This action uses tools specified at the
PromptRunnerlevel. - Multi-Input Dependencies: This method requires both
StarPersonandHoroscope- showing complex data flow orchestration. - Tool-Enabled LLM:
withToolGroup(CoreToolGroups.WEB)adds web search tools to this LLM call, allowing it to search for current news stories. - Goal Achievement:
@AchievesGoalmarks this as a terminal action that completes the agent’s objective. - Complex Input Requirements: The final action requires three different data types, showing sophisticated orchestration.
- Creative Configuration: High temperature (0.9) optimizes for creative, entertaining output - appropriate for amusing writeups.
- Structured Prompt with Data: The prompt includes both the horoscope summary and formatted news stories using XML-style tags. This ensures the LLM has all the context it needs from earlier actions.
- Final Output: Returns
Writeup, completing the agent’s goal with personalized content.
State is managed by the framework, through the process blackboard.
The Inferred Execution Plan for the Example
Based on the type signatures alone, Embabel automatically infers this execution plan for the example agent above:
Goal: Produce a Writeup (final return type of @AchievesGoal action)
The initial plan:
- To emit
Writeup→ needwriteup()action writeup()requiresStarPerson,RelevantNewsStories, andHoroscope- To get
StarPerson→ needextractStarPerson()action - To get
Horoscope→ needretrieveHoroscope()action (requiresStarPerson) - To get
RelevantNewsStories→ needfindNewsStories()action (requiresStarPersonandHoroscope) extractStarPerson()requiresUserInput→ must be provided by user
Execution sequence:
UserInput → extractStarPerson() → StarPerson → retrieveHoroscope() → Horoscope → findNewsStories() → RelevantNewsStories → writeup() → Writeup and achieves goal.
Key Benefits of Type-Driven Flow
Automatic Orchestration: No manual workflow definition needed - the agent figures out the sequence from type dependencies. This is particularly beneficial if things go wrong, as the planner can re-evaluate the situation and may be able to find an alternative path to the goal.
Dynamic Replanning: After each action, the agent reassesses what’s possible based on available data objects.
Type Safety: Compile-time guarantees that data flows correctly between actions. No magic string keys.
Flexible Execution: If multiple actions could produce the required input type, the agent chooses based on context and efficiency. (Actions can have cost and value.)
This demonstrates how Embabel transforms simple method signatures into sophisticated multi-step agent behavior, with the complex orchestration handled automatically by the framework.




