从单次对话到持续记忆,从单体Agent到多Agent协作,Spring AI Agent Utils工具包为Java开发者提供了一套完整的智能体构建范式。本文深度解析六大核心模式的设计原理、最佳实践与工程落地要点。
引言:为什么Java开发者需要Agentic Patterns?
大语言模型(LLM)正在从"对话工具"进化为"执行引擎"。然而,将LLM集成到企业应用中并非易事——上下文窗口有限、多步骤任务容易遗漏、跨会话记忆缺失、多Agent协作缺乏标准……这些问题困扰着每一个试图构建生产级AI Agent的开发者。
Spring官方博客近期推出的Agentic Patterns系列文章(共6篇),系统性地回答了这些问题。该系列基于spring-ai-agent-utils工具包,将Claude Code的设计思想移植到Spring AI生态,形成了一套模型无关、可组合、可扩展的智能体构建模式。
本文作为系列综述,将:
- 全景式呈现六大模式的核心设计,帮助读者建立系统性认知
- 深入剖析工程实现细节,包括版本依赖、配置要点、踩坑指南
- 提供组合使用的最佳实践,构建真正可落地的企业级Agent
环境准备
在开始之前,请确保开发环境满足以下要求:
基础环境
| 组件 | 版本要求 | 说明 |
|---|---|---|
| JDK | 17+ | 推荐使用JDK 21(LTS) |
| Spring Boot | 3.2+ | 推荐使用3.4.x最新稳定版 |
| Spring AI | 2.0.0-M2+ | 支持多模型供应商 |
| spring-ai-agent-utils | 1.0.0+ | 核心工具包 |
Maven依赖
<dependencies>
<!-- Spring AI 核心 -->
<dependency>
<groupId>org.springframework.ai</groupId>
<artifactId>spring-ai-core</artifactId>
<version>2.0.0-M2</version>
</dependency>
<!-- Agent Utils 工具包 -->
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-agent-utils</artifactId>
<version>1.0.0</version>
</dependency>
<!-- A2A协议支持(可选) -->
<dependency>
<groupId>org.springframework.experimental.ai</groupId>
<artifactId>spring-ai-a2a-server-autoconfigure</artifactId>
<version>0.2.0</version>
</dependency>
</dependencies>
💡 提示:各深入系列文章包含更详细的代码示例和配置说明。本文作为综述,聚焦架构设计与模式组合。
系列概览:六大核心能力图谱
| 篇章 | 核心能力 | 解决的问题 | 关键工具类 |
|---|---|---|---|
| Part 1 | Agent Skills | 如何让Agent动态加载领域知识? | SkillsTool, FileSystemTools, ShellTools |
| Part 2 | AskUserQuestionTool | 如何让Agent主动询问用户需求? | AskUserQuestionTool |
| Part 3 | TodoWriteTool | 如何防止多步骤任务遗漏? | TodoWriteTool |
| Part 4 | Subagent Orchestration | 如何构建多Agent协作系统? | TaskToolCallbackProvider |
| Part 5 | A2A Integration | 如何实现跨平台Agent互操作? | spring-ai-a2a-server-autoconfigure |
| Part 6 | AutoMemoryTools | 如何实现跨会话持久记忆? | AutoMemoryToolsAdvisor |
核心设计理念:六大模式并非孤立存在,而是遵循"感知→规划→执行→协作→记忆"的完整Agent生命周期。下图展示了它们的协作关系:
Part 1: Agent Skills —— 模块化知识注入
核心问题
传统Agent开发中,领域知识往往被硬编码在System Prompt中,导致:
- 上下文膨胀:所有知识一次性加载,浪费Token
- 难以维护:修改知识需要重新部署
- 无法复用:不同项目需要重复编写
设计原理
Agent Skills借鉴了AgentSkills.io规范,采用**渐进式披露(Progressive Disclosure)**策略:
Discovery(发现)→ Activation(激活)→ Execution(执行)
- Discovery:启动时仅加载Skill的
name和description,嵌入到Tool Schema中 - Activation:当用户请求语义匹配某个Skill时,LLM调用
Skill工具加载完整指令 - Execution:LLM按指令执行,必要时加载参考文件或执行辅助脚本
工程实现
目录结构:
my-skill/
├── SKILL.md # 必需:指令 + 元数据(YAML frontmatter)
├── scripts/ # 可选:辅助脚本
├── references/ # 可选:参考文档
└── assets/ # 可选:模板、资源
SKILL.md示例:
---
name: code-reviewer
description: Reviews Java code for best practices, security issues, and Spring Framework conventions.
---
# Code Reviewer
## Instructions
When reviewing code:
1. Check for security vulnerabilities (SQL injection, XSS, etc.)
2. Verify Spring Boot best practices
3. Look for potential null pointer exceptions
4. Suggest improvements for readability
代码配置:
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(SkillsTool.builder()
.addSkillsDirectory(".claude/skills")
.build())
.defaultTools(FileSystemTools.builder().build())
.defaultTools(ShellTools.builder().build())
.build();
⚠️ 踩坑指南
| 问题 | 原因 | 解决方案 |
|---|---|---|
| 脚本执行失败 | 缺少运行时环境 | 预装Python/Node.js等运行时,或使用Docker容器隔离 |
| Skills未加载 | 目录路径错误 | 生产环境使用classpath:资源路径:.addSkillsResource(resourceLoader.getResource("classpath:.claude/skills")) |
| Token超限 | Skill指令过长 | 精简SKILL.md,将详细文档放入references/按需加载 |
版本依赖
- Spring AI:
2.0.0-M2+ - spring-ai-agent-utils:
0.4.2+
Part 2: AskUserQuestionTool —— 交互式需求澄清
核心问题
传统AI交互遵循"用户提供Prompt → AI假设 → AI输出"模式。当假设与需求不符时,用户被迫反复修正,浪费时间与上下文。
设计原理
AskUserQuestionTool将Agent从"假设型响应者"转变为"协作型伙伴":
用户请求 → Agent生成澄清问题 → 用户选择/输入 → Agent精准响应
每个问题支持:
- 单选/多选:通过
multiSelect标志控制 - 自由文本:用户始终可以输入自定义答案
- 选项描述:每个选项附带说明,帮助用户理解后果
工程实现
配置代码:
ChatClient chatClient = chatClientBuilder
.defaultTools(AskUserQuestionTool.builder()
.questionHandler(this::handleQuestions)
.build())
.build();
QuestionHandler实现(控制台版):
private Map<String, String> handleQuestions(List<Question> questions) {
Map<String, String> answers = new HashMap<>();
Scanner scanner = new Scanner(System.in);
for (Question q : questions) {
System.out.println("\n" + q.header() + ": " + q.question());
for (int i = 0; i < q.options().size(); i++) {
Option opt = q.options().get(i);
System.out.printf(" %d. %s - %s%n", i + 1, opt.label(), opt.description());
}
String response = scanner.nextLine().trim();
// 解析数字选择或作为自由文本
// ...
}
return answers;
}
与MCP Elicitation的关系
| 特性 | AskUserQuestionTool | MCP Elicitation |
|---|---|---|
| 触发方式 | Agent本地决定 | MCP Server发起 |
| 数据格式 | 预定义选项 | JSON Schema |
| 适用场景 | Agent主动澄清 | 服务端需要用户输入 |
Spring AI同时支持两者——AskUserQuestionTool用于Agent本地交互,@McpElicitation注解用于MCP服务端场景。
Part 3: TodoWriteTool —— 结构化任务追踪
核心问题
研究表明,LLM在长上下文中存在"迷失中间"问题——容易遗忘列表中间的任务项。当Agent同时处理文件编辑、测试执行、文档更新时,关键步骤可能悄然消失。
设计原理
TodoWriteTool的核心约束:同一时刻只能有一个任务处于in_progress状态。这强制Agent顺序执行,而非分散尝试。
任务生命周期:
pending → in_progress → completed
↘ cancelled
工程实现
配置代码:
ChatClient chatClient = chatClientBuilder
.defaultTools(TodoWriteTool.builder().build())
.defaultAdvisors(
ToolCallAdvisor.builder().conversationHistoryEnabled(false).build(),
MessageChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder().build()).build())
.build();
⚠️ 关键配置:必须启用
ChatMemory和ToolCallAdvisor,否则Todo状态无法持久化到对话历史中。
事件驱动的进度更新:
ChatClient chatClient = chatClientBuilder
.defaultTools(TodoWriteTool.builder()
.todoEventHandler(event ->
applicationEventPublisher.publishEvent(
new TodoUpdateEvent(this, event.todos())))
.build())
.build();
何时使用?
Tool Schema内置了启发式规则:
"Use this tool when a task requires 3 or more distinct steps."
Agent自主判断是否需要创建任务列表——这是自我治理(Self-Governing) 行为的体现。
Part 4: Subagent Orchestration —— 层级化多Agent架构
核心问题
单体Agent的上下文窗口是有限资源。当Agent需要同时处理代码探索、文件编辑、测试运行、架构规划时,上下文会被无关信息污染,导致性能下降。
设计原理
核心思想:让专业的人做专业的事。将任务委派给专用Subagent,每个Subagent在独立的上下文窗口中执行。
Main Agent (Orchestrator)
├── Explore Agent (只读探索)
├── Code Agent (读写执行)
├── Plan Agent (架构设计)
└── Bash Agent (命令执行)
内置Subagent
| Subagent | 职责 | 可用工具 |
|---|---|---|
| Explore | 快速只读探索代码库 | Read, Grep, Glob |
| General-Purpose | 多步骤研究与执行 | 所有工具 |
| Plan | 软件架构设计 | Read-only + search |
| Bash | Git操作、构建、终端任务 | Bash only |
工程实现
配置代码:
var taskTools = TaskToolCallbackProvider.builder()
.chatClientBuilder("default", chatClientBuilder)
.subagentReferences(
ClaudeSubagentReferences.fromRootDirectory("src/main/resources/agents"))
.build();
ChatClient chatClient = chatClientBuilder
.defaultToolCallbacks(taskTools)
.build();
自定义Subagent文件(.claude/agents/code-reviewer.md):
---
name: code-reviewer
description: Expert code reviewer. Use proactively after writing code.
tools: Read, Grep, Glob
disallowedTools: Edit, Write
model: sonnet
---
You are a senior code reviewer with expertise in software quality.
**When Invoked:**
1. Run `git diff` to see recent changes
2. Focus analysis on modified files
3. Check surrounding code context
多模型路由
根据任务复杂度分配不同模型:
var taskTools = TaskToolCallbackProvider.builder()
.chatClientBuilder("default", sonnetBuilder) // 默认
.chatClientBuilder("haiku", haikuBuilder) // 快速廉价
.chatClientBuilder("opus", opusBuilder) // 复杂分析
.build();
Subagent通过model字段声明偏好,Task工具自动路由。
⚠️ 重要约束:Subagent不能再创建Subagent(不能将
Task放入tools列表),防止无限嵌套。
Part 5: A2A Integration —— 跨平台Agent互操作
核心问题
前四篇关注单Agent能力增强,但企业场景中,Agent往往需要跨越系统边界协作。不同团队、不同平台、不同技术栈的Agent如何互通?
A2A协议简介
Agent2Agent (A2A) Protocol是一个开放标准,基于HTTP、SSE、JSON-RPC构建,提供:
- Agent Discovery:通过
AgentCard(标准JSON文档)发现能力 - 标准端点:
/.well-known/agent-card.json暴露Agent元数据 - 消息交换:JSON-RPC格式的
sendMessage请求
Spring AI A2A集成
目前支持服务端集成——将Spring AI Agent暴露为A2A Server:
@Configuration
public class WeatherAgentConfiguration {
@Bean
public AgentCard agentCard(@Value("${server.port:8080}") int port,
@Value("${server.servlet.context-path:/}") String contextPath) {
return new AgentCard.Builder()
.name("Weather Agent")
.description("Provides weather information for cities")
.url("http://localhost:" + port + contextPath + "/")
.skills(List.of(new AgentSkill.Builder()
.id("weather_search")
.name("Search weather")
.description("Get temperature for any city")
.build()))
.protocolVersion("0.3.0")
.build();
}
@Bean
public AgentExecutor agentExecutor(ChatClient.Builder chatClientBuilder,
WeatherTools weatherTools) {
ChatClient chatClient = chatClientBuilder.clone()
.defaultSystem("You are a weather assistant.")
.defaultTools(weatherTools)
.build();
return new DefaultAgentExecutor(chatClient, (chat, requestContext) -> {
String userMessage = DefaultAgentExecutor
.extractTextFromMessage(requestContext.getMessage());
return chat.prompt().user(userMessage).call().content();
});
}
}
自动暴露的端点:
POST / // JSON-RPC sendMessage
GET /.well-known/agent-card.json // Agent Card(标准位置)
GET /card // Agent Card(备用位置)
Agent Client示例
Host Agent发现并调用远程Agent:
@Service
public class RemoteAgentConnections {
private final Map<String, AgentCard> agentCards = new HashMap<>();
public RemoteAgentConnections(@Value("${remote.agents.urls}") List<String> agentUrls) {
for (String url : agentUrls) {
AgentCard card = A2A.getAgentCard(url,
path + ".well-known/agent-card.json", null);
this.agentCards.put(card.name(), card);
}
}
@Tool(description = "Sends a task to a remote agent.")
public String sendMessage(String agentName, String task) {
// 使用A2A Java SDK Client发送消息
// ...
}
}
未来规划
Spring AI社区正在探索:
- 客户端自动配置
- SSE传输支持(流式响应)
- Spring Boot Actuator集成(可观测性)
- 安全认证与授权
Part 6: AutoMemoryTools —— 跨会话持久记忆
核心问题
Spring AI的ChatMemory是对话历史窗口——当窗口满时,旧消息被驱逐。即使即将推出的Session API支持递归摘要,精确事实仍会在压缩中丢失。
设计原理
ChatMemory vs AutoMemoryTools:
| 维度 | ChatMemory | AutoMemoryTools |
|---|---|---|
| 内容 | 完整对话历史 | 精选的持久事实 |
| 淘汰 | 滑动窗口驱逐 | 永久保存 |
| 更新 | 自动 | AI模型主动写入 |
| 用途 | 当前任务上下文 | 跨会话知识积累 |
核心理念:AI模型只写入"值得永久保存"的内容——用户偏好、项目决策、行为修正——形成Markdown文件,无限期存活。
记忆类型
| 类型 | 内容示例 | 写入时机 |
|---|---|---|
user | 角色、目标、沟通风格 | 用户自我介绍时 |
feedback | "停止总结"、"是的,这样对" | 用户纠正时 |
project | 迁移目标、冻结日期 | 项目决策时 |
reference | Linear看板、Grafana仪表盘 | 发现外部系统时 |
记忆结构
索引文件(MEMORY.md):
- [User Profile](user_profile.md) — Alice, backend engineer, prefers short answers
- [Feedback Testing](feedback_testing.md) — always use real DB in integration tests
- [Project Auth Rewrite](project_auth.md) — driven by legal compliance, not tech debt
记忆文件(user_profile.md):
---
name: user profile
description: Alice — backend engineer, prefers short answers
type: user
---
Backend engineer named Alice.
Prefers concise, direct responses without trailing summaries.
三种集成方式
| 方式 | 特点 | 适用场景 |
|---|---|---|
| Option A: AutoMemoryToolsAdvisor | 零样板代码,自动注入System Prompt | 快速上手 |
| Option B: 手动配置AutoMemoryTools | 完全控制System Prompt结构 | 需要组合自定义Prompt |
| Option C: FileSystemTools + ShellTools | 无专用Memory工具,复用现有工具 | Agent已有文件系统访问能力 |
Option A示例:
ChatClient chatClient = ChatClient.builder(chatModel)
.defaultAdvisors(
AutoMemoryToolsAdvisor.builder()
.memoriesRootDirectory("/home/user/.agent/memories")
.build(),
MessageChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder().maxMessages(100).build()).build(),
ToolCallAdvisor.builder().disableInternalConversationHistory().build())
.build();
记忆整理
长期运行后,记忆库会积累冗余、重叠或过时条目。AutoMemoryToolsAdvisor支持自动触发整理:
Instant lastInteraction = Instant.now();
AutoMemoryToolsAdvisor.builder()
.memoriesRootDirectory(memoryDir)
.memoryConsolidationTrigger((request, instant) -> {
// 超过60秒无交互时整理
if (instant.isAfter(lastInteraction.plusSeconds(60))) {
return true;
}
// 用户说bye时整理
var msg = request.prompt().getLastUserOrToolResponseMessage().getText();
return msg != null && msg.toLowerCase().contains("bye");
})
.build();
组合使用:构建企业级Agent的完整架构
六大模式并非独立使用,而是组合形成完整的Agent能力栈。以下是一个全功能代码助手Agent的配置示例:
@Configuration
public class EnterpriseAgentConfig {
@Value("${agent.memory.dir}")
String memoryDir;
@Bean
public ChatClient enterpriseAgent(ChatClient.Builder builder) {
// 1. Skills: 动态加载领域知识
var skillsTool = SkillsTool.builder()
.addSkillsDirectory(".claude/skills")
.build();
// 2. TodoWrite: 多步骤任务追踪
var todoTool = TodoWriteTool.builder()
.todoEventHandler(this::handleTodoEvent)
.build();
// 3. AskUserQuestion: 交互式澄清
var askTool = AskUserQuestionTool.builder()
.questionHandler(this::handleQuestions)
.build();
// 4. Subagents: 任务委派
var taskTools = TaskToolCallbackProvider.builder()
.chatClientBuilder("default", builder)
.subagentReferences(
ClaudeSubagentReferences.fromRootDirectory("src/main/resources/agents"))
.build();
// 5. AutoMemory: 跨会话记忆
var memoryAdvisor = AutoMemoryToolsAdvisor.builder()
.memoriesRootDirectory(memoryDir)
.memoryConsolidationTrigger(this::shouldConsolidate)
.build();
return builder
.defaultToolCallbacks(skillsTool, taskTools)
.defaultTools(todoTool, askTool,
FileSystemTools.builder().build(),
ShellTools.builder().build())
.defaultAdvisors(
memoryAdvisor,
ToolCallAdvisor.builder().conversationHistoryEnabled(false).build(),
MessageChatMemoryAdvisor.builder(
MessageWindowChatMemory.builder().maxMessages(100).build()).build())
.defaultSystem("""
You are an enterprise code assistant.
Use Skills for domain expertise.
Use TodoWrite for complex tasks.
Use Task tool to delegate to specialists.
Use AskUserQuestion when requirements are unclear.
""")
.build();
}
}
依赖版本矩阵
| 组件 | 版本 |
|---|---|
| Spring Boot | 3.2+ |
| Spring AI | 2.0.0-M2+ |
| spring-ai-agent-utils | 1.0.0+ |
| spring-ai-a2a-server-autoconfigure | 0.2.0+ |
| JDK | 17+ |
⚠️ 注意:Spring Boot 4.0尚未发布,当前稳定版本为3.4.x。spring-ai-agent-utils 1.0.0版本已正式发布,建议使用最新稳定版。
总结:从单次对话到持续智能体
Spring AI Agentic Patterns系列揭示了Agent架构的演进路径:
单次对话 → 多轮对话 → 任务执行 → 多Agent协作 → 跨平台协作 → 持续记忆
这套模式的核心价值在于:
- 模型无关性:所有工具支持OpenAI、Anthropic、Google Gemini等多供应商
- 可组合性:六大模式可独立使用,也可叠加组合
- 渐进式复杂度:从简单Skill开始,逐步添加Todo、Subagent、Memory
- 企业级就绪:通过Advisor机制集成Spring生态(安全、监控、配置)
对于Java开发者而言,spring-ai-agent-utils工具包提供了一个从Demo到生产的清晰路径。正如高质量代码的特征——可编译、可运行、易维护、有注释——好的Agent架构同样应该:逻辑自洽、执行可靠、结构清晰、意图明确。
资源汇总
官方资源
示例项目
| 示例 | 说明 |
|---|---|
| skills-demo | Agent Skills演示 |
| todo-demo | TodoWriteTool演示 |
| subagent-demo | Subagent编排演示 |
| memory-tools-advisor-demo | AutoMemory完整演示 |
系列原文
- Part 1: Agent Skills
- Part 2: AskUserQuestionTool
- Part 3: TodoWriteTool
- Part 4: Subagent Orchestration
- Part 5: A2A Integration
- Part 6: AutoMemoryTools
参考资料:Spring Blog官方Agentic Patterns系列,作者Christian Tzolov (Part 1-4,6)、Ilayaperumal Gopinathan (Part 5)