title: "Spring之父Rod Johnson携Embabel归来:Java程序员的Agent时代正式开启" description: "Rod Johnson——那个写出Spring、把Java从EJB泥潭里救出来的男人,带着全新的Java Agent框架Embabel回来了。用GOAP算法做确定性规划、强类型Action、Spring原生集成,Java程序员不用转Python也能写生产级Agent。" tags: [Java, Agent, Spring, Embabel, AI工程化, Rod Johnson] image: "./images/embabel-rod-johnson-java-agent-cover.jpg"
Spring之父Rod Johnson携Embabel归来:Java程序员的Agent时代正式开启
开篇:「Java已死」这句话,我听了快十五年了。每次技术变革都有人念一遍——微服务时代念过一次,云原生时代念过一次,现在AI时代又有人拿出来说。但有意思的是,每次喊「Java不行了」的时候,总有一个人站出来给Java续上十年命。上一次是2002年的Spring,这一次,是2026年的Embabel。
一、从EJB到Agent:历史总是惊人地相似
1.1 当年Rod Johnson干了什么
如果你是个有十年以上工龄的Java老炮,应该对EJB这个词有心理阴影。零几年的时候,企业级Java的「官方标准答案」就是EJB。写一个业务组件,得继承一堆接口,配一堆XML,部署一次等半天。那时候说Java臃肿、说Java要完,真不是造谣,是实打实的难用。
2002年,一个叫Rod Johnson的澳洲人写了本书,叫《Expert One-on-One J2EE Design and Development》。书的核心观点就一句话:官方那套不行,我给你们写个更好的。书里附的那套代码,后来长成了Spring Framework。
Spring干的事很简单:普通Java对象(POJO)就能干活,不用继承什么特定接口,配置简单,测试好写。然后呢?然后Spring赢了,EJB那套直接被扫进了历史的垃圾堆。再往后Spring Boot、Spring Cloud一路铺开,全家桶把企业Java的命一路续到今天。
说Spring给Java续命十年,一点都不夸张。
1.2 今天的处境,和当年太像了
说回现在。打开GitHub搜Agent框架,LangChain、LangGraph、CrewAI、AutoGPT……清一色Python。公司要做AI,不少技术负责人的第一反应也是招Python的人,或者让Java的人去学。
我自己是Java出身,写了十几年Spring。这两年看着Python圈热热闹闹,说心里没落差是假的。不是说Python不好,但你让一个写了十年Java、对JVM生态门儿清的工程师,转头去跟Python的动态类型、缩进语法、虚拟环境打交道,那酸爽谁试谁知道。
Rod Johnson在访谈里聊到这事的时候,火气比我还大。他说很多人爱拿Java当靶子,假装这门语言二十年没动过。但他倒也没把Python框架贬得一文不值,原话很公允:
「那些框架不差,但JVM上有它们给不了的东西——几十年攒下来的类型安全的业务代码,成熟的IDE和重构工具,还有一大群写Java顺手、写Python别扭的工程师。」
这句话我深以为然。Java的护城河从来不是语言本身,是生态、是工具链、是几千万开发者积累的最佳实践。
二、Embabel的核心思路:规划这件事,不让大模型碰
2.1 从游戏行业借来的GOAP算法
Embabel最核心的设计,是从游戏行业借来的一个算法——GOAP(Goal-Oriented Action Planning,目标导向行动规划)。早年是给游戏里的NPC规划行为用的,比如《F.E.A.R.》里的敌人AI,用的就是这套东西。
我第一次看到的时候挺意外的,没想到一个Java Agent框架会跑去游戏行业找答案。但仔细一想,这招真的妙。
用GOAP写Agent,你只定义两样东西:
- Action:Agent能干的每个动作
- Goal:最终要达成什么目标
动作按什么顺序串,不用你写死,也不是大模型现场拍的,是框架里的规划器(Planner)算出来的。每执行完一步,它根据最新的状态重新算一遍,再决定下一步。
Embabel GOAP 工作流程: ┌─────────────────────────────────────────────────────┐ │ Goal(目标) │ │ "写一篇技术博客并审校发布" │ └──────────────────────────┬──────────────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ Planner(确定性规划器) │ │ 输入:当前状态 + 可用Actions + Goal │ │ 输出:Action 执行序列(A*搜索算法) │ │ 特点:不调大模型,纯代码计算,可复现、可审计 │ └──────────────────────────┬──────────────────────────┘ ↓ ┌────────────────┴────────────────┐ ↓ ↓ ┌───────────────────┐ ┌───────────────────┐ │ Action 1: 调研 │ │ Action 2: 写稿 │ │ 输入:Topic │ │ 输入:Research │ │ 输出:Research │ │ 输出:Draft │ └─────────┬─────────┘ └─────────┬─────────┘ ↓ ↓ └────────────────┬────────────────┘ ↓ ┌─────────────────────────────────────────────────────┐ │ 状态更新 → 重新规划 → 下一步 │ │ (每一步执行完都重新评估,动态调整路径) │ └─────────────────────────────────────────────────────┘2.2 为什么说这才是生产级的思路
我最看重Embabel的一点是:这个规划过程完全不调大模型,就是一段确定性代码在跑。
这意味着什么?
| 特性 | 大模型驱动的规划 | Embabel确定性规划 |
|---|---|---|
| 可复现性 | 同样的输入,两次结果可能不一样 | 同样的输入,路径永远一样 |
| 可审计性 | 为什么选这一步?答不上来 | 每一步为什么执行,日志写得明明白白 |
| 成本 | 规划过程烧token | 规划不花一分钱token |
| 调试难度 | 玄学,靠prompt工程 | 跟调试普通Java代码一样 |
| 出问题追责 | 「模型幻觉了」 | 能精确到哪一步逻辑有问题 |
我之前用Python框架搭过demo,跑得确实漂亮。但你要问我敢不敢直接放生产环境?心里真有点发怵。原因不复杂——大部分框架是让大模型自己决定下一步干什么,模型今天这么走,明天那么走,出了问题没法复盘。审计问你这一步为什么执行,你总不能说「大模型觉得该这么干」吧?
Rod Johnson盯上的,就是这条缝。大模型负责不确定的那部分(写文案、做总结、搞分类),规划和流程交给确定性的代码。这个分工一摆出来,Java程序员二十年在工程化上攒的东西——类型、测试、审计、监控——突然全都用上了。
2.3 Java程序员最爱的强类型
还有一点特别对Java程序员的胃口:强类型。
在Python的Agent框架里,Action之间传的基本上是字典和字符串。你传了个什么进去、期望得到什么出来,全靠文档和约定。改个字段名,你得全局搜索慢慢找,漏了一个就是线上bug。
Embabel不一样。Action之间传的不是字典,是真正的领域对象——Java Record或者Kotlin data class。输入什么类型,输出什么类型,编译期就给你卡住。改个字段名,IDE能把受影响的Action全找出来,一键重构。
// 定义领域对象(Java Record,编译期类型安全) public record Topic(String title, String category) {} public record Research(String summary, List<String> references) {} public record BlogDraft(String content, String title) {} public record Review(List<String> suggestions, int score) {} // 定义Action —— 输入输出都是强类型 @Action(description = "根据主题调研并写出初稿") public BlogDraft writeDraft(Topic topic) { // 调模型写稿 return aiService.generateDraft(topic); } @Action(description = "审校初稿并给出修改意见") public Review reviewDraft(BlogDraft draft) { // 调模型审校 return aiService.review(draft); }对比一下Python版本的感觉:
# Python 框架里常见的写法 —— 字典传参,全靠约定 def write_draft(state: dict) -> dict: topic = state.get("topic") # 有没有这个key?运行时才知道 content = llm.generate(topic) return {"draft": content} # 下游能不能取到?全靠文档我之前写Python Agent,重构基本靠全局搜索+人肉检查,对比太强烈了。对于有几十万行业务代码的企业来说,类型安全不是「好不好用」的问题,是「敢不敢上生产」的问题。
三、上手体验:Spring开发者的舒适区
3.1 跟Spring AI的关系:Servlet API和Spring MVC的区别
Rod Johnson自己打过一个比方,我觉得挺准的:
Spring AI 相当于 Servlet API—— 管的是怎么接模型、怎么调向量库这些底层的事Embabel 相当于 Spring MVC—— 是在上面组织业务逻辑、编排Agent的那一层
这个比喻太到位了。你直接用Spring AI也能写Agent,就像你直接用Servlet也能写Web应用一样——不是不能写,是写起来费劲,所有流程都得自己拼。Embabel就是在Spring AI上面给你搭好了MVC那一层。
所以Embabel整个建在Spring AI上面,项目里原来配的模型接入、向量数据库,全都不用动。你原来怎么调大模型,现在还怎么调,Embabel只管编排。
3.2 写一个Agent有多简单
熟悉Spring的人,看一眼代码就懂了:
@Agent(description = "写一个主题的技术博客并审校发布") public class BlogWriterAgent { @Autowired private AiService aiService; @Action(description = "根据主题调研相关资料") public Research doResearch(Topic topic) { return aiService.research(topic); } @Action(description = "根据调研结果写出初稿") public BlogDraft writeDraft(Research research) { return aiService.generateDraft(research); } @Action(description = "审校初稿并给出修改意见") public Review reviewDraft(BlogDraft draft) { return aiService.review(draft); } @Action(description = "根据修改意见润色终稿") public FinalDraft polishDraft(BlogDraft draft, Review review) { return aiService.polish(draft, review); } @Goal public boolean isPublished(FinalDraft draft) { return draft != null && draft.isPublished(); } }几个关键点:
@Agent直接就是个Spring注解,你的类照常走组件扫描和依赖注入- 每个Action用
@Action标注,描述给规划器看 @Goal定义终止条件——什么时候算干完了- 先调研还是先写稿、要不要润色、需不需要返工,规划器看着当前状态自己定
你不用写if-else串流程,也不用写prompt让大模型自己想下一步。框架替你把确定性的部分扛了,你只需要专注每个Action具体干什么。
3.3 调用方式也很Spring
@Service public class BlogService { @Autowired private AgentRunner agentRunner; public void publishBlog(String topic) { var goal = new PublishBlogGoal(new Topic(topic, "tech")); var result = agentRunner.run(BlogWriterAgent.class, goal); // result 里有完整的执行轨迹、每一步的输入输出 } }跟调用一个普通Spring Bean没什么区别。而且因为规划是确定性的,你甚至可以给Agent写单元测试——这在Python框架那边基本是想都不敢想的事。
四、「生产级」这三个字,靠什么支撑
框架上个月刚发正式版,Apache 2.0协议,随便商用,Maven中央仓库直接拉。但真正让我觉得「这玩意儿能上生产」的,是下面这几件事。
4.1 可测试性:Agent也能写单元测试
官方把「Agent可以像Spring Bean一样写单元测试」当成核心卖点,我觉得这才是真正懂企业开发的人会关心的事。
@SpringBootTest class BlogWriterAgentTest { @Autowired private AgentTestRunner testRunner; @Test void should_research_before_writing() { // Mock 掉模型返回 var mockAi = mock(AiService.class); when(mockAi.research(any())).thenReturn(new Research("...", List.of())); when(mockAi.generateDraft(any())).thenReturn(new BlogDraft("...", "...")); // 运行Agent var trace = testRunner.run( BlogWriterAgent.class, new PublishBlogGoal(new Topic("test", "tech")) ); // 断言执行顺序 assertThat(trace.getActions()) .extracting(ActionExecution::getName) .containsExactly("doResearch", "writeDraft", "reviewDraft", "polishDraft"); // 断言发给模型的prompt里有关键词 assertThat(trace.getPromptFor("writeDraft")) .contains("test"); // 甚至能验温度参数设了多少 assertThat(trace.getAction("writeDraft").getTemperature()) .isEqualTo(0.7); } }给Agent写单测这件事,我在Python那圈框架里基本没见过有人认真做。大部分人的做法是「跑几次看看效果」,然后就祈祷上线别出问题。
4.2 成本控制:每一步都能选不同的模型
Embabel支持每一步动作单独选模型。这个功能看起来不起眼,实际上在生产环境里能省大钱。
| 任务类型 | 建议模型 | 成本对比 |
|---|---|---|
| 写长文、复杂推理 | GPT-4o / Claude Opus | 高 |
| 分类、提取、摘要 | GPT-4o Mini / DeepSeek Flash | 低10-50倍 |
| 涉及客户数据的敏感操作 | 本地部署模型(Ollama等) | 数据不出内网 |
| Embedding | 本地ONNX模型 | 零成本 |
@Action(description = "分类用户反馈") @ModelConfig(model = "deepseek-chat", temperature = 0.1) // 便宜的小模型就行 public FeedbackCategory classifyFeedback(String feedback) { return aiService.classify(feedback); } @Action(description = "生成复杂的技术方案") @ModelConfig(model = "claude-opus", temperature = 0.7) // 吃能力的用大模型 public TechnicalSolution generateSolution(Requirements req) { return aiService.generateSolution(req); }token省了,合规上也好交代。对于企业级应用来说,这不是锦上添花,是刚需。
4.3 生态兼容度:国内团队友好
模型支持方面,除了OpenAI、Anthropic、Google这些国际大厂,DeepSeek、智谱GLM、MiniMax都在支持列表里,Ollama本地部署也直接认。国内团队用起来没什么门槛。
工程化配套也补得挺齐:
- ✅RAG接口转正—— 不是实验性API,不怕半路改
- ✅MCP双向支持—— 既能调别人的MCP工具,也能把自己的Agent发布成MCP Server
- ✅A2A协议—— Agent之间能互相通信、协作
- ✅Spring Boot Actuator—— 健康状态、指标直接挂上去统一监控
- ✅ONNX本地Embedding—— 隔离网环境也能用
- ✅Observability—— 完整的执行轨迹、token用量统计、延迟监控
官方文档里有个数字我印象很深:大约七成的生产应用跑在JVM上。这些公司几十年的业务逻辑全在Java里,做AI的正确姿势是把能力接进现有系统,不是把系统换成Python重写一遍。
五、说几句实话:框架不是完美的
5.1 目前的短板
当然,这框架不是没毛病,我照实说。
| 方面 | 现状 | 影响 |
|---|---|---|
| 语言 | 核心代码是Kotlin写的 | Java调用完全无感,但想读源码得适应一下Kotlin语法 |
| Spring Boot版本 | 目前停在Boot 3.5 | Boot 4还没正式支持,迁移指南已经有了 |
| 生态成熟度 | GitHub Star跟LangChain十万级没法比 | 社区案例少,踩坑可能得自己摸 |
| 适用场景 | 适合有Spring体系的企业 | 不在Spring生态里的话,LangChain4j可能更顺手 |
5.2 谁该关注,谁可以再等等
强烈建议关注的:- 已经在Spring体系里跑了很多年、现在想往业务里塞Agent的团队 - 对可测试性、可审计性、可观测性有硬性要求的企业级应用 - Java/Kotlin为主技术栈、不想为了写Agent特意转Python的团队
可以再等等的:- 纯Python技术栈的团队——LangChain生态更成熟 - 不在Spring体系里的Java项目——LangChain4j可能更轻量 - 需要最前沿的Agent玩法的——新框架功能跟进需要时间
六、我的看法:这可能是Java在AI时代的关键一战
Embabel能不能复制Spring当年的奇迹,我不下结论。Agent这个领域变得太快,框架能不能活到格局稳定那天,谁也说不准。
但Rod Johnson自己有句话我印象很深:
「Embabel可能是最后一波由人类亲自选择的框架——因为以后挑框架、搭技术栈这种事,可能越来越多是AI工具替人做决定。」
这句话有点扎心,但可能是对的。
不过在那一天到来之前,我觉得Embabel的方向是对的:
- 确定性和不确定性要分开—— 流程交给代码,创意交给模型
- 工程化是底线—— 测试、监控、审计,一个都不能少
- 拥抱现有生态—— 不是推翻重来,是给Java程序员递一把新工具
当年Spring把Java从EJB的泥潭里捞出来,这回Rod Johnson想捞的,是我们这些被Python圈在外面的Java程序员。从EJB到Spring,从Servlet到Spring MVC,从JDBC到Spring Data——Rod Johnson好像特别擅长干一件事:在大家都觉得Java不行了的时候,用一个更优雅的方案告诉所有人:不是Java不行,是之前的方案太烂。
反正我Star先点了。值不值得跟,看后续。但作为一个写了十几年Java的老程序员,看到有人还在为JVM生态认真做事,心里挺暖的。
写在最后
Java会不会死?这个问题争论了十几年。我的答案一直是:只要还有人在给Java生态创造好东西,它就死不了。
从Spring到Spring Boot,从Hibernate到MyBatis,从Dubbo到Spring Cloud——每一次「Java不行了」的唱衰背后,都有新的框架、新的思路在冒出来。Embabel会不会成为AI时代的Spring?我不知道。但有人在尝试,这件事本身就很有价值。
你觉得Embabel能成吗?欢迎在评论区聊聊你的看法。
参考资料:本文基于「Java知音」公众号文章《Rod Johnson又来给Java续命了!》整理与扩展,技术解读为作者独立观点。
Embabel开源地址:https://github.com/embabel/embabel-agent
📚 延伸阅读 · 我的付费专栏
觉得这篇文章对你有帮助?我把同类主题的系统化内容沉淀成了付费专栏,欢迎订阅支持持续输出:
| 专栏 | 定价 | 内容 |
|---|---|---|
| 大模型工程师修炼手记 | 9.9 元 | 51 篇 AI 编程 / Agent 深度实战 |
| AI时代程序员的自我提升 | 89.9 元 | 27 篇 AI 时代成长方法论 |
💡 一杯咖啡的价格,换来系统化的知识体系;你的订阅,是我持续创作的最大动力。