- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
ToolMessage是 openJiuwen agent-core-java 中用于承载工具(tool)执行结果的统一消息载体,属于com.openjiuwen.core.foundation.llm.schema消息体系的一员。它继承自BaseMessage,以tool角色将工具返回内容回灌到对话上下文中,供大模型在下一轮继续推理。阅读本文后,你将掌握ToolMessage的字段语义、全部构造方法与序列化行为,并了解它在工具超时、执行失败、中断恢复、消息流式合并等真实场景中的调用方式,能够直接在 Agent 开发中正确构造与消费 tool 消息。
类概览:tool 角色消息在消息体系中的定位
ToolMessage定义于 core/src/main/java/com/openjiuwen/core/foundation/llm/schema/ToolMessage.java,源码注释明确说明它镜像了 Python 版本 openJiuwen 中openjiuwen/core/foundation/llm/schema/message.py的同名类,保证了 Java 端与 Python 端在消息协议上的一致性。
public class ToolMessage extends BaseMessage在llm.schema包中,消息类族呈如下继承关系:
| 类 | 角色 | 说明 |
|---|---|---|
BaseMessage | — | 消息基类,定义role、content、name、metadata公共字段 |
SystemMessage | system | 系统提示消息 |
UserMessage | user | 用户输入消息 |
AssistantMessage | assistant | 模型回复消息,可携带toolCalls工具调用请求 |
ToolMessage | tool | 工具执行结果消息,通过toolCallId与 assistant 发出的调用请求一一对应 |
BaseMessage(见 core/src/main/java/com/openjiuwen/core/foundation/llm/schema/BaseMessage.java)提供了以下公共字段与能力:
role:消息角色(system/user/assistant/tool);content:消息内容,默认值为空字符串"",经normalizeContent做可序列化校验;name:可选的参与者名称(如工具名);metadata:扩展元数据,默认LinkedHashMap空集合;modelDump()/model_dump():将消息导出为Map<String, Object>,供模型接口请求或协议转换使用;getContentAsString()/getContentAsList():按内容类型取字符串或列表形态。
ToolMessage正是在此基础上添加了toolCallId字段,用于把工具结果与模型发出的调用请求关联起来。
字段详解:toolCallId 与继承自 BaseMessage 的公共字段
ToolMessage自身仅声明一个字段:
| 字段 | JSON 属性名 | 说明 |
|---|---|---|
private String toolCallId | tool_call_id | 保存toolCallId标识,用于关联 assistant 消息中对应的ToolCall.id |
注意,该字段使用了 Jackson 的@JsonProperty("tool_call_id")注解(源码第 29-30 行),因此在 JSON 反序列化与modelDump()导出时均使用下划线命名的tool_call_id键,这与 OpenAI Chat 兼容协议一致。
其余字段全部继承自BaseMessage:
role:ToolMessage 的 role 固定为tool;content:工具返回的执行结果文本(或结构化的可序列化对象);name:工具名称,便于在多工具场景中标识结果来源;metadata:可选的附加元数据。
此外,类上标注了 Lombok 注解@Data、@SuperBuilder、@NoArgsConstructor、@AllArgsConstructor、@EqualsAndHashCode(callSuper = true)与@JsonInclude(JsonInclude.Include.NON_NULL),意味着:
- 支持链式 Builder 构造与无参/全参构造;
equals/hashCode会连同父类字段一并比较;- 序列化时自动忽略
null字段,保证content之外的字段缺省时不写入 JSON。
构造方法:两种便捷入参方式
| 签名 | 说明 |
|---|---|
public ToolMessage(String content, String toolCallId) | 以内容与调用 ID 构造实例,内部调用super("tool", content)将 role 固定为tool |
public ToolMessage(String content, String toolCallId, String name) | 在上一构造基础上额外设置name(工具名) |
两处构造方法均为源码级事实(ToolMessage.java):
public ToolMessage(String content, String toolCallId) { super("tool", content); this.toolCallId = toolCallId; } public ToolMessage(String content, String toolCallId, String name) { this(content, toolCallId); setName(name); }实际项目中三种构造方式都会被使用:
- 只有内容与调用 ID(如中断/失败占位消息,见下文 LlmEventHandler 示例);
- 内容、调用 ID 加工具名(工具正常执行后的完整结果,
name携带工具名便于模型区分来源); - 无参构造 +
setToolCallId/setContent/setName(用于从 JSON 或协议 Map 反序列化恢复,如 Serializer.java 中的restoreToolMessage)。
方法详解:getRole 的兜底语义与 modelDump 导出
getRole():默认值兜底
@Override public String getRole() { String value = super.getRole(); return value != null ? value : "tool"; }ToolMessage重写了getRole():当父类role字段为null(例如通过无参构造创建、尚未显式 setRole 时),返回默认值"tool"。这一兜底逻辑保证了工具消息无论以何种方式构造,角色语义始终正确,与AssistantMessage.getRole()对"assistant"的处理方式完全对称。
modelDump():导出为协议友好的 Map
@Override public Map<String, Object> modelDump() { Map<String, Object> result = super.modelDump(); result.put("role", getRole()); if (toolCallId != null) { result.put("tool_call_id", toolCallId); } return result; }modelDump()在父类导出结果(role、content、可选的name、metadata)基础上,强制覆盖role为"tool",并在toolCallId非空时写入tool_call_id键;同时提供 Python 风格别名model_dump()。该方法是消息进入 LLM 请求体前的标准转换入口。
由测试 MessageTest.java 可以验证上述行为:
ToolMessage toolMessage = new ToolMessage("done", "call-9"); assertEquals("tool", toolMessage.getRole()); assertEquals("call-9", toolMessage.modelDump().get("tool_call_id"));实战链路:ToolMessage 在工具调用生命周期中的典型用法
ToolMessage并非孤立的数据类,而是贯穿工具调用链路的实际载体。以下用法均可从仓库源码中找到依据。
正常执行:将工具结果回灌上下文
在单智能体能力管理器中,工具执行完成后会构造ToolMessage携带结果(见 core/src/main/java/com/openjiuwen/core/singleagent/AbilityManager.java):
toolMessage = new ToolMessage(String.valueOf(toolResult), toolCall.getId(), toolCall.getName());content为工具返回值文本、toolCallId取自模型发出的ToolCall.id、name为工具名,三者构成了完整可追溯的工具消息。随后它被写入上下文(AgentContext),模型在下一轮即可看到该工具的输出。通用工具消息写入入口封装在 MessageUtils.addToolMessage 中,直接对接上下文引擎的addMessages。
超时与失败:以 ToolMessage 承载错误占位
工具执行可能超时或抛错,这两种情况同样通过ToolMessage表达:
- 超时:
AbilityManager.safeRailedExecuteOne在工具超时时构造超时提示消息(AbilityManager.java):
String timeoutMsg = "Tool execution timed out after " + timeoutMillis + "ms for tool: " + toolCall.getName(); ToolMessage toolMsg = new ToolMessage(timeoutMsg, toolCall.getId(), toolCall.getName());- 失败:AbilityExecutionError.java 中
AbilityExecutionError.of(toolCall, message)会把错误文本封装为ToolMessage并随异常抛出,供上层统一处理。
控制器场景:中断占位与失败标记
在 core/src/main/java/com/openjiuwen/core/application/llm/LlmEventHandler.java 中,ToolMessage还被用作流程占位消息:
- 任务中断时写入
new ToolMessage("[INTERRUPTED - Waiting for user input]", task.getTaskId())(第 497-500 行),表示等待用户输入; - 任务失败时写入
new ToolMessage("[FAILED - " + errorMsg + "]", task.getTaskId())(第 548 行); - 工具无返回结果时写入
new ToolMessage("", task.getTaskId(), toolName)(第 461 行),保证对话上下文始终有完整的 tool 消息。
由此可见,ToolMessage是工具调用流程中“正常结果、超时、失败、中断、空结果”等所有分支的统一返回形态。
流式场景:ToolMessageChunk 的合并语义
与消息类族配套,ToolMessage还有流式片段版本ToolMessageChunk(core/src/main/java/com/openjiuwen/core/foundation/llm/schema/ToolMessageChunk.java),它继承ToolMessage并实现merge方法:
public ToolMessageChunk merge(Object other) { if (!(other instanceof ToolMessageChunk otherChunk)) { throw new IllegalArgumentException("Cannot merge ToolMessageChunk with " + other); } return ToolMessageChunk.builder() .role("tool") .content(getContentAsString() + otherChunk.getContentAsString()) .toolCallId((otherChunk.getToolCallId() != null && !otherChunk.getToolCallId().isEmpty()) ? otherChunk.getToolCallId() : getToolCallId()) .build(); }合并规则为:content顺序拼接;toolCallId优先取后一个片段非空的值,否则沿用前一片段的值。仓库中的 MessageChunkPythonParityTest.java 与 MessageChunkTest.java 专门用于校验该类与 Python 端片段合并行为的一致性。
序列化与持久化:JSON 字段与协议恢复
由于toolCallId标注了@JsonProperty("tool_call_id"),ToolMessage与 OpenAI 兼容 JSON 之间可以无损互转。在 core/src/main/java/com/openjiuwen/core/graph/store/Serializer.java 的restoreToolMessage中,反序列化要求:
- 允许的字段集合为
{type, role, content, name, toolCallId}; role必须为"tool";toolCallId为必需字段(requireStringField),content通过fromProtocolValue还原为协议值,name可缺省。
这意味着一份合法的 tool 消息协议表示至少包含role=tool、content与tool_call_id三个要素。同样的恢复逻辑还出现在上下文落盘场景中,例如 SessionModelContext.java 与 OffloadMessageBuffer.java,它们均从持久化 Map 中读取tool_call_id键重建ToolMessage。
小结
| 维度 | 要点 |
|---|---|
| 定位 | llm.schema包中表示 tool 角色完整消息的类,继承BaseMessage |
| 核心字段 | toolCallId(JSON 键tool_call_id),关联 assistant 的ToolCall.id |
| 构造方式 | (content, toolCallId)、(content, toolCallId, name)、无参 + setter、@SuperBuilder |
| 角色兜底 | getRole()在父类 role 为 null 时返回"tool" |
| 导出协议 | modelDump()输出含role、content、tool_call_id的 Map |
| 典型用途 | 工具正常结果、超时/失败/中断占位消息回灌上下文 |
| 流式扩展 | ToolMessageChunk.merge拼接 content、择优保留 toolCallId |
在 openJiuwen Agent Core 的工具调用闭环中,ToolMessage是连接“模型发出 ToolCall”与“工具执行结果回到上下文”的关键一环。正确理解并构造ToolMessage,是开发 ReAct、Workflow 等各类 Agent 时保证多轮工具对话上下文完整、可追溯的基础。更多消息类族信息可参考 llm/schema 包说明 及同目录下的AssistantMessage、ToolCall等文档。
- 人工智能
- AI Agent
- Agent 框架
- 工具调用
- Agent 工作流
- RAG
- MCP Clients
- 强化学习
【免费下载链接】agent-core-java
openJiuwen agent core Java是 openJiuwen Core的 Java版本,提供AI Agent开发、运行、调优与演进相关的全套SDK能力。
相关推荐
openJiuwen Agent Core Java 消息模型详解:AssistantMessage 的字段、工具调用与序列化机制
openJiuwen Agent Core Java 消息模型详解:AssistantMessage 的字段、工具调用与序列化机制 本文围绕 openJiuwe
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习openJiuwen Agent Core 消息基类 BaseMessage 完全指南:字段、构造、序列化与流式合并机制
openJiuwen Agent Core 消息基类 BaseMessage 完全指南:字段、构造、序列化与流式合并机制 com.openjiuwen.core
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习cuDF-Polars 基准测试完全指南:用 PDS-H(TPC-H 变体)评测 GPU 流式执行引擎
cuDF Polars 基准测试完全指南:用 PDS H(TPC H 变体)评测 GPU 流式执行引擎 本指南围绕 cuDF 仓库中 benchmarks.md
人工智能AI AgentAgent 框架工具调用Agent 工作流RAGMCP Clients强化学习
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考