如果你写 Java 写过十年以上,Rod Johnson 这个名字不需要介绍。2002 年那本 Expert One-on-One J2EE Design and Development 和他随后放出的 Spring,把整个企业级 Java 从 EJB 的泥潭里捞了出来。二十多年后,这位老哥又回来了,带着一个叫 Embabel 的 JVM 智能体框架。
他的原话很冲:"自从我创立 Spring 以来,从没这么确信一个新项目是必要的。"("Not since I founded the Spring Framework have I been so convinced that a new project is needed.")对于一个见惯了技术泡沫的人,这种表态本身就值得停下来看看。
一、Embabel 站在哪一层:它不是来替代 Spring AI 的
很多人第一反应是:"Spring AI 不是已经有了吗?"------这是对 Embabel 最大的误解。
Rod Johnson 自己的类比非常锋利:
| 层级 | 类比 | 代表 |
|---|---|---|
| 底层基础设施 | Servlet API | Spring AI / LangChain4j |
| 高级应用框架 | Spring MVC | Embabel |
Spring AI 解决的是"怎么连上 LLM、怎么管 Embedding、怎么接向量库"这类管道问题。Embabel 构建在 Spring AI 之上,解决的是"怎么让智能体在复杂业务流程里可靠、可解释、确定性 地完成多步骤任务"这类编排(orchestration) 问题。
二者是互补关系,不是竞争。Embabel 直接吃 Spring AI 的模型连接能力,所以 Spring AI 生态里的 @Tool、ChatClient 那套你都能继续用,只是"谁来编排动作顺序"这件事交给了 Embabel。
❝
实战经验:评估一个新框架时,先问它"站在哪一层"。Embabel 的定位是 Spring AI 之上的编排层,这意味着它不重复造模型适配的轮子,而是把精力放在"流程可控"上。对企业里已经有一套 Spring 技术栈的团队,这条路径比直接用 Python 的 LangChain 平滑得多------你不用为了写 Agent 去学一门新语言。
它的基本定义(引自官方 README):Embabel (Em-BAY-bel) is a framework for authoring agentic flows on the JVM that seamlessly mix LLM-prompted interactions with code and domain models. Supports intelligent path finding towards goals. Written in Kotlin but offers a natural usage model from Java.
注意最后一句:用 Kotlin 写,但给 Java 开发者提供地道的用法 。框架 95% 是 Kotlin 实现的,但你写业务时一个 kt 导入都看不到,Java 代码完全地道。这点对纯 Java 团队很友好。
二、核心概念:Action、Goal、Condition、Domain Model、Plan
Embabel 把"智能体工作流"建模成五个核心概念,全部来自官方 Key Concepts:
- Action(动作) :智能体执行的具体步骤。比如"写一个草稿""做事实核查""调用航班服务"。
- Goal(目标) :智能体想要达成的最终状态。
- Condition(条件) :执行某动作前的前置条件 ,或判断目标是否达成的依据。每完成一个动作,系统都会重新评估所有条件。
- Domain Model(领域模型) :支撑整个流程的对象模型,为 Action、Goal、Condition 提供数据基础。通常用 Java record 或 Kotlin data class 强类型化。
- Plan(规划) :为达成目标而串联起来的动作序列。规划由系统动态生成,不是程序员硬编码的。每执行完一个动作,系统重新规划一次。
最关键的一句话:应用开发者通常不需要直接操作这些概念,因为绝大多数条件都可以由代码中的数据流自动推导出来,系统据此推断前置条件(pre-condition)和后置条件(post-condition)。
换句话说,你只管写动作(@Action 方法)和领域对象,规划器自己从方法签名推断"谁依赖谁"。
三、杀手锏:GOAP 确定性规划,不让 LLM 决定"下一步"
这是 Embabel 最反直觉、也最硬核的设计。
主流 Agent 框架(无论 Python 还是 Java)的通病是:你声明一堆工具,让 LLM 自己决定下一步调哪个。Demo 很惊艳,一上生产就露馅------动作序列靠幻觉、流程不可测、出错无法解释。
Embabel 借用游戏 AI 里的 GOAP(Goal-Oriented Action Planning,目标导向动作规划) 。GOAP 最早由 Jeff Orkin 在 F.E.A.R.(2005)里落地,后来被多款游戏用于角色决策。它的做法是:
- 你定义目标(Goal)和动作(Action),每个动作标注前置条件 和执行效果;
- 一个确定性的、非 LLM 的规划器,从"当前世界状态"出发,用搜索算法(底层是 A* 一类的最短路径思想)算出通往目标的动作序列;
- 计划在执行前就可检视;
- LLM 只在每个动作"内部"做内容工作(写、总结、分类),永远不决定下一步跑谁。
Rod Johnson 的点评一针见血:"你给它相同的世界状态和目标,它每次都会生成同样的计划。而不像 LLM 一样,背后藏着几百亿参数,让你根本无法解释它为何那样决策。"

