Skip to content
Embabel 文档版本 0.3.0-SNAPSHOT 源快照 08199866 更新于 2026-09-29

02 · 概览(Overview)

作者:Rod Johnson、Alex Hein-Heifetz、Dr. Igor Dayen、Arjen Poutsma、Jasper Blues

智能体 AI(Agentic AI),指的是让大语言(以及其它多模态)模型不只是生成文本, 而是充当会推理、目标驱动的智能体:能够规划、调用工具,并让自身行为适应性地达成结果。

JVM 是承载它的一个极具吸引力的平台,因为其强类型安全为"把 LLM 驱动的行为接入真实系统"提供了护栏。 又因为大量生产应用本来就跑在 JVM 上,它自然成为嵌入 AI 的地方。

尽管智能体 AI 被过度炒作,但其中相当一部分停留在学术 demo 里、缺少实用价值。 而直接融入到既有的、企业级的 JVM 应用中,我们就能在不重写核心系统、不"拆掉工厂来装新机器"的前提下释放 AI 能力。

术语表(Glossary)

开始之前,先解释几个术语 —— 如果你是刚踏入应用型 AI 开发,这些词可能比较陌生。 本节假定你已经从最终用户的角度知道什么是大语言模型(LLM)。

如果你已经是资深的智能体 AI 工程师,可以略过本节。

  • Agent(智能体) Embabel 框架中的 Agent 是一个自包含的组件,把领域逻辑、AI 能力与工具使用打包在一起,代表用户去实现某个特定目标。 它内部暴露多个 @Action 方法,每个方法代表智能体可以采取的离散步骤。动作依赖通常是结构化的(有时是自然语言的)输入。 输入被用来代表用户执行任务 —— 执行领域代码、调用 AI 模型,甚至把另一个 Agent 当作子流程来调用。 调用 AI 模型时,模型可以被授予工具,以扩展其能力来达成目标。 输出是一个新类型,代表对输入的某种变换;不过在执行过程中可能产生一个或多个副作用, 例如向数据库写入新记录、在电商站点下单等等。

  • Tools(工具) 工具通过让 LLM 与外部世界交互,扩展了 LLM 的原始能力。 单靠自身,语言模型只能基于训练数据与上下文窗口生成回复,容易给出不准确或"幻觉"的答案。 工具使用的灵感来自一种叫做 ReAct(Reason + Act,推理 + 行动)的技术,而 ReAct 本身又建立在思维链(Chain of Thought)推理之上; 不过现在大多数 LLM 允许直接声明工具,不必再依赖提示词工程技巧。 当工具存在时,LLM 会解读用户请求、规划步骤,然后在一个循环中把某些任务委派给工具, 从而在"推理(需要做什么?)"与"行动(哪个工具能做?)"之间交替。 工具的好处包括:

    • 通过委派给领域特定或外部系统,回答超出 LLM 训练范围的问题、完成相应的任务;
    • 产生有用的副作用,例如创建数据库记录、生成可视化、预订航班,或调用系统设计者提供的任意流程。

    简而言之,工具是弥合"文本预测"与"现实世界行动"之间鸿沟的一种方式,把 LLM 变成既能推理又能执行的实用智能体。 在 Embabel 中,很多工具就是绑定的领域对象。

  • MCP 模型上下文协议(Model Context Protocol,MCP)是一种托管与共享工具的标准方式。 普通工具通常直接接到某个 Agent 或应用里;而 MCP Server 让工具可以被不同模型与运行时发现和复用 —— 它们可以系统级注册或在运行时注册,并通过统一协议被调用。Embabel 既可消费、也可发布这类工具,用于系统集成。

  • DICE(Domain-Integrated Context Engineering,领域集成上下文工程) 通过把 LLM 的输入与输出都锚定在带类型的领域对象上,来增强上下文工程。 上下文不再是非类型化的提示词,而是用具备业务语义的模型来结构化,从而带来精确性、可测试性,以及与既有系统的无缝集成。 DICE 把上下文变成一种可复用、可检视、可可靠操纵的产物。

