目录:
1. 面向生产环境的智能体工程平台
2. 快速上手 从零构建生产级智能体
3. Agent —— 智能体的核心抽象与工程化实践
4. Message & Event —— 消息模型与事件流深度解
5. Middleware —— 无侵入式智能体扩展机制深度解析
6. Model —— 统一模型接入层与容错机制深度解析
7. Permission System —— 权限控制系统深度解析
8. Tool —— 工具系统架构与生产级实践深度解析
9. Context —— 运行时上下文与状态管理深度解析

一、引言:为什么消息与事件是智能体框架的"神经系统"

在 AgentScope Java 2.0 的构建块体系中,Message(消息)Event(事件) 共同构成了智能体的"神经系统":

  • Message 是智能体之间、智能体与模型之间传递信息的静态载体——它定义了"说什么"
  • Event 是推理-行动循环中每一步的动态信号——它定义了"正在发生什么"

消息与事件模块,打造可观测、可交互的执行流。

本文深入解析这两大核心抽象的设计哲学、类型体系与工程实践。

二、消息模型(Message):统一 ContentBlock 架构

2.1 设计哲学

AgentScope 2.0 对消息层进行了彻底重构,核心设计原则:

原则 说明
统一抽象 文本、图片、音频、视频、工具调用、工具结果统一收敛到 ContentBlock
强类型校验 使用 Java 17 sealed class + record,构造期按 role 校验,非法组合直接报错
可持久化 Msg 是可序列化的最小对话单元,直接写入 AgentState
多模态原生 不是"附加"多模态,而是从类型系统层面原生支持

2.2 Msg 消息结构

Msg
├── role: MsgRole (USER / ASSISTANT / SYSTEM / TOOL)
├── content: List<ContentBlock>
├── generateReason: GenerateReason (可选)
└── name: String (可选,用于多 Agent 场景标识)

核心定义:
Msg 是消息主体,由 role + List 组成,是可持久化的最小对话单元。

// 源码位置: io.agentscope.core.message.Msg
public final class Msg {
    private final MsgRole role;
    private final List<ContentBlock> content;
    private final GenerateReason generateReason;
    private final String name;
    // ...
}

2.3 MsgRole —— 消息角色

角色 说明 允许的 ContentBlock
USER 用户输入 TextBlock, ImageBlock, DataBlock
ASSISTANT 模型输出 TextBlock, ThinkingBlock, ToolUseBlock
SYSTEM 系统指令 TextBlock
TOOL 工具返回 ToolResultBlock

强校验机制:构造期按 role 校验 ContentBlock 类型,非法组合在构造时即抛出异常,而非运行时才暴露。这是 2.0 相比 1.x 的重大改进——将错误前移到编译/构造阶段。

2.4 ContentBlock 类型体系

ContentBlock 是消息内容的原子片段,采用 sealed class 设计,确保类型安全与穷举性:

public sealed interface ContentBlock
    permits TextBlock, ImageBlock, DataBlock,
            ThinkingBlock, ToolUseBlock, ToolResultBlock {
}
2.4.1 TextBlock —— 纯文本
record TextBlock(String text) implements ContentBlock {}

最基础的内容类型,承载对话文本。

2.4.2 ImageBlock —— 图片
record ImageBlock(Source source, String mediaType) implements ContentBlock {}

支持多模态图片输入,Source 定义数据来源(Base64 / URL / 文件路径)。

2.4.3 DataBlock —— 文件/数据
record DataBlock(Source source, String fileName, String mediaType) implements ContentBlock {}

承载文件、音频、视频等二进制数据。

2.4.4 Source —— 数据源抽象

ImageBlock 和 DataBlock 共享 Source 抽象:

Source 类型 说明
Base64Source 内联 Base64 编码
URLSource 远程 URL 引用
FileSource 本地文件路径
2.4.5 ThinkingBlock —— 模型思考
record ThinkingBlock(String thinking) implements ContentBlock {}

承载模型的推理过程(Chain-of-Thought),仅在 ASSISTANT 角色消息中出现。这一设计使得"思考过程"成为一等公民,可被中间件拦截、记录或展示。

2.4.6 ToolUseBlock —— 工具调用请求
record ToolUseBlock(
    String id,          // 调用唯一标识
    String name,        // 工具名称
    Map<String, Object> input  // 调用参数
) implements ContentBlock {}

出现在 ASSISTANT 角色消息中,表示模型决定调用某个工具。

2.4.7 ToolResultBlock —— 工具调用结果
record ToolResultBlock(
    String toolUseId,   // 对应的 ToolUseBlock id
    String content,     // 执行结果
    boolean isError     // 是否执行出错
) implements ContentBlock {}

