Spring AI Alibaba 1.x 系列【5】ReactAgent 构建器深度源码解析

文章目录

  • [1. 前言](#1. 前言)
  • [2. 抽象类 Builder](#2. 抽象类 Builder)
    • [2.1 基础配置](#2.1 基础配置)
    • [2.2 工具配置](#2.2 工具配置)
    • [2.3 AI 模型配置](#2.3 AI 模型配置)
    • [2.4 钩子与拦截器配置](#2.4 钩子与拦截器配置)
    • [2.5 输出配置](#2.5 输出配置)
    • [2.6 监控与日志配置](#2.6 监控与日志配置)
    • [2.7 异步执行](#2.7 异步执行)
    • [2.8 抽象构建方法](#2.8 抽象构建方法)
    • [2.9 其他](#2.9 其他)
  • [3. 默认实现类 DefaultBuilder](#3. 默认实现类 DefaultBuilder)
    • [3.1 参数校验](#3.1 参数校验)
    • [3.2 配置合并](#3.2 配置合并)
    • [3.3 构建对话客户端](#3.3 构建对话客户端)
    • [3.4 构建 LLM 节点(AgentLlmNode)](#3.4 构建 LLM 节点(AgentLlmNode))
    • [3.5 拦截器分类](#3.5 拦截器分类)
    • [3.6 工具收集与合并](#3.6 工具收集与合并)
    • [3.7 构建工具节点(AgentToolNode)](#3.7 构建工具节点(AgentToolNode))
    • [3.8 实例化 ReactAgent 并返回](#3.8 实例化 ReactAgent 并返回)

1. 前言

ReactAgentSpring AI Alibaba 中具备自主决策、工具调用能力的核心组件,其内部涉及大量配置项,比如大语言模型(LLM)配置、工具集合、钩子(Hook)与拦截器、日志与监控、异步执行策略等。

如果直接通过构造器传入所有参数,会导致代码冗余、可读性差、配置灵活度低,且难以应对不同场景下的个性化需求。所以提供了 Builder 模式,通过链式调用的方式,将复杂对象的构建过程拆解为多个独立的配置步骤,既保证了配置的灵活性,又确保了构建过程的规范性。

接下来我们详细介绍下抽象类 Builder (抽象构建器)与实现类 DefaultBuilder(默认构建器) ,前者定义配置规范,后者实现具体构建逻辑,二者协同完成 ReactAgent 的全流程构建。

2. 抽象类 Builder

Builder 作为抽象基类,核心作用是定义 ReactAgent 所有可配置项的规范,提供统一的链式调用 API

2.1 基础配置

负责配置智能体的基础标识与行为约束 ,包括智能体名称(name)、描述(description)、核心指令(instruction)、系统提示词(systemPrompt)等。其中,名称是必填项,用于唯一标识智能体,系统提示词则用于约束 LLM 的推理行为。

相关配置字段:

java 复制代码
	/** 智能体名称 */
	protected String name;
	
	/** 智能体描述 */
	protected String description;

	/** 智能体指令(核心行为约束) */
	protected String instruction;

	/** 系统提示词 */
	protected String systemPrompt;

	/** 自定义模板渲染器(如自定义分隔符) */
	protected TemplateRenderer templateRenderer;

链式配置方法:

java 复制代码
public Builder name(String name) {
    this.name = name;
    return this;
}

public Builder description(String description) {
    this.description = description;
    return this;
}

public Builder instruction(String instruction) {
    this.instruction = instruction;
    return this;
}

public Builder systemPrompt(String systemPrompt) {
    this.systemPrompt = systemPrompt;
    return this;
}

2.2 工具配置

工具是 Agent 实现外部交互的核心,该模块支持多种工具配置方式,满足不同场景下的工具管理需求:

  • 直接传入 ToolCallback 工具实例
  • 通过 ToolCallbackProvider 批量获取工具
  • 通过工具名称(toolNames)解析工具
  • 同时支持配置工具解析器
  • 工具执行异常处理器

相关配置字段:

java 复制代码
    /** 工具回调集合(直接配置) */
	protected List<ToolCallback> tools = new ArrayList<>();

	/** 工具回调提供者集合 */
	protected List<ToolCallbackProvider> toolCallbackProviders = new ArrayList<>();

	/** 工具名称集合(通过名称解析工具) */
	protected List<String> toolNames = new ArrayList<>();

	/** 工具回调解析器 */
	protected ToolCallbackResolver resolver;

	/** 工具执行异常处理器 */
	protected ToolExecutionExceptionProcessor toolExecutionExceptionProcessor;

	/** 工具上下文参数 */
	protected Map<String, Object> toolContext = new HashMap<>();

链式配置方法:

java 复制代码
	/**
	 * 设置工具回调集合
	 */
	public Builder tools(List<ToolCallback> tools) {
		Assert.notNull(tools, "tools cannot be null");
		Assert.noNullElements(tools, "tools cannot contain null elements");
		this.tools.addAll(tools);
		return this;
	}

	/**
	 * 设置工具回调(可变参数)
	 */
	public Builder tools(ToolCallback... tools) {
		Assert.notNull(tools, "tools cannot be null");
		Assert.noNullElements(tools, "tools cannot contain null elements");
		this.tools.addAll(List.of(tools));
		return this;
	}

	/**
	 * 通过对象方法注册工具
	 */
	public Builder methodTools(Object... toolObjects) {
		Assert.notNull(toolObjects, "toolObjects cannot be null");
		Assert.noNullElements(toolObjects, "toolObjects cannot contain null elements");
		this.tools.addAll(Arrays.asList(ToolCallbacks.from(toolObjects)));
		return this;
	}

	/**
	 * 设置工具回调提供者
	 */
	public Builder toolCallbackProviders(ToolCallbackProvider... toolCallbackProviders) {
		Assert.notNull(toolCallbackProviders, "toolCallbackProviders cannot be null");
		Assert.noNullElements(toolCallbackProviders, "toolCallbackProviders cannot contain null elements");
		this.toolCallbackProviders.addAll(List.of(toolCallbackProviders));
		return this;
	}

	/**
	 * 通过工具名称注册工具
	 */
	public Builder toolNames(String... toolNames) {
		Assert.notNull(toolNames, "toolNames cannot be null");
		Assert.noNullElements(toolNames, "toolNames cannot contain null elements");
		this.toolNames.addAll(List.of(toolNames));
		return this;
	}

	/**
	 * 设置工具回调解析器
	 */
	public Builder resolver(ToolCallbackResolver resolver) {
		this.resolver = resolver;
		return this;
	}

	/**
	 * 设置工具执行异常处理器
	 */
	public Builder toolExecutionExceptionProcessor(ToolExecutionExceptionProcessor toolExecutionExceptionProcessor) {
		this.toolExecutionExceptionProcessor = toolExecutionExceptionProcessor;
		return this;
	}

	/**
	 * 设置工具上下文参数
	 */
	public Builder toolContext(Map<String, Object> toolContext) {
		Assert.notNull(toolContext, "toolContext cannot be null");
		Assert.noNullElements(toolContext.keySet(), "toolContext keys cannot contain null elements");
		Assert.noNullElements(toolContext.values(), "toolContext values cannot contain null elements");
		this.toolContext.putAll(toolContext);
		return this;
	}

2.3 AI 模型配置

配置大语言模型相关组件,包括 ChatModel(大语言模型实例)、ChatClient(聊天客户端)、ChatOptions(模型聊天参数)。

支持两种配置方式:

  • 直接配置 ChatClient(已废弃,推荐使用 ChatModel
  • 配置 ChatModel 后自动构建 ChatClient ,适配 Spring AI 的生态规范

相关配置字段:

java 复制代码
protected ChatModel model;
protected ChatOptions chatOptions;
protected ChatClient chatClient;

链式配置方法:

java 复制代码
	/**
	 * 设置聊天客户端(已废弃)
	 * @deprecated 推荐使用 model() 方法
	 */
	@Deprecated
	public Builder chatClient(ChatClient chatClient) {
		this.chatClient = chatClient;
		return this;
	}

	/**
	 * 设置大语言模型
	 */
	public Builder model(ChatModel model) {
		this.model = model;
		return this;
	}

	/**
	 * 设置模型聊天配置
	 */
	public Builder chatOptions(ChatOptions chatOptions) {
		this.chatOptions = chatOptions;
		return this;
	}

2.4 钩子与拦截器配置

用于扩展智能体的执行流程,包括钩子(hooks)和拦截器(interceptors):

  • 钩子用于在智能体执行的关键节点插入自定义逻辑
  • 拦截器则分为模型拦截器(modelInterceptors)和工具拦截器(toolInterceptors),分别用于拦截 LLM 推理和工具调用过程,实现日志记录、参数修改等扩展功能。

钩子与拦截器配置字段:

java 复制代码
	/** 钩子集合 */
	protected List<Hook> hooks = new ArrayList<>();

	/** 通用拦截器集合 */
	protected List<Interceptor> interceptors = new ArrayList<>();

	/** 模型拦截器集合 */
	protected List<ModelInterceptor> modelInterceptors = new ArrayList<>();

	/** 工具拦截器集合 */
	protected List<ToolInterceptor> toolInterceptors = new ArrayList<>();

链式配置方法:

java 复制代码
	/**
	 * 设置钩子集合
	 */
	public Builder hooks(List<? extends Hook> hooks) {
		Assert.notNull(hooks, "hooks cannot be null");
		Assert.noNullElements(hooks, "hooks cannot contain null elements");
		this.hooks.addAll(hooks);
		return this;
	}

	/**
	 * 设置钩子(可变参数)
	 */
	public Builder hooks(Hook... hooks) {
		Assert.notNull(hooks, "hooks cannot be null");
		Assert.noNullElements(hooks, "hooks cannot contain null elements");
		this.hooks.addAll(List.of(hooks));
		return this;
	}

	/**
	 * 设置拦截器集合
	 */
	public Builder interceptors(List<? extends Interceptor> interceptors) {
		Assert.notNull(interceptors, "interceptors cannot be null");
		Assert.noNullElements(interceptors, "interceptors cannot contain null elements");
		this.interceptors.addAll(interceptors);
		return this;
	}

	/**
	 * 设置拦截器(可变参数)
	 */
	public Builder interceptors(Interceptor... interceptors) {
		Assert.notNull(interceptors, "interceptors cannot be null");
		Assert.noNullElements(interceptors, "interceptors cannot contain null elements");
		this.interceptors.addAll(List.of(interceptors));
		return this;
	}

2.5 输出配置

控制智能体的输出格式与内容,包括输出键(outputKey)、输出策略(outputKeyStrategy)、输入/输出Schema和类型(inputSchemaoutputType等),支持自定义输出格式,适配不同的业务返回需求。

相关配置字段:

java 复制代码
	/** 是否包含内容输出 */
	protected boolean includeContents = true;

	/** 是否返回推理内容 */
	protected boolean returnReasoningContents;

	/** 输出键名 */
	protected String outputKey;

	/** 输出键策略 */
	protected KeyStrategy outputKeyStrategy;

	/** 输入Schema */
	protected String inputSchema;

	/** 输入类型 */
	protected Type inputType;

	/** 输出Schema */
	protected String outputSchema;

	/** 输出类型 */
	protected Class<?> outputType;

链式配置方法:

java 复制代码
	/**
	 * 设置输出键名
	 */
	public Builder outputKey(String outputKey) {
		this.outputKey = outputKey;
		return this;
	}

	/**
	 * 设置输出键策略
	 */
	public Builder outputKeyStrategy(KeyStrategy outputKeyStrategy) {
		this.outputKeyStrategy = outputKeyStrategy;
		return this;
	}

	/**
	 * 设置输入Schema
	 */
	public Builder inputSchema(String inputSchema) {
		this.inputSchema = inputSchema;
		return this;
	}

	/**
	 * 设置输入类型
	 */
	public Builder inputType(Type inputType) {
		this.inputType = inputType;
		return this;
	}

	/**
	 * 设置输出Schema
	 */
	public Builder outputSchema(String outputSchema) {
		this.outputSchema = outputSchema;
		return this;
	}

	/**
	 * 设置输出类型
	 */
	public Builder outputType(Class<?> outputType) {
		this.outputType = outputType;
		return this;
	}

	/**
	 * 设置是否包含内容输出
	 */
	public Builder includeContents(boolean includeContents) {
		this.includeContents = includeContents;
		return this;
	}

	/**
	 * 设置是否返回推理内容
	 */
	public Builder returnReasoningContents(boolean returnReasoningContents) {
		this.returnReasoningContents = returnReasoningContents;
		return this;
	}

2.6 监控与日志配置

集成 Spring 生态的可观测性组件,包括 ObservationRegistry(观测注册表)、自定义观测规范等,同时支持通过 enableLogging 开启日志记录,便于追踪智能体的执行过程、排查异常,符合企业级应用的可观测性需求。

相关配置字段:

java 复制代码
	/** 观测注册表(监控) */
	protected ObservationRegistry observationRegistry;

	/** 自定义聊天客户端观测规范 */
	protected ChatClientObservationConvention customObservationConvention;

	/** 顾问观测规范 */
	protected AdvisorObservationConvention advisorObservationConvention;

	/** 是否开启日志 */
	protected boolean enableLogging;

链式配置方法:

java 复制代码
	/**
	 * 设置观测注册表
	 */
	public Builder observationRegistry(ObservationRegistry observationRegistry) {
		this.observationRegistry = observationRegistry;
		return this;
	}

	/**
	 * 设置自定义聊天客户端观测规范
	 */
	public Builder customObservationConvention(ChatClientObservationConvention customObservationConvention) {
		this.customObservationConvention = customObservationConvention;
		return this;
	}

	/**
	 * 设置顾问观测规范
	 */
	public Builder advisorObservationConvention(AdvisorObservationConvention advisorObservationConvention) {
		this.advisorObservationConvention = advisorObservationConvention;
		return this;
	}

	/**
	 * 设置是否开启日志
	 */
	public Builder enableLogging(boolean enableLogging) {
		this.enableLogging = enableLogging;
		return this;
	}

2.7 异步执行

配置工具的并行执行策略,包括是否开启并行执行(parallelToolExecution)、最大并行工具数(maxParallelTools)、工具执行超时时间(toolExecutionTimeout)等,通过异步执行提升 IO 密集型工具的调用效率,优化智能体响应速度。

相关配置字段:

java 复制代码
	/** 是否释放线程(异步执行) */
	protected boolean releaseThread;
	
	/** 异步执行器 */
	protected Executor executor;
	
	/** 是否并行执行工具 */
	protected boolean parallelToolExecution = false;

	/** 最大并行工具数 */
	protected int maxParallelTools = 5;

	/** 工具执行超时时间 */
	protected Duration toolExecutionTimeout = Duration.ofMinutes(5);

	/** 是否将同步工具包装为异步 */
	protected boolean wrapSyncToolsAsAsync = false;

链式配置方法:

java 复制代码
	/**
	 * 设置并行节点执行器
	 * @param executor 异步执行器
	 * @return 构建器实例
	 */
	public Builder executor(Executor executor) {
		Assert.notNull(executor, "executor cannot be null");
		this.executor = executor;
		return this;
	}

	/**
	 * 开启/关闭工具并行执行
	 * 开启后单次响应中的多个工具会并发执行,提升IO密集型工具性能
	 */
	public Builder parallelToolExecution(boolean parallel) {
		this.parallelToolExecution = parallel;
		return this;
	}

	/**
	 * 设置最大并行工具执行数(防止资源耗尽)
	 * @throws IllegalArgumentException 数值小于1时抛出异常
	 */
	public Builder maxParallelTools(int max) {
		if (max < 1) {
			throw new IllegalArgumentException("maxParallelTools must be at least 1");
		}
		this.maxParallelTools = max;
		return this;
	}

	/**
	 * 设置单工具执行超时时间
	 * 超时后工具会被取消并返回错误
	 */
	public Builder toolExecutionTimeout(Duration timeout) {
		this.toolExecutionTimeout = Objects.requireNonNull(timeout, "timeout cannot be null");
		return this;
	}

	/**
	 * 开启/关闭同步工具自动包装为异步
	 * 开启后无需修改工具实现即可支持并行执行
	 */
	public Builder wrapSyncToolsAsAsync(boolean wrap) {
		this.wrapSyncToolsAsAsync = wrap;
		return this;
	}

	/**
	 * 设置是否释放线程
	 */
	public Builder releaseThread(boolean releaseThread) {
		this.releaseThread = releaseThread;
		return this;
	}

2.8 抽象构建方法

定义了 build() 抽象构建方法,由子类实现具体的 ReactAgent 构建逻辑:

java 复制代码
	/**
	 * 抽象构建方法
	 * 子类实现具体的ReactAgent构建逻辑
	 */
	public abstract ReactAgent build();

2.9 其他

其他配置项还有:

  • saver: 检查点保存器,用于持久化智能体执行流程,实现状态断点保存与恢复
  • compileConfig: 编译配置,定义智能体执行图的运行规则、递归限制等核心执行参数
  • stateSerializer: 状态序列化器,负责智能体运行状态的序列化与反序列化,支撑状态存储传输

相关配置字段:

java 复制代码
	/** 检查点保存器(流程持久化) */
	protected BaseCheckpointSaver saver;

	/** 编译配置 */
	protected CompileConfig compileConfig;

	/** 状态序列化器 */
	protected StateSerializer stateSerializer;

链式配置方法:

java 复制代码
	/**
	 * 设置检查点保存器
	 */
	public Builder saver(BaseCheckpointSaver saver) {
		Assert.notNull(saver, "saver cannot be null");
		this.saver = saver;
		return this;
	}

	/**
	 * 设置编译配置
	 */
	public Builder compileConfig(CompileConfig compileConfig) {
		Assert.notNull(compileConfig, "compileConfig cannot be null");
		this.compileConfig = compileConfig;
		return this;
	}


	/**
	 * 设置状态序列化器
	 * @param stateSerializer 状态序列化器
	 * @return 构建器实例
	 */
	public Builder stateSerializer(StateSerializer stateSerializer) {
		this.stateSerializer = stateSerializer;
		return this;
	}

	/**
	 * 设置SpringAI状态序列化器(已废弃)
	 * @deprecated 推荐使用 {@link #stateSerializer(StateSerializer)}
	 */
	@Deprecated
	public Builder stateSerializer(SpringAIStateSerializer stateSerializer) {
		this.stateSerializer = stateSerializer;
		return this;
	}

	/**
	 * 构建编译配置
	 * 未自定义配置时,生成默认编译配置
	 */
	protected CompileConfig buildConfig() {
		if (compileConfig != null) {
			return compileConfig;
		}

		SaverConfig saverConfig = SaverConfig.builder()
				.register(saver)
				.build();
		return CompileConfig.builder()
				.saverConfig(saverConfig)
				.recursionLimit(Integer.MAX_VALUE)
				.releaseThread(releaseThread)
				.build();
	}

3. 默认实现类 DefaultBuilder

DefaultBuilder 作为 Builder 的唯一实现类,核心职责是实现 build() 方法,将 Builder 中配置的所有参数,转化为可直接使用的 ReactAgent 实例。

构建流程可分为以下关键步骤:

  1. 参数校验
  2. 配置合并
  3. 构建对话客户端
  4. 构建 LLM 节点
  5. 拦截器分类
  6. 工具收集与合并
  7. 构建工具节点
  8. 实例化并返回最终 Agent
java 复制代码
/**
 * ReactAgent 默认构建器实现
 * 负责完成智能体的参数校验、配置合并、LLM节点构建、工具节点构建、工具/拦截器收集等核心逻辑
 */
public class DefaultBuilder extends Builder {

	private static final Logger logger = LoggerFactory.getLogger(DefaultBuilder.class);

	/** 工具名称适配警告日志常量 */
	public static final String POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING
				= "LLM may have adapted the tool name '{}', especially if the name was truncated due to length limits. If this is the case, you can customize the prefixing and processing logic using McpToolNamePrefixGenerator";

	/**
	 * 构建ReactAgent实例(核心构建方法)
	 * 流程:参数校验 → 配置合并 → 构建聊天客户端 → 构建LLM节点 → 分类拦截器 → 收集工具 → 构建工具节点
	 * @return 完整配置的ReactAgent实例
	 */

	@Override
	public ReactAgent build() {

		//................


		// 返回最终构建的ReactAgent实例
		return new ReactAgent(llmNode, toolNode, buildConfig(), this);
	}	
}

3.1 参数校验

首先对必填配置项进行校验,确保智能体名称(name)非空,且至少配置 ChatClientChatModel 二者之一,避免因配置缺失导致构建失败。

对应源码如下:

java 复制代码
		// 校验:智能体名称不能为空
		if (!StringUtils.hasText(this.name)) {
			throw new IllegalArgumentException("Agent name must not be empty");
		}

		// 校验:必须提供聊天客户端或模型实例二选一
		if (chatClient == null && model == null) {
			throw new IllegalArgumentException("Either chatClient or model must be provided");
		}

3.2 配置合并

合并源配置(来自 ChatModelChatClient 的默认配置)与代理级配置(开发者通过 Builder 设置的 chatOptions ),遵循代理配置优先级高于源配置的规则。

入口方法:

java 复制代码
		// 获取源配置:优先从ChatClient获取,其次从ChatModel获取
		ChatOptions sourceOptions = (chatClient != null)
				? getChatClientDefaultOptions(chatClient)
				: getChatModelDefaultOptions(model);
		// 合并配置:代理配置优先级高于源配置
		ChatOptions effectiveOptions = mergeSourceOptionsWithAgentOptions(sourceOptions, this.chatOptions);

配置合并(核心方法):

java 复制代码
	/**
	 * 合并源配置与代理级配置
	 * 代理配置优先级更高,使用工具类完成配置合并
	 * @param sourceOptions 源配置(ChatModel/ChatClient)
	 * @param agentOptions 代理自定义配置
	 * @return 合并后的最终配置
	 */
	@SuppressWarnings("unchecked")
	private static ChatOptions mergeSourceOptionsWithAgentOptions(ChatOptions sourceOptions,
			ChatOptions agentOptions) {
		if (sourceOptions == null) {
			return agentOptions;
		}
		if (agentOptions == null) {
			return sourceOptions;
		}
		Class<?> sourceOptionsClass = sourceOptions.getClass();
		if (sourceOptionsClass == agentOptions.getClass()) {
			logger.warn(
					"chatOptions type ({}) should be consistent with the default options type ({}) from ChatModel/ChatClient for proper merging.",
					agentOptions.getClass().getName(), sourceOptionsClass.getName());
		}
		return (ChatOptions) ModelOptionsUtils.merge(agentOptions, sourceOptions, sourceOptionsClass);
	}

反射获取 ChatModelChatClient 的默认聊天配置方法:

java 复制代码
	/**
	 * 反射获取ChatModel的默认聊天配置
	 * @param model 聊天模型实例
	 * @return 默认配置,获取失败返回null
	 */
	private static ChatOptions getChatModelDefaultOptions(ChatModel model) {
		if (model == null) {
			return null;
		}
		try {
			Method m = model.getClass().getMethod("getDefaultOptions");
			Object o = m.invoke(model);
			return o instanceof ChatOptions ? (ChatOptions) o : null;
		}
		catch (ReflectiveOperationException e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not read default options from ChatModel via reflection: {}", e.getMessage());
			}
			return null;
		}
	}

	/**
	 * 反射获取ChatClient的默认聊天配置
	 * @param chatClient 聊天客户端实例
	 * @return 默认配置,获取失败返回null
	 */
	@SuppressWarnings("unchecked")
	private static ChatOptions getChatClientDefaultOptions(ChatClient chatClient) {
		try {
			Field defaultRequestField = chatClient.getClass().getDeclaredField("defaultChatClientRequest");
			defaultRequestField.setAccessible(true);
			Object defaultChatClientRequest = defaultRequestField.get(chatClient);
			if (defaultChatClientRequest == null) {
				return null;
			}
			Field chatOptionsField = defaultChatClientRequest.getClass().getDeclaredField("chatOptions");
			chatOptionsField.setAccessible(true);
			Object options = chatOptionsField.get(defaultChatClientRequest);
			return options instanceof ChatOptions ? (ChatOptions) options : null;
		}
		catch (NoSuchFieldException | IllegalAccessException e) {
			if (logger.isDebugEnabled()) {
				logger.debug("Could not read default options from ChatClient via reflection: {}", e.getMessage());
			}
			return null;
		}
	}

3.3 构建对话客户端

若未配置 ChatClient,则基于 ChatModel 和合并后的配置,自动构建 ChatClient实例,简化开发者配置成本。

核心源码如下:

java 复制代码
		// 未提供ChatClient时,基于Model构建新的客户端
		if (chatClient == null) {
			ChatClient.Builder clientBuilder = ChatClient.builder(model,
					this.observationRegistry == null ? ObservationRegistry.NOOP : this.observationRegistry,
					this.customObservationConvention, this.advisorObservationConvention);
			if (effectiveOptions != null) {
				clientBuilder.defaultOptions(effectiveOptions);
			}
			chatClient = clientBuilder.build();
		} else {
			// 已提供ChatClient时,更新默认配置
			chatClient = chatClient.mutate().defaultOptions(effectiveOptions).build();
		}

提示 :直接配置 ChatClient 的方法已过时,推荐使用 ChatModel ,让 Builder 自动构建 ChatClient实例。

3.4 构建 LLM 节点(AgentLlmNode)

LLM 节点是 ReactAgent 的大脑,负责推理决策与工具调用判断。DefaultBuilder 通过 AgentLlmNode.Builder,将系统提示词、模板渲染器、输出 Schema、工具集合等配置,注入到 LLM 节点中,同时支持开启推理日志,便于调试 LLM 的推理过程。

核心源码如下:

java 复制代码
		// 初始化LLM节点构建器
		AgentLlmNode.Builder llmNodeBuilder = AgentLlmNode.builder()
				.agentName(this.name)
				.chatOptions(effectiveOptions)
				.chatClient(chatClient);
		// 配置输出键
		if (outputKey != null && !outputKey.isEmpty()) {
			llmNodeBuilder.outputKey(outputKey);
		}

		// 配置系统提示词
		if (systemPrompt != null) {
			llmNodeBuilder.systemPrompt(systemPrompt);
		}

		// 配置模板渲染器
		if (templateRenderer != null) {
			llmNodeBuilder.templateRenderer(templateRenderer);
    }
    
		// 配置指令
		if (instruction != null) {
			llmNodeBuilder.instruction(instruction);
		}

		// 处理输出格式:优先使用自定义Schema,其次根据输出类型生成
		String outputSchema = null;
		if (StringUtils.hasLength(this.outputSchema) ) {
			outputSchema = this.outputSchema;
		} else if (this.outputType != null) {
			FormatProvider formatProvider = new BeanOutputConverter<>(this.outputType);
			outputSchema = formatProvider.getFormat();
		}

		if (StringUtils.hasLength(outputSchema)) {
			llmNodeBuilder.outputSchema(outputSchema);
		}
		// 将统一拦截器按类型分离为模型拦截器和工具拦截器
		separateInterceptorsByType();
		
		// 收集所有来源的工具
		List<ToolCallback> allTools = gatherLocalTools();
		
		// 为LLM节点设置工具集合
		if (CollectionUtils.isNotEmpty(allTools)) {
			llmNodeBuilder.toolCallbacks(Collections.unmodifiableList(allTools));
		}

		// 开启推理日志
		if (enableLogging) {
			llmNodeBuilder.enableReasoningLog(true);
		}

		// 构建LLM节点
		AgentLlmNode llmNode = llmNodeBuilder.build();

3.5 拦截器分类

将统一的拦截器列表(interceptors)按类型分离为模型拦截器和工具拦截器,分别用于后续 LLM 推理和工具调用的拦截处理,实现拦截逻辑的分类管理,提升代码可维护性。

对应源码如下:

java 复制代码
	/**
	 * 将统一的拦截器列表按类型分离为模型拦截器和工具拦截器
	 */
	protected void separateInterceptorsByType() {
		if (CollectionUtils.isNotEmpty(interceptors)) {
			modelInterceptors = new ArrayList<>();
			toolInterceptors = new ArrayList<>();
			
			for (Interceptor interceptor : interceptors) {
				if (interceptor instanceof ModelInterceptor) {
					modelInterceptors.add((ModelInterceptor) interceptor);
				}
				if (interceptor instanceof ToolInterceptor) {
					toolInterceptors.add((ToolInterceptor) interceptor);
				}
			}
		}
	}

3.6 工具收集与合并

从多个来源收集工具,同时支持从工具解析器中提取工具(通过反射兼容不同实现),确保工具集合的完整性,满足 ReactAgent 的行动能力需求。

按优先级排序:

  • 钩子工具(优先级最高)
  • 拦截器工具
  • 用户自定义工具(直接配置、工具提供者、工具名称解析等)

收集顺序:

  1. 用户直接配置的工具
  2. 工具提供者提供的工具
  3. 工具名称解析的工具
  4. 解析器内置工具
  5. 模型拦截器携带的工具
  6. 钩子携带的工具

对应核心源码片段如下:

java 复制代码
	/**
	 * 收集所有来源的工具
	 * <p>
	 * 工具来源优先级:钩子工具 > 拦截器工具 > 用户自定义工具
	 * 收集顺序:
	 * 1. 用户直接配置的工具
	 * 2. 工具提供者提供的工具
	 * 3. 工具名称解析的工具
	 * 4. 解析器内置工具
	 * 5. 模型拦截器携带的工具
	 * 6. 钩子携带的工具
	 *
	 * @return 合并后的所有工具集合
	 */
	protected List<ToolCallback> gatherLocalTools() {
		// 常规工具:用户直接配置的工具集合
		List<ToolCallback> regularTools = new ArrayList<>();
		
		// 从用户配置的工具中提取
		if (CollectionUtils.isNotEmpty(tools)) {
			regularTools.addAll(tools);
		}
		
		// 从工具回调提供者中提取
		if (CollectionUtils.isNotEmpty(toolCallbackProviders)) {
			for (var provider : toolCallbackProviders) {
				regularTools.addAll(List.of(provider.getToolCallbacks()));
			}
		}
		
		// 根据工具名称解析工具
		if (CollectionUtils.isNotEmpty(toolNames)) {
			for (String toolName : toolNames) {
				// 去重:已存在的工具跳过
				if (regularTools.stream().anyMatch(tool -> tool.getToolDefinition().name().equals(toolName))) {
					continue;
				}
				
				// 校验解析器不能为空
				if (this.resolver == null) {
					throw new IllegalStateException(
							"ToolCallbackResolver is null; cannot resolve tool name: " + toolName);
				}
				ToolCallback toolCallback = this.resolver.resolve(toolName);
				if (toolCallback == null) {
					logger.warn(POSSIBLE_LLM_TOOL_NAME_CHANGE_WARNING, toolName);
					throw new IllegalStateException("No ToolCallback found for tool name: " + toolName);
				}
				regularTools.add(toolCallback);
			}
		}
		
		// 常规工具为空且解析器存在时,尝试从解析器提取工具
		if (regularTools.isEmpty() && this.resolver != null) {
			// 解析器实现ToolCallbackProvider接口的情况
			if (this.resolver instanceof ToolCallbackProvider provider) {
				ToolCallback[] resolverTools = provider.getToolCallbacks();
				if (resolverTools != null && resolverTools.length > 0) {
					regularTools.addAll(List.of(resolverTools));
					if (logger.isDebugEnabled()) {
						logger.debug("Extracted {} tools from ToolCallbackResolver (ToolCallbackProvider)",
								resolverTools.length);
					}
				}
			} else {
				// 反射获取解析器中的工具(兼容方案)
				try {
					Field toolsField = this.resolver.getClass().getDeclaredField("tools");
					toolsField.setAccessible(true);
					Object toolsObj = toolsField.get(this.resolver);
					if (toolsObj instanceof java.util.Map) {
						@SuppressWarnings("unchecked") java.util.Map<String, ToolCallback> toolsMap =
								(java.util.Map<String, ToolCallback>) toolsObj;
						if (!toolsMap.isEmpty()) {
							regularTools.addAll(toolsMap.values());
							if (logger.isDebugEnabled()) {
								logger.debug("Extracted {} tools from ToolCallbackResolver via reflection",
										toolsMap.size());
							}
						}
					}
				} catch (NoSuchFieldException | IllegalAccessException | ClassCastException e) {
					// 反射失败则忽略,属于预期行为
					if (logger.isTraceEnabled()) {
						logger.trace("Could not extract tools from resolver via reflection: {}", e.getMessage());
					}
				}
			}
		}
		
		// 从模型拦截器中提取工具
		List<ToolCallback> interceptorTools = new ArrayList<>();
		if (CollectionUtils.isNotEmpty(modelInterceptors)) {
			interceptorTools = modelInterceptors.stream().flatMap(interceptor -> interceptor.getTools().stream())
					.toList();
		}

		// 从钩子中提取工具
		List<ToolCallback> hookTools = new ArrayList<>();
		if (CollectionUtils.isNotEmpty(hooks)) {
			for (Hook hook : hooks) {
				List<ToolCallback> toolsFromHook = hook.getTools();
				if (CollectionUtils.isNotEmpty(toolsFromHook)) {
					hookTools.addAll(toolsFromHook);
					if (logger.isDebugEnabled()) {
						logger.debug("Collected {} tools from hook '{}'", toolsFromHook.size(), hook.getName());
					}
				}
			}
		}

		// 合并所有工具:钩子工具 → 拦截器工具 → 常规工具
		List<ToolCallback> allTools = new ArrayList<>();
		allTools.addAll(hookTools);
		allTools.addAll(interceptorTools);
		allTools.addAll(regularTools);

		return allTools;
	}

工具收集完成后,会将所有工具设置到 AgentLlmNode 中,并最终构建 LLM 节点:

java 复制代码
		// 为LLM节点设置工具集合
		if (CollectionUtils.isNotEmpty(allTools)) {
			llmNodeBuilder.toolCallbacks(Collections.unmodifiableList(allTools));
		}

		// 开启推理日志
		if (enableLogging) {
			llmNodeBuilder.enableReasoningLog(true);
		}

		// 构建LLM节点
		AgentLlmNode llmNode = llmNodeBuilder.build();

3.7 构建工具节点(AgentToolNode)

工具节点(AgentToolNode)负责工具的实际执行,DefaultBuilder 将收集到的工具、工具上下文、异步执行策略等配置注入工具节点,同时设置默认的工具执行异常处理器(未自定义时)。

对应源码片段如下:

java 复制代码
		// 初始化并构建工具节点
		AgentToolNode toolNode;
		AgentToolNode.Builder toolBuilder = AgentToolNode.builder()
				.agentName(this.name)
				.parallelToolExecution(this.parallelToolExecution)
				.maxParallelTools(this.maxParallelTools)
				.toolExecutionTimeout(this.toolExecutionTimeout)
				.wrapSyncToolsAsAsync(this.wrapSyncToolsAsAsync);

		// 设置工具解析器
		if (resolver != null) {
			toolBuilder.toolCallbackResolver(resolver);
		}
		// 设置工具集合
		if (CollectionUtils.isNotEmpty(allTools)) {
			toolBuilder.toolCallbacks(allTools);
		}

		// 开启工具执行日志
		if (enableLogging) {
			toolBuilder.enableActingLog(true);
		}
		// 设置工具执行异常处理器(无自定义配置则使用默认)
		if (toolExecutionExceptionProcessor == null) {
			toolBuilder.toolExecutionExceptionProcessor(DefaultToolExecutionExceptionProcessor.builder()
					.alwaysThrow(false)
					.build());
		} else {
			toolBuilder.toolExecutionExceptionProcessor(toolExecutionExceptionProcessor);
		}

		// 设置工具上下文
		if (toolContext != null && !toolContext.isEmpty()) {
			toolBuilder.toolContext(toolContext);
		}

		toolNode = toolBuilder.build();

3.8 实例化 ReactAgent 并返回

最终,调用 ReactAgent 构造方法进行实例化并返回:

java 复制代码
	@Override
	public ReactAgent build() {

		//................


		// 返回最终构建的ReactAgent实例
		return new ReactAgent(llmNode, toolNode, buildConfig(), this);
	}

构造方法:

java 复制代码
/**
 * ReactAgent 全参构造器
 * @param llmNode 大语言模型节点
 * @param toolNode 工具执行节点
 * @param compileConfig 图编译配置
 * @param builder 构建器实例
 */
public ReactAgent(AgentLlmNode llmNode, AgentToolNode toolNode, CompileConfig compileConfig, Builder builder) {
	// 初始化父类基础属性
	super(builder.name, builder.description, builder.includeContents, builder.returnReasoningContents, builder.outputKey, builder.outputKeyStrategy);

	// 线程安全的状态存储容器
	this.threadIdStateMap = new ConcurrentHashMap<>();

	// 赋值基础行为与结构配置
	this.instruction = builder.instruction;
	this.llmNode = llmNode;
	this.toolNode = toolNode;
	this.compileConfig = compileConfig;
	this.hooks = builder.hooks;
	this.modelInterceptors = builder.modelInterceptors;
	this.toolInterceptors = builder.toolInterceptors;
	this.includeContents = builder.includeContents;
	this.inputSchema = builder.inputSchema;
	this.inputType = builder.inputType;
	this.outputSchema = builder.outputSchema;
	this.outputType = builder.outputType;

	// 设置状态序列化器,未指定则使用默认的 Jackson 序列化器
	this.stateSerializer = Objects.requireNonNullElseGet(builder.stateSerializer,
			() -> new SpringAIJacksonStateSerializer(OverAllState::new));

	// 异步执行器
	this.executor = builder.executor;

	// 收集并合并来自 Hook 的拦截器
	List<ModelInterceptor> mergedModelInterceptors = collectAndMergeModelInterceptors();
	List<ToolInterceptor> mergedToolInterceptors = collectAndMergeToolInterceptors();

	// 将合并后的拦截器设置到对应节点
	if (mergedModelInterceptors != null && !mergedModelInterceptors.isEmpty()) {
		this.llmNode.setModelInterceptors(mergedModelInterceptors);
	}
	if (mergedToolInterceptors != null && !mergedToolInterceptors.isEmpty()) {
		this.toolNode.setToolInterceptors(mergedToolInterceptors);
	}

	// 标记当前 Agent 是否绑定了可用工具
	hasTools = toolNode.getToolCallbacks() != null && !toolNode.getToolCallbacks().isEmpty();
}
相关推荐
语戚2 小时前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
最初的↘那颗心2 小时前
Agent 实战:构建第一个 Agent 与记忆系统设计
java·大模型·agent·spring ai·记忆系统
程序猿_极客2 小时前
SpringBoot 三大参数注解详解:@RequestParam @RequestBody @PathVariable 区别及常用开发注解
java·spring boot·后端·面试八股文·springboot注释
代码青铜2 小时前
如何用 Zion 实现 AI 图片分析与电商文案自动生成流程
大数据·人工智能
俊哥V2 小时前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
小胖java2 小时前
校园通衢公告枢纽系统
java·spring boot
Crazy________2 小时前
docker4.8
java·开发语言·eureka
cch89182 小时前
Laravel 2.x:早期框架的奠基之路
java·开发语言
AINative软件工程2 小时前
跑 OpenClaw 一周烧了 300 块,我是怎么砍到 180 的
人工智能