为什么我们需要一个智能体框架?

LLM 还不够聪明、不能直接解决问题吗?有了 MCP 工具,不就够了吗? LLM 的能力似乎日新月异,MCP 又能让 LLM 获得大量强力工具,使其更加强大。

但对于业务应用而言,仍然有很多理由需要一个更高层的编排技术。以下是其中最重要的几条:

  • 可解释性(Explainability):解决问题时,为什么做出了这些选择?
  • 可发现性(Discoverability):如何才能在每一步找到正确的工具,并确保模型不会在多个工具之间选错?
  • 混合使用模型的能力:不必只依赖最大的模型,很多任务可以用本地的、更便宜的、私有的模型完成。
  • 在流程任意位置注入护栏(guardrails)的能力
  • 管理流程执行、引入更强韧性的能力
  • 大规模下的流程组合性(composability):我们很快会看到不只是单机上运行的 Agent,而是Agent 联邦。
  • 与敏感既有系统(如数据库)更安全的集成 —— 让再好的 LLM 拥有写权限都是危险的。

智能体框架把复杂任务拆成更小、更可管理的组件,从而带来更强的控制力与可预测性。

智能体框架既提供 "代码能动性"(code agency),也提供 "LLM 能动性"(LLM agency)。 NVIDIA Research 的这篇论文对此有很好的阐述。

延伸阅读:

  • Embabel: A new Agent Platform For the JVM
  • The Embabel Vision

Embabel 的差异化(Embabel Differentiators)

那么 Embabel 与其它智能体框架有何不同?我们相信,Embabel 智能体框架是企业内开发智能体 AI 的最佳选择。

更成熟的规划(Sophisticated Planning)

它超越了有限状态机、也超越了带嵌套的顺序执行:引入了一个真正的规划步骤,使用的是非 LLM 的 AI 算法。 这使系统能够把已知步骤以全新的顺序组合起来,完成它并未被编程去做的任务,并能对并行化等运行时行为做出决策。

更强的可扩展性与复用(Superior Extensibility and Reuse)

得益于动态规划,新增领域对象、动作、目标与条件就能扩展系统的能力, 而不需要修改 FSM 定义或既有代码。

强类型与面向对象(Strong Typing and Object Orientation)

动作、目标与条件由领域模型驱动,而领域模型可以带行为。 一切都是强类型的,提示词与手写代码可以干净地互操作。不再有魔法 Map,并能享受完整的重构支持。

平台抽象(Platform Abstraction)

编程模型与平台内部实现之间干净分离,让你可以本地运行,而在生产环境不改一行代码就能获得更高的服务质量(QoS)。

混合使用多家 LLM(LLM Mixing)

很容易构建混合使用多个 LLM 的应用,从而在可负担与高能力之间取得平衡, 让系统针对不同任务利用不同模型的长处。尤其是它便于把本地模型用于"点状"任务 —— 这对成本与隐私都很重要。

与 Spring 及 JVM 的集成(Spring and JVM Integration)

构建在 Spring 与 JVM 之上,因此很容易接入既有的企业功能与能力。例如:

  • Spring 可以注入并管理 Agent,包括用 Spring AOP 装饰函数;
  • 可以选用成熟的持久化与事务管理方案。

为可测试性而生(Designed for Testability)

从底层开始,单元测试与 Agent 端到端测试都很容易。

核心概念(Core Concepts)

智能体框架把任务拆成更小的独立交互,让 LLM 的使用更可预测、更聚焦。

Embabel 用以下要素来建模智能体流程:

  • Actions(动作):智能体采取的步骤。它们是智能体行为的基本构件。
  • Goals(目标):智能体试图达成什么。
  • Conditions(条件):规划期间用于求值的条件。每执行完一个动作,条件都会被重新评估。
  • Domain Model(领域模型):支撑整个流程、并为动作/目标/条件提供信息的对象。