每完成一个动作,规划器重新评估世界状态、重新规划。官方明确说这就是一个 OODA 循环(观察-判断-决策-行动) 。
❝
为什么"不让 LLM 做规划"在企业场景如此重要?因为企业系统(金融交易、订单处理、合规审计)容不得那 10% 的随机性。生成式 AI 天生是非确定性的------同样的提示每次结果可能不同。把"编排"这件事交给确定性算法,等于在随机的 AI 响应之上,重新给你套了一层可解释、可预测的工程外壳。这是 Embabel 区别于大多数框架的根本。
值得一提的是,Embabel 还开箱支持 Utility AI(效用 AI):同样的动作,但不靠严格的前置/后置条件,而是根据(可能动态的)效用评分来选择动作。也就是说 GOAP 不是唯一选项,框架把选择留给了你。
四、三种执行模式:确定性 ↔ 自主性
AgentPlatform 支持三种执行模式,覆盖不同场景的确定性需求(引自官方 README):
- Focused(聚焦) :用户代码直接调用某个特定 Agent,传入输入。适合事件驱动、代码驱动的流程。
- Closed(封闭) :用户意图被分类以选择 Agent;平台在它已知的 Agent 里挑,但只跑该 Agent 内定义的动作。
- Open(开放) :平台评估用户意图,从所有已知的目标与动作出发,动态组装一个定制 Agent 去达成目标。最强,但也最不确定------可能发现开发者没设想过的路径,甚至组合多个模型提供商的能力。
即便在 Open 模式下,平台执行的单个步骤仍然是你显式定义的;GoalChoiceApprover 接口还能进一步限制目标选择范围。

