Administrator
发布于 2026-07-27 / 483 阅读
4

Java AI 框架选型:Spring AI、LangChain4j、Embabel 怎么选

三个人吵了两周,我算了一笔账

7 月初,团队为"新项目用哪个 Java AI 框架"吵了两周。三个人,三种意见,各自写了 demo,各自的 benchmark 都证明自己选的更好。

我做了件不太受欢迎的事:把这两周的成本算了出来。

参与讨论:3 人 × 14 天 × 60% 投入 ≈ 25 人日
写 demo + benchmark:            ≈ 9 人日
评审会议:4 场 × 2 小时 × 5 人     ≈ 5 人日
-------------------------------------------
合计                            ≈ 39 人日

按我们团队的成本口径,39 人日约等于 ¥11.7 万。而这三个框架,任意一个都能满足需求,差别在 5% 以内。

后来我们定了条规矩:框架选型的预算是 5 人日,超了就用当时的默认选项。这篇记录这次争论里真正有价值的部分,以及我们最后定下来的选型规则。

先把三家的定位讲清楚

很多选型讨论之所以低效,是因为大家在比较"哪个更好",而这三个东西压根不是一个物种。

框架核心定位它的世界观2026 年的状态
Spring AIAI 能力的 Spring 化封装模型是外部资源,用依赖注入管理2.0 已 GA,生态最全
LangChain4j工具箱 + 编排库Agent 是一组可组合的组件成熟稳定,社区活跃
Embabel目标驱动的规划式 AgentAgent 应该自己规划路径达成目标较新,设计激进

换个说法:

  • Spring AI 解决的是"接入"。怎么把模型、向量库、MCP 服务接进来,用 Spring 的方式管理。它最擅长的就是让你写最少的代码接上最多的东西;
  • LangChain4j 解决的是"编排"。链式调用、工具、记忆、RAG 的各个环节,它都给了实现,而且给了不止一种;
  • Embabel 解决的是"规划"。你给目标(Goal)和可用的动作(Action),它用规划算法决定先做什么后做什么,而不是靠模型自己想。

这三个是可以叠加的。事实上 Embabel 底层就能用 Spring AI 的 ChatModel。把它们当三选一,是讨论一开始就跑偏的地方。

实测:同一个需求,三个实现

与其争论,不如写代码。我们定了一个真实的小场景:用户报"订单支付失败",Agent 需要查订单、查支付流水、判断原因、给出建议。三个框架各实现一遍,同一个同学写,避免水平差异。

Spring AI 2.0

@Service
public class PaymentFailureAgent {

    private final ChatClient chatClient;

    public PaymentFailureAgent(ChatClient.Builder b) {
        this.chatClient = b
            .defaultSystem("""
                你是支付问题排查助手。
                必须先查订单再查支付流水,不要凭空猜测。
                """)
            .defaultTools(new OrderTools(), new PaymentTools())
            .defaultAdvisors(
                MessageChatMemoryAdvisor.builder(chatMemory).build(),
                new QuestionAnswerAdvisor(vectorStore))
            .build();
    }

    public String handle(String userMsg, String sessionId) {
        return chatClient.prompt()
            .user(userMsg)
            .advisors(a -> a.param(CONVERSATION_ID, sessionId))
            .call()
            .content();
    }
}

32 行,写了 40 分钟。工具循环、记忆、RAG 全部由框架接管。代价是:你想干预中间过程,得用 advisor 插进去,改动时比较绕。

LangChain4j

@AiService
public interface PaymentFailureAgent {

    @SystemMessage("""
        你是支付问题排查助手。
        必须先查订单再查支付流水,不要凭空猜测。
        """)
    @ToolBox({OrderTools.class, PaymentTools.class})
    String handle(@MemoryId String sessionId, @UserMessage String msg);
}

// 装配,这里能看出"工具箱"的味道
@Bean
PaymentFailureAgent agent(ChatLanguageModel model,
                          ChatMemoryProvider memory,
                          ContentRetriever retriever) {
    return AiServices.builder(PaymentFailureAgent.class)
        .chatLanguageModel(model)
        .chatMemoryProvider(memory)
        .contentRetriever(retriever)
        .tools(new OrderTools(), new PaymentTools())
        .build();
}

28 行,写了 35 分钟。声明式接口很讨喜,而且它的组件化程度最高——记忆、检索、工具都有多种实现可选(比如记忆就有 MessageWindowChatMemoryTokenWindowChatMemory)。缺点是配置项多,上手要读文档。

Embabel

@Agent(description = "处理用户支付失败的问题")
public class PaymentFailureAgent {

    @Action
    Order lookupOrder(UserRequest req) {
        return orderService.query(req.orderNo());
    }

    @Action(pre = "lookupOrder")
    PaymentTrace lookupPayment(Order order) {
        return paymentService.trace(order.paymentId());
    }

    @Action(pre = "lookupPayment")
    @AchievesGoal(description = "给出支付失败的原因和处理建议")
    Answer diagnose(Order order, PaymentTrace trace) {
        // 规划到这里才调模型,前面全是确定性的代码
        return llm.diagnose(order, trace);
    }
}

25 行,写了 55 分钟(其中 20 分钟在读文档理解 pre 条件和 goal 的匹配机制)。

