Spring Boot + Embabel 入门:用 Java 写一个会规划步骤的 AI 博客助手
如果你已经会写 Spring Boot 应用,并且正在尝试把大语言模型接入业务系统,那么你很快会遇到一个现实问题:直接调用一次 LLM 很简单,但把多个 LLM 调用、数据库查询、规则判断、工具调用和业务对象组织成一个可靠流程,就没有那么简单了。
Spring AI 更像是”让 Spring 应用能方便地调用模型、向量库和工具”的基础设施,而 Embabel 站在更高一层。Embabel 是一个运行在 JVM 上的 agentic flow 框架,它的目标是把 LLM 提示词交互、普通 Java/Kotlin 代码和强类型领域模型 混合起来,用 Agent、Action、Goal、Plan 等概念来组织流程。
Embabel 官方文档将 Agent 描述为一个自包含组件,它把领域逻辑、AI 能力和工具使用组合在一起,用来代表用户完成某个特定目标。
本文会用一个非常贴近内容生产场景的例子来入门:我们要基于 Spring Boot + Embabel 写一个”AI 博客助手”。用户输入一个主题,系统先理解写作需求,再生成大纲,再生成初稿,最后审稿并输出最终 Markdown 文章。示例力求通俗易懂,但代码会尽量完整,方便你直接改造成自己的项目。
1. Embabel 到底解决什么问题
在没有 Agent 框架时,很多人会这样写 AI 流程:Controller 收到请求,然后 Service 里按顺序调用 parseTopic()、generateOutline()、writeDraft()、reviewDraft()。这种写法当然可以工作,但它有一个明显缺点:流程顺序完全写死在代码里。一旦你想增加 SEO 优化、事实检查、敏感词审查、数据库查询或外部工具调用,Service 很容易变成又长又难维护的”流程脚本”。
Embabel 的思路不同。你不是先写一个固定流程,而是先定义 Agent 能做哪些动作,也就是 @Action;再告诉系统什么结果算达成目标,也就是 @AchievesGoal 或 Goal。Embabel 会根据当前已有对象、每个 Action 的入参和返回值,动态规划该执行哪些步骤。官方文档把这种工作方式称为围绕 Actions、Goals、Conditions、Domain Model 和 Plan 建模,并且每个 Action 完成后会重新评估状态和计划。
| 概念 | 通俗理解 | 在代码里的常见形态 |
|---|---|---|
| Agent | 一个具备一组能力的”智能业务组件” | 标注 @Agent 的 Spring Bean |
| Action | Agent 能执行的一个步骤 | 标注 @Action 的 Java 方法 |
| Goal | Agent 最终要得到的结果 | @AchievesGoal 或目标类型 |
| Domain Model | 流程中流转的强类型业务对象 | Java record / class |
| Plan | 为了达到目标而生成的步骤序列 | 由 Embabel 平台动态规划 |
这就是 Embabel 和普通 LLM 调用最大的区别:它不是把 AI 当成一个文本生成接口,而是把 AI 放进一个由强类型对象和业务代码共同约束的执行系统里。这对于 Java 开发者非常友好,因为你仍然可以使用 Spring 的依赖注入、配置管理、测试体系、事务、数据库访问和普通业务服务。
2. 本文要做什么
我们要实现一个简单但完整的”博客生成助手”。用户通过 HTTP 接口提交主题,后端返回一篇经过审稿的 Markdown 博客。
整个流程可以想象成下面这样:
flowchart LR
A[用户输入主题] --> B[BlogIdea\n理解主题和目标读者]
B --> C[BlogOutline\n生成文章大纲]
C --> D[BlogDraft\n生成初稿]
D --> E[ReviewedBlogPost\n审稿并输出最终文章]
这四个节点都不是随便传递的 Map 或 JSON 字符串,而是 Java record。Embabel 很鼓励这种强类型方式,因为它能让 LLM 的输出变成业务代码能理解、能测试、能重构的对象。
| 步骤 | 输入对象 | 输出对象 | 是否调用 LLM | 说明 |
|---|---|---|---|---|
| 理解主题 | UserInput | BlogIdea | 是 | 从自然语言里提取主题、读者和写作风格 |
| 生成大纲 | BlogIdea | BlogOutline | 是 | 生成结构化章节大纲 |
| 生成初稿 | BlogOutline | BlogDraft | 是 | 根据大纲写 Markdown 初稿 |
| 审稿定稿 | BlogDraft | ReviewedBlogPost | 是 | 改进内容并返回最终结果 |
需要说明的是,Embabel 的 API 在不同版本中仍在持续演进。本文代码采用官方文档和 2026 年社区示例中较一致的写法:@Agent、@Action、@AchievesGoal、OperationContext 以及 AgentInvocation.builder(agentPlatform)。如果你使用的版本有细微差异,请优先以所选版本的官方文档为准。
3. 环境准备与版本选择
Embabel 官方文档说明,最简单的开始方式是把 Embabel 的 Spring Boot starter 加入项目,并且 release 版本会发布到 Maven Central。Maven 仓库页面显示,com.embabel.agent:embabel-agent-starter 已有多个 Central 版本,其中 0.4.0 发布于 2026-05-19,0.3.5 是 0.3.x 的较新版本。
为了让示例更稳妥,本文建议新手先使用 Spring Boot 3.5.x、Java 21+ 和 Embabel 0.4.0。如果你所在公司项目还停留在 Java 17,也可以先查阅当前 Embabel 版本的兼容性要求;官方示例仓库的快速开始明确列出了 Java 21+、至少一个 LLM API Key 和 Maven 3.9+ 作为前置条件。
| 软件 | 推荐版本 | 说明 |
|---|---|---|
| JDK | 21+ | Java 21 是当前企业项目较容易接受的 LTS 选择 |
| Spring Boot | 3.5.x | 更适合作为入门示例;Boot 4 相关兼容性请看所选版本文档 |
| Maven | 3.9+ | 官方示例仓库建议 Maven 3.9+ |
| Embabel | 0.4.0 | Maven Central 当前较新 release 版本之一 |
| 模型供应商 | OpenAI-compatible | 示例使用 OPENAI_API_KEY 环境变量 |
4. 创建 Spring Boot 项目
你可以在 Spring Initializr 创建一个普通 Maven 项目。项目元信息可以这样填写:
| 配置项 | 示例值 |
|---|---|
| Group | com.example |
| Artifact | embabel-blog-demo |
| Package | com.example.embabelblog |
| Java | 21 |
| Dependencies | 暂时只选 Spring Web 即可 |
项目创建好以后,我们手动修改 pom.xml,加入 Embabel 依赖。
5. Maven 配置
下面是完整的 pom.xml 示例。这里使用 embabel-agent-starter,因为我们要做一个由 HTTP Controller 驱动的 Web 应用;同时加入 embabel-agent-starter-openai,用于接入 OpenAI 或 OpenAI-compatible 模型。官方文档说明,基础 starter 会把 Agent Platform Bean 放进 Spring 容器,应用可以通过依赖注入使用;如果不是用模板项目,并且要使用 OpenAI-compatible 模型,还需要加入 embabel-agent-starter-openai。
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>3.5.0</version>
<relativePath/>
</parent>
<groupId>com.example</groupId>
<artifactId>embabel-blog-demo</artifactId>
<version>0.0.1-SNAPSHOT</version>
<name>embabel-blog-demo</name>
<description>Spring Boot + Embabel getting started demo</description>
<properties>
<java.version>21</java.version>
<embabel-agent.version>0.4.0</embabel-agent.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-starter</artifactId>
<version>${embabel-agent.version}</version>
</dependency>
<dependency>
<groupId>com.embabel.agent</groupId>
<artifactId>embabel-agent-starter-openai</artifactId>
<version>${embabel-agent.version}</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
如果你使用的是 snapshot 版本,而不是 Maven Central 上的 release 版本,还需要额外配置 Embabel 的 release 和 snapshot 仓库。入门阶段建议优先用 release 版本,减少依赖解析和 API 变化带来的困扰。
6. 配置 application.yml
接下来在 src/main/resources/application.yml 中配置应用名称、端口和默认 LLM。OpenAI-compatible 场景至少需要 OPENAI_API_KEY,也可以通过配置文件指定 API Key 和 base URL。
spring:
application:
name: embabel-blog-demo
server:
port: 8080
embabel:
models:
default-llm: gpt-4.1-mini
agent:
platform:
models:
openai:
api-key: ${OPENAI_API_KEY}
base-url: ${OPENAI_BASE_URL:}
default-llm 指定默认模型。OPENAI_API_KEY 从环境变量读取,不建议把真实 Key 写死在代码仓库里。启动应用前,你可以这样设置环境变量:
export OPENAI_API_KEY="你的 API Key"
如果你使用的是兼容 OpenAI 协议的模型服务,例如企业内部网关或其他云厂商,可以同时设置:
export OPENAI_BASE_URL="https://your-compatible-endpoint.example.com/v1"
7. 启动类:开启 Agent 扫描
新建 src/main/java/com/example/embabelblog/EmbabelBlogApplication.java。@EnableAgents 用来启用 Embabel Agent 扫描。社区示例和教程中都采用这种方式把 @Agent 标注的类注册到平台中。
package com.example.embabelblog;
import com.embabel.agent.config.annotation.EnableAgents;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
@EnableAgents
public class EmbabelBlogApplication {
public static void main(String[] args) {
SpringApplication.run(EmbabelBlogApplication.class, args);
}
}
你可以把 @EnableAgents 理解成”让 Spring Boot 启动时顺便发现 Embabel Agent”。发现以后,Embabel 才知道应用里有哪些 Agent、Action 和 Goal。
8. 定义领域模型
接下来创建 model 包。领域模型是 Embabel 示例中非常重要的一部分。不要急着写提示词,也不要一上来就让模型返回一大段 JSON 字符串。更推荐先问自己:这个流程中会产生哪些业务对象?每一步的输入输出分别是什么?
8.1 BlogIdea
BlogIdea 表示系统从用户输入中理解出来的写作意图。
package com.example.embabelblog.model;
public record BlogIdea(
String topic,
String targetAudience,
String tone,
String keyMessage
) {
}
| 字段 | 含义 | 示例 |
|---|---|---|
topic | 文章主题 | Spring Boot 全局异常处理 |
targetAudience | 目标读者 | 初中级 Java 开发者 |
tone | 语气风格 | 通俗、实践导向 |
keyMessage | 核心观点 | 统一异常处理能提升 API 一致性 |
8.2 BlogOutline
BlogOutline 表示文章大纲。为了让代码简洁,我们在一个 record 中定义嵌套 record。
package com.example.embabelblog.model;
import java.util.List;
public record BlogOutline(
String title,
String summary,
List<Section> sections
) {
public record Section(
String heading,
String purpose,
List<String> keyPoints
) {
}
}
这里的 sections 是结构化章节列表。相比直接让模型返回 Markdown,大纲对象更容易检查,也更容易在后续增加”调整大纲""插入 SEO 关键词”等动作。
8.3 BlogDraft
BlogDraft 表示根据大纲写出的初稿。
package com.example.embabelblog.model;
public record BlogDraft(
String title,
String markdown,
int estimatedReadingMinutes
) {
}
初稿未必完美,所以我们不把它当作最终目标。它只是流程中间产物。
8.4 ReviewedBlogPost
ReviewedBlogPost 表示审稿后的最终文章。这个类型会作为我们 HTTP 接口的返回结果。
package com.example.embabelblog.model;
import java.util.List;
public record ReviewedBlogPost(
String title,
String markdown,
List<String> improvements,
List<String> warnings
) {
}
improvements 用于说明审稿阶段做了哪些改进,warnings 用于提醒可能还需要人工确认的地方。例如,模型可能会提醒”某个版本号需要根据官方文档再次核对”。
8.5 GenerateBlogRequest
为了让 Controller 接收 JSON 请求,再定义一个简单请求对象。
package com.example.embabelblog.model;
public record GenerateBlogRequest(
String topic,
String targetAudience,
String tone
) {
}
9. 编写 BlogWritingAgent
现在进入 Embabel 的核心部分。新建 agent 包,并创建 BlogWritingAgent.java。
一个 Embabel Agent 就是一个 Spring Bean,它包含多个 @Action 方法。每个 Action 方法的参数代表它需要哪些对象,返回值代表它执行后产生什么对象。Embabel 正是根据这些输入输出类型来推断流程路径的。
package com.example.embabelblog.agent;
import com.embabel.agent.api.annotation.AchievesGoal;
import com.embabel.agent.api.annotation.Action;
import com.embabel.agent.api.annotation.Agent;
import com.embabel.agent.api.common.OperationContext;
import com.embabel.agent.domain.io.UserInput;
import com.example.embabelblog.model.BlogDraft;
import com.example.embabelblog.model.BlogIdea;
import com.example.embabelblog.model.BlogOutline;
import com.example.embabelblog.model.ReviewedBlogPost;
@Agent(description = "根据用户主题生成适合初学者阅读的技术博客")
public class BlogWritingAgent {
@Action(description = "从用户输入中提取博客写作意图")
public BlogIdea understandIdea(UserInput input, OperationContext context) {
String prompt = """
你是一名资深技术编辑。请从用户输入中提取博客写作意图,返回结构化对象。
要求:
1. topic 表示文章主题。
2. targetAudience 表示目标读者,如果用户没有说明,默认为"初中级 Java 开发者"。
3. tone 表示写作风格,如果用户没有说明,默认为"通俗易懂、实践导向"。
4. keyMessage 表示文章最想传达的核心观点。
用户输入:
%s
""".formatted(input.getContent());
return context.ai()
.withDefaultLlm()
.createObject(prompt, BlogIdea.class);
}
@Action(description = "根据写作意图生成博客大纲")
public BlogOutline createOutline(BlogIdea idea, OperationContext context) {
String prompt = """
你是一名 Java 技术博客作者。请根据下面的写作意图生成一份清晰的大纲。
主题:%s
目标读者:%s
语气:%s
核心观点:%s
大纲要求:
1. 标题要具体,不要空泛。
2. summary 用 2 到 3 句话说明文章内容。
3. sections 建议 5 到 7 个章节。
4. 每个章节都要有 heading、purpose 和 keyPoints。
5. keyPoints 使用简短中文短语,方便后续扩写。
""".formatted(
idea.topic(),
idea.targetAudience(),
idea.tone(),
idea.keyMessage()
);
return context.ai()
.withDefaultLlm()
.createObject(prompt, BlogOutline.class);
}
@Action(description = "根据博客大纲生成 Markdown 初稿")
public BlogDraft writeDraft(BlogOutline outline, OperationContext context) {
String prompt = """
你是一名擅长 Spring Boot 和 Java 的技术作者。请根据下面的大纲写一篇 Markdown 初稿。
标题:%s
摘要:%s
章节:%s
写作要求:
1. 使用中文。
2. 面向初学者,解释要通俗。
3. 每个章节都要有完整段落,不要只写列表。
4. 如果涉及代码,请给出简短但可理解的 Java 示例。
5. estimatedReadingMinutes 根据正文长度估算。
""".formatted(
outline.title(),
outline.summary(),
outline.sections()
);
return context.ai()
.withDefaultLlm()
.createObject(prompt, BlogDraft.class);
}
@Action(description = "审阅初稿,修正表达和技术细节,输出最终博客")
@AchievesGoal(description = "生成一篇经过审阅的中文技术博客")
public ReviewedBlogPost reviewDraft(BlogDraft draft, OperationContext context) {
String prompt = """
你是一名严格但友好的技术审稿人。请审阅并改进下面的博客初稿。
原标题:%s
原文:
%s
审稿要求:
1. 修正明显的技术表述问题。
2. 让语言更适合初学者阅读。
3. 保留 Markdown 格式。
4. improvements 列出你做出的主要改进。
5. warnings 列出仍建议人工确认的内容;如果没有,请返回空列表。
""".formatted(draft.title(), draft.markdown());
return context.ai()
.withDefaultLlm()
.createObject(prompt, ReviewedBlogPost.class);
}
}
这段代码有几个关键点值得慢慢看。
首先,BlogWritingAgent 标注了 @Agent。这说明它不是普通 Service,而是一个 Embabel 可以识别的 Agent。它仍然是 Spring 管理的组件,所以你完全可以通过构造器注入自己的 Repository、Service、配置类或审计组件。
其次,四个方法都标注了 @Action。understandIdea() 的输入是 UserInput,输出是 BlogIdea;createOutline() 的输入是 BlogIdea,输出是 BlogOutline;writeDraft() 的输入是 BlogOutline,输出是 BlogDraft;reviewDraft() 的输入是 BlogDraft,输出是 ReviewedBlogPost。这种类型关系就是 Embabel 规划流程的重要依据。
最后,reviewDraft() 多了一个 @AchievesGoal。它告诉 Embabel:当系统成功得到 ReviewedBlogPost 时,这次博客生成任务就算完成。
10. 编写 REST Controller
官方文档展示了如何注入 AgentPlatform,然后通过 AgentInvocation.builder(agentPlatform) 构建调用,并用 invoke(input) 执行。我们可以把这个思路放进 Spring MVC Controller 中。
新建 controller/BlogController.java:
package com.example.embabelblog.controller;
import com.embabel.agent.api.common.AgentInvocation;
import com.embabel.agent.api.common.AgentPlatform;
import com.embabel.agent.api.common.ProcessOptions;
import com.embabel.agent.domain.io.UserInput;
import com.example.embabelblog.model.GenerateBlogRequest;
import com.example.embabelblog.model.ReviewedBlogPost;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
@RequestMapping("/api/blogs")
public class BlogController {
private final AgentPlatform agentPlatform;
public BlogController(AgentPlatform agentPlatform) {
this.agentPlatform = agentPlatform;
}
@PostMapping("/generate")
public ReviewedBlogPost generate(@RequestBody GenerateBlogRequest request) {
String userMessage = """
请帮我写一篇技术博客。
主题:%s
目标读者:%s
写作风格:%s
""".formatted(
request.topic(),
defaultIfBlank(request.targetAudience(), "初中级 Java 开发者"),
defaultIfBlank(request.tone(), "通俗易懂、实践导向")
);
UserInput input = new UserInput(userMessage);
AgentInvocation<ReviewedBlogPost> invocation = AgentInvocation
.builder(agentPlatform)
.options(ProcessOptions.builder()
.verbosity(v -> v.showPrompts(false))
.build())
.build(ReviewedBlogPost.class);
return invocation.invoke(input);
}
private static String defaultIfBlank(String value, String defaultValue) {
return value == null || value.isBlank() ? defaultValue : value;
}
}
Controller 只说明:我现在有一个 UserInput,我想要一个 ReviewedBlogPost。至于从输入到最终结果要经过哪些 Action,则交给 Embabel 的平台根据 Agent 中声明的能力去规划。这正是重点所在——你不再需要手写:
BlogIdea idea = agent.understandIdea(input);
BlogOutline outline = agent.createOutline(idea);
BlogDraft draft = agent.writeDraft(outline);
ReviewedBlogPost result = agent.reviewDraft(draft);
如果你的 Embabel 版本中 AgentInvocation 或 UserInput 的包名、构造方式略有不同,请参考当前版本官方文档中的示例。Embabel 仍在快速发展,这类 API 命名变化并不罕见。
11. 运行和测试
启动前先设置 API Key:
export OPENAI_API_KEY="你的 API Key"
然后运行:
mvn spring-boot:run
使用 curl 调用接口:
curl -X POST http://localhost:8080/api/blogs/generate \
-H "Content-Type: application/json" \
-d '{
"topic": "Spring Boot 中如何优雅地处理全局异常",
"targetAudience": "刚入门 Spring Boot 的 Java 开发者",
"tone": "通俗易懂,带一点实战经验"
}'
你将得到类似下面的响应(真实内容会因为模型输出而变化):
{
"title": "Spring Boot 全局异常处理入门:让你的 API 错误返回更统一",
"markdown": "# Spring Boot 全局异常处理入门\n\n在实际项目中...",
"improvements": [
"补充了为什么需要统一异常处理的背景说明",
"调整了示例代码的命名,使其更贴近 Spring Boot 项目",
"增加了初学者容易踩坑的解释"
],
"warnings": [
"示例代码需要根据项目实际 Spring Boot 版本调整依赖"
]
}
如果你打开应用日志,通常可以看到 Embabel 规划和执行 Action 的过程:先 formulated plan,然后依次 executing action,最后 goal achieved。这类日志对理解 Agent 为什么这么执行非常有帮助。
12. 为什么这个例子比直接调用 LLM 更好
你可能会问:这个例子里每一步都在调用 LLM,那我直接写四个方法顺序调用不也可以吗?从功能结果看,短期确实可以。但 Embabel 的价值在于后续演进。
| 后续需求 | 手写固定流程 | Embabel 风格 |
|---|---|---|
| 增加 SEO 优化 | 修改 Service 流程代码 | 增加一个输入 BlogDraft、输出 SeoBlogDraft 的 Action |
| 增加敏感词审查 | 在流程中插入条件判断 | 增加审查 Action,让目标依赖审查后的对象 |
| 接入数据库素材 | 在 Service 中手动查询 | 在 Action 中注入 Repository 或 Service |
| 多模型混用 | 手动管理不同模型客户端 | 按配置和角色选择不同 LLM,框架支持 LLM mixing |
| 排查执行过程 | 自己打日志 | 平台可输出规划和执行过程 |
也就是说,Embabel 对初学者的第一印象可能是”多了一层抽象”,但当流程从两个步骤变成十几个步骤,并且混合 LLM、数据库、规则、工具和外部系统时,这层抽象就会让系统更容易维护。
13. 在 Action 中注入普通 Spring Service
Embabel 并不要求每个 Action 都调用 LLM。实际上,企业项目中更推荐把 LLM 用在它擅长的地方,例如理解自然语言、生成草稿、总结内容;而把确定性逻辑留给普通 Java 代码。Action 可以是 LLM 步骤,也可以是数据库查询、评分、校验或调用业务服务这样的确定性步骤。
比如你可以定义一个素材服务:
package com.example.embabelblog.service;
import org.springframework.stereotype.Service;
import java.util.List;
@Service
public class BlogReferenceService {
public List<String> findReferences(String topic) {
return List.of(
"公司内部 Java 开发规范",
"Spring Boot 官方参考文档",
"历史故障复盘文档"
);
}
}
然后在 Agent 中注入它:
@Agent(description = "根据用户主题生成适合初学者阅读的技术博客")
public class BlogWritingAgent {
private final BlogReferenceService referenceService;
public BlogWritingAgent(BlogReferenceService referenceService) {
this.referenceService = referenceService;
}
@Action(description = "查找和主题相关的内部参考资料")
public BlogReferences findReferences(BlogIdea idea) {
return new BlogReferences(referenceService.findReferences(idea.topic()));
}
}
这样一来,Agent 既可以使用 LLM,也可以使用普通业务代码。对于生产系统来说,这非常重要:不要让 LLM 接管所有逻辑;让 LLM 处理不确定的语言任务,让 Java 处理确定的业务规则。
14. 常见问题
14.1 Embabel 和 Spring AI 是什么关系
Spring AI 是 Spring 生态里的 AI 基础抽象,重点是模型调用、向量库、工具调用等能力。Embabel 是更高层的 Agent 框架,它可以基于 Spring AI 等底层能力来编排目标驱动的工作流。简单来说:Spring AI 帮你和模型、工具、向量库交互;Embabel 帮你构建会根据目标运行 Action 的 Agent。
14.2 我一定要用 Shell starter 吗
不一定。官方文档列出了三类常见 starter:
| Starter | 适用场景 | 本文是否使用 |
|---|---|---|
embabel-agent-starter-shell | 学习、调试、命令行交互 | 否 |
embabel-agent-starter-mcpserver | 作为 MCP 服务暴露 Agent | 否 |
embabel-agent-starter | Web API、微服务、自定义启动模式 | 是 |
14.3 为什么要用 Java record
Java record 很适合表达不可变的数据传输对象。Embabel 强调强类型领域模型,record 可以让输入输出结构更清楚,也便于测试和重构。相比让模型返回一段散乱文本,返回 BlogOutline、BlogDraft、ReviewedBlogPost 这样的对象更适合进入业务流程。
14.4 如果模型输出不符合结构怎么办
生产环境中需要增加校验和兜底。你可以在 record 字段上配合 Bean Validation,也可以在 Action 返回后增加一个校验 Action。如果校验失败,可以让另一个 Action 修复格式,或者直接返回可解释的错误。Embabel 的价值不是让 LLM 永不出错,而是把不确定输出放进一个更可观察、更可控制的流程里。
14.5 可以不用 OpenAI 吗
可以。官方文档列出了 Anthropic、DeepSeek、Gemini、Mistral AI、LM Studio、Ollama 等 provider starter 或配置方式。如果你希望本地运行模型,可以尝试 Ollama 或 LM Studio;如果你在企业内网中使用 OpenAI-compatible 网关,可以配置对应的 base URL。
15. 入门后的改造方向
当你跑通本文示例以后,可以尝试做几个增强版本:
| 改造方向 | 需要新增的对象 | 需要新增的 Action |
|---|---|---|
| SEO 优化 | SeoSuggestion、SeoBlogPost | optimizeSeo() |
| 事实检查 | FactList、FactCheckReport | extractFacts()、checkFacts() |
| 存入数据库 | SavedBlogPost | saveBlogPost() |
| 生成配图提示词 | ImagePrompt | createCoverImagePrompt() |
| 多语言翻译 | TranslatedBlogPost | translatePost() |
这种扩展方式体现了 Embabel 的优势:你不是不断修改一个巨大流程,而是在增加新的类型和 Action,让平台拥有更多可组合的能力。
16. 总结
本文用一个”AI 博客助手”示例介绍了 Spring Boot + Embabel 的基本用法。一个 Embabel 应用通常包括 Maven starter、模型配置、@EnableAgents、领域模型、@Agent、@Action、@AchievesGoal 以及通过 AgentPlatform 发起调用。
更重要的是,Embabel 带来的不是”更花哨的提示词写法”,而是一种更适合 Java 企业应用的 AI 工程化思路:用强类型对象承载上下文,用 Action 拆分能力,用 Goal 表达目标,用平台规划执行路径,并把 LLM 调用和普通业务代码放在同一个 Spring 体系中管理。
如果你刚开始学习 Embabel,建议先写一个像本文这样的简单 Agent,理解对象如何在 Action 之间流动;然后再逐步加入数据库、业务服务、工具调用、多模型配置和测试。这样学习曲线会更平滑,也更容易把 Embabel 用到真实项目里。
References
- Embabel Agent Framework User Guide
- Embabel Agent Framework — GitHub
- Embabel First Look: Building Agentic Flows on the JVM
- Build AI Agents in Java with Embabel: Step-by-Step Guide
- Creating an AI Agent in Java Using Embabel Agent Framework
- Embabel Agent Examples
- Maven Repository: Embabel Agent Starter