The First JVM-Native AI Agent Frameworks —And Why Rod Johnson Built One of Them
Spring’s creator re-entered open source to bring GOAP-powered agent planning to the JVM. JetBrains shipped a coroutine-first competitor the same quarter. Here is what each decision means for your team.
“Not since I founded the Spring Framework have I been so convinced that a new project is needed.” — Rod Johnson, May 2025
That sentence landed with weight in the Java community. Rod Johnson had not shipped a major open-source project since Spring, which he founded in 2003 and which subsequently became the dominant framework in enterprise Java. His return — not to extend Spring, but to build an entirely new agent framework on top of it — is the most significant Java open-source announcement in years. Then, almost simultaneously, JetBrains revealed Koog at KotlinConf: a Kotlin-native, coroutine-first agent framework that the company had been quietly building to power its own AI products. Two frameworks, two philosophies, the same quarter. This article unpacks both.
1. Why 2025 Needed a JVM Agent Framework at All
For the first two years of the large-language-model era, the default answer for teams that wanted to build an AI agent was: write it in Python. LangChain, LlamaIndex, CrewAI — the entire agentic toolchain was Python-first, and the JVM ecosystem responded with adapters, thin wrappers, and a lot of HTTP calls to Python microservices. That situation was workable but deeply unsatisfying, particularly for the enormous proportion of business-critical logic that already runs on the JVM.
Spring AI and LangChain4j emerged as lower-level JVM primitives — ways to call models, manage embeddings, and invoke tools. But they stopped short of the higher-level orchestration problem: how do you build an agent that can plan a multi-step workflow, choose between competing strategies, inject guardrails, and tell you afterward why it made the choices it did? That gap is precisely what both Embabel and Koog were built to fill, and their answers are architecturally quite different.
“Much of the critical business logic in the world is running on the JVM, and for good reason. Gen AI enabling it is of critical importance.”— Rod Johnson, InfoWorld interview, June 2025
2. Embabel: The Architecture of Intent
Embabel’s most distinctive idea is its planning model. Rather than asking developers to wire together a graph of nodes — the pattern popularised by LangGraph — Embabel borrows an algorithm from video game AI called Goal-Oriented Action Planning (GOAP). The concept is elegant: you describe what you want to achieve (a Goal) and what actions are available (annotated methods), and a non-LLM planning algorithm figures out a path from the current world state to the goal, chaining actions by their pre- and post-conditions.
This is a deliberate choice with meaningful consequences. Because the planner is deterministic — it is not asking a language model to decide what to do next — the sequence of steps is explainable. You can inspect the plan before it executes. You can add custom conditions that consult Spring repositories, call APIs, or check any system of record. And when something goes wrong, you know exactly which action in the plan failed and why, rather than staring at an opaque LLM trace.
2.1 The GOAP Loop
The runtime cycle works as follows. Embabel first discovers all available actions and goals from your annotated Spring components. Given an input — a user request, an event, an API call — it selects the most appropriate goal, determines current world state (a set of conditions derived from the presence or absence of domain objects on the process blackboard), plans a sequence of actions, and executes the first one. After each action, it replans. This OODA (Observe, Orient, Decide, Act) loop continues until the goal’s preconditions are satisfied.
The blackboard pattern: Embabel agents communicate through a shared blackboard — a typed object store attached to the current process. Actions read and write strongly typed domain objects (Kotlin data classes or Java records). The presence of an object type on the blackboard is itself a condition the planner can reason about. This is what makes the planning deterministic — it is reasoning over types, not strings.
In practice, writing an Embabel agent feels remarkably close to writing a Spring service. You define a Kotlin data class for your domain (say, a NewsStory), annotate a class with @Agent, and write @Action methods whose parameter types represent what the action needs and whose return types represent what it produces. Embabel infers pre- and post-conditions from these signatures automatically. The planner connects the dots.
@Agent
class NewsResearchAgent {
@Action
fun fetchHeadlines(query: UserQuery): RawHeadlines =
// call a web search tool or MCP server
@Action
fun summarise(headlines: RawHeadlines): NewsSummary =
// prompt an LLM with the headlines
@Goal
fun deliverBriefing(summary: NewsSummary): Briefing =
// format and return the final output
}
As of the year-end 2025 update, Johnson has expanded this model with a Utility planner (for exploration when no specific goal is known upfront), a Supervisor pattern, and a state-machine model that can combine with GOAP — the same action definitions work across all planner types. The framework also ships an AgenticTool concept that maps directly to LangChain’s supervisor-with-workers pattern, giving teams migrating from Python a familiar anchor.
3. Koog: The Architecture of Flow
JetBrains took a different path. Koog is built entirely on Kotlin coroutines and a graph-based strategy model. Where Embabel reasons about goals and conditions, Koog asks developers to define a strategy as a directed graph of nodes — each node performing an operation (calling an LLM, invoking a tool, summarising history, running a subgraph) — connected by edges that represent flow control. The graph can include loops, branches, fallbacks, and parallel branches.
This approach is more explicit than Embabel’s planning model and will feel immediately familiar to teams that have used LangGraph. You are drawing the flow rather than declaring the intent. That explicitness is a trade-off: you have fine-grained control over exactly what happens when, but adding a new capability means updating the graph structure, whereas in Embabel the planner can discover new paths automatically from newly annotated actions.
Coroutines as the concurrency model: Every tool in Koog is a
suspend funannotated with@Tool. The framework generates a JSON schema for the LLM automatically and executes tools in sandboxed coroutines. Parallel tool invocation — dispatching multiple tool calls simultaneously and collecting results — is a first-class feature, handled by the coroutine dispatcher with configurable timeout, cancellation, and backoff policies.
Koog’s other key differentiator is its scope. Because it is built on Kotlin Multiplatform, the same agent code can run on JVM backends, Android, iOS, and even browser-targeting WebAssembly. JetBrains is not just dog-fooding this — Koog powers the AI features in its own IDE suite, including AI Assistant. That production history shows in the feature set: built-in history compression to manage token costs in long-running agents, checkpointing so agents can resume exactly where they left off after a failure, and native OpenTelemetry integration with first-party exporters for Langfuse and Weights & Biases Weave.
val agent = AIAgent(
executor = anthropicExecutor(model = "claude-sonnet-4-6"),
tools = listOf(::searchWeb, ::sendEmail),
strategy = strategy("research-and-report") {
val headlines = node { callLLM("Summarise today's headlines") }
val report = node { callLLM("Draft a briefing from: $headlines") }
edge(headlines, report)
}
)
agent.run("Send me the morning briefing")
4. Architectural Philosophies: Side by Side
Both frameworks are written in Kotlin, both support Java interop, both embrace MCP (Model Context Protocol), and both ship Spring Boot integration. But underneath those surface similarities are genuine and consequential architectural differences. Understanding them is the key to choosing correctly for your context.
| Dimension | Embabel | Koog | Edge |
|---|---|---|---|
| Planning model | GOAP — a non-LLM algorithm. Deterministic, explainable, replans after each action. Goals inferred from types. | Explicit graph strategy. Developer defines nodes and edges; LLM drives tool selection within that topology. | Even |
| Concurrency model | Spring executor / virtual thread model. Concurrency is the standard Spring abstraction. | Kotlin coroutines throughout. Parallel tool calls are structured concurrency with cancellation and backoff baked in. | Koog |
| Primary language feel | @Agent, @Action, @Goal annotations mirror the Spring bean model. Java is a first-class goal. | Kotlin-idiomatic DSL — strategy { }, node { }, suspend functions. Java interop exists but is secondary. | Even |
| Deployment target | JVM only. TypeScript and Python ports are on the roadmap once the JVM model is stable. | Kotlin Multiplatform: JVM, Android, iOS, JS, WebAssembly. One codebase, all runtimes. | Koog |
| Adding a capability | Add an @Action method — the planner discovers it automatically. | Update the strategy graph manually: new nodes, new edges, possibly restructure existing branches. | Embabel |
| Fault tolerance | Guardrails at any action boundary; Spring transaction model applies. | First-class checkpointing — persist and resume mid-graph after failure. | Koog |
| History / context | Blackboard state. No built-in token compression. | Intelligent history compression and configurable summarisation for long-running agents. | Koog |
| Type safety | Strongly typed domain objects; compile-time conditions from method signatures. | Strongly typed tools; JSON schema auto-generated from suspend fun signatures. | Even |
| Spring ecosystem fit | Deeply integrated — builds on Spring AI, feels like writing a Spring service. | Spring Boot and Ktor integrations available; not Spring-native by design. | Embabel |
| Production maturity | v0.3.4 on Maven Central. Active development, newer in production usage. | v0.6.4 on Maven Central. Powers JetBrains AI Assistant in production. | Koog |
Framework capability comparison
5. What This Means for Spring Teams
If your team is running Spring Boot in production today — and a very large proportion of enterprise Java teams are — the choice between these two frameworks is not purely technical. It is also about how much you want to stay inside the mental model you already know.
Embabel is, in many ways, Spring’s unofficial answer to the agent era. It builds on Spring AI, integrates with Spring’s dependency injection model, respects the same packaging and testing conventions, and was designed by the person who invented those conventions. Johnson has been explicit: the goal is for writing an Embabel agent to feel as natural as writing a Spring MVC controller. For a team that already has hundreds of Spring services, the onboarding cost of Embabel is genuinely low. You annotate your domain classes, expose tools from your existing @Service beans, and the framework discovers the rest.
Spring AI compatibility: Embabel builds on Spring AI and is therefore compatible with the same model providers, vector stores, and observability integrations that Spring teams have already configured. If you have already moved to Spring AI, adding Embabel is an additive step, not a replacement. You keep your existing setup and gain the GOAP planning layer on top.
Koog, on the other hand, is the right choice when your team is already Kotlin-first and values the consistency of the coroutine concurrency model throughout the stack. It is also the only option if you need to deploy agents to mobile (Android or iOS) or run agent logic in a browser — scenarios that are increasingly relevant as on-device AI becomes feasible. The Spring Boot and Ktor integrations in Koog are mature, so existing Spring teams can absolutely use it, but they will be working with a Kotlin-idiomatic API rather than an annotation-driven Spring one.
Team fit scoring (0–10 scale)
6. The Planning vs. Graph Trade-off in Practice
The most consequential difference between the two frameworks is invisible until you try to extend an existing agent with new capabilities. In Koog, adding a new behaviour to an agent means updating the strategy graph — defining new nodes, new edges, and sometimes restructuring existing flow branches. This is fine, and it is how LangGraph works too, but it means that extending the agent is a structural change.
In Embabel, adding a new capability is often just adding a new @Action-annotated method to the right Spring component. If that action’s return type satisfies a condition needed by an existing goal, the planner will automatically incorporate it into its plans the next time it is relevant. This is the “discoverability” property Johnson talks about — the system can do more than you explicitly programmed, because the planner reasons about what is possible from available actions, not from a graph you drew by hand.
That said, this power comes with a responsibility: because the planner can chain actions in unexpected ways, testing becomes more important and more interesting. Embabel explicitly addresses this with a testing framework that lets you mock the blackboard state, assert on which actions were planned, and verify that goals were satisfied. But teams should expect to think carefully about action pre- and post-conditions — a poorly specified condition can lead the planner somewhere unintended.
| Dimension | Embabel | Koog | Verdict |
|---|---|---|---|
| Adding a new capability | Add an @Action method; planner discovers it | Update the strategy graph manually | Embabel easier |
| Debugging a run | Inspect the plan; deterministic action sequence | OpenTelemetry traces; spans per node/tool call | Even — different models |
| Parallel tool calls | Supported; Spring executor model | First-class; coroutine-native, configurable concurrency | Koog stronger |
| Fault tolerance & resumption | Guardrails at any action boundary; Spring transaction model | Checkpointing; persist and resume mid-graph | Koog stronger |
| Type safety | Strongly typed domain objects; compile-time conditions | Strongly typed tools; JSON schema auto-generated | Even — both excellent |
| Mobile / multiplatform | JVM only | JVM, Android, iOS, Wasm/JS | Koog only option |
| Spring ecosystem fit | Deeply integrated; feels like Spring development | Integration available; Spring Boot + Ktor supported | Embabel stronger |
| History / context management | Blackboard state; no built-in compression | Intelligent history compression; configurable summarisation | Koog stronger |
| Maturity signal | v0.3.4 on Maven Central; active development | v0.6.4 on Maven Central; production-used at JetBrains | Koog more mature |
7. The Rod Johnson Factor
It would be a mistake to treat Embabel purely as a technical artifact without acknowledging what Rod Johnson’s return to open source actually means. When he wrote the original Spring Framework in 2002, he was solving a problem that the entire Java enterprise world felt but had not cleanly articulated: EJB2 was too heavy, and someone needed to show a better way. The framework took off because the problem was real and the solution was genuine.
His framing of Embabel follows the same pattern. The argument is not that Python frameworks are bad — it is that the JVM has unique assets that no Python framework can offer: decades of type-safe business logic, IDE tooling, refactoring support, compiled performance, and a workforce of developers who know Java and Kotlin but not Python. Embabel’s goal is to make those assets available to AI-enabled applications without asking teams to abandon the platform or the programming model that makes them productive.
Whether Embabel ultimately succeeds at that ambition is a question that 2026 will start to answer. But the intention — and the track record of the person behind it — deserve to be taken seriously. Johnson has already said the roadmap includes TypeScript and Python ports of the Embabel model, once the JVM implementation is solid. If that happens, the competitive landscape for agent frameworks shifts considerably.
8. Getting Started: Dependencies at a Glance
Both frameworks are available on Maven Central and work with standard Gradle or Maven builds. Here are the minimal dependencies to get started with each.
Embabel (Gradle Kotlin DSL)
dependencies {
implementation("com.embabel.agent:embabel-agent-api:0.3.4")
implementation("com.embabel.agent:embabel-agent-spring-boot-starter:0.3.4")
}
Koog (Gradle Kotlin DSL)
dependencies {
implementation("ai.koog:koog-agents-jvm:0.6.4")
// explicit version pins required:
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.10.2")
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:1.8.1")
}
Both projects are under active development with frequent releases. Before starting a new project it is worth checking the Embabel releases and Koog releases pages for the latest versions, as patch releases have been landing weekly for both frameworks throughout early 2026.
9. Which Should Your Team Choose?
The honest answer is that neither framework is wrong — they are optimised for different team profiles and different kinds of agents. Rather than a recommendation, here is a decision framework based on the architectural differences we have covered.
Choose Embabel if: your team is deep in Spring and wants to add agent capabilities to existing Spring applications with minimum friction; you need the explainability that comes from deterministic planning; the business domain is complex and benefits from a rich, typed domain model that the planner can reason about; and you do not need to deploy agents to mobile or browser runtimes.
Choose Koog if: your team is already Kotlin-first and values the consistency of coroutines as the concurrency model; you need multiplatform deployment to Android, iOS, or browsers; you want production-grade checkpointing and history compression out of the box from day one; or you prefer the explicit control of a graph-based flow over the implicit behaviour of a planner.
There is also a third option that deserves a mention: use both. Spring Boot is the integration target for Koog, which means a single application can run Koog agents for workflows that benefit from explicit graph control and Embabel agents for workflows that benefit from goal-directed planning. The two frameworks are not mutually exclusive, and as both mature it is not hard to imagine teams treating them as complementary tools in the same way they treat different Spring sub-projects today.
10. What We Have Learned
The year 2025 finally gave the JVM its own native AI agent frameworks, and the two that emerged are shaped by genuinely different philosophies. Embabel, built by Rod Johnson as his first major open-source project since Spring, brings Goal-Oriented Action Planning from game AI to the JVM — a deterministic, explainable planning model that infers agent behaviour from typed domain objects and fits naturally into the Spring programming model most enterprise teams already know. Koog, built by JetBrains from the practical experience of shipping AI features in their own IDE products, takes a graph-based, coroutine-first approach that offers explicit flow control, first-class parallel tool invocation, persistent checkpointing, and Kotlin Multiplatform support across JVM, Android, iOS, and the browser.
Neither is universally superior. Spring-deep teams adding intelligence to existing services will likely reach for Embabel first; Kotlin-first teams building new agents or targeting mobile will likely reach for Koog. The more interesting question, as both frameworks mature through 2026, is whether the JVM ecosystem finally produces the production-grade agent tooling that makes it unnecessary to justify Python for this layer of the stack — which is, in the end, exactly what both projects are trying to do.