这个写法让我眼前一亮:前面两步是纯 Java 代码,不调模型。Embabel 的规划器根据 @Action 的前置条件和目标,自己排出执行顺序,只有最后需要生成自然语言的一步才用 LLM。

这个设计的价值在于可预测性。我们跑 200 次测试,Embabel 版本的执行路径完全一致(查订单 → 查流水 → 诊断),而前两个版本有 7% 的请求会跳过查订单直接查流水,还有 3 次模型调用了不存在的工具。

三家的实测数据

指标Spring AI 2.0LangChain4jEmbabel
实现代码行数322825
首次跑通耗时40 分钟35 分钟55 分钟
任务完成率(200 例)91.5%92.0%94.5%
执行路径一致性93%92%100%
平均 token/次4,8204,9102,340
P99 延迟8.1s8.4s5.2s
模型调用次数/次3.83.91.2
接入 MCP 服务的代码量8 行21 行需自建
接入向量库种类20+15+靠 Spring AI

Embabel 在 token 和延迟上的优势来自"少调模型"。这个优势在确定性强的流程里非常明显,但在需要模型自主决策的开放场景里会消失——因为它本来就不擅长那种场景。

过度评估的真实代价

回到开头那 39 人日。它不是最坏的部分,最坏的是这三个隐形成本:

机会成本。这两周我们本来可以上线两个小功能。而框架选型的产出,事后看和"直接选 Spring AI"的差别不到 5%。

沉没成本导致的选择扭曲。一旦有人为一个方案写了 800 行 demo,他就很难客观地承认另一个方案更好。这不是人品问题,是人性。我见过太多次。

团队共识的磨损。吵了两周之后,落选方案的支持者心里是有疙瘩的。这种情绪会影响后面几个月的协作。我们这次最后选了 Spring AI,支持 Embabel 的同学虽然接受了,但我知道他到现在还觉得可惜。

我们现在的选型规则

这两周最大的产出不是选了哪个框架,是我们把它固化成了规则。现在团队里任何人要做技术选型,直接套这个。

规则一:默认选项优先

我们团队的默认选项是 Spring AI 2.0。理由很简单:团队 6 个人里 5 个熟 Spring,模型/向量库/MCP 的接入最全,出了问题社区和官方文档覆盖最好。

要用别的框架,你得说明"默认选项做不到什么"。不是"更好",是"做不到"。这个门槛筛掉了 80% 的无谓讨论。

规则二:按场景而非按框架选型

if (流程确定、强合规要求、要省钱)        -> Embabel(规划式)
else if (需要细粒度干预、多 Agent 协作)  -> AgentScope Java
else if (快速接入、生态整合、团队熟悉)   -> Spring AI 2.0
else if (需要高度定制的编排逻辑)         -> LangChain4j
else                                    -> Spring AI 2.0(默认)

注意最后一行。没有明确理由就用默认项,这条比前面几条都重要。

规则三:预算硬约束

选型预算按项目规模定:

  • 小项目(1 人月以内):1 人日。超时直接用默认项;
  • 中项目(3 人月):3 人日
  • 大项目(6 人月以上):5 人日,且必须产出一份可公开的比较报告。

超过预算要在周会上说明理由。这个约束实施一个月,我们砍掉了两次提议中的选型调研。

规则四:抽象层隔离,允许后悔

这条最实在。让框架可替换,比选对框架更重要。

// 业务代码只依赖这个接口,不依赖任何框架类型
public interface AgentPort {
    AgentReply run(String sessionId, String userMsg);
}

我们现在所有 Agent 都通过这层调用。换框架的代价从"改 47 个文件"变成"改一个实现类"。去年我们真的换过一次,一周完成,包括回归。有了这层,选型的心理压力小很多——选错了不至于伤筋动骨。

几个具体的建议

如果你正在做这个选择,这是我基于这半年实践能给的具体意见。

不要为了 Embabel 的 token 省一半而全量切换。它的规划式模型要求你把流程想清楚、拆成 Action,这对需求频繁变化的业务是负担。我们只在"支付失败排查""退款资格审核"这两个流程稳定的场景用它。

不要因为 LangChain4j 组件多就觉得它更适合复杂场景。组件多意味着选择多,选择多意味着团队容易选得不一致。我们有个项目三个人用了三种记忆实现,review 的时候才发现。

Spring AI 2.0 最大的优势不是功能,是它把 AI 变成了 Spring 的一部分。配置走 application.yml,Bean 走依赖注入,可观测性直接接 Micrometer。这意味着你现有的运维体系、监控体系、发布体系全都能复用。这个价值在选型时容易被低估,在运维半年后会变得很明显。

别信 benchmark,包括我上面那张表。那是我们场景的数字,跑在 4 核 8G 上,用的是我们的 prompt 和我们的数据。换到你那里可能完全反过来。要做的是:拿你自己的一个真实场景,跑 200 条数据,看三个数字——完成率、单次成本、P99。别的都可以不看。

写在后面

现在回头看,《Java AI 框架选型:Spring AI、LangChain4j、Embabel 怎么选》本身不算多难,难的是线上真出问题那十分钟里的判断。经验都是这么来的。

参考