出现在 TOOL 角色消息中,表示工具执行完毕后的返回。

2.5 ContentBlock 类型全景图

ContentBlock (sealed interface)
├── TextBlock          ← 纯文本(USER/ASSISTANT/SYSTEM)
├── ImageBlock         ← 图片(USER)
├── DataBlock          ← 文件/音频/视频(USER)
├── ThinkingBlock      ← 模型思考过程(ASSISTANT)
├── ToolUseBlock       ← 工具调用请求(ASSISTANT)
└── ToolResultBlock    ← 工具执行结果(TOOL)

2.6 GenerateReason —— 生成原因

标识本轮 Assistant 消息的终止原因:

枚举值 说明
END_TURN 模型自然结束回复
TOOL_USE 模型请求调用工具(循环继续)
MAX_ITERATIONS 达到最大迭代次数,强制终止

这一设计让上层逻辑可以精确判断"为什么停止了",而非猜测。

2.7 快捷消息工厂

框架提供静态工厂方法简化消息创建:

// 用户消息
Msg userMsg = new UserMessage("帮我查一下北京天气");

// 系统消息
Msg sysMsg = new SystemMessage("你是一个天气助手");

// 带图片的多模态消息
Msg multiModal = Msg.builder()
    .role(MsgRole.USER)
    .content(TextBlock.of("这张图里有什么?"))
    .content(ImageBlock.of(Source.base64(imageBytes), "image/png"))
    .build();

2.8 常用模式

模式一:提取纯文本
String text = msg.getTextContent();  // 拼接所有 TextBlock
模式二:读取结构化输出
WeatherResult result = msg.getStructuredData(WeatherResult.class);
模式三:消息在 ReAct 循环中的传递
UserMessage → [推理] → AssistantMessage(ToolUseBlock)
    → [工具执行] → ToolMessage(ToolResultBlock)
    → [推理] → AssistantMessage(TextBlock, GenerateReason.END_TURN)

三、事件系统(Event):可观测的执行流

3.1 设计理念

每一步——模型调用、文本增量、工具执行、工具结果——都以类型化事件流出。订阅一次,前端 UI 实时跟上。

AgentScope 2.0 的事件系统提供约 35 个事件类,覆盖智能体执行的完整生命周期。事件通过 streamEvents() 以 Flux 形式流出,天然适配响应式编程与 SSE(Server-Sent Events)。

3.2 AgentEvent 基类

每个事件都继承自 AgentEvent,提供统一的元数据:

public abstract class AgentEvent {
    public String getId();            // 唯一事件标识符
    public String getCreatedAt();     // ISO 8601 时间戳
    public AgentEventType getType();  // 事件类型枚举
    public String getSource();        // 来源路径
}

source 字段的精妙设计:

  • 顶层 Agent:source = null
  • 子 Agent:source = “main/sub-agent-1”(斜杠分隔路径)

这使得在多层嵌套的子 Agent 架构中,每个事件都能精确追溯到产生它的 Agent 层级。

3.3 事件生命周期:标准三段式模式

AgentScope 2.0 的事件遵循标准三段式(Start → Delta → End):

┌─────────────────────────────────────────────────────┐
│  XxxStartEvent    →  标记某个阶段开始                  │
│  XxxDeltaEvent    →  流式增量数据(可多次触发)          │
│  XxxEndEvent      →  标记某个阶段结束                  │
└─────────────────────────────────────────────────────┘

这种设计使得:

  • 前端渲染可以精确知道何时开始显示、何时追加内容、何时关闭
  • 性能监控可以精确计算每个阶段的耗时
  • 异常处理可以精确定位中断点

3.4 事件分类详解

3.4.1 智能体调用事件
事件 说明
AgentStartEvent Agent 开始处理请求
AgentEndEvent Agent 完成本轮回复
3.4.2 模型调用事件
事件 说明
ModelCallStartEvent 开始调用 LLM
ModelCallEndEvent LLM 返回完成
3.4.3 文本块事件
事件 说明
TextBlockStartEvent 文本生成开始
TextBlockDeltaEvent 文本增量(流式输出核心)
TextBlockEndEvent 文本生成结束
3.4.4 思考块事件
事件 说明
ThinkingBlockStartEvent 模型开始思考
ThinkingBlockDeltaEvent 思考内容增量
ThinkingBlockEndEvent 思考结束
3.4.5 数据块事件
事件 说明
DataBlockStartEvent 数据块开始
DataBlockDeltaEvent 数据增量
DataBlockEndEvent 数据块结束
3.4.6 工具调用事件
事件 说明
ToolCallStartEvent 工具调用开始(含工具名、参数)
ToolCallEndEvent 工具调用完成
3.4.7 工具结果事件
事件 说明
ToolResultEvent 工具执行结果返回
3.4.8 异常与中断事件
事件 说明
ErrorEvent 执行异常
InterruptEvent 执行被中断
3.4.9 HITL(Human-in-the-Loop)事件
事件 说明
PermissionRequestEvent 请求人工审批
PermissionResponseEvent 人工审批结果
3.4.10 子 Agent 事件
事件 说明
SubAgentStartEvent 子 Agent 启动
SubAgentEndEvent 子 Agent 完成

