Spring AI Alibaba 1.x 系列【40】多智能体核心模式 - 智能体作为工具(Agent as Tool)

文章目录

  • [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 执行特定任务并返回结果。

执行流程:

  1. 控制器接收输入并决定调用哪个工具(子 Agent)
  2. 工具 Agent 根据控制器的指令运行其任务
  3. 工具 Agent 将结果返回给控制器
  4. 控制器决定下一步或完成任务

提示 :作为工具使用的 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

核心职责:

  1. 接收用户输入(行业方向),统一调度所有子 Agent;
  2. 按「选题→创作→润色→合规→排版」顺序调用工具;
  3. 处理子 Agent 返回结果(如合规检测失败,终止流程并提示);
  4. 汇总最终排版后的文案,返回给用户。
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 的工具化调用的。

核心流程:

  1. 业务创建 ReactAgent
  2. 调用 AgentTool.create(agent)
  3. 反射获取 executeAgent 方法
  4. 包装入参 JSON Schema
  5. 生成 Spring AI 标准 ToolDefinition
  6. 创建 AgentToolExecutor 实例
  7. 构建 MethodToolCallback 并返回
  8. 大模型发起工具调用 → 执行 executeAgent
  9. 解析入参 → 执行 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 工具在【工具执行节点】完成后,返回来当前也是工具响应对象:

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

相关推荐
小羊没烦恼!2 天前
微服务化的基石——持续集成
java·大数据·word·powerpoint·.net
回眸&啤酒鸭2 天前
【回眸】Minicart 电商购物车核心功能落地指南
人工智能
一隅论数智2 天前
给AI一张“业务概念地图“:本体如何从哲学走向企业智能
大数据·人工智能·经验分享·笔记·学习·学习方法·政务
AI的探索之旅2 天前
97 个 OpenCV 实例(三十):双目立体,从标定到点云
人工智能·opencv·计算机视觉
AlbertZein2 天前
Step-5-Preview 上手实测:3D 游戏、金融分析、网页设计一次跑完
人工智能·aigc
俊昭喜喜里2 天前
java中的继承和多态的区别
java
小羊没烦恼!2 天前
初探性能优化——2个月到4小时的性能提升
java·开发语言·windows·算法·c#
LaughingZhu2 天前
Product Hunt 每日热榜 | 2026-09-19
人工智能·深度学习·神经网络·搜索引擎·百度
譕痕2 天前
JSONObject与JSONArray封装数据格式区别
java·json