2026/10/13 10:04:35

openJiuwen agent-core-java 旧版控制器消息工具类 MessageHandlerUtils 深度解析:LLM 输入拼装、Tool Call 任务转换与上下文写入

openJiuwen agent-core-java 旧版控制器消息工具类 MessageHandlerUtils 深度解析:LLM 输入拼装、Tool Call 任务转换与上下文写入 人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习【免费下载链接】agent-core-javaopenJiuwen agent core Java是 openJiuwen Core的 Java版本提供AI Agent开发、运行、调优与演进相关的全套SDK能力。项目地址https://gitcode.com/openJiuwen/agent-core-java点击查看免费下载本文深入剖析 openJiuwen agent-core-java 旧版控制器legacy controller消息链路中复用的静态工具类MessageHandlerUtils它是 LLM 输入拼装、tool call 转任务、上下文写入与用户输入过滤的统一入口。读完本文你将掌握该工具类每个方法的职责边界、底层实现原理、对应的测试验证方式以及在自己的旧版控制器/Reasoner 集成中正确使用它的完整方法。类定位旧版控制器与 Reasoner 的共享消息工具层MessageHandlerUtils位于core/src/main/java/com/openjiuwen/core/controller/legacy/utils/MessageHandlerUtils.java声明为public final class私有构造函数禁止实例化源码第 52 行是一个纯静态工具类。它的 javadoc 明确指出其设计目标为 legacy controller message flow 提供工具方法并镜像 Python 版本openjiuwen/core/controller/legacy/utils.py中的同名类源码第 41–45 行。从包结构看它服务于core/src/main/java/com/openjiuwen/core/controller/legacy/下的旧版控制器体系包括基于消息队列的BaseController见 BaseController.java、IntentDetectionController以及reasoner子包中的AgentReasoner、DefaultPlanner等组件。同时同包下的 ReasonerUtils.java 也直接调用其包级方法sliceChatHistory获取聊天历史这印证了文档中旧版控制器和 reasoner 复用的定位。从职责上看这个工具类覆盖四条消息处理主线LLM 输入拼装把用户输入、prompt 模板、聊天历史组合成发送给模型的消息序列Tool Call 转任务把模型返回的 tool call 解析成 legacyTask供控制器调度执行上下文写入把用户消息、AI 消息、工具执行结果写入ModelContext同时做好去重与过滤用户输入过滤按 schema 提取并校验用户输入字段。方法总览文档给出了完整的方法清单逐一说明如下均来自 MessageHandlerUtils.java方法返回说明formatLlmInputs(...)ListBaseMessage组合 system prompt 与聊天历史。concatSystemPromptWithChatHistory(...)ListBaseMessage合并系统提示和聊天历史必要时避免重复 system 消息。parseLlmOutput(AssistantMessage response, Object agentConfig)ListTask从 tool call 解析任务。createTasksFromToolCalls(ListToolCall toolCalls, Object agentConfig)ListTask为每个 tool call 生成一个 legacyTask。isInteractionResult(Object execResult)boolean判断执行结果是否属于交互型返回。createInterruptResult(Exception e, String toolName)MapString, Object创建统一的中断错误结构。validateExecutionInputs(...)boolean校验执行输入是否有效。shouldAddUserMessage(String query, ContextEngine contextEngine, Session session)boolean决定是否把用户消息写入上下文避免重复或工具后消息。addUserMessage(...)/addAiMessage(...)/addToolResult(...)void把用户消息、AI 消息或工具结果写入ModelContext。getChatHistory(...)ListBaseMessage获取最近若干轮聊天历史。filterInputs(MapString, Object schema, MapString, Object userData)MapString, Object按 schema 提取并校验用户输入。需要说明的是源码中addUserMessage、addAiMessage、addToolResult实际返回CompletionStageVoid异步完成信号parseLlmOutput与createTasksFromToolCalls的配置参数类型为包内定义的AgentConfigView视图接口文档以简化签名描述其功能语义实际调用时以源码签名为准。LLM 输入拼装formatLlmInputs 与 concatSystemPromptWithChatHistoryformatLlmInputs组装带模板的系统提示formatLlmInputs(Object inputs, ListBaseMessage chatHistory, AgentConfigView config, MapString, Object keywords)的核心流程如下源码第 55–77 行规范化用户输入若inputs是InteractiveInput交互型输入则使用空字段表若是Map则做一次深拷贝deepCopyMap逐层复制 Map/List/Collection见源码第 382–407 行避免后续修改污染调用方数据其他类型则包装为query字段。合并关键词keywords非空时并入字段表用于 prompt 模板的变量填充。渲染系统提示通过PromptTemplate.builder().content(config.promptTemplate()).build().format(userFields).toMessages()生成 system 消息序列——即把模板中的{{变量}}用字段表填充。拼接聊天历史调用concatSystemPromptWithChatHistory完成最终组装。测试formatLlmInputsDeepCopiesDictAndMergesKeywords见 MessageHandlerUtilsTest.java验证了三个关键行为模板hello {{name}} {{extra}}被填充为hello Ada kw聊天历史原样保留在系统提示之后深拷贝保证了调用方传入的inputsmap 不被写入extra字段。concatSystemPromptWithChatHistory避免重复 system 消息该方法源码第 79–90 行的逻辑非常简洁只有当聊天历史为空或以system角色开头时才跳过追加系统提示。换言之聊天历史为空 → 直接返回系统提示聊天历史第一条是system→ 不再重复追加直接沿用历史中的 system 消息聊天历史第一条不是system→ 在头部插入系统提示。这样既能保证模型总是能看到系统指令又不会在历史已自带 system 消息时产生重复。测试concatSystemPromptSkipsPromptWhenHistoryAlreadyStartsWithSystem验证了历史已以 system 开头则系统提示被跳过这一分支。Tool Call 转任务parseLlmOutput 与 createTasksFromToolCalls模型返回AssistantMessage后控制器需要把其中的 tool call 转换为可调度的 legacyTask这是该工具类最核心的职责。解析入口parseLlmOutputparseLlmOutput(BaseMessage response, AgentConfigView config)源码第 92–97 行仅处理AssistantMessage取出其getToolCalls()列表交给createTasksFromToolCalls非 AssistantMessage 或空 tool call 列表则返回空任务列表。任务构建createTasksFromToolCallscreateTasksFromToolCalls(ListToolCall toolCalls, AgentConfigView config)源码第 99–142 行为每个tool call 生成一个Task匹配工作流workflow遍历config.workflows()若某工作流的name与 tool call 的name相等则构建TaskInput(targetId workflow.id() _ workflow.version(), targetName toolName, arguments 解析后的参数)并创建TaskType.WORKFLOW类型的任务。这正是文档所述把 tool call 的id直接作为任务 ID把name写入TaskInput.targetName把参数 JSON 文本写入TaskInput.arguments的实现细节——注意这里的targetId是工作流id_版本号的拼接形式。匹配插件plugin若某插件的name与 tool call 的name相等则构建TaskInput(, toolName, arguments)创建TaskType.PLUGIN任务targetId为空串。参数解析parseToolArguments用JsonUtils.safeJsonLoads把 tool call 的参数字符串解析为 JSON 对象解析失败时抛出AGENT_CONTROLLER_TOOL_EXECUTION_PROCESS_ERROR错误并在UserConfig.isSensitive()关闭时输出包含参数原文的日志、开启时仅输出脱敏信息源码第 348–370 行。兜底报错若所有 tool call 都没有匹配到任何已配置工具抛出AGENT_TOOL_NOT_FOUND错误failed to create task from tool calls。其中Task与TaskInput的定义见 Task.java 与 TaskInput.javaTaskInput序列化字段为target_id、target_name、arguments任务类型枚举TaskType见 TaskType.java包含PLUGIN、WORKFLOW、MCP、UNDEFINED四种。测试createTasksFromToolCallsCreatesWorkflowAndPluginWhenNamesBothMatchMessageHandlerUtilsTest.java演示了同名单场景一个名为dispatch的 tool call 同时命中工作流与插件生成两个任务第一个为WORKFLOW类型taskIdcall-1、targetIdworkflow-id_v2第二个为PLUGIN类型targetId为空而createTasksRaisesWhenNoToolCallMatchesAnyConfiguredTool则验证了未命中时抛出agent tool not found的异常路径。配套方法determineTaskType与任务构建配套的determineTaskType(String toolName, AgentConfigView config)源码第 144–161 行按同样规则判断某工具名对应WORKFLOW还是PLUGIN未命中时抛出AGENT_TOOL_NOT_FOUND消息not find tool call type: toolName供调用方在无需完整构建任务时快速判定类型。执行结果判定与中断错误结构isInteractionResult识别交互型返回isInteractionResult(Object execResult)源码第 163–168 行判定执行结果是否为交互型返回条件是结果是Map、其error键在 Python 语义下为真值、且value键是List。其中isPythonTruthy实现了 Python 风格的真值判断源码第 409–429 行null为假、Boolean取其值、数值非 0 为真、字符串非空为真、集合与 Map 非空为真。测试determineTaskTypeAndInteractionResultMirrorPythonBranches验证了{error:yes,value:[…]}为真、errorfalse或value非 List 均为假。createInterruptResult统一中断错误结构createInterruptResult(Exception exception, String toolName)源码第 170–176 行把异常封装成统一结构{error: true, value: 异常消息或 null, tool_name: 工具名}。该结构恰好满足isInteractionResult的判定条件error为真、value为 List 除外用于中断流程向上传递错误信息。validateExecutionInputs执行输入校验validateExecutionInputs(Object execResult, Object subTaskResult)源码第 178–180 行的实现非常轻量仅校验execResult ! null作为调用链上的基本防御。上下文写入addUserMessage / addAiMessage / addToolResult三个写入方法都通过ContextEngine的DEFAULT_CONTEXT_ID获取当前会话的ModelContext后追加消息返回CompletionStageVoid上下文或会话为空时直接返回已完成状态不做任何写入。shouldAddUserMessage避免重复与工具后消息写入用户消息前必须先经过shouldAddUserMessage(Object query, ContextEngine contextEngine, AgentSessionApi session)源码第 182–205 行的裁决其返回false跳过写入的两种情形工具后消息上下文最后一条消息角色为tool说明当前请求是对工具结果的追问不应再插入新的用户消息日志Skipping user message - post-tool-call request重复消息最后一条是user角色且内容与本次query相同日志Skipping duplicate user message。addUserMessage内部再次调用shouldAddUserMessage做双重保障源码第 209–211 行。测试userMessageHelpersSkipDuplicatesAndPostToolMessagesMessageHandlerUtilsTest.java验证了连续两次写入相同hello后上下文仅保留一条消息随后追加一条ToolMessage后再调用shouldAddUserMessage(next, ...)返回false。addAiMessage 与 addToolResultaddAiMessage(AssistantMessage aiMessage, ...)源码第 228–238 行直接把AssistantMessage写入ModelContext。addToolResult(Event event, ...)源码第 240–273 行从事件的TaskResult中提取输出——优先调用getOutput()失败则反射读取output字段extractTaskOutput随后做结果归一化OutputSchema取其payload.output、WorkflowOutputView取其result()、其他类型尝试getResult()反射读取最后用JsonUtils.safeJsonDumps序列化并封装成ToolMessage其toolCallId取事件的taskId从而把工具输出回填到对应的 tool call 链路。测试addToolResultSerializesTaskOutputAndToolCallId验证了OutputSchema载荷{output:[ok]}被序列化为[ok]ToolMessage.toolCallId等于事件任务 IDtask-1。聊天历史获取getChatHistory 与 Python 风格切片getChatHistory(ContextEngine contextEngine, AgentSessionApi session, AgentConfigView config)源码第 275–281 行仅在config.constrain()非空时生效读取constrain().reservedMaxChatRounds()作为保留轮数内部调用包级方法sliceChatHistory。sliceChatHistory源码第 319–331 行的实现值得一提它取出ModelContext中的全部消息然后按Python 风格负数切片计算起始下标——pythonSliceStart(size, -2 * chatHistoryMaxTurn)即从末尾截取最近2 * maxTurn条消息一轮由 user assistant 两条构成且负下标越界时安全收敛到 0pythonSliceStart源码第 476–481 行。当maxTurn0时-2*00切片从 0 开始即返回全部历史。测试getChatHistoryUsesPythonTailSliceIncludingZeroTurnsMessageHandlerUtilsTest.java验证3 条消息、保留 1 轮时返回后 2 条保留 0 轮时返回全部 3 条。用户输入过滤filterInputsfilterInputs(MapString, Object schema, MapString, Object userData)源码第 283–304 行按 schema 提取用户输入按schema 的键顺序LinkedHashMap遍历schema 中某字段的值为 Map 且含requiredtrue时若用户未提供该字段抛出IllegalArgumentException消息为missing required parameter: key用户提供了的字段被放入结果 Map顺序与 schema 一致requiredfalse且未提供的字段被跳过schema 为 null/空时返回空 Map。测试filterInputsKeepsSchemaOrderAndRaisesForMissingRequiredFieldMessageHandlerUtilsTest.java验证schema{first(required), second(optional), third(loose)}与用户数据{first:1, third:3}得到{first:1, third:3}保持 schema 顺序而缺少first时抛出IllegalArgumentException: missing required parameter: first。配置视图窄化配置面MessageHandlerUtils为了保持对上层配置的松耦合在类内部定义了若干窄化视图接口源码第 493–595 行AgentConfigViewpromptTemplate()、workflows()、plugins()、constrain()WorkflowViewid()、version()、name()PluginViewname()ConstrainViewreservedMaxChatRounds()WorkflowOutputViewresult()。同时提供了配套的不可变记录类型SimpleAgentConfig、Workflow、Plugin、Constrain便于测试与简单调用方直接构造配置测试中大量使用例如new SimpleAgentConfig(, List.of(new Workflow(...)), List.of(new Plugin(...)), new Constrain(1))。如果你的 Agent 配置类实现了这些视图接口或传入与视图字段一致的配置即可无缝接入本工具类。使用建议与注意事项纯静态调用本类不可实例化所有方法均为public static直接以MessageHandlerUtils.xxx(...)调用配置对象需实现AgentConfigView或其记录形态。tool call 匹配规则工作流/插件均按name精确匹配同名时会同时生成两类任务工作流优先需确认配置中工具名唯一性。异常语义createTasksFromToolCalls未命中抛出AGENT_TOOL_NOT_FOUNDfilterInputs缺失必填字段抛出IllegalArgumentExceptiontool call 参数解析失败抛出AGENT_CONTROLLER_TOOL_EXECUTION_PROCESS_ERROR且日志内容受UserConfig.isSensitive()控制。异步写入addUserMessage/addAiMessage/addToolResult返回CompletionStage在异步链路中应正确链式处理而非阻塞等待。去重由写入方保证addUserMessage内部自带shouldAddUserMessage检查重复写入同一 query 或工具后写入会被自动跳过无需调用方重复判断。作为旧版控制器消息链路的总装车间MessageHandlerUtils将模型交互中最琐碎也最易出错的拼装、转换、写入与过滤逻辑收敛为可复用的静态方法配合 MessageHandlerUtilsTest.java 中覆盖各分支的测试用例是理解 legacy 控制器消息流转机制的理想切入点。赞分享人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习【免费下载链接】agent-core-javaopenJiuwen agent core Java是 openJiuwen Core的 Java版本提供AI Agent开发、运行、调优与演进相关的全套SDK能力。项目地址https://gitcode.com/openJiuwen/agent-core-java点击查看免费下载相关推荐openJiuwen Agent Core Javalegacy 控制器工具类 MessageHandlerUtils 与 ReasonerUtils 深度解析openJiuwen Agent Core Javalegacy 控制器工具类 MessageHandlerUtils 与 ReasonerUtils 深度解人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen agent-core-java 上下文消息分析利器ContextUtils 静态工具类深度解析openJiuwen agent core java 上下文消息分析利器ContextUtils 静态工具类深度解析 本文以 ContextUtils.md人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习NativeWind Display 工具类全解flex 与 hidden 在 React Native 中的兼容范围与实现原理NativeWind Display 工具类全解flex 与 hidden 在 React Native 中的兼容范围与实现原理 导读 在 React Nat人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习上一篇Mermaid 本地编辑器离线构建完整教程从零跑通到静态产物下一篇Security-101 IAM 核心概念精讲身份与访问管理、最小权限与认证授权的实战指南创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考