文章目录
- [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. 前言
ReactAgent 是 Spring 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和类型(inputSchema、outputType等),支持自定义输出格式,适配不同的业务返回需求。
相关配置字段:
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 实例。
构建流程可分为以下关键步骤:
- 参数校验
- 配置合并
- 构建对话客户端
- 构建
LLM节点 - 拦截器分类
- 工具收集与合并
- 构建工具节点
- 实例化并返回最终
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)非空,且至少配置 ChatClient 或 ChatModel 二者之一,避免因配置缺失导致构建失败。
对应源码如下:
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 配置合并
合并源配置(来自 ChatModel 或 ChatClient 的默认配置)与代理级配置(开发者通过 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);
}
反射获取 ChatModel 、ChatClient 的默认聊天配置方法:
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 的行动能力需求。
按优先级排序:
- 钩子工具(优先级最高)
- 拦截器工具
- 用户自定义工具(直接配置、工具提供者、工具名称解析等)
收集顺序:
- 用户直接配置的工具
- 工具提供者提供的工具
- 工具名称解析的工具
- 解析器内置工具
- 模型拦截器携带的工具
- 钩子携带的工具
对应核心源码片段如下:
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();
}