由此,Embabel 能够生成 plan(计划):一串达成目标的动作序列。 计划由系统动态制定,而不是由程序员写死。 每个动作完成后系统都会重规划, 从而能够适应新信息、并观察上一个动作产生的效果。这实际上就是一个 OODA 循环。

应用开发者通常不需要直接处理条件与规划:大多数条件源于代码中定义的数据流, 系统据此推断前置条件与后置条件,从而(重新)评估计划。

完整示例

下面看一个完整示例,它展示了 Embabel 如何从输入/输出类型推断条件、并管理动作之间的数据流。 该示例来自 Embabel Agent Examples 仓库:

java
@Agent(description = "Find news based on a person's star sign") // ①
public class StarNewsFinder {

    private final HoroscopeService horoscopeService; // ②
    private final int storyCount;

    public StarNewsFinder(
            HoroscopeService horoscopeService, // ③
            @Value("${star-news-finder.story.count:5}") int storyCount) {
        this.horoscopeService = horoscopeService;
        this.storyCount = storyCount;
    }

    @Action // ④
    public StarPerson extractStarPerson(UserInput userInput, OperationContext context) { // ⑤
        return context.ai()
                .withLlm(OpenAiModels.GPT_41)
                .createObject("""
                        Create a person from this user input, extracting their name and star sign:
                        %s""".formatted(userInput.getContent()), StarPerson.class); // ⑥
    }

    @Action // ⑦
    public Horoscope retrieveHoroscope(StarPerson starPerson) { // ⑧
        // 使用普通的注入式 Spring 服务 —— 不是 LLM
        return new Horoscope(horoscopeService.dailyHoroscope(starPerson.sign())); // ⑨
    }

    @Action // ⑩
    public RelevantNewsStories findNewsStories(
            StarPerson person, Horoscope horoscope, OperationContext context) { // ⑪
        var prompt = """
                %s is an astrology believer with the sign %s.
                Their horoscope for today is: %s
                Given this, use web tools to find %d relevant news stories.
                """.formatted(person.name(), person.sign(), horoscope.summary(), storyCount);

        return context.ai().withDefaultLlm()
                .withToolGroup(CoreToolGroups.WEB) // ⑫
                .createObject(prompt, RelevantNewsStories.class);
    }

    @AchievesGoal(description = "Write an amusing writeup based on horoscope and news") // ⑬
    @Action
    public Writeup writeup(
            StarPerson person, RelevantNewsStories stories, Horoscope horoscope,
            OperationContext context) { // ⑭
        var llm = LlmOptions.fromCriteria(ModelSelectionCriteria.getAuto())
                .withTemperature(0.9); // ⑮

        var storiesFormatted = stories.items().stream()
                .map(s -> "- " + s.url() + ": " + s.summary())
                .collect(Collectors.joining("\n"));

        var prompt = """
                Write something amusing for %s based on their horoscope and news stories.
                Format as Markdown with links.
                %s

                %s
                """.formatted(person.name(), horoscope.summary(), storiesFormatted); // ⑯

        return context.ai().withLlm(llm).createObject(prompt, Writeup.class); // ⑰
    }
}

