ARTICLE · 1232630
[AI工程] Spring AI 第十五篇(Spring AI Agent Utils): Claude Code 的那套工具箱,被人在 Java 里重写了一遍
💡 这两年用惯了 AI 编程工具的人会有一种共识:真正让 Agent 好用的不是模型,是它手里那套工具——能读文件、能跑命令、能 grep、能把复杂任务拆成 todo、能在需要时反问一句。
问题来了:这些能力在 Claude Code、Qoder 这类工具里是内置的,但当你用自己的 Java 服务做一个"能干活"的 Agent 时,全得从零写:进程管理、超时、输出截断、路径越权防护、子智能体委派、跨会话记忆……写到最后你会发现自己在重造一个小型 IDE。
Spring AI Agent Utils 就是冲这件事来的:它是 Spring AI 社区(org.springaicommunity)下的一个 incubating 项目,把 Claude Code 的核心工具与 Agent Skills 机制用 Spring AI 重写了一遍,作为一组 @Tool 直接挂到 ChatClient.defaultTools(...) 上。注:写这篇时最新版本是 0.12.0(2026-08-30 发布),Apache 2.0 许可,仍处于孵化阶段。
这一篇按"它有什么 → 怎么接 → 哪三样组合才真正产生 Agentic 行为 → 安全边界在哪 → 什么时候该用它"来盘,顺便和第十五篇讲的 Agent 五种模式、以及 Spring AI Alibaba 的 Agent Framework 做个分工对照。
下面开启本篇: [AI工程] 玩转 Spring AI Agent Utils:Claude Code 的那套工具箱,被人在 Java 里重写了一遍
1. 先说清楚它是什么、不是什么
一句话:把 Claude Code 的工具面板,翻译成 Spring AI 的工具 Bean。
1.1 基本盘
org.springaicommunity:spring-ai-agent-utilsspring-ai-agent-utils-bom) | |
| 0.12.0 | |
一个小提醒:README 的 Quick Start 里有句 “You need Spring AI version `` or later”,版本号是个空占位(写这篇时如此)。以 Requirements 段写的
2.0.0 or later为准。
1.2 模块划分
spring-ai-agent-utils-common 子智能体 SPI:SubagentDefinition / SubagentResolver / SubagentExecutor / SubagentTypespring-ai-agent-utils 核心库:工具、Advisor、技能、Claude 子智能体实现spring-ai-agent-utils-a2a A2A 协议子智能体(远程 Agent 编排)spring-ai-agent-utils-bom 统一版本管理examples/ 9 个可运行 Demo(code-agent / ask-user-question / skills / subagent / subagent-a2a / todo / memory x3)
Q1:它和 Spring AI 官方是什么关系?
社区项目,不在 org.springframework.ai 坐标下,也不承诺跟随官方发布节奏。这决定了使用姿势:把它当"高质量参考实现 + 可用的积木",而不是平台依赖。0.x 阶段 API 会变,锁版本并在升级时过一遍回归。
Q2:和 Anthropic 官方 SDK 有什么区别?
这里没有任何 Claude 专有协议——工具是普通 Spring AI @Tool,模型可以是 Spring AI Alibaba 接的千问、DeepSeek、OpenAI、Gemini,任选。它复刻的是 Claude Code 的能力形状(工具集 + 提示词约定),不是它的运行时。
2. 工具体系全景
分五组看,一次记住。
2.1 核心工具(任何 Agentic 行为的地基)
AgentEnvironment | |||
FileSystemTools | allowedDirectory(...) 收口 | ||
ShellTools | bash_id、stdout/stderr 分离、正则过滤输出、输出超 30000 字符截断 | ||
GrepTool | |||
GlobTool | |||
SmartWebFetchTool | |||
BraveWebSearchToolBraveWebFetchTool |
2.2 另外四组
AskUserQuestionTool | header 最多 12 字符、支持单选/多选与自由文本;只能主 Agent 用,子智能体不能与用户交互 | ||
SkillsTool | |||
AutoMemoryToolsAutoMemoryToolsAdvisor | MemoryView / MemoryCreate / MemoryStrReplace / MemoryInsert / MemoryDelete / MemoryRename);沙箱目录;MEMORY.md 索引;自带配套 system prompt | ||
TodoWriteToolTaskTool | in_progress;Task 支持多模型路由与可插拔后端 |
一句话理解:核心工具给手,Skills 给知识,Memory 给记性,Task/Todo 给组织度——四样凑齐才像个能干活的 Agent。
3. 十分钟接一个能干活的最小 Agent
代码基于 README 的 Quick Start 改写,去掉了演示噪音。
3.1 依赖
<dependencyManagement><dependencies><dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-agent-utils-bom</artifactId><version>0.12.0</version><type>pom</type><scope>import</scope></dependency></dependencies></dependencyManagement><dependencies><dependency><groupId>org.springaicommunity</groupId><artifactId>spring-ai-agent-utils</artifactId></dependency></dependencies>
3.2 装配
@BeanChatClient codeAgent(ChatClient.Builder builder, // 注入 Builder,别 ChatClient.builder(chatModel)@Value("{agent.skills.paths}") List<Resource> skillPaths,@Value("classpath:/prompt/MAIN_AGENT_SYSTEM_PROMPT_V2.md") Resource systemPrompt) {var taskTool = TaskTool.builder().subagentTypes(ClaudeSubagentType.builder().chatClientBuilder("default", builder.clone()).skillsResources(skillPaths).braveApiKey(braveApiKey).build()).build();return builder.defaultSystem(p -> p.text(systemPrompt).param(AgentEnvironment.ENVIRONMENT_INFO_KEY, AgentEnvironment.info()).param(AgentEnvironment.GIT_STATUS_KEY, AgentEnvironment.gitStatus())).defaultTools(taskTool,SkillsTool.builder().addSkillsResources(skillPaths).build(),FileSystemTools.builder().allowedDirectory(Path.of("/workspace/project")).build(), // ← 收口ShellTools.builder().build(),GrepTool.builder().build(),GlobTool.builder().build(),SmartWebFetchTool.builder(builder.clone().build()).build(),BraveWebSearchTool.builder(braveApiKey).build(),TodoWriteTool.builder().build(),AskUserQuestionTool.builder().questionHandler(new CommandLineQuestionHandler()) // CLI 现成实现.build()).defaultAdvisors(MessageChatMemoryAdvisor.builder(MessageWindowChatMemory.builder().maxMessages(500).build()).build()).build();}
四个必看的点:
defaultTools(Object...)——这正好是第十一篇 6.2 强调的 2.0 姿势(字符串 bean 名已失效) | |
MAIN_AGENT_SYSTEM_PROMPT_V2.mdAUTO_MEMORY_TOOLS_SYSTEM_PROMPT.md 都打进 jar 的 classpath:/prompt/。只挂工具不给提示词,模型大概率不会用 | |
ToolCallAdvisor | @Deprecated(forRemoval = true) 并继承自 ToolCallingAdvisor,新写用后者,conversationHistoryEnabled(false) 仍然可用 |
ChatClient.builder(chatModel)。这个静态工厂在 2.0.1 里给的 ObservationRegistry 是 NOOP,于是工具调用、模型调用一个 span 都不上报(第十四篇 2.2 展开过)。生产装配请注入 ChatClient.Builder,上面那段代码就是这么改的 |
Q:为什么 TodoWriteTool 要配 conversationHistoryEnabled(false)?
它的官方示例是 ToolCallAdvisor.builder().conversationHistoryEnabled(false) + MessageChatMemoryAdvisor 一起用。原因是 todo 更新靠递归工具调用推进,工具循环内部再自带一份会话历史,会和 memory advisor 的历史重叠、把上下文撑爆。这个技巧对任何"工具会连续多轮"的场景都成立(第十一篇 7.2 讨论过工具循环与 memory 的边界)。
4. Skills、子智能体、A2A:把能力做成外置资产
这一块是我最看好的部分——它把"教 Agent 做事"从代码里搬到了 Markdown。
4.1 Skill 的形状
---name: ai-tutordescription: 用户要求讲解技术概念时使用;能产出带引用来源的教程 PDF---# AI Tutor## Instructions1. 先确认受众水平与目标篇幅2. 需要实时资料时调用 BraveWebSearchTool / SmartWebFetchTool3. 按"概念 → 最小例子 → 常见误区 → 进阶"四段写4. 用 ShellTools 跑辅助脚本,最后生成 PDF
运行机制是三段:启动只登记 name/description → 语义命中才加载全文 → Agent 按全文执行。所以技能可以写得很长很细,不常驻上下文,这是它比"塞进 system prompt"省 Token 的根本原因。技能目录放 src/main/resources/.claude/skills/ 就能随包发布,也能从依赖 JAR 里加载——这意味着技能可以像 SDK 一样被版本化分发。
4.2 子智能体的价值不是"多几个模型"
model 字段决定用哪个模型,贵的只用在关键节点 | |
子智能体用带 YAML front-matter 的 Markdown 定义(例如 agents/spring-ai-expert.md),TaskTool 通过可插拔的 resolver / executor 加载与执行。
4.3 A2A:把"子智能体"扩到跨进程
A2ASubagentDefinition | -a2a | SubagentDefinition |
A2ASubagentResolver | -a2a | /.well-known/agent-card.json 拉元数据 |
A2ASubagentExecutor | -a2a |
编排器启动时自动发现远程 Agent,并让它和本地 Claude 子智能体一起进入可委派列表。这和第九篇讲的 MCP 是互补的:MCP 统一"工具"的接入,A2A 统一"另一个 Agent"的接入。
5. 跨会话记忆:同一个模式,三条实现路线
三个 demo 演示的是同一件事,差别在"给 Agent 哪套工具"。
5.1 记忆与会话历史的分工
存取的约定是一成不变的两步:写一个带 front-matter 的 typed .md 文件 → 往 MEMORY.md 追加一行指针;下次会话先读索引,再选择性加载相关条目。类型就四种:user(背景/偏好)、feedback(纠正与确认)、project(决策/约束/截止)、reference(外部系统指针)。
# user_role.md —— 步骤 1 写出的记忆文件---name: user-roledescription: 后端出身、正在补 AI 工程侧,解释概念时要类比后端已有知识type: user---内容本体。**Why:** 记下的原因(未来判断这条还成不成立要用)**How to apply:** 这条建议在什么场景生效
# MEMORY.md —— 步骤 2 追加的指针- [User role](user_role.md) — 后端出身,讲概念先类比后端已有知识(≤150 字符)
索引的约束很硬:一条记忆一行、约 150 字符内,MEMORY.md 超过 200 行之后的内容会被截断。所以它必然需要一个"整理"动作——这正是 5.2 里 memoryConsolidationTrigger 存在的理由,别把它当锦上添花。
5.2 三条路线怎么选
AutoMemoryTools | MemoryCreate / MemoryView)和隔离 | |||
FileSystemToolsShellTools | allowedDirectory 收口) | |||
AutoMemoryToolsAdvisor | memoryConsolidationTrigger 定期让模型总结清理 |
我的判断:教学与可控性选第一种,真实编程助手类应用选第二种(少一套工具,行为更统一),第三种适合你已经在用 Advisor 编排的团队。
选第三种时有个顺序细节要对上:AutoMemoryToolsAdvisor.builder() 的默认 order 是 HIGHEST_PRECEDENCE + 200,和 MessageChatMemoryAdvisor 的默认值完全一样。按第十一篇 7.1 那条规则(order 相同则相对顺序不保证),这两者谁先写上下文是随机的;长期记忆该压在短期历史上还是反过来,得自己显式给值。它排在 ToolCallingAdvisor(+300)外侧是有意的——长期记忆每次请求只注入一次,不会在工具循环里重复读盘。另外文档里还有个 AutoDreamAdvisor,做的是请求之外的异步整理,可以理解为 memoryConsolidationTrigger 的后台版。
抄代码时注意一个拼写:注入记忆根目录的 prompt 参数名是
MEMORIES_ROOT_DIERCTORY(文档里就是这个拼错的写法)。你以为改成DIRECTORY更干净,替换就悄悄失效了。
6. Demo 导览:按你想解决的问题挑一个跑
9 个 Demo,我建议的打开顺序如下。
code-agent-demo | |||
todo-demo | TodoWriteTool | ||
memory/memory-tools-demo | MEMORY.md 索引 | ||
skills-demo | ai-tutor | ||
subagent-demo | TaskTool 调度 | ||
ask-user-question-demo | QuestionHandler、单选/多选 | ||
memory/memory-filesystem-tools-demo | |||
subagent-a2a-demo | |||
memory/memory-tools-advisor-demo |
前置条件统一:Java 17+、Maven 3.6+、至少一个模型 Key(Anthropic / OpenAI / Google),Web 搜索要 BRAVE_API_KEY。跑法就是 cd examples/xxx && mvn spring-boot:run。
7. 安全边界、与官方编排的分工,以及别用它的场景
7.1 这套工具的能力越强,越要把三件事配齐
FileSystemTools.builder().allowedDirectory(...).workspace(Workspace.local(...)) | .. 直接拒;规范化后必须落在允许目录内(按路径分量比较,/allowed-evil 骗不过去);符号链接解析真实路径、悬空链接一律拒 | |
ShellTools | spring-ai-agent-utils-docker-cli(Docker ExecBackend),把 Agent 的 shell 跑在沙箱容器里 | |
ToolContext、写操作在服务层做归属校验、AskUserQuestionTool 让高危动作显式确认 |
还有一点容易被忽略:成本。SmartWebFetchTool 内部还要再调一次模型做摘要,TaskTool 每委派一次就是一个新上下文。这些在监控上都表现为"单次提问模型调用数 > 1"(第十四篇 4 节那条派生指标)。
7.2 和 Spring AI Alibaba Agent Framework 怎么分工
工具集@Tool + 提示词约定) | 编排层SequentialAgent / ParallelAgent / LlmRoutingAgent / SupervisorAgent / LoopAgent / ReactAgent) | |
spring-ai-alibaba-bom | ||
7.3 什么时候不该用它
allowedDirectory 挡住 Shell | |
@Tool 自己写更快,读它源码当参考就行 |
7.4 知识脉络图
spring-ai-agent-utils(0.12.0, incubating, Apache 2.0)├── 核心工具:AgentEnvironment / FileSystemTools / ShellTools / GrepTool / GlobTool / SmartWebFetch / BraveWebSearch├── 交互:AskUserQuestionTool(主 Agent 专用,2~4 选项、1~10 题、header ≤12 字符)├── 知识外置:SkillsTool(Markdown+front-matter;启动登记描述,命中才加载全文;可从 JAR 分发)├── 记忆:AutoMemoryTools(6 操作 + 沙箱 + MEMORY.md)|FileSystemTools 通用方案|AutoMemoryToolsAdvisor(自动接线 + 合并触发)├── 编排:TodoWriteTool(唯一 in_progress,配 conversationHistoryEnabled(false))|TaskTool + ClaudeSubagentType(独立上下文/工具白名单/多模型)├── 跨进程:A2A(AgentCard + /.well-known + JSON-RPC)├── 隔离:allowedDirectory 三层防护 / Shell 超时与截断 / docker-cli ExecBackend└── 装配:BOM + defaultTools(Object...) + classpath:/prompt/*.md(提示词是能力的一部分)
最后总结
如果你想给 Java 服务加"能干活的 Agent":直接看 code-agent-demo,它把工具集、MCP、多模型、CLI 装配串成了一遍,比逐个读文档快。如果你只缺一两个能力: TodoWriteTool(让计划可见)和SkillsTool(把领域知识从代码里搬出来)是投入产出比最高的两个,其余先别引。如果你想做长期记忆:三种实现里我偏向 AutoMemoryTools——沙箱 + 显式语义在多人协作时更不容易出事;要统一文件系统访问再换FileSystemTools方案。如果你担心安全:把它当"能力放大器"看, allowedDirectory/ Shell 超时 / 容器后端三件套必须配,且高危动作要有用户确认。对后端 / 架构开发者,我更看重它的一点是:它证明了 Claude Code 的能力可以用一组普通工具 + 一份好提示词复现。这套形状(工具白名单 + 渐进披露的技能 + 文件型记忆 + 显式 todo)值得直接抄进你自己的 Agent 设计里,即便你一行它的代码都不引。
一句话结论:
spring-ai-agent-utils现在最有价值的地方,不是让你少写代码,而是它把"一个能干活的 Agent 该有哪些部件"讲清楚了一个 Java 版本。
参考资料 & 致谢
[1] Spring-AI-Agent-Utils 笔记 - 语雀
[2] spring-ai-agent-utils - GitHub 仓库
[3] Spring AI Agent Utils 官方文档
[4] FileSystemTools 文档(allowedDirectory 与三层防护)
[5] ShellTools 文档(超时、后台进程、输出过滤)
[6] AutoMemoryTools 文档
[7] AutoMemoryToolsAdvisor 文档(order 默认值与 memoryConsolidationTrigger)
[8] Task Tools / 子智能体文档
[9] Claude Code - 官网(工具与 Skills 的出处)
[10] Spring AI Reference
[11] Maven Central - org.springaicommunity:spring-ai-agent-utils















































































