编排与MCP融合:使用Quarkus Flow和AGENTS.md构建受管控的智能体工作流
编排与 MCP 的融合:使用 Quarkus Flow 和 AGENTS.md 构建受治理的 Agentic 工作流
使用大型语言模型(LLM)构建自主 AI 代理时,编写单轮演示脚本很容易。然而,将多代理循环投入生产会带来严重的架构挑战。代理会产生幻觉、无限循环而无法收敛、需要人工批准高风险操作,并且需要标准的工具调用集成以及清晰的操作治理。
历史上,Java 开发者面临两难选择:要么依赖重量级的外部工作流集群(如 Temporal 或 Camunda),这会增加运维开销;要么在自己的服务中手工编写脆弱的 while 循环和自定义状态机。
Quarkus Flow 弥合了这一差距。Quarkus Flow 基于云原生计算基金会(CNCF)Serverless Workflow 规范构建,将轻量级、符合规范的工作流编排直接带入你的 Quarkus 应用。当与 LangChain4j、模型上下文协议(MCP)工具连接以及 AGENTS.md 上下文治理相结合时,Java 开发者可以使用惯用的 CDI 和流畅的 Java DSL 构建确定性、可观测性和韧性的代理式 AI 工作流。
现代代理技术栈:Quarkus Flow、MCP 与 AGENTS.md
要运行生产级 AI 代理,你需要三个不同的层次:编排、标准化工具连接和行为治理。
- 编排(Quarkus Flow):在 JVM 内部管理状态转换、重试、条件循环、最大迭代上限和人机协同(HITL)门控。
- 工具标准化(MCP):使用模型上下文协议(MCP)将代理连接到企业数据、数据库和 API,而无需为每个 LLM 主机编写自定义 API 适配器。
- 行为治理(AGENTS.md):一个项目级 Markdown 规范,定义系统边界、代理角色、所需输出格式和安全规则,代理在运行时读取这些内容。
使用 AGENTS.md 定义治理
不要将提示字符串硬编码在 Java 类深处,而是将 AGENTS.md 文件放在 src/main/resources 中。这样开发者和提示工程师无需重新编译应用程序即可调整系统指令和安全边界。
以下是基于参考仓库的 src/main/resources/AGENTS.md 文件:
实践示例:带 MCP 与 AGENTS.md 的多代理工作流
让我们构建一个生产级的内容发布代理工作流,与 quarkus-flow-mcp-agents 的精确结构一致。该工作流从 AGENTS.md 读取系统指令,使用 Writer Agent 通过 MCP Server 获取真实数据,将草稿提交给 Critic Agent,并在获得批准或达到最大迭代次数前循环执行。
注意:你可以在 https://github.com/danieloh30/quarkus-flow-mcp-agents.git 找到完整的参考实现仓库。
1. pom.xml 依赖
2. 应用程序配置:src/main/resources/application.properties
3. 使用 @LoopAgent 编排写审循环
ArticlePublisher 是使用 Quarkus Flow 声明式 API 将多代理循环连接起来的编排器。以下是每个注解的作用:
- @LoopAgent:重复运行 WriterAgent 和 CriticAgent(最多 3 次迭代)。在构建时,Quarkus Flow 会将其编译为 CNCF Serverless Workflow 定义——运行时无需单独的工作流引擎。
- @ExitCondition:一个静态方法(isApproved),用于检查评论家的评审是否以“APPROVED”开头。它在每次循环迭代后运行(testExitAtLoopEnd = true)。如果为真,则提前跳出循环。
- @Output:一个静态方法(extractArticle),用于提取最终结果。它从共享代理作用域中拉取草稿,并将其作为工作流输出返回。
- 流程:Writer 草拟 → Critic 评审 → 如果未批准,Writer 根据反馈修改 → 重复直到批准或达到 3 次迭代 → 返回最终草稿。
4. WriterAgent——借助 MCP 驱动的研究进行草拟
WriterAgent 是一个声明式 LLM 代理,通过 Brave Search 研究主题并撰写技术博客文章。
- @Agent:将该方法标记为代理入口点。outputKey = "draft" 将结果存储在共享作用域中,以便其他代理(如 CriticAgent)可以访问它。
- @ToolBox(WebSearchTool.class):让 LLM 访问 webSearch 工具。LLM 根据提示决定何时调用它——并非强制调用。这就是 MCP 工具连接到声明式代理的方式。
- @SystemMessage:指示 LLM 在写作前先研究,生成准确内容,并根据先前的反馈进行修订。最后一点对于循环至关重要——在第 2 次及以后的迭代中,LLM 会在聊天记忆中看到评论家的反馈,并相应调整草稿。
该接口没有实现——Quarkus 在构建时生成它。
5. CriticAgent——审查准确性与清晰度
CriticAgent 是循环中的质量门。它审查草稿,要么批准,要么拒绝并提供反馈。
- @Agent:outputKey = "review" 将评审存储在共享作用域中。ArticlePublisher 中的 @ExitCondition 读取此键来决定是否退出循环。
- @UserMessage:从共享作用域注入 {draft} 变量,因此评论家始终审查最新版本的文章。
- @SystemMessage:强制执行严格的契约:如果草稿可接受,响应必须以“APPROVED:”开头。这就是 @ExitCondition 能工作的原因——它是一个简单的字符串检查,而不是另一次 LLM 调用。
未附加任何工具——评论家仅依靠 LLM 的推理来评估草稿。
6. WebSearchTool——桥接 MCP 与声明式代理
WebSearchTool 是一个 CDI Bean,它将 Brave Search MCP 服务器连接到代理工作流。
- 存在的原因:@McpToolBox 仅适用于 @RegisterAiService,不适用于 @Agent。该类通过编程方式创建 MCP 客户端并将其作为 @Tool 暴露,弥合了这一差距。
- MCP 客户端设置:构造函数使用 stdio 传输创建 DefaultMcpClient,将 npx -y @brave/brave-search-mcp-server 作为子进程启动。BRAVE_API_KEY 通过环境变量传递。
- @Tool:webSearch 方法构建一个针对 MCP 服务器上 brave_web_search 工具的 ToolExecutionRequest,执行它并返回结果。LLM 将其视为一个可以调用的常规函数。
- @PreDestroy:在 CDI 上下文关闭时清理 MCP 客户端(以及子进程)。
这种模式——将 MCP 客户端包装在 @Tool CDI Bean 中,并通过 @ToolBox 附加它——对于你想连接到声明式 @Agent 的任何 MCP 服务器都是可复用的。
生产防护与企业就绪性
将代理 AI 系统部署到企业云环境中需要严格的治理、追踪和高性能:
- 通过 MCP 实现标准化工具:通过无状态模型上下文协议端点消费外部系统,工具定义与 LLM 主机代码解耦。
- 使用 AGENTS.md 进行上下文控制:业务分析师和安全负责人可以在不重新部署代码工件的情况下审计或更新提示指南。
- 人机协同(HITL):使用 Quarkus Flow 事件过滤器或暂停状态,挂起执行,直到人类管理员批准敏感的工具操作。
- OpenTelemetry 和分布式追踪:Quarkus Flow 和 quarkus-opentelemetry 在每个工作流转换、LLM 调用和 MCP 请求中传递 W3C 追踪上下文。
- GraalVM 原生镜像:将整个技术栈——Quarkus Flow 引擎、LangChain4j、MCP 连接和 REST 接口——编译成超快速的原生二进制文件,启动时间小于 10 毫秒,内存占用极小。
通过结合 Quarkus Flow、LangChain4j、MCP 和 AGENTS.md,Java 开发者可以将不可维护的 AI 脚本替换为干净、符合规范且企业就绪的代理式架构。
AI Quarkus Java(编程语言)
DZone 贡献者表达的观点属于他们自己。