各注记含义:

  • ① Agent 声明:@Agent 注解把它定义为一个能够执行多步流程的智能体。
  • ② Spring 集成:就是普通的 Spring 依赖注入 —— 智能体同时使用 LLM 服务和传统的业务服务。
  • ③ 服务注入:HoroscopeService 像任何 Spring bean 一样被注入 —— 智能体可以无缝混合 AI 与非 AI 操作。
  • ④ 动作定义:@Action 把方法标记为智能体可以采取的步骤。每个动作代表一种能力。
  • ⑤ 输入条件推断:方法签名 extractStarPerson(UserInput userInput, ...) 告诉 Embabel: 前置条件是"必须有一个可用的 UserInput 对象"。
    • 所需数据:智能体需要用户输入才能继续;
    • 能力:该动作可以从非结构化输入中提取出结构化数据。
  • ⑥ 输出条件创建:返回 StarPerson 会创建后置条件:"世界状态中现在有一个 StarPerson 对象"。
    • 数据可用性:这个输出会成为后续动作的输入;
    • 类型安全:领域模型强制保障结构。
  • ⑦ 非 LLM 动作:并非所有动作都使用 LLM —— 这里展示了 AI 与传统编程的混合。
  • ⑧ 数据流链路:方法签名 retrieveHoroscope(StarPerson starPerson) 创建了: 前置条件"必须存在一个 StarPerson 对象"(来自上一个动作)。
    • 依赖:该动作只能在 extractStarPerson 完成后执行。
  • ⑨ 服务集成:使用的是注入的 horoscopeService,而不是 LLM。
    • 普通服务调用:该动作调用传统的 Spring 服务 —— 展示智能体如何融合 AI 与常规操作。
  • ⑩ 另一个动作:该动作使用在 PromptRunner 层面指定的工具。
  • ⑪ 多输入依赖:该方法同时需要 StarPerson 与 Horoscope —— 体现了复杂的数据流编排。
  • ⑫ 启用工具的 LLM:withToolGroup(CoreToolGroups.WEB) 为这次 LLM 调用加上了 Web 搜索工具,使其能检索当前的新闻。
  • ⑬ 目标达成:@AchievesGoal 把它标记为终结动作,完成智能体的目标。
  • ⑭ 复杂的输入要求:最后一个动作需要三种不同的数据类型,体现了精细的编排。
  • ⑮ 创意配置:较高的温度(0.9)有利于产出有创意、有趣味的输出 —— 对"写段子"这类任务很合适。
  • ⑯ 带数据的结构化提示词:提示词中同时包含运势摘要与格式化后的新闻(使用 XML 风格标签), 确保 LLM 拥有来自前面动作的全部上下文。
  • ⑰ 最终输出:返回 Writeup,以个性化内容完成智能体的目标。

状态由框架通过进程黑板(blackboard)管理。

该示例被推断出的执行计划

仅根据类型签名,Embabel 就会为上面的示例智能体自动推断出如下执行计划:

目标:产出 Writeup(@AchievesGoal 动作的最终返回类型)

初始计划:

  • 要产出 Writeup → 需要 writeup() 动作
  • writeup() 需要 StarPerson、RelevantNewsStories 与 Horoscope
  • 要得到 StarPerson → 需要 extractStarPerson() 动作
  • 要得到 Horoscope → 需要 retrieveHoroscope() 动作(需要 StarPerson)
  • 要得到 RelevantNewsStories → 需要 findNewsStories() 动作(需要 StarPerson 与 Horoscope)
  • extractStarPerson() 需要 UserInput → 必须由用户提供

执行顺序:

UserInput → extractStarPerson() → StarPerson → retrieveHoroscope() → Horoscope
          → findNewsStories() → RelevantNewsStories → writeup() → Writeup ✅ 达成目标

类型驱动流程的关键收益

  • 自动编排(Automatic Orchestration):无需手动定义工作流 —— 智能体从类型依赖关系中自行推导出顺序。 这在出问题时尤其有价值:规划器可以重新评估局面,并可能找到通往目标的替代路径。
  • 动态重规划(Dynamic Replanning):每个动作之后,智能体都会根据当前可用的数据对象重新评估"现在能做什么"。
  • 类型安全(Type Safety):编译期保证数据在动作之间正确流动。没有魔法字符串 key。
  • 灵活执行(Flexible Execution):如果多个动作都能产出所需的输入类型,智能体会依据上下文与效率来选择 (动作可以有成本 cost 与价值 value)。

这展示了 Embabel 如何把简单的方法签名转化为精细的多步智能体行为,而复杂的编排由框架自动处理。

BytesRouter 是社区翻译站点,所有译文仅供参考,一切以官方英文原文为准。