Spring框架的创始人开发了一个Java AI智能体框架

大家好,我是Java1234_小锋老师。

又一个 AI 框架,它有什么不一样?

提到 AI 智能体,很多人首先想到的是 Python。可如果公司的订单、用户、库存和审批系统都跑在 Java 上,为了接入 AI 再单独维护一套 Python 服务,事情很快就会变复杂。

Spring 框架创始人 Rod Johnson 给出了另一种答案:直接在 JVM 世界里构建智能体。他发起的开源项目 Embabel Agent,希望让 Java 和 Kotlin 开发者用熟悉的 Spring 编程方式,把大模型、业务服务与自动任务规划组合起来。

Embabel 的名字读作 "Em-BAY-bel"。它并不是给聊天接口包一层 Java 外壳,而是试图解决一个更具体的问题:开发者只提供能力和目标,框架根据当前情况决定下一步应该做什么。

Embabel 到底是什么

可以把 Embabel 想象成一位"懂业务流程的调度员"。开发者把系统能够执行的操作告诉它,例如识别用户需求、查询数据库、搜索资料、调用大模型和生成报告;再告诉它最终目标,它便尝试找到一条可行路径。

框架中的几个核心概念并不难理解:

  • Action(动作):智能体能做的一件事,例如查询天气或生成行程。
  • Goal(目标):任务最后要得到的结果。
  • Condition(条件):某个动作能否执行、目标是否完成的判断条件。
  • Domain Model(领域模型):在动作之间流转的 Java 对象。
  • Plan(计划):为了实现目标而排列出来的一组动作。

它和写死的工作流最大的区别,在于计划不是由程序员提前固定好的。Embabel 默认使用 GOAP(目标导向行动规划),并在每个动作完成后重新检查现状、调整后续步骤。这里的规划使用传统 AI 算法,而不是让大模型随意决定一切,因此流程边界仍由代码控制。

它是怎样完成任务的

假设用户说:"帮我安排一次周末杭州两日游。"系统可能按下面的方式运行:
#mermaid-svg-dnmYVwsQ0HDODDwC{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;fill:#333;}@keyframes edge-animation-frame{from{stroke-dashoffset:0;}}@keyframes dash{to{stroke-dashoffset:0;}}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-animation-slow{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 50s linear infinite;stroke-linecap:round;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-animation-fast{stroke-dasharray:9,5!important;stroke-dashoffset:900;animation:dash 20s linear infinite;stroke-linecap:round;}#mermaid-svg-dnmYVwsQ0HDODDwC .error-icon{fill:#552222;}#mermaid-svg-dnmYVwsQ0HDODDwC .error-text{fill:#552222;stroke:#552222;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-thickness-normal{stroke-width:1px;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-thickness-thick{stroke-width:3.5px;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-pattern-solid{stroke-dasharray:0;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-thickness-invisible{stroke-width:0;fill:none;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-pattern-dashed{stroke-dasharray:3;}#mermaid-svg-dnmYVwsQ0HDODDwC .edge-pattern-dotted{stroke-dasharray:2;}#mermaid-svg-dnmYVwsQ0HDODDwC .marker{fill:#333333;stroke:#333333;}#mermaid-svg-dnmYVwsQ0HDODDwC .marker.cross{stroke:#333333;}#mermaid-svg-dnmYVwsQ0HDODDwC svg{font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:16px;}#mermaid-svg-dnmYVwsQ0HDODDwC p{margin:0;}#mermaid-svg-dnmYVwsQ0HDODDwC .label{font-family:"trebuchet ms",verdana,arial,sans-serif;color:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster-label text{fill:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster-label span{color:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster-label span p{background-color:transparent;}#mermaid-svg-dnmYVwsQ0HDODDwC .label text,#mermaid-svg-dnmYVwsQ0HDODDwC span{fill:#333;color:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC .node rect,#mermaid-svg-dnmYVwsQ0HDODDwC .node circle,#mermaid-svg-dnmYVwsQ0HDODDwC .node ellipse,#mermaid-svg-dnmYVwsQ0HDODDwC .node polygon,#mermaid-svg-dnmYVwsQ0HDODDwC .node path{fill:#ECECFF;stroke:#9370DB;stroke-width:1px;}#mermaid-svg-dnmYVwsQ0HDODDwC .rough-node .label text,#mermaid-svg-dnmYVwsQ0HDODDwC .node .label text,#mermaid-svg-dnmYVwsQ0HDODDwC .image-shape .label,#mermaid-svg-dnmYVwsQ0HDODDwC .icon-shape .label{text-anchor:middle;}#mermaid-svg-dnmYVwsQ0HDODDwC .node .katex path{fill:#000;stroke:#000;stroke-width:1px;}#mermaid-svg-dnmYVwsQ0HDODDwC .rough-node .label,#mermaid-svg-dnmYVwsQ0HDODDwC .node .label,#mermaid-svg-dnmYVwsQ0HDODDwC .image-shape .label,#mermaid-svg-dnmYVwsQ0HDODDwC .icon-shape .label{text-align:center;}#mermaid-svg-dnmYVwsQ0HDODDwC .node.clickable{cursor:pointer;}#mermaid-svg-dnmYVwsQ0HDODDwC .root .anchor path{fill:#333333!important;stroke-width:0;stroke:#333333;}#mermaid-svg-dnmYVwsQ0HDODDwC .arrowheadPath{fill:#333333;}#mermaid-svg-dnmYVwsQ0HDODDwC .edgePath .path{stroke:#333333;stroke-width:2.0px;}#mermaid-svg-dnmYVwsQ0HDODDwC .flowchart-link{stroke:#333333;fill:none;}#mermaid-svg-dnmYVwsQ0HDODDwC .edgeLabel{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dnmYVwsQ0HDODDwC .edgeLabel p{background-color:rgba(232,232,232, 0.8);}#mermaid-svg-dnmYVwsQ0HDODDwC .edgeLabel rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dnmYVwsQ0HDODDwC .labelBkg{background-color:rgba(232, 232, 232, 0.5);}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster rect{fill:#ffffde;stroke:#aaaa33;stroke-width:1px;}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster text{fill:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC .cluster span{color:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC div.mermaidTooltip{position:absolute;text-align:center;max-width:200px;padding:2px;font-family:"trebuchet ms",verdana,arial,sans-serif;font-size:12px;background:hsl(80, 100%, 96.2745098039%);border:1px solid #aaaa33;border-radius:2px;pointer-events:none;z-index:100;}#mermaid-svg-dnmYVwsQ0HDODDwC .flowchartTitleText{text-anchor:middle;font-size:18px;fill:#333;}#mermaid-svg-dnmYVwsQ0HDODDwC rect.text{fill:none;stroke-width:0;}#mermaid-svg-dnmYVwsQ0HDODDwC .icon-shape,#mermaid-svg-dnmYVwsQ0HDODDwC .image-shape{background-color:rgba(232,232,232, 0.8);text-align:center;}#mermaid-svg-dnmYVwsQ0HDODDwC .icon-shape p,#mermaid-svg-dnmYVwsQ0HDODDwC .image-shape p{background-color:rgba(232,232,232, 0.8);padding:2px;}#mermaid-svg-dnmYVwsQ0HDODDwC .icon-shape .label rect,#mermaid-svg-dnmYVwsQ0HDODDwC .image-shape .label rect{opacity:0.5;background-color:rgba(232,232,232, 0.8);fill:rgba(232,232,232, 0.8);}#mermaid-svg-dnmYVwsQ0HDODDwC .label-icon{display:inline-block;height:1em;overflow:visible;vertical-align:-0.125em;}#mermaid-svg-dnmYVwsQ0HDODDwC .node .label-icon path{fill:currentColor;stroke:revert;stroke-width:revert;}#mermaid-svg-dnmYVwsQ0HDODDwC :root{--mermaid-font-family:"trebuchet ms",verdana,arial,sans-serif;} 否