五、类型安全的领域模型:一个能跑的 Agent
Embabel 的全部优势,最终都落在"强类型"三个字上。你用 Java record 把 LLM 的输入输出结构钉死,编译期就能保证数据在动作间正确流动,没有魔法字符串 key,IDE 重构完全可用。
下面是一个完整的示例(采用 0.4.0+/1.x 的流式 API)。它定义了一个"写博客 + 审校"的智能体:你只写了两个动作和一个目标,动作之间的顺序由规划器推断,全程没有任何"第一步调 A、第二步调 B"的硬编码流程。
typescript
// 领域模型:用 Java record 强类型化 LLM 的结构化输出
public record UserInput(String content) {}
public record BlogDraft(String title, String content) {}
public record ReviewedPost(String title, String content, String feedback) {}
@Agent(description = "Write and review a blog post about a given topic")
public class BlogWriterAgent {
@Action(description = "Write a first draft of the blog post")
public BlogDraft writeDraft(UserInput userInput, AI ai) {
return ai.withDefaultLlm()
.withId("blog-post-draft-writer")
.creating(BlogDraft.class)
.fromPrompt("""
Write a blog post about %s.
Keep it practical and beginner-friendly.
Use short sentences and plain language.
Include code examples but keep them short and simple.
Write the content in Markdown.
""".formatted(userInput.content()));
}
@Action(description = "Review and improve the draft")
@AchievesGoal(description = "A reviewed and polished blog post")
public ReviewedPost reviewDraft(BlogDraft draft, AI ai) {
return ai.withLlmByRole("reviewer")
.withId("blog-post-reviewer")
.creating(ReviewedPost.class)
.fromPrompt("""
Review and improve this blog post:
Title: %s
Content: %s
Fix any technical errors.
Tighten the writing.
Provide a revised title, revised content,
and a brief summary of the changes you made as feedback.
""".formatted(draft.title(), draft.content()));
}
}
几个要点拆一下:
@Agent是 Spring 构造型注解,加了它类既是 Spring Bean(白捡组件扫描、依赖注入),又被注册进 Agent 运行时。@Action标记一个动作,方法签名里的BlogDraft输入就是规划器推断"依赖关系"的依据------因为reviewDraft需要BlogDraft,规划器自然知道得先跑writeDraft。@AchievesGoal告诉规划器:完成这个动作 = 目标达成。ai.withDefaultLlm()用默认模型;ai.withLlmByRole("reviewer")按角色换模型。这背后是 Embabel 的 LLM mixing 思想------不同任务用最合适的模型,成本与能力自己平衡。角色在application.yaml里配:
yaml
embabel:
models:
default-llm: gpt-4.1-mini
llm:
reviewer: gpt-4.1
spring:
ai:
openai:
api-key: ${OPENAI_API_KEY}
❝
踩坑提醒 :Embabel 的 API 迭代很快。上面这套
ai.withDefaultLlm().creating(X.class).fromPrompt(...)是 0.4.0 之后引入的流式写法;更早的版本(以及官方 main 分支 README 的部分示例)用的是ai.withLlm(OpenAiModels.GPT_41).createObjectIfPossible(prompt, X.class)风格。二者功能等价,方法名不同。写代码前先mvn dependency:tree确认你引入的 Embabel 版本,照着对应版本的官方文档写,别凭记忆拼方法名。
Maven 依赖(以 1.0.0 GA 为例,已发布到 Maven Central,无需额外仓库):
xml
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-starter-shell</artifactId>
<version>1.0.0</version>
</dependency>
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-openai-spring-boot-starter</artifactId>
</dependency>
embabel-agent-starter 是基础平台(按 DI 注入),embabel-agent-starter-shell 额外带交互式控制台,适合调试。还有 embabel-agent-starter-ollama、embabel-agent-starter-dockermodels 等按需引入。注意 0.2.0 之前的快照版需要从 repo.embabel.com 加私有仓库,1.0.0 这种 GA 版直接在 Maven Central 上。
六、工具、MCP 与 A2A:把企业系统接进来
光会调 LLM 不够,Agent 得能碰真实系统。Embabel 在这块的设计很"企业向":
- ToolGroup 抽象 :把一组相关工具聚成一个组。自带的
SelfToolGroup暴露 Agent 自身方法,McpToolGroup桥接 MCP 服务器。你在@Action上声明toolGroups = {CoreToolGroups.WEB}就能让这个动作具备联网能力,不用关心协议细节。 - MCP 原生支持 :既能作为 MCP 客户端消费外部工具,也能通过
@EnableAgentMcpServer把自己的 Agent 暴露成 MCP 服务(经 SSE 暴露),直接融进 MCP 生态。1.0 起 MCP 工具的错误会正确向上传播------旧版会"静默消失",这点对排障很重要。 - A2A 协议 :启用
a2aprofile,Agent 就能原生与其他 A2A 服务通信,走向多智能体协作。 - RAG:1.0 起 RAG API 从实验晋升为稳定,可以在 JVM 上构建向量支撑的 Agent。
- ONNX 本地嵌入:不调外部 API 就能跑本地嵌入模型,对成本敏感或气隙(air-gapped)部署是关键能力。
模型覆盖上,因为底层是 Spring AI,所以 OpenAI、Anthropic、Bedrock、Google GenAI、Ollama、LM Studio 等主流提供商全支持,环境变量配 OPENAI_API_KEY、ANTHROPIC_API_KEY 即可。
七、测试:这是 Embabel 最被低估的优势
企业落地的另一半是"能不能测"。Embabel 在设计之初就把可测试性当一等公民,这点很多框架根本没考虑。
单元测试 用 FakeOperationContext 和 FakePromptRunner,不真正调 LLM 就能隔离测动作:
ini
var context = FakeOperationContext.create();
context.expectResponse(new Story("Once upon a time..."));
var story = agent.craftStory(userInput, context.ai());
var prompt = context.getLlmInvocations().getFirst().getMessages().getFirst().getContent();
assertTrue(prompt.contains("knight"));
集成测试 继承 EmbabelMockitoIntegrationTest,在完整启动的 Spring Boot 环境里跑整个工作流,还能验证超参数(比如温度):
less
whenCreateObject(prompt -> prompt.contains("Craft a short story"), Story.class)
.thenReturn(new Story("AI will transform our world..."));
var invocation = AgentInvocation.create(agentPlatform, ReviewedStory.class);
var result = invocation.invoke(input);
verifyCreateObjectMatching(
prompt -> prompt.contains("Craft a short story"),
Story.class,
llm -> llm.getLlm().getTemperature() == 0.7
);
❝
我见过一些"AI 功能上线即翻车"的项目,根因就是 LLM 调用没法测、没法断言。Embabel 这套 Fake 上下文 + 超参数校验,让你能在 CI 里锁住"提示词里到底有没有关键约束、温度配没配错"。对要上生产的团队,这一条比花哨的规划算法更值钱。
八、横向对比:Embabel vs Spring AI vs LangChain4j
| 维度 | Embabel | Spring AI | LangChain4j |
|---|---|---|---|
| 定位 | 高层编排框架(Spring MVC of AI) | 底层抽象(Servlet API of AI) | 社区驱动的 Agent 库 |
| 规划方式 | GOAP/Utility AI 确定性规划器,非 LLM | 通常手动编排或简单链 | 多靠 LLM 路由决策 |
| 类型安全 | 强类型领域模型,record 化 | 中等,偏底层 API | Builder/注解,RAG 强 |
| 与 Spring 关系 | 构建于 Spring AI 之上 | Spring 官方 | Spring 集成一般 |
| 可解释性 | 高(白盒规划路径) | 取决于你怎么写 | 中(常黑盒) |
| 适合场景 | 复杂、多步骤、需可审计的企业流程 | 轻量接入 AI / 已有 Spring 项目 | 快速验证、RAG 场景 |
一句话总结:Embabel 不替代 Spring AI 或 LangChain4j,它在它们上面加了一层"纪律"。如果你只是想给现有 Spring 服务加个聊天接口,Spring AI 够了;如果你要的是一群模型按可控、可解释的流程协作完成多步骤任务,Embabel 才是那个更对位的工具。
九、我的实战判断
作为写了十几年 Java、又在做 LLM 方向的人,我对 Embabel 的判断是:方向对,但别急着上核心生产。
对的地方有三:第一,把规划从 LLM 手里拿回来,是这个赛道里少数真正解决"企业级可靠性"问题的设计,而不是又一个"让模型自己决定"的套壳;第二,强类型 + 领域模型,正好踩中 Java 开发者最舒服的那条肌肉记忆,重构、测试、可维护性都在;第三,构建在 Spring AI 之上,不重复造轮子,生态继承成本几乎为零。
要警惕的地方也得说清楚:项目仍在快速迭代,API 版本间有差异,生产环境落地前务必锁定版本、读完你那个版本的官方文档。另外,GOAP 的确定性带来可解释性,但也意味着"智能体自主发现全新路径"的能力弱于纯 LLM 路由的框架------这是取舍,不是缺陷。对金融、订单、合规这类"错不起"的场景,我反而觉得这是优点。
如果你是 Java/Spring 团队,想认真做 Agent,Embabel 值得现在就开始关注、用官方 Java 模板跑通第一个例子。它现在最像 2003 年的 Spring:雏形已立、理念清晰、生态刚起。至于能不能复刻 Spring 的传奇,时间会给出答案,但 Rod Johnson 这次的判断,至少在我这个老兵看来,站得住脚。
参考资料:Embabel 官方仓库 README(github.com/embabel/embabel-agent)、官方 Java 模板(github.com/embabel/java-agent-template)、Dan Vega《Embabel First Look》。版本信息以 1.0.0 GA(2026 年 7 月)为基准,API 示例已标注对应版本区间。