消息与事件¶
消息(Message)与事件(Event)是 AgentScope 中两种基础数据结构。
消息 — 智能体间通信与持久化的基本单元。每个
Msg代表一个完整的对话轮次,存储在上下文中并在智能体之间传递。事件 — 前端交互与流式传输的基本单元。事件携带增量进度更新(文本 token、工具调用片段、权限请求等),驱动实时界面和人工介入工作流。
单次 call 调用产生的事件序列最终汇聚成恰好一条 assistant Msg,这保证了完整的消息状态始终可以从事件流中还原。
消息¶
Msg(位于 io.agentscope.core.message)代表对话中的一个轮次——用户输入、智能体回复或系统指令,内容以有序的类型化块(ContentBlock)列表表示。
Tip
一条 assistant 消息对应智能体一次完整的 call 周期(反复推理和执行,直到产出最终回复)。
结构¶
Msg 类的核心字段(getter)如下:
方法 |
类型 |
说明 |
|---|---|---|
|
|
唯一消息标识符 |
|
|
发送方名称(可空) |
|
|
|
|
|
有序内容块列表(不可变) |
|
|
任意键值元数据 |
|
|
创建时间( |
|
|
Token 用量(仅 assistant 消息) |
|
|
退出原因: |
内容块¶
消息内容由类型化的块组成,每种块代表一类独立信息。块类位于 io.agentscope.core.message:
块类型 |
说明 |
允许出现在 |
|---|---|---|
|
纯文本内容 |
USER、ASSISTANT、SYSTEM |
|
二进制数据(图片、音频、视频),通过 base64 或 URL;统一替代旧的 ImageBlock/AudioBlock/VideoBlock |
USER、ASSISTANT |
|
旧版具体多媒体块(仍兼容,新代码建议用 |
USER |
|
模型推理过程(思维链) |
ASSISTANT |
|
工具调用,包含 |
ASSISTANT |
|
工具执行结果,包含 |
ASSISTANT |
|
以用户上下文形式注入循环的指令 |
ASSISTANT |
Note
角色约束在构造时强制执行:USER 消息只能包含 text/data/image/audio/video 块;SYSTEM 消息只能包含 TextBlock;ASSISTANT 消息可包含所有块类型。
创建消息¶
按 role 固定的子类提供便捷构造(io.agentscope.core.message.UserMessage / AssistantMessage / SystemMessage / ToolResultMessage)。当 content 是普通字符串时,会自动包装为 TextBlock。
import io.agentscope.core.message.AssistantMessage;
import io.agentscope.core.message.Base64Source;
import io.agentscope.core.message.DataBlock;
import io.agentscope.core.message.SystemMessage;
import io.agentscope.core.message.TextBlock;
import io.agentscope.core.message.UserMessage;
// 用户消息 —— 文本
UserMessage userText = new UserMessage("user", "这张图片里有什么?");
// 多模态用户消息
UserMessage userMulti =
new UserMessage(
"user",
TextBlock.builder().text("描述这张图片:").build(),
DataBlock.builder()
.source(Base64Source.builder()
.data("...")
.mediaType("image/png")
.build())
.build());
// 系统消息 —— 仅文本
SystemMessage systemMsg = new SystemMessage("system", "你是一个有用的助手。");
// 助手消息 —— 允许所有块类型
AssistantMessage assistantMsg = new AssistantMessage("agent", "结果如下...");
需要更多可选字段(metadata、timestamp、usage、generateReason)时使用各子类的 builder():
UserMessage msg =
UserMessage.builder()
.name("user")
.textContent("Hello")
.build();
访问内容¶
Msg 提供了一组辅助方法用于提取特定块类型:
方法 |
返回值 |
|---|---|
|
所有 |
|
按类型过滤后的块列表 |
|
首个匹配类型的块,无则返回 null |
|
若存在指定类型的块则返回 |
import io.agentscope.core.message.ToolUseBlock;
import io.agentscope.core.message.ToolResultBlock;
// 获取所有文本内容
String text = msg.getTextContent();
// 获取所有工具调用
List<ToolUseBlock> toolCalls = msg.getContentBlocks(ToolUseBlock.class);
// 检查消息是否包含工具结果
if (msg.hasContentBlocks(ToolResultBlock.class)) {
// ...
}
事件¶
事件是消息的流式对应物。智能体执行过程中会持续产出一系列 AgentEvent 对象(位于 io.agentscope.core.event),表示增量进度——文本 token 到达、工具调用逐步构建、结果流式返回。每个事件都是轻量且自包含的。
事件生命周期¶
每个事件都携带 getReplyId(),将其关联到正在构建的消息。在一次回复中,getBlockId() 或 getToolCallId() 标识事件所属的内容块。事件遵循 start → delta → end 模式:
sequenceDiagram
participant Client
participant Agent
Agent->>Client: AgentStartEvent
rect rgba(100, 150, 255, 0.1)
Note over Client,Agent: 推理阶段
Agent->>Client: ModelCallStartEvent
rect rgba(200, 200, 100, 0.1)
Note over Client,Agent: TextBlock (blockId)
Agent->>Client: TextBlockStartEvent
Agent->>Client: TextBlockDeltaEvent (×N)
Agent->>Client: TextBlockEndEvent
end
rect rgba(200, 200, 100, 0.1)
Note over Client,Agent: DataBlock (blockId)
Agent->>Client: DataBlockStartEvent
Agent->>Client: DataBlockDeltaEvent (×N)
Agent->>Client: DataBlockEndEvent
end
rect rgba(200, 200, 100, 0.1)
Note over Client,Agent: ToolUseBlock (toolCallId)
Agent->>Client: ToolCallStartEvent
Agent->>Client: ToolCallDeltaEvent (×N)
Agent->>Client: ToolCallEndEvent
end
Agent->>Client: ModelCallEndEvent
end
rect rgba(100, 255, 150, 0.1)
Note over Client,Agent: 执行阶段
rect rgba(200, 200, 100, 0.1)
Note over Client,Agent: ToolResultBlock (toolCallId)
Agent->>Client: ToolResultStartEvent
Agent->>Client: ToolResultTextDeltaEvent (×N)
Agent->>Client: ToolResultDataDeltaEvent (×N)
Agent->>Client: ToolResultEndEvent
end
end
Agent->>Client: AgentEndEvent
同一次回复中的所有事件共享相同的 replyId。在回复内部,用 blockId 关联文本/思考/数据块事件,用 toolCallId 关联工具调用和工具结果事件。
事件类型¶
所有事件继承自 AgentEvent(位于 io.agentscope.core.event),提供以下公共方法:
方法 |
类型 |
说明 |
|---|---|---|
|
|
唯一事件标识符 |
|
|
ISO 8601 时间戳 |
|
|
事件类型枚举 |
事件按类别分组如下。除特别说明外,每个事件还携带 getReplyId(),关联到正在构建的消息。
生命周期事件
AgentStartEvent — 智能体开始新的回复。
| 方法 | 类型 | 说明 |
|------|------|------|
| `getReplyId()` | `String` | 回复消息 ID |
| `getSessionId()` | `String` | 会话 ID |
| `getName()` | `String` | 智能体名称 |
| `getRole()` | `String` | 智能体角色(默认 `"assistant"`) |
**AgentEndEvent** — 智能体完成回复。
| 方法 | 类型 | 说明 |
|------|------|------|
| `getReplyId()` | `String` | 回复消息 ID |
**ExceedMaxItersEvent** — 智能体达到最大推理-执行迭代次数。
| 方法 | 类型 | 说明 |
|------|------|------|
| `getReplyId()` | `String` | 回复消息 ID |
**RequestStopEvent** — 中间件或工具发起的提前停止请求。
文本流式事件
TextBlockStartEvent — 新的文本块开始。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
文本块唯一标识符 |
TextBlockDeltaEvent — 增量文本内容到达。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
文本块唯一标识符 |
|
|
增量文本内容 |
TextBlockEndEvent — 文本块完成。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
文本块唯一标识符 |
思考流式事件
ThinkingBlockStartEvent / ThinkingBlockDeltaEvent / ThinkingBlockEndEvent —— 与文本流式事件结构对应,仅用于模型的思维链内容。
数据流式事件
DataBlockStartEvent / DataBlockDeltaEvent / DataBlockEndEvent —— 与文本流式事件结构对应,承载图片 / 音频 / 视频等二进制数据:
DataBlockStartEvent:getMediaType()返回 MIME 类型(如"image/png")。DataBlockDeltaEvent:getData()返回增量 base64 编码数据。
工具调用流式事件
ToolCallStartEvent — 智能体开始一次工具调用。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
工具调用唯一标识符 |
|
|
被调用的工具名称 |
ToolCallDeltaEvent — 增量工具调用参数到达;getDelta() 返回 JSON 参数片段。
ToolCallEndEvent — 工具调用参数完成。
工具结果流式事件
ToolResultStartEvent — 工具开始执行(带 toolCallId、toolCallName)。
ToolResultTextDeltaEvent — 工具的增量文本输出;getDelta() 返回文本片段。
ToolResultDataDeltaEvent — 工具的二进制数据输出;与 DataBlockDeltaEvent 类似,包含 mediaType / data / url 字段。
ToolResultEndEvent — 工具执行完成。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
对应工具调用的 ID |
|
|
最终状态: |
模型调用事件
ModelCallStartEvent — 模型 API 调用开始(带 modelName)。
ModelCallEndEvent — 模型 API 调用完成(带 inputTokens / outputTokens)。
人工介入事件
RequireUserConfirmEvent — 智能体暂停等待用户确认。
方法 |
类型 |
说明 |
|---|---|---|
|
|
回复消息 ID |
|
|
待用户确认的工具调用列表 |
RequireExternalExecutionEvent — 智能体暂停等待外部执行。
UserConfirmResultEvent — 用户提供确认结果(输入事件)。携带 List<ConfirmResult>。
ExternalExecutionResultEvent — 外部系统提供执行结果(输入事件)。携带 List<ToolResultBlock>。
从事件流重建消息¶
事件与消息并非相互独立,而是同一数据的两种视图。streamEvents 产出的事件流可以按 replyId / blockId / toolCallId 聚合还原成完整的 AssistantMessage。这保证了最终消息状态可以仅凭事件流完整还原。
可以参考 agentscope-core 中的 agent/StreamingHook.java 与 agentscope-examples/documentation/.../streaming/AgentEventStreamExample.java,它们演示了用 Reactor 算子按 block 分组并累积内容的标准做法。
import io.agentscope.core.event.AgentEvent;
import io.agentscope.core.event.AgentStartEvent;
import io.agentscope.core.event.AgentEndEvent;
import io.agentscope.core.event.TextBlockDeltaEvent;
import io.agentscope.core.event.ToolCallStartEvent;
import io.agentscope.core.event.ToolResultEndEvent;
StringBuilder accumulated = new StringBuilder();
agent.streamEvents(userMsg)
.doOnNext(event -> {
if (event instanceof AgentStartEvent start) {
System.out.println("[start replyId=" + start.getReplyId() + "]");
} else if (event instanceof TextBlockDeltaEvent delta) {
accumulated.append(delta.getDelta());
} else if (event instanceof ToolCallStartEvent tc) {
System.out.println("[tool] " + tc.getToolCallName());
} else if (event instanceof ToolResultEndEvent end) {
System.out.println("[tool result state=" + end.getState() + "]");
} else if (event instanceof AgentEndEvent end) {
System.out.println("\n[end] full text:\n" + accumulated);
}
})
.blockLast();
Tip
这种设计让部署更加灵活:后端可以通过 SSE 把事件流推给前端,前端在客户端侧重建消息。即使连接中断,从任意检查点重放事件序列也能精确恢复消息状态。
示例:流式界面¶
构建流式界面的典型模式(Spring WebFlux SSE 形态可参考 streaming/StreamingWebExample.java):
import io.agentscope.core.event.AgentEndEvent;
import io.agentscope.core.event.AgentStartEvent;
import io.agentscope.core.event.TextBlockDeltaEvent;
import io.agentscope.core.event.ToolCallStartEvent;
import io.agentscope.core.event.ToolResultEndEvent;
import io.agentscope.core.message.UserMessage;
agent.streamEvents(new UserMessage("user", "帮我修复这个 bug"))
.doOnNext(event -> {
if (event instanceof AgentStartEvent start) {
System.out.println("[start replyId=" + start.getReplyId() + "]");
} else if (event instanceof TextBlockDeltaEvent delta) {
System.out.print(delta.getDelta());
} else if (event instanceof ToolCallStartEvent tc) {
System.out.println("\n[正在调用 " + tc.getToolCallName() + "...]");
} else if (event instanceof ToolResultEndEvent end) {
System.out.println("[工具执行完成:" + end.getState() + "]");
} else if (event instanceof AgentEndEvent end) {
System.out.println("\n[完成]");
}
})
.blockLast();
延伸阅读¶
智能体如何在 ReAct 循环中产出事件和消息
消息如何存储与持久化