用户提出旅行需求
提取城市、日期和偏好
当前信息是否足够
补充或确认信息
调用业务服务查询景点
大模型编排行程
得到两日游计划

这个流程看起来像普通编排,但 Embabel 关注的是"数据状态"。当系统已经有了旅行需求对象,就可以执行景点查询;有了需求和景点列表,才可以生成最终行程。方法的参数类型和返回类型,实际上也在告诉规划器动作的前置条件与执行结果。

为什么 Java 开发者值得关注

Embabel 很有 Spring 味道。@Agent@Action 等注解负责声明能力,智能体本身也可以成为 Spring Bean,继续使用依赖注入、配置管理、事务、数据库访问和企业内部服务。

这带来了几个很实际的好处:

  1. 不必搬走原有业务代码。 已有的 Service、Repository 和第三方 SDK 可以直接成为智能体动作的一部分。
  2. 强类型更容易维护。 动作之间传递的是明确的 Java 对象,而不是层层拼接的 Map 或 JSON 字符串,重构和单元测试都更踏实。
  3. 大模型不是唯一执行者。 查数据库、计算价格、权限校验可以走确定性代码,只有理解和生成内容时才调用模型。
  4. 动作可以复用。 增加新的动作、目标和领域对象后,规划器有机会组合出新的执行路径,不必反复修改一张庞大的流程图。
  5. 支持混用模型。 简单步骤可以选择便宜或本地的模型,复杂步骤再交给能力更强的模型,在成本、隐私和效果之间做平衡。

项目文档用一个形象的类比解释了它与 Spring AI 的关系:如果说 Spring AI 更接近 Servlet API,那么 Embabel 想做的事情更像 Spring MVC------它建立在 Spring AI 之上,提供更高层的智能体编程模型。

用一个周末旅行助手看懂 Embabel

下面是一个经过简化的 Java 示例。为了突出核心思路,省略了包名和部分工程配置,也没有使用 Lombok。

java 复制代码
/**
 * 周末旅行智能体:把用户输入转换为可执行的旅行计划。
 */
@Agent(description = "根据用户偏好生成周末旅行计划")
public class WeekendTripAgent {

    private final AttractionService attractionService;