3.5 事件类型枚举(AgentEventType)

所有事件类型通过 AgentEventType 枚举统一管理,便于 switch 分发:

public enum AgentEventType {
    AGENT_START, AGENT_END,
    MODEL_CALL_START, MODEL_CALL_END,
    TEXT_BLOCK_START, TEXT_BLOCK_DELTA, TEXT_BLOCK_END,
    THINKING_BLOCK_START, THINKING_BLOCK_DELTA, THINKING_BLOCK_END,
    DATA_BLOCK_START, DATA_BLOCK_DELTA, DATA_BLOCK_END,
    TOOL_CALL_START, TOOL_CALL_END,
    TOOL_RESULT,
    ERROR, INTERRUPT,
    PERMISSION_REQUEST, PERMISSION_RESPONSE,
    SUB_AGENT_START, SUB_AGENT_END,
    // ... 更多类型
}

3.6 执行流程中的事件序列

一次典型的 ReAct 循环产生的事件序列:

AgentStartEvent
  ├── ModelCallStartEvent
  │     ├── ThinkingBlockStartEvent
  │     ├── ThinkingBlockDeltaEvent × N
  │     ├── ThinkingBlockEndEvent
  │     ├── TextBlockStartEvent
  │     ├── TextBlockDeltaEvent × N
  │     ├── TextBlockEndEvent
  │     └── ToolCallStartEvent (模型决定调用工具)
  ├── ModelCallEndEvent
  ├── ToolCallStartEvent (工具实际执行)
  ├── ToolResultEvent
  ├── ModelCallStartEvent (第二轮推理)
  │     ├── TextBlockStartEvent
  │     ├── TextBlockDeltaEvent × N
  │     └── TextBlockEndEvent
  ├── ModelCallEndEvent
  └── AgentEndEvent

3.7 从事件流重建消息

事件流不仅是"观察窗口",还可以反向重建完整的 Msg:

