文章目录
- [1. 概述](#1. 概述)
- [2. 案例演示](#2. 案例演示)
-
- [2.1 创建子智能体](#2.1 创建子智能体)
-
- [2.1.1 选题 Agent(生成热点选题)](#2.1.1 选题 Agent(生成热点选题))
- [2.1.2 文案创作 Agent(基于选题写初稿)](#2.1.2 文案创作 Agent(基于选题写初稿))
- [2.1.3 内容润色 Agent(优化文案质感)](#2.1.3 内容润色 Agent(优化文案质感))
- [2.1.4 敏感词合规检测 Agent( 校验违规内容)](#2.1.4 敏感词合规检测 Agent( 校验违规内容))
- [2.1.5 格式排版 Agent( 适配平台格式)](#2.1.5 格式排版 Agent( 适配平台格式))
- [2.2 主管调度 Agent](#2.2 主管调度 Agent)
- [2.3 效果测试](#2.3 效果测试)
- [3. AgentTool](#3. AgentTool)
-
- [3.1 对外入口方法](#3.1 对外入口方法)
- [3.2 反射获取执行方法](#3.2 反射获取执行方法)
- [3.3 获取原始入参 Schema](#3.3 获取原始入参 Schema)
- [3.4 构建标准工具定义](#3.4 构建标准工具定义)
- [3.5 包装原始 Schema](#3.5 包装原始 Schema)
- [3.6 创建工具执行器实例](#3.6 创建工具执行器实例)
- [3.7 构建 MethodToolCallback](#3.7 构建 MethodToolCallback)
- [3.8 执行](#3.8 执行)
1. 概述
在 Agent as Tool 模式中,一个 Agent (控制器)将其他 Agent 视为工具(AgentTool),在需要时调用。控制器 Agent 管理编排,而工具 Agent 执行特定任务并返回结果。
执行流程:
- 控制器接收输入并决定调用哪个工具(子
Agent) - 工具
Agent根据控制器的指令运行其任务 - 工具
Agent将结果返回给控制器 - 控制器决定下一步或完成任务
提示 :作为工具使用的 Agent 通常不期望与用户继续对话。它们的角色是执行任务并将结果返回给控制器 Agent。如果需要子 Agent 能够与用户对话,请改用交接 (HandOff)模式。
2. 案例演示
实现「新媒体文案全流程自动化生产」,所有子 Agent 仅执行专项任务、返回结果,不与用户直接交互,全流程由主管 Agent 统一编排、汇总输出。
流程闭环:

2.1 创建子智能体
2.1.1 选题 Agent(生成热点选题)
核心职责 :基于行业方向,生成 3 个可落地的新媒体选题,包含选题标题、核心切入点、适配平台。
java
// 选题Agent(工具)
ReactAgent topicAgent = ReactAgent.builder()
.name("topic_agent")
.model(dashScopeChatModel)
.description("""
新媒体选题生成工具,专注于生成贴合热点、可落地的文案选题,
输出3个选项,包含选题标题、核心切入点、适配平台(公众号/小红书/抖音)
""")
.instruction("""
你是专业的新媒体选题专家,结合当前热点和用户给定的行业方向,生成3个优质选题。
要求:
1. 选题有话题性,易传播;
2. 每个选题包含「标题+核心切入点+适配平台」;
3. 语言简洁,不冗余;
4. 适配新媒体用户喜好,避免过于官方。
""")
.build();
2.1.2 文案创作 Agent(基于选题写初稿)
核心职责 :根据主管 Agent 选定的选题,生成完整文案初稿,适配对应平台文风。
java
// 文案创作Agent(工具)
ReactAgent writeAgent = ReactAgent.builder()
.name("write_agent")
.model(dashScopeChatModel)
.description("""
新媒体文案创作工具,根据给定的选题、平台,生成完整文案初稿,
贴合平台文风(公众号偏详细、小红书偏活泼、抖音偏简洁)
""")
.instruction("""
你是专业的新媒体文案写手,根据用户提供的「选题标题+核心切入点+适配平台」,生成完整文案初稿。
要求:
1. 贴合平台文风,不偏离选题;
2. 结构清晰,有开头、主体、结尾;
3. 语言流畅,符合新媒体用户阅读习惯;
4. 无需添加格式,仅输出纯文本初稿。
""")
.build();
2.1.3 内容润色 Agent(优化文案质感)
核心职责:打磨文案语句、调整语气、优化逻辑,提升可读性和传播性,不改变核心内容。
java
// 内容润色Agent(工具)
ReactAgent polishAgent = ReactAgent.builder()
.name("polish_agent")
.model(dashScopeChatModel)
.description("""
文案润色工具,优化文案语句流畅度、调整语气、优化逻辑,
提升可读性和传播性,不改变原文核心内容和平台适配性
""")
.instruction("""
你是专业的文案润色专家,对给定的新媒体文案初稿进行润色。
要求:
1. 修正语病、优化语句,让表达更流畅;
2. 调整语气,贴合对应平台文风(公众号温和详细、小红书活泼有网感、抖音简洁有力);
3. 优化段落逻辑,让内容更有层次感;
4. 不改变原文核心观点和内容;
5. 保留原文结构,不新增/删减核心信息。
""")
.build();
2.1.4 敏感词合规检测 Agent( 校验违规内容)
核心职责:检测文案中的敏感词、违规词、不合规表述,返回检测结果(是否通过、违规词)。
java
// 敏感词合规检测Agent(工具)
ReactAgent complianceAgent = ReactAgent.builder()
.name("compliance_agent")
.model(dashScopeChatModel)
.description("""
文案合规检测工具,检测文案中的敏感词、违规词、不合规表述。
必须返回明确结论:【合规通过】或【合规不通过】+ 原因
""")
.instruction("""
你是专业的文案合规检测专家,对给定文案进行全面的合规检测。
检测范围:
1. 政治敏感类:涉政人物、事件、机构的不当表述,国家主权相关问题
3. 低俗不良类:色情低俗、暴力血腥、封建迷信、歧视性内容
输出格式(必须严格遵守):
- 若文案合规,输出:【合规通过】文案无违规内容。
- 若文案不合规,输出:【合规不通过】发现以下违规问题:1) [具体违规词/表述] - [违规类型];2) ...
注意:检测要严格但不苛刻,正常商业表述不要过度拦截。
""")
.build();
2.1.5 格式排版 Agent( 适配平台格式)
核心职责:根据文案适配平台,添加对应格式(标题加粗、分段、表情、话题标签等),输出可直接发布的版本。
java
// 格式排版Agent(工具)
ReactAgent formatAgent = ReactAgent.builder()
.name("format_agent")
.model(dashScopeChatModel)
.description("""
文案格式排版工具,根据给定的文案和适配平台,添加对应格式
(标题、分段、表情、话题标签等),输出可直接发布的版本
""")
.instruction("""
你是专业的新媒体排版专家,根据文案和适配平台,进行格式优化。
要求:
1. 公众号:标题加粗(用##)、分段清晰、关键句子加粗、结尾添加引导语;
2. 小红书:标题吸睛(加emoji)、分段简短、每段1-2句话、结尾添加3-5个相关话题标签(#XXX);
3. 抖音:标题简短有力(加emoji)、分段极短、重点句子加粗、结尾添加互动引导;
4. 不改变文案核心内容,仅优化格式。输入格式:文案内容|适配平台。
""")
.build();
2.2 主管调度 Agent
核心职责:
- 接收用户输入(行业方向),统一调度所有子
Agent; - 按「选题→创作→润色→合规→排版」顺序调用工具;
- 处理子
Agent返回结果(如合规检测失败,终止流程并提示); - 汇总最终排版后的文案,返回给用户。
java
// 主管调度Agent(核心,集中式管控全流程)
ReactAgent supervisorAgent = ReactAgent.builder()
.name("supervisor_agent")
.model(dashScopeChatModel)
.description("""
新媒体文案生产主管,统一调度选题、创作、润色、合规检测、排版工具,
按流程完成文案生产,汇总最终结果返回用户,
不直接执行具体任务,仅负责调度和结果整合
""")
.instruction("""
你的职责是作为新媒体文案生产主管,按以下流程调度工具,完成文案生产:
1. 第一步:调用 topic_agent 工具,传入用户给定的行业方向,获取3个选题;
2. 第二步:从3个选题中选择最优1个(贴合热点、易传播、适配平台清晰),
调用 write_agent 工具,传入该选题信息,生成文案初稿;
3. 第三步:调用 polish_agent 工具,传入文案初稿,进行润色优化;
4. 第四步:调用 compliance_agent 工具,传入润色后的文案,进行合规检测;
- 若结果包含【合规通过】,执行下一步;
- 若结果包含【合规不通过】,终止流程,返回违规提示和具体违规问题;
5. 第五步:调用 format_agent 工具,传入润色后合规的文案 + 选题中的适配平台,进行格式排版;
6. 最终:汇总排版后的文案,返回给用户,同时附上选题说明和合规检测结果。
注意事项:
- 严格按顺序调用工具,不跳过任何步骤;
- 每个工具调用后,必须获取结果,再进行下一步;
- 工具返回的结果,仅提取核心信息,不冗余;
- 若任何工具调用失败,返回具体失败原因。
""")
.tools(
AgentTool.getFunctionToolCallback(topicAgent),
AgentTool.getFunctionToolCallback(writeAgent),
AgentTool.getFunctionToolCallback(polishAgent),
AgentTool.getFunctionToolCallback(complianceAgent),
AgentTool.getFunctionToolCallback(formatAgent)
)
.build();
2.3 效果测试
对话输入:
java
美妆行业,聚焦平价彩妆,面向18-25岁女生,贴合学生党需求
依次调用工具:

最终生成结果(仅测试):

3. AgentTool
Agent 转换为工具后,也是个 ToolCallback ,其加载、执行流程和 Tool Calling 那一套也是一样一样的,之前也都说过了。

所以,这里重点关注一下 AgentTool 是如何将 ReactAgent 封装为可调用的工具(ToolCallback),实现 Agent 的工具化调用的。
核心流程:
- 业务创建
ReactAgent - 调用
AgentTool.create(agent) - 反射获取
executeAgent方法 - 包装入参
JSON Schema - 生成
Spring AI标准ToolDefinition - 创建
AgentToolExecutor实例 - 构建
MethodToolCallback并返回 - 大模型发起工具调用 → 执行
executeAgent - 解析入参 → 执行
Agent图 → 返回助手消息
源码结构:
java
public class AgentTool {
/**
* 工具调用结果转换器
*/
private static final ToolCallResultConverter CONVERTER = new MessageToolCallResultConverter();
/**
* 获取 Agent 工具回调(快捷方法)
*/
public static ToolCallback getFunctionToolCallback(ReactAgent agent) {
//.........
}
/**
* 创建 Agent 工具回调
*/
public static ToolCallback create(ReactAgent agent) {
//.........
}
/**
* 包装参数 Schema
*/
private static String wrapSchemaInInputParameter(String originalSchema) {
//.........
}
/**
* Agent 工具执行器
*/
public static class AgentToolExecutor {
private final ReactAgent agent;
/**
* 构造方法
*/
public AgentToolExecutor(ReactAgent agent) {
//.........
}
/**
* 执行 Agent 逻辑
*/
public AssistantMessage executeAgent(String input, ToolContext toolContext) {
//.........
}
/**
* 提取入参值
*/
private String extractInputValue(String input) {
//.........
}
}
}
3.1 对外入口方法
对外提供的工具创建方法:
java
/**
* 获取 Agent 对应的函数工具回调(快捷入口)
* @param agent ReactAgent 实例
* @return 可直接注册给大模型的 Spring AI ToolCallback
*/
public static ToolCallback getFunctionToolCallback(ReactAgent agent) {
return AgentTool.create(agent);
}
核心方法源码:
java
/**
* 核心方法
*
* @param agent ReactAgent 实例
* @return Spring AI 标准工具回调
* @throws IllegalStateException 未找到 executeAgent 方法时抛出
*/
public static ToolCallback create(ReactAgent agent) {
// 反射获取执行方法,脱离 @Tool 注解依赖
java.lang.reflect.Method method = ReflectionUtils.findMethod(AgentToolExecutor.class,
"executeAgent", String.class, ToolContext.class);
if (method == null) {
throw new IllegalStateException("Could not find executeAgent method in AgentToolExecutor class");
}
// 获取原始入参 Schema(优先 inputSchema,其次根据 inputType 自动生成)
String originalSchema = StringUtils.hasLength(agent.getInputSchema())
? agent.getInputSchema()
: (agent.getInputType() != null)
? JsonSchemaGenerator.generateForType(agent.getInputType())
: null;
// 构建工具定义:名称、描述、入参格式
DefaultToolDefinition.Builder builder = ToolDefinitions.builder(method)
.name(agent.name())
.description(agent.description());
// 包装 Schema 为标准 input 格式
if (StringUtils.hasLength(originalSchema)) {
String wrappedInputSchema = wrapSchemaInInputParameter(originalSchema);
builder.inputSchema(wrappedInputSchema);
}
ToolDefinition toolDefinition = builder.build();
// 创建工具执行器
AgentToolExecutor executor = new AgentToolExecutor(agent);
// 构建并返回 Spring AI 标准工具回调
return MethodToolCallback.builder()
.toolDefinition(toolDefinition)
.toolMethod(method)
.toolObject(executor)
.toolCallResultConverter(CONVERTER)
.build();
}
3.2 反射获取执行方法
通过 Spring 提供的 ReflectionUtils 反射获取 AgentToolExecutor 的 executeAgent 方法对象:
java
java.lang.reflect.Method method = ReflectionUtils.findMethod(
AgentToolExecutor.class, // 要在哪个类里找方法
"executeAgent", // 要找的方法名字
String.class, // 方法第1个参数类型
ToolContext.class // 方法第2个参数类型
);

AgentToolExecutor 是 AgentTool 的内部类,定位为 Agent 工具执行器,负责真正执行 Agent 工具:
java
public static class AgentToolExecutor {
/**
* 持有的 ReactAgent 实例
*/
private final ReactAgent agent;
/**
* 构造方法
* @param agent ReactAgent 实例
*/
public AgentToolExecutor(ReactAgent agent) {
this.agent = agent;
}
/**
* 工具执行入口:反射调用
* <p>
* 执行流程:
* <ol>
* <li>解析AI传入的 input 参数</li>
* <li>构建指令消息 + 用户消息</li>
* <li>执行 Agent 图工作流</li>
* <li>返回最终助手消息</li>
* </ol>
*
* @param input 大模型传入的参数(JSON格式)
* @param toolContext Spring AI 自动注入的工具上下文(状态、配置等)
* @return Agent 执行结果(AssistantMessage)
*/
public AssistantMessage executeAgent(String input, ToolContext toolContext) {
// 提取真实用户输入
String actualInput = extractInputValue(input);
// 构建消息列表:指令 + 用户输入
List<Message> messagesToAdd = new ArrayList<>();
if (StringUtils.hasLength(agent.instruction())) {
messagesToAdd.add(AgentInstructionMessage.builder().text(agent.instruction()).build());
}
messagesToAdd.add(new UserMessage(actualInput));
// 执行 Agent 图
Optional<OverAllState> resultState = agent.getAndCompileGraph().invoke(Map.of("messages", messagesToAdd));
// 获取执行结果并返回最后一条助手消息
Optional<List> messages = resultState.flatMap(overAllState -> overAllState.value("messages", List.class));
if (messages.isPresent()) {
@SuppressWarnings("unchecked")
List<Message> messageList = (List<Message>) messages.get();
return (AssistantMessage) messageList.get(messageList.size() - 1);
}
throw new RuntimeException("Failed to execute agent tool or failed to get agent tool result");
}
/**
* 从 {"input": "实际内容"} 格式中提取真实入参
* <p>
* 兼容:标准JSON、非法JSON、纯字符串,解析失败直接返回原字符串。
*
* @param input 工具入参
* @return 提取后的实际输入
*/
private String extractInputValue(String input) {
if (!StringUtils.hasText(input)) {
return input;
}
try {
ObjectMapper objectMapper = JsonParser.getObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(input, new TypeReference<HashMap<String, Object>>() {});
if (jsonMap != null && jsonMap.containsKey("input")) {
Object inputValue = jsonMap.get("input");
if (inputValue != null) {
if (inputValue instanceof String) {
return (String) inputValue;
}
// 非字符串类型序列化为JSON
return JsonParser.getObjectMapper().writeValueAsString(inputValue);
}
}
}
catch (Exception e) {
// 解析失败直接使用原始输入
}
return input;
}
}
3.3 获取原始入参 Schema
在 ReacAgent 构建时支持定义 AI 智能体的入参格式:
inputSchema:用户手动写的JSON Schema(入参格式描述)inputType:用户传一个Java类,框架自动生成JSON Schema

inputSchema 示例:
java
{
"type": "object",
"properties": {
"query": { "type": "string" },
"userId": { "type": "string" }
},
"required": ["query"]
}
AgentTool 会先获取原始入参 Schema :
java
// 获取原始入参 Schema(优先 inputSchema,其次根据 inputType 自动生成)
String originalSchema = StringUtils.hasLength(agent.getInputSchema())
? agent.getInputSchema()
: (agent.getInputType() != null)
? JsonSchemaGenerator.generateForType(agent.getInputType())
: null;
3.4 构建标准工具定义
把 Agent 的名称、描述封装为 Spring AI 工具定义:
java
DefaultToolDefinition.Builder builder = ToolDefinitions.builder(method)
.name(agent.name())
.description(agent.description());
3.5 包装原始 Schema
将原始 Schema 包装为统一的入参格式,不管你传入什么 Schema,最终都会变成:
json
{
"type": "object",
"properties": {
"input": 【你的原始Schema】
},
"required": ["input"]
}
设计关键:
- 统一
AI工具入参格式:AI永远只需要读取input字段,不用关心用户传了什么。 - 格式永远合法:不会出现无效
JSON、无效Schema
AgentTool 中会先进行包装,然后设置给 ToolDefinition :
java
if (StringUtils.hasLength(originalSchema)) {
String wrappedInputSchema = wrapSchemaInInputParameter(originalSchema);
builder.inputSchema(wrappedInputSchema);
}
包装方法:
java
/**
* @param originalSchema 原始入参Schema
* @return 包装后的标准Schema字符串
*/
private static String wrapSchemaInInputParameter(String originalSchema) {
ObjectMapper objectMapper = JsonParser.getObjectMapper();
try {
// 解析原始Schema,解析失败则默认使用 string 类型
Map<String, Object> originalSchemaMap = null;
if (StringUtils.hasLength(originalSchema)) {
try {
originalSchemaMap = objectMapper.readValue(originalSchema, new TypeReference<HashMap<String, Object>>() {});
}
catch (Exception e) {
originalSchemaMap = Map.of("type", "string");
}
}
else {
originalSchemaMap = Map.of("type", "string");
}
// 构建标准包装结构
Map<String, Object> wrappedSchema = new HashMap<>();
wrappedSchema.put("type", "object");
Map<String, Object> properties = new HashMap<>();
properties.put("input", originalSchemaMap);
wrappedSchema.put("properties", properties);
wrappedSchema.put("required", List.of("input"));
return objectMapper.writeValueAsString(wrappedSchema);
}
catch (Exception e) {
// 异常降级:返回最简可用Schema
return String.format("""
{
"type": "object",
"properties": {
"input": {
"type": "string"
}
},
"required": ["input"]
}
""");
}
}
示例,传入:
json
{
"type": "object",
"properties": {
"username": { "type": "string" },
"age": { "type": "number" }
}
}
包装后:
json
{
"type": "object",
"properties": {
"input": {
"type": "object",
"properties": {
"username": { "type": "string" },
"age": { "type": "number" }
}
}
},
"required": ["input"]
}
3.6 创建工具执行器实例
调用 AgentToolExecutor 构造函数,传入 Agent 实例对象:
java
AgentToolExecutor executor = new AgentToolExecutor(agent);
public AgentToolExecutor(ReactAgent agent) {
this.agent = agent;
}
3.7 构建 MethodToolCallback
构建方法型工具并返回:
java
// Build MethodToolCallback
return MethodToolCallback.builder()
.toolDefinition(toolDefinition)
.toolMethod(method)
.toolObject(executor)
.toolCallResultConverter(CONVERTER) // 结果转换器
.build();
3.8 执行
工具绑定的 Method 对象是 AgentToolExecutor#executeAgent 方法,所以工具反射执行时,也就进入到了这里。
executeAgent 是工具执行入口,执行流程:
- 解析
AI传入的input参数 - 构建指令消息 + 用户消息
- 执行
Agent图工作流 - 返回最终助手消息
源码如下:
java
/**
* 工具执行入口:反射调用
*
* @param input 大模型传入的参数(JSON格式)
* @param toolContext Spring AI 自动注入的工具上下文(状态、配置等)
* @return Agent 执行结果(AssistantMessage)
*/
public AssistantMessage executeAgent(String input, ToolContext toolContext) {
// 提取真实用户输入
String actualInput = extractInputValue(input);
// 构建消息列表:指令 + 用户输入
List<Message> messagesToAdd = new ArrayList<>();
if (StringUtils.hasLength(agent.instruction())) {
messagesToAdd.add(AgentInstructionMessage.builder().text(agent.instruction()).build());
}
messagesToAdd.add(new UserMessage(actualInput));
// 执行 Agent 图
Optional<OverAllState> resultState = agent.getAndCompileGraph().invoke(Map.of("messages", messagesToAdd));
// 获取执行结果并返回最后一条助手消息
Optional<List> messages = resultState.flatMap(overAllState -> overAllState.value("messages", List.class));
if (messages.isPresent()) {
@SuppressWarnings("unchecked")
List<Message> messageList = (List<Message>) messages.get();
return (AssistantMessage) messageList.get(messageList.size() - 1);
}
throw new RuntimeException("Failed to execute agent tool or failed to get agent tool result");
}
/**
* 从 {"input": "实际内容"} 格式中提取真实入参
* <p>
* 兼容:标准JSON、非法JSON、纯字符串,解析失败直接返回原字符串。
*
* @param input 工具入参
* @return 提取后的实际输入
*/
private String extractInputValue(String input) {
if (!StringUtils.hasText(input)) {
return input;
}
try {
ObjectMapper objectMapper = JsonParser.getObjectMapper();
Map<String, Object> jsonMap = objectMapper.readValue(input, new TypeReference<HashMap<String, Object>>() {});
if (jsonMap != null && jsonMap.containsKey("input")) {
Object inputValue = jsonMap.get("input");
if (inputValue != null) {
if (inputValue instanceof String) {
return (String) inputValue;
}
// 非字符串类型序列化为JSON
return JsonParser.getObjectMapper().writeValueAsString(inputValue);
}
}
}
catch (Exception e) {
// 解析失败直接使用原始输入
}
return input;
}
因为配置了 MessageToolCallResultConverter 工具调用结果转换器,还会将工具执行后的任意返回值,转换为 AI 模型能识别的字符串格式(纯文本 / JSON):
java
public class MessageToolCallResultConverter implements ToolCallResultConverter {
private static final Logger logger = LoggerFactory.getLogger(MessageToolCallResultConverter.class);
/**
* 转换工具执行结果
*
* 说明:
* 由于 Spring AI 的 ToolResponseMessage 目前只支持文本类型,
* 所以本方法统一返回 String,未来可扩展支持图片/音频/文件等类型。
*
* @param result 工具执行的原始结果
* @param returnType 工具方法的返回类型
* @return 转换后的字符串结果(文本或JSON)
*/
public String convert(@Nullable Object result, @Nullable Type returnType) {
// 1. 如果工具返回类型是 void(无返回值)
if (returnType == Void.TYPE) {
logger.debug("工具无返回值,转换为默认响应:Done");
return JsonParser.toJson("Done");
}
// 2. 如果工具返回的是 AI 助手消息(AssistantMessage)
else if (result instanceof AssistantMessage assistantMessage) {
// 优先取消息文本内容
if (StringUtils.hasLength(assistantMessage.getText())) {
return assistantMessage.getText();
}
// 如果消息包含媒体资源(图片/音频等)→ 暂不支持
else if (CollectionUtils.isNotEmpty(assistantMessage.getMedia())) {
throw new UnsupportedOperationException(
"当前 Spring AI ToolResponseMessage 仅支持文本类型," +
"图片/音频/视频/文件类型将在未来支持。"
);
}
// 空消息 → 返回默认完成标识
logger.warn("工具返回了空的 AssistantMessage,转换为默认响应:Done");
return JsonParser.toJson("Done");
}
// 3. 其他所有类型(对象、List、Map、字符串等)→ 转为 JSON
else {
logger.debug("将工具结果转为 JSON 格式");
return JsonParser.toJson(result);
}
}
}
最终,子 Agent 工具在【工具执行节点】完成后,返回来当前也是工具响应对象:

工具执行结果返回给【模型执行节点】,直到无需工具调用后,整个流程结束。