    /**
     * 注入普通的 Spring 业务服务。
     */
    public WeekendTripAgent(AttractionService attractionService) {
        this.attractionService = attractionService;
    }

    /**
     * 使用大模型从自然语言中提取结构化旅行需求。
     */
    @Action
    public TravelRequest extractRequest(UserInput input, Ai ai) {
        String prompt = """
                从下面的内容中提取目的地、出行天数和兴趣偏好:
                %s
                """.formatted(input.getContent());
        return ai.withDefaultLlm().createObject(prompt, TravelRequest.class);
    }

    /**
     * 调用确定性的业务服务查询景点,不消耗大模型额度。
     */
    @Action
    public AttractionList findAttractions(TravelRequest request) {
        return attractionService.search(request.destination(), request.interests());
    }

    /**
     * 当行程生成后,当前智能体的目标即告完成。
     */
    @AchievesGoal(description = "生成一份可直接阅读的周末旅行计划")
    @Action
    public TravelPlan createPlan(
            TravelRequest request,
            AttractionList attractions,
            Ai ai) {
        String prompt = """
                请根据旅行需求和候选景点生成按天安排的行程。
                旅行需求:%s
                候选景点:%s
                要求路线合理,并留出用餐和休息时间。
                """.formatted(request, attractions);
        return ai.withDefaultLlm().createObject(prompt, TravelPlan.class);
    }
}

/**
 * 旅行需求领域对象。
 */
public record TravelRequest(
        String destination,
        int days,
        List<String> interests) {
}

/**
 * 候选景点集合。
 */
public record AttractionList(List<String> names) {
}

/**
 * 最终旅行计划。
 */
public record TravelPlan(String markdown) {
}

这段代码最值得注意的不是提示词,而是三个方法之间的类型关系。extractRequest 产出 TravelRequest,它让 findAttractions 具备执行条件;当 TravelRequestAttractionList 都已存在时,框架才可以调用 createPlan 达成目标。

真实项目还可以继续加入预算检查、天气查询、酒店搜索等动作。规划器会根据目标和当前已有的数据选择路径,而不是要求我们把所有分支都塞进一个超长的 if-else

如何开始体验

根据项目 README,稳定版本从 0.2.0 起已经发布到 Maven Central。本文写作时,README 给出的快速开始版本是 0.3.0

xml 复制代码
<dependency>
    <groupId>com.embabel.agent</groupId>
    <artifactId>embabel-agent-starter</artifactId>
    <version>0.3.0</version>
</dependency>

最省事的体验方式,是从官方的 Java 项目模板 创建工程,准备 Maven 和模型 API Key,再运行示例。框架也提供 Kotlin DSL,并支持 OpenAI、Anthropic、Ollama 等多种模型接入方式。版本和配置变化较快,正式使用前应以 官方文档 为准。

现阶段需要注意什么

Embabel 的方向很吸引人,但它仍是一个年轻项目。动态规划不代表智能体可以凭空获得能力:能执行哪些动作、能访问哪些数据、哪些目标允许被选择,都需要开发者明确控制。

在生产环境中,还应重点考虑权限隔离、提示词注入、模型调用成本、失败重试、过程审计和人工确认。尤其是付款、删除数据、发送通知这类有副作用的动作,不能仅凭模型输出就直接放行。

此外,框架 API 与版本仍可能快速变化。适合先从内部知识助手、内容整理、报告生成等风险较低的场景做小范围验证,再决定是否进入核心业务链路。

写在最后

Embabel 最有意思的地方,并不是"Java 也能调用大模型",而是它把 Spring 开发者熟悉的强类型、依赖注入和可测试性,带进了智能体开发。

对已经拥有大量 JVM 资产的团队来说,这条路线很现实:AI 不需要取代现有系统,而是可以成为现有业务能力的新调度方式。Embabel 能否成长为智能体时代的"Spring MVC"还有待观察,但 Rod Johnson 再次从编程模型入手解决复杂度问题,本身就值得 Java 开发者持续关注。

相关推荐
夏天拐跑了西瓜1 小时前
Eureka注册中心——单机与高可用集群搭建
java·spring cloud·微服务·eureka
Caster_Z1 小时前
IntelliJ IDEA 安装 Qoder CN (Formerly Lingma)
java·ide·intellij-idea
魔镜er1 小时前
10-CNN案例-图像分类
人工智能·分类·cnn
前端炒粉1 小时前
fetch+readablestream(Streamable)流式输出
java·服务器·前端
晴天161 小时前
Hugging Face 白嫖指南-Day28
人工智能
、如果1 小时前
ima Skills 自进化实战:三层结构、反馈与版本验证
android·人工智能·ai编程
qq_25294131681 小时前
低空飞行物目标检测数据集 | 低空飞行物 鸟类检测 反无人机 航空安全 目标检测 YOLO格式9017期
人工智能·yolo·目标检测·计算机视觉·无人机·低空飞行物
珍珠先生1 小时前
第18章 泛型入门
java
大厂码农老A1 小时前
汤森路透的座上宾?Qwen3.5-397B-A17B到底有什么本事?
前端·人工智能·后端