- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
导读
在 openJiuwen agent-core-java(openJiuwen Core 的 Java 版本)中,控制器(Controller)负责把用户输入拆解为一个个可调度、可执行、可追踪的"任务(Task)"。而com.openjiuwen.core.common.constants.TaskType正是任务路由中用于标识任务类型的核心枚举:它定义了PLUGIN(插件任务)、WORKFLOW(工作流任务)、MCP(MCP 任务)与UNDEFINED(未知回退值)四种类型,并配套getValue()与fromValue()两个方法。本文将以 TaskType.java 为骨架,结合 TaskScheduler、TaskExecutorRegistry、MessageHandlerUtils 等源码实现与测试用例,系统讲解 TaskType 的枚举定义、字符串线格式(wire format)、解析语义,以及它在任务创建、路由、执行器注册和调度执行全链路中的实际作用,帮助你准确使用该类型并理解控制器任务模型的底层原理。
一、TaskType 枚举定义
1.1 类位置与定位
TaskType位于:
core/src/main/java/com/openjiuwen/core/common/constants/TaskType.java它是com.openjiuwen.core.common.constants包下的一个普通 Java 枚举,其 Javadoc 明确指出该枚举是对 Python 版 openjiuwen Core 中openjiuwen/core/common/constants/enums.py的TaskType的镜像实现(Mirrors Python'sTaskType)。这意味着:
- Java 版与 Python 版的枚举值、字符串值保持一一对应,跨语言、跨进程(例如经由消息队列或序列化数据交互)时,任务类型标识可以互通;
- 该枚举属于
common.constants层,是任务模型、控制器、调度器等模块共用的公共契约,而不是某个业务模块的私有类型。
1.2 枚举值一览
依据 TaskType.md 与源码,枚举共包含 4 个值:
| 枚举值 | 字符串值 | 说明 |
|---|---|---|
PLUGIN | "plugin" | 插件任务。 |
WORKFLOW | "workflow" | 工作流任务。 |
MCP | "mcp" | MCP 任务。 |
UNDEFINED | "undefined" | 未知输入的回退值。 |
对应源码实现如下(TaskType.java):
public enum TaskType { PLUGIN("plugin"), WORKFLOW("workflow"), MCP("mcp"), UNDEFINED("undefined"); private final String value; TaskType(String value) { this.value = value; } public String getValue() { return value; } public static TaskType fromValue(String value) { for (TaskType type : values()) { if (type.value.equals(value)) { return type; } } return UNDEFINED; } }1.3 为什么字符串值用小写
PLUGIN、WORKFLOW、MCP、UNDEFINED是 Java 枚举常量的命名(大写下划线风格),而getValue()返回的"plugin"、"workflow"、"mcp"、"undefined"是实际参与序列化、存储与跨模块传递的字符串值。从源码中的序列化逻辑可以确认,任务持久化与跨模块传输使用的是小写字符串值,例如:
- controller/schema/Task.java 的
toMap()将task_type字段以原始字符串写入 Map; - LlmEventHandler.java 在序列化任务列表时直接输出
task.getTaskType()(即"plugin"/"workflow"/"mcp"/"undefined")。
因此,凡是需要与外部系统、存储或 Python 版 Core 对齐的字符串,必须使用getValue()的小写值,而不能直接使用 Java 枚举名。
二、两个方法的语义与解析规则
2.1 getValue():返回字符串值
public String getValue()返回当前枚举实例对应的字符串值。常见用法是构造任务时直接传入:
Task task = new Task(session.getSessionId(), randomWorkflowTaskId(), TaskType.WORKFLOW.getValue());这段代码正是 WorkflowEventHandler.java 在路由新建工作流任务时的真实写法。
2.2 fromValue():宽松解析,失败回退 UNDEFINED
public static TaskType fromValue(String value)- 遍历全部枚举值,若某枚举的
value与入参完全相等(equals匹配,大小写敏感),则返回对应枚举; - 若没有任何匹配(包括传入
null或空字符串),不会抛异常,而是统一返回UNDEFINED。
这一"宽容回退"的设计非常关键:它是任务模型中"未知输入"的兜底标识,避免了解析异常导致链路中断。与之形成对照的是 DependencyType,后者在fromValue无匹配时抛出IllegalArgumentException——两者对比可见 TaskType 的设计意图是"尽量不失败、让未知类型走 UNDEFINED 分支"。
三、TaskType 在任务模型中的角色
3.1 新控制器模型:字符串字段承载类型
在新版控制器数据模型 controller/schema/Task.java 中,taskType被定义为普通字符串字段(@JsonProperty("task_type")),构造时直接接受TaskType.getValue()的产物:
public Task(String sessionId, String taskId, String taskType) { ... }对应的测试 TaskSchemaTest.java 验证了getTaskType()返回原始字符串;WorkflowEventHandlerTest.java 则断言任务类型等于"workflow"。
3.2 旧控制器模型:直接以枚举类型存储
在旧版(legacy)任务模型 controller/legacy/task/Task.java 中,taskType字段的类型就是TaskType,且默认值为UNDEFINED:
private TaskType taskType = TaskType.UNDEFINED;TaskTypesTest.java 断言新建任务的默认taskType为TaskType.UNDEFINED,这印证了"未知/未指定时回退 UNDEFINED"的默认语义。
四、任务类型是如何产生的:从 LLM 工具调用到 TaskType 映射
任务类型不是凭空而来的,而是在"意图识别 / 规划"阶段根据用户配置与 LLM 输出生成的。核心实现在 MessageHandlerUtils.java:
4.1 determineTaskType:按工具名匹配
public static TaskType determineTaskType(String toolName, AgentConfigView config) { for (WorkflowView workflow : safeWorkflows(config)) { if (workflow != null && Objects.equals(toolName, workflow.name())) { return TaskType.WORKFLOW; } } for (PluginView plugin : safePlugins(config)) { if (plugin != null && Objects.equals(toolName, plugin.name())) { return TaskType.PLUGIN; } } throw ErrorHelper.buildError( StatusCode.AGENT_TOOL_NOT_FOUND, "error_msg", "not find tool call type: " + toolName ); }逻辑要点:
- 若工具名与某个已配置工作流(WorkflowView)的名字相等 →
WORKFLOW; - 否则若与某个插件(PluginView)的名字相等 →
PLUGIN; - 都匹配不到则抛出
AGENT_TOOL_NOT_FOUND错误。
4.2 createTasksFromToolCalls:同时命中则生成两类任务
for (WorkflowView workflow : safeWorkflows(config)) { if (workflow != null && Objects.equals(workflow.name(), toolName)) { TaskInput taskInput = new TaskInput( workflow.id() + "_" + workflow.version(), toolName, parseToolArguments(toolCall, toolName, "workflow")); result.add(createTask(toolCall.getId(), TaskType.WORKFLOW, taskInput)); break; } } for (PluginView plugin : safePlugins(config)) { if (plugin != null && Objects.equals(plugin.name(), toolName)) { TaskInput taskInput = new TaskInput("", toolName, parseToolArguments(toolCall, toolName, "plugin")); result.add(createTask(toolCall.getId(), TaskType.PLUGIN, taskInput)); break; } }值得注意的是:当同一个工具名同时匹配工作流与插件时,会同时生成一个WORKFLOW任务和一个PLUGIN任务(测试 MessageHandlerUtilsTest.java 验证了这一点),由后续调度器按序执行。若一个工具名都没匹配上,则抛出AGENT_TOOL_NOT_FOUND。
4.3 工作流智能体场景:路由阶段直接落 WORKFLOW
在 WorkflowEventHandler.java 中,routeNewTask/routeResume均以TaskType.WORKFLOW.getValue()构造任务并置为SUBMITTED状态后交给 TaskManager,例如:
Task task = new Task(session.getSessionId(), randomWorkflowTaskId(), TaskType.WORKFLOW.getValue()); task.setDescription(taskData.targetName()); task.setStatus(TaskStatus.SUBMITTED); requireTaskManager().addTask(task);对应测试 WorkflowControllerTest.java 断言intent.getTask().getTaskType()为TaskType.WORKFLOW。
4.4 UNDEFINED 的典型使用:默认规划器兜底
旧版默认规划器 DefaultPlanner.java 在为每条消息创建默认任务时,直接将taskType置为UNDEFINED:
Task task = new Task(); task.setTaskType(TaskType.UNDEFINED); task.setDescription(desc); task.setStatus(TaskStatus.PENDING);PlannerTest.java 断言该任务类型为TaskType.UNDEFINED。这说明UNDEFINED不仅用于"解析失败的回退值",也被用作"尚未由意图识别结果决定类型"的占位值。
五、从 taskType 到执行器:TaskExecutorRegistry 分发机制
任务创建后,调度器依据taskType字符串从执行器注册表中找到对应的TaskExecutor。这是 TaskType 在运行期最核心的作用。
5.1 注册表按字符串键存储
TaskExecutorRegistry.java 内部使用ConcurrentHashMap<String, Function<TaskExecutorDependencies, TaskExecutor>>,以任务类型字符串为键:
public void addTaskExecutor(String taskType, Function<TaskExecutorDependencies, TaskExecutor> taskExecutorBuilder) { taskExecutorBuilders.put(taskType, taskExecutorBuilder); } public TaskExecutor getTaskExecutor(String taskType, TaskExecutorDependencies dependencies) { Function<TaskExecutorDependencies, TaskExecutor> builder = taskExecutorBuilders.get(taskType); if (builder == null) { throw ErrorHelper.buildError(StatusCode.AGENT_CONTROLLER_TASK_EXECUTION_ERROR, "error_msg", "task executor not found for type: " + taskType); } return builder.apply(dependencies); }- 注册键就是
TaskType.getValue()的字符串值(如"workflow"、"plugin"、"mcp"); - 查找不到对应执行器时抛出
AGENT_CONTROLLER_TASK_EXECUTION_ERROR,提示 "task executor not found for type: xxx"。
5.2 执行时按类型取执行器
TaskScheduler.java 的executeTask中:
Task task = tasks.get(0); Loggers.CONTROLLER.info("Executing task {} (type: {})", taskId, task.getTaskType()); // 2. Create TaskExecutor TaskExecutorDependencies dependencies = new TaskExecutorDependencies( config, executorAbilityManager, contextEngine, taskManager, eventQueue); TaskExecutor executor = taskExecutorRegistry.getTaskExecutor(task.getTaskType(), dependencies);随后调度器将任务状态更新为WORKING,通过executor.executeAbility(taskId, session)迭代产出ControllerOutputChunk,并按输出类型(TASK_COMPLETION/TASK_INTERACTION/TASK_FAILED)更新任务状态。
5.3 自定义执行器的注册入口
Controller提供了链式 API 注册自定义任务执行器(Controller.java):
public Controller addTaskExecutor( String taskType, Function<TaskExecutorDependencies, TaskExecutor> taskExecutorBuilder ) { taskScheduler.getTaskExecutorRegistry().addTaskExecutor(taskType, taskExecutorBuilder); return this; }实际案例:Deep Agent(Task Loop)在初始化任务循环运行时时,注册了类型为"deep_agent_task"的自定义执行器(DeepAgent.java):
taskScheduler .getTaskExecutorRegistry() .addTaskExecutor(TaskLoopEventExecutor.DEEP_TASK_TYPE, dependencies -> new CoreTaskLoopEventExecutor(dependencies, this, this::invokeInnerRound));其中DEEP_TASK_TYPE = "deep_agent_task"(TaskLoopEventExecutor.java),而 TaskLoopEventHandler.java 创建任务时也使用同一字符串常量。测试 TaskLoopEventHandlerTest.java 对此进行了断言。
这说明:TaskType 只是框架内置的四种标准类型,任务系统本身是开放可扩展的——任何自定义任务类型字符串都可以通过addTaskExecutor注册对应执行器并接入调度链路。
六、TaskType 在序列化与跨系统交互中的注意点
- 字符串大小写敏感:
fromValue("Workflow")、fromValue("WORKFLOW")均不匹配,返回UNDEFINED。请始终使用getValue()的小写值; - 序列化字段名为
task_type:在 controller/schema/Task.java 中通过@JsonProperty("task_type")绑定,与 Python 版数据模型保持一致; - 回退语义:
UNDEFINED是"未知输入"的合法回退值,不会被当作错误抛出;但若一个已提交任务带着UNDEFINED进入调度且未注册对应执行器,TaskExecutorRegistry会抛出AGENT_CONTROLLER_TASK_EXECUTION_ERROR; - 跨语言对齐:枚举值是 Python 版
enums.py的镜像,凡涉及与 Python 端、外部系统交换任务数据,务必使用相同的字符串值。
七、小结与速查
| 你想做的事 | 推荐做法 | 参考 |
|---|---|---|
| 构造一个工作流任务 | new Task(sessionId, taskId, TaskType.WORKFLOW.getValue()) | WorkflowEventHandler.java |
| 解析外部传入的类型字符串 | TaskType.fromValue(str),无匹配自动得UNDEFINED | TaskType.java |
| 判断类型是否"未知" | 与TaskType.UNDEFINED比较 | DefaultPlanner.java |
| 注册自定义任务类型执行器 | controller.addTaskExecutor(type, deps -> executor) | Controller.java、DeepAgent.java |
| 从 LLM 工具调用生成任务类型 | 使用MessageHandlerUtils.determineTaskType/createTasksFromToolCalls | MessageHandlerUtils.java |
TaskType虽是一个只有四个枚举值的小类,但它贯穿了 openJiuwen agent-core-java 控制器从"意图识别 → 任务创建 → 类型标注 → 执行器分发 → 调度执行"的完整链路,并与 Python 版 Core 保持严格对齐。理解它的字符串契约与回退语义,是正确使用控制器任务模型、以及扩展自定义任务类型的第一步。
- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
相关推荐
openJiuwen agent-core-java 控制器意图分类枚举 IntentType 全解:9 类任务意图、LLM 识别与事件路由机制
openJiuwen agent core java 控制器意图分类枚举 IntentType 全解:9 类任务意图、LLM 识别与事件路由机制 导读 Inte
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习drawio-desktop 完整上手指南:免费打开 Visio VSDX 文件的跨平台桌面工具
drawio desktop 完整上手指南:免费打开 Visio VSDX 文件的跨平台桌面工具 同事用 Visio 画好的流程图发过来,是 .vsdx 格式,
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core-java 控制器(Controller)机制详解:从事件分发、任务调度到意图识别
openJiuwen agent core java 控制器(Controller)机制详解:从事件分发、任务调度到意图识别 com.openjiuwen.co
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考