TextBlockDelta × N  →  拼接  →  TextBlock
ToolCallStart + ToolResult  →  ToolUseBlock + ToolResultBlock
这使得即使只订阅了事件流,也能完整还原对话历史。
## 四、事件订阅与流式输出实战
### 4.1 基础订阅
```java
agent.streamEvents(new UserMessage("介绍 AgentScope 2.0"))
    .doOnNext(event -> {
        switch (event.getType()) {
            case TEXT_BLOCK_DELTA ->
                System.out.print(((TextBlockDeltaEvent) event).getDelta());
            case TOOL_CALL_START ->
                System.out.println("\n🔧 调用工具: " +
                    ((ToolCallStartEvent) event).getToolCallName());
            case THINKING_BLOCK_DELTA ->
                System.out.print("💭 " +
                    ((ThinkingBlockDeltaEvent) event).getDelta());
            case AGENT_END ->
                System.out.println("\n✅ 回复完成");
        }
    })
    .blockLast();

4.2 Spring WebFlux SSE 端点

@GetMapping(value = "/chat/stream", produces = MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<ServerSentEvent<String>> streamChat(
        @RequestParam String message,
        @RequestParam String sessionId,
        @RequestParam String userId) {

    RuntimeContext ctx = RuntimeContext.builder()
        .sessionId(sessionId)
        .userId(userId)
        .build();

    return agent.streamEvents(new UserMessage(message), ctx)
        .filter(e -> e.getType() == AgentEventType.TEXT_BLOCK_DELTA)
        .map(e -> ServerSentEvent.<String>builder()
            .data(((TextBlockDeltaEvent) e).getDelta())
            .build());
}

4.3 多 Agent 事件追踪

agent.streamEvents(new UserMessage("帮我完成数据分析"))
    .doOnNext(event -> {
        String source = event.getSource();
        if (source != null) {
            // 来自子 Agent 的事件
            System.out.println("[" + source + "] " + event.getType());
        } else {
            // 来自主 Agent 的事件
            System.out.println("[main] " + event.getType());
        }
    })
    .blockLast();

五、消息与事件的协作关系

5.1 静态 vs 动态

维度 Message (Msg) Event (AgentEvent)
本质 静态数据载体 动态执行信号
生命周期 持久化存储 瞬时流过
用途 上下文传递、状态恢复 实时渲染、监控、干预
粒度 完整消息 增量片段
产生时机 推理完成后 推理过程中

5.2 转换关系

事件流(实时)                    消息(持久化)
─────────────                    ─────────────
TextBlockDelta × N    ──聚合──→  Msg(ASSISTANT, [TextBlock])
ToolCallStart         ──记录──→  Msg(ASSISTANT, [ToolUseBlock])
ToolResult            ──记录──→  Msg(TOOL, [ToolResultBlock])

5.3 事件驱动的消息更新

在 2.0 架构中,消息的构建是事件驱动的:

// 内部实现逻辑(简化)
MsgBuilder builder = Msg.builder().role(MsgRole.ASSISTANT);

eventStream.subscribe(event -> {
    if (event instanceof TextBlockEndEvent e) {
        builder.content(new TextBlock(e.getFullText()));
    }
    if (event instanceof ToolCallStartEvent e) {
        builder.content(new ToolUseBlock(e.getId(), e.getName(), e.getInput()));
    }
});

// 流结束后 → 完整 Msg 写入 AgentState

六、与 1.x 的对比:消息模型演进

维度 1.x 2.0
消息类型 多种 Msg 子类(TextMsg, ImageMsg…) 统一 Msg + ContentBlock
类型安全 运行时检查 构造期 sealed class 强校验
多模态 附加支持 原生一等公民
工具调用 特殊字段 ToolUseBlock / ToolResultBlock
思考过程 无独立表示 ThinkingBlock 独立承载
事件系统 Hook 回调(扁平) 35+ 类型化事件(结构化)
流式输出 有限支持 完整三段式事件流
子 Agent 追踪 source 路径精确标识

七、工程化最佳实践

7.1 事件日志与审计

agent.streamEvents(userMsg, ctx)
    .doOnNext(event -> {
        auditLog.record(AuditEntry.builder()
            .eventId(event.getId())
            .timestamp(event.getCreatedAt())
            .type(event.getType())
            .source(event.getSource())
            .userId(ctx.getUserId())
            .sessionId(ctx.getSessionId())
            .build());
    })
    .subscribe();

7.2 Token 消耗监控

.doOnNext(event -> {
    if (event instanceof ModelCallEndEvent e) {
        metrics.recordTokenUsage(
            e.getModelName(),
            e.getPromptTokens(),
            e.getCompletionTokens()
        );
    }
})

7.3 异常告警

.doOnNext(event -> {
    if (event instanceof ErrorEvent e) {
        alertService.fire(Alert.builder()
            .level(AlertLevel.CRITICAL)
            .message("Agent 执行异常: " + e.getErrorMessage())
            .source(event.getSource())
            .build());
    }
})

7.4 HITL 审批流集成

.doOnNext(event -> {
    if (event instanceof PermissionRequestEvent e) {
        // 推送到审批系统
        approvalService.submit(ApprovalRequest.builder()
            .toolName(e.getToolName())
            .parameters(e.getParameters())
            .agentSource(event.getSource())
            .build());
    }
})

八、设计哲学总结

AgentScope Java 2.0 的消息与事件系统体现了三个核心设计原则:

8.1 类型即文档

使用 sealed class + record,让编译器成为第一道防线。开发者无需查阅文档即可通过 IDE 自动补全了解所有可能的 ContentBlock 和 Event 类型。

8.2 事件即接口

事件流是框架与外部世界的唯一实时接口。无论是 Web 前端、TUI 终端、监控系统还是审批流程,都通过同一套事件流接入。

8.3 消息即状态

Msg 不只是"传话",它是 AgentState 的持久化单元。会话恢复、上下文压缩、记忆提炼,全部基于 Msg 进行操作。

九、结语

AgentScope Java 2.0 的消息与事件系统,用类型安全解决了"消息混乱"问题,用结构化事件解决了"执行黑箱"问题,用三段式模式解决了"流式渲染"问题。
对于 Java 开发者而言,这套设计完美契合了 JVM 生态的强类型传统:

  • sealed class 保证穷举性
  • record 保证不可变性
  • Flux 保证响应式
  • 构造期校验保证 fail-fast

消息定义了智能体"说什么",事件定义了智能体"怎么做"。两者合一,构成了一个可观测、可干预、可信赖的智能体执行流。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