Spring 之父的 Agent 框架 Embabel:把"规划"从 LLM 手里抢回来

如果你写 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)里落地,后来被多款游戏用于角色决策。它的做法是:

  1. 你定义目标(Goal)和动作(Action),每个动作标注前置条件执行效果
  2. 一个确定性的、非 LLM 的规划器,从"当前世界状态"出发,用搜索算法(底层是 A* 一类的最短路径思想)算出通往目标的动作序列;
  3. 计划在执行前就可检视;
  4. 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-ollamaembabel-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 协议 :启用 a2a profile,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_KEYANTHROPIC_API_KEY 即可。


七、测试:这是 Embabel 最被低估的优势

企业落地的另一半是"能不能测"。Embabel 在设计之初就把可测试性当一等公民,这点很多框架根本没考虑。

单元测试FakeOperationContextFakePromptRunner,不真正调 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 示例已标注对应版本区间。

相关推荐
Wang's Blog10 小时前
Java框架快速入门: Spring Security+OAuth2之数据库和实体类的RBAC改造
java·数据库·spring
郑州光合科技余经理10 小时前
国际版外卖系统:税率字段怎么和订单主流程解耦
android·java·开发语言·前端·后端·php·ai编程
小虎AI生活12 小时前
微信 AI 社交灰度测试:企业 AI 落地真正的门槛在知识库治理
ai编程
猫吻鱼12 小时前
【AI 01】【Spring AI 基础使用】
java·spring·spring ai
蚕豆糯米饭12 小时前
Github Copilot 研发效能提升实战指南
ai·github·copilot·ai编程
Ray Wang13 小时前
Agent学习
ai编程
程序员老刘15 小时前
Android Studio Quail 4发布,看日志我以为谷歌放弃Flutter了
flutter·android studio·ai编程
Wang's Blog15 小时前
Java框架快速入门: Spring Security+OAuth2之JWT核心概念与实战
java·spring·log4j
VIP_CQCRE15 小时前
在 Visual Studio 里接入 Ace Data Cloud:用 OpenAI 兼容接口提升 AI 编程效率
openai·api·ai编程·visual studio·ace data cloud
Young丶15 小时前
讲透 Claude Code 系列 (四):Claude Skills 完全指南:可复用的“专业能力包”从入门到精通
人工智能·ai·ai编程·ai coding