
# Turbo Agent 事件协议 v2.0：落库整合规范（Trace / Run / Flow）

最后更新：2026-01-13

本文档用于**把状态流（run.lifecycle）与数据流（content.*）的落库模型整合到一个统一结构**，并明确 Store 在以下场景下的行为：

- 在线流式响应（速度优先）与异步落库（吞吐优先）并存
- 断线重连：先从 Store 拉取当前状态，再接续监听并丢弃已落库事件
- 回滚（control.rollback）：按时间戳截断 + 回放重建
- 嵌套调用：同一根 trace 下以 trace_path 表达子节点

重要约定（与 core schema / 既有实现一致）：

- **trace_id**：对观察侧永远是“全局 Root Trace ID”（纯 UUID）。
- **trace_path**：嵌套链路（子执行者的原始 trace_id 追加形成的列表）。Root 节点应省略 `trace_path` 或使用空数组 `[]`。
- **run.lifecycle.* == 一次 run 的状态流**：每个 `run_id` 只对应一条生命周期骨架（由 `run.lifecycle.*` 事件序列构成）；同一 `trace` 节点下允许多个 `run_id`，仅用于重试/挂起后恢复。
- **run_path**：事件信封携带的“run 递归队列”，用于描述同一 trace 节点下的多次运行链路（重试/恢复）。Store 应将其落到 RunRecord 中，以便管理重试关系。
- **timestamp 是唯一排序与去重依据**：Store 落库必须使用事件信封 `timestamp`（UTC Unix 秒级时间戳 * 1000 的毫秒浮点），并写入 `updated_at`（或等价字段），不得使用消费时间。

---

## 0. 事件流聚合器架构（Aggregator Architecture）

### 0.1 设计目标

本架构提供一套**内存中事件流聚合器**，用于将流式事件协议转换为结构化状态快照，支持：

1. **实时聚合**：事件到达即刻累积到聚合器状态，无需等待完整流结束
2. **快照导出**：随时导出当前聚合状态（`snapshot()`），用于 UI 渲染或中间检查点
3. **状态转换**：将聚合结果转换为持久化模型（`to_state()`），对接数据库 schema
4. **树形路由**：基于 `trace_path` 自动构建 trace 树并路由事件到正确节点
5. **断点恢复**：通过 `watermark_timestamp` 支持幂等消费与断线重连

### 0.2 核心聚合器层级

```
EventTreeAggregator (事件树聚合器)
    ├── TraceEventAggregator (Trace 节点聚合器) - 按 trace_id 分组
    │       ├── SingleRunEventAggregator (Run 聚合器) - 按 run_id 分组
    │       │       ├── ReActFlow (ReAct 轮次聚合器) - 按 react_id 分组
    │       │       │       ├── TextBuffer (文本累积)
    │       │       │       ├── TextBuffer (推理累积)
    │       │       │       └── ActionAggregate[] (动作聚合) - 按 action_id 分组
    │       │       └── AgentResultAggregate (智能体交付结果聚合)
    │       └── control (控制流聚合：interrupt/suggestions/rollback)
```

### 0.3 聚合器职责划分

#### EventTreeAggregator（事件树聚合器）
- **输入**：原始事件流（BaseEvent）
- **职责**：
  - 根据 `trace_path` 构建和维护 trace 节点树
  - 路由事件到对应的 `TraceEventAggregator`
  - 必要时修正 `trace_id`（使用 `model_copy` 确保节点内校验通过）
- **输出**：
  - `snapshot_all()`: 所有 trace 节点的聚合快照字典
  - `snapshot_root()`: 根节点快照
- **关键字段**：
  - `root_trace_id`: 根 trace 标识
  - `nodes: Dict[str, TraceEventAggregator]`: 扁平化的 trace 节点字典

#### TraceEventAggregator（Trace 节点聚合器）
- **输入**：属于该 trace 的事件（已由 EventTreeAggregator 路由）
- **职责**：
  - 管理该 trace 下的多个 run（重试/恢复场景）
  - 聚合控制流事件（`control.interrupt/suggestions/rollback`）
  - 维护父子关系元信息（`parent_trace_id`, `parent_action_id`, `children`）
  - 跨 run 合并数据（input 取首次，output 取最后一次）
- **输出**：
  - `snapshot()`: `TraceAggregationResult`（包含所有 run 的合并状态）
  - `to_state()`: 根据 `executor_type` 转换为 `Message`（Agent）或 `ToolCallRecord`（Tool）
- **关键字段**：
  - `runs: Dict[str, SingleRunEventAggregator]`: run_id -> run 聚合器
  - `run_order: List[str]`: 有序的 run_id 列表（用于重试链路追溯）
  - `control: Dict`: 控制流当前态（latest_interrupt/suggestions/rollback）
  - `parent_trace_id`, `parent_action_id`, `children`: 树结构元信息

#### SingleRunEventAggregator（Run 聚合器）
- **输入**：属于该 run 的事件（已由 TraceEventAggregator 路由）
- **职责**：
  - 聚合生命周期事件（`run.lifecycle.*`）到 `status/history/keepalive`
  - 管理多个 `ReActFlow`（按 `react_id` 分组）
  - 维护 `action_id -> react_id` 映射（用于 Delta/End 事件的 react 归属推断）
  - 聚合 `AgentResultAggregate`（智能体交付结果）
- **输出**：
  - `snapshot()`: `NodeAggregationResult`（单个 run 的完整状态）
- **关键字段**：
  - `react_flows: Dict[Optional[str], ReActFlow]`: react_id -> ReActFlow
  - `action_react_map: Dict[str, Optional[str]]`: action_id -> react_id 映射
  - `status`, `input_data`, `output`, `usage`, `error`: 从生命周期事件累积
  - `executor_id`, `executor_type`, `executor_path`: 执行者元信息

#### ReActFlow（ReAct 轮次聚合器）
- **输入**：属于该 react_id 的内容事件（已由 SingleRunEventAggregator 路由）
- **职责**：
  - 聚合文本流（`content.text.*`）
  - 聚合推理流（`content.reasoning.*`）
  - 管理该轮次的多个 `ActionAggregate`（按 `action_id` 分组）
  - 提供 `to_state()` 转换为 `ReActRound`
- **输出**：
  - `to_state(belong_to_message_id)`: `ReActRound`
- **关键字段**：
  - `text: TextBuffer`: 文本累积器
  - `reasoning: TextBuffer`: 推理累积器
  - `actions: Dict[str, ActionAggregate]`: action_id -> ActionAggregate

#### ActionAggregate（动作聚合器）
- **输入**：属于该 action_id 的事件（已由 ReActFlow 路由）
- **职责**：
  - 聚合 Action 调用（`content.action.*`）：intent/arguments
  - 聚合 Action 结果（`content.action.result.*`）：output/error/status
  - 提供 `to_state()` 转换为 `Action`（包含嵌套的 `ToolCallRecord`）
- **输出**：
  - `to_state()`: `Action`（其中 `records[0].id = result_record_id`，对应子 trace_id）
- **关键字段**：
  - `intent_parts`, `arguments`: Action 调用信息
  - `result_record_id`, `result_output`, `result_error`, `result_status`: Action 结果信息

### 0.4 事件路由流程

```
事件到达 -> EventTreeAggregator.on_event()
    |
    ├─> 解析 trace_path，定位或创建目标 trace 节点
    |   （若 trace_path=[C1, G1]，则路由到 leaf trace G1）
    |
    ├─> 必要时 patch trace_id（使用 model_copy 确保节点内校验通过）
    |
    └─> TraceEventAggregator.on_event()
            |
            ├─> 控制流事件（control.*）-> 聚合到 control/control_history
            |
            └─> 其他事件 -> 定位或创建对应 run -> SingleRunEventAggregator.on_event()
                    |
                    ├─> 生命周期事件（run.lifecycle.*）-> 更新 status/history/keepalive
                    |
                    ├─> 文本/推理/Action 事件 -> 识别 react_id -> ReActFlow.on_event()
                    |       |
                    |       ├─> content.text.* -> TextBuffer 累积
                    |       ├─> content.reasoning.* -> TextBuffer 累积
                    |       └─> content.action.* / content.action.result.* -> ActionAggregate 累积
                    |
                    └─> Agent 结果事件（content.agent.result.*）-> AgentResultAggregate 累积
```

### 0.5 react_id 与 action_id 映射机制

**问题**：`content.action.delta/end` 和 `content.action.result.*` 事件可能不携带 `react_id`，但必须路由到正确的 `ReActFlow`。

**解决方案**：
1. **注册阶段**：当收到 `content.action.start`（通常携带 `react_id`）时，记录 `action_id -> react_id` 映射到 `action_react_map`
2. **查找阶段**：后续 Delta/End/Result 事件优先使用事件自带的 `react_id`，若缺失则从 `action_react_map` 查找
3. **兜底策略**：若查找失败，使用 `react_id=None`（归入默认 flow）

### 0.6 状态维护字段

所有聚合器（`TraceEventAggregator`, `ReActFlow`, `ActionAggregate`）统一维护：

- **`is_closed`**：是否终结（由事件流控制，如 `run.lifecycle.completed/failed/cancelled` 或 `content.action.result.end`）
- **`is_stored`**：是否已持久化写库（由外部调用方通过 `set_stored()` 控制）
- **`stored_at`**：最近一次写库时间戳（UTC 毫秒浮点）
- **`updated_at`**：最近更新时间戳（由事件 `timestamp` 自动维护）

说明：
- `is_closed` 用于判断该聚合单元是否可以最终落库（避免落库不完整数据）
- `is_stored` 用于幂等性控制（避免重复落库）
- 聚合器本身**不负责落库逻辑**，仅提供状态标记与 `to_state()` 转换接口

### 0.7 使用示例

#### 场景 1：在线流式聚合

```python
from turbo_agent_core.events.aggregators import EventTreeAggregator

# 初始化树聚合器
tree = EventTreeAggregator(root_trace_id="trace_abc")

# 事件流到达
for event in event_stream:
    tree.on_event(event)
    
    # 实时获取根节点快照（用于 UI 推送）
    snapshot = tree.snapshot_root()
    if snapshot:
        ws_push(snapshot)

# 流结束后转换为持久化模型
root_agg = tree.nodes.get(tree.root_trace_id)
if root_agg:
    state = root_agg.to_state()  # Message 或 ToolCallRecord
    if isinstance(state, Message):
        db.save_message(state)
    elif isinstance(state, ToolCallRecord):
        db.save_tool_call_record(state)
```

#### 场景 2：断线重连与幂等消费

```python
# 1. 从 Store 拉取已落库状态
stored_trace = db.get_root_trace("trace_abc")
watermark = stored_trace["watermark_timestamp"]

# 2. 恢复聚合器状态（可选：从 stored_trace 重建）
tree = EventTreeAggregator(root_trace_id="trace_abc")

# 3. 监听新事件流
for event in event_stream:
    # 幂等过滤：丢弃已落库事件
    if event.timestamp <= watermark:
        continue
    
    tree.on_event(event)
    
    # 推进 watermark
    watermark = max(watermark, event.timestamp)
```

#### 场景 3：嵌套调用与子 trace

```python
# 事件流中包含子 trace（trace_path 非空）
# EventTreeAggregator 自动构建树结构

tree = EventTreeAggregator(root_trace_id="root_123")

# 收到 trace_path=["child_456"] 的事件
event = BaseEvent(
    trace_id="root_123",  # 对外呈现的全局 Root Trace ID
    trace_path=["child_456"],  # 子执行者的原始 trace_id
    ...
)
tree.on_event(event)

# 访问子节点
child_agg = tree.nodes.get("child_456")
child_snapshot = child_agg.snapshot()

# 导出所有节点快照
all_snapshots = tree.snapshot_all()
# {"root_123": TraceAggregationResult(...), "child_456": TraceAggregationResult(...)}
```

#### 场景 4：回滚与截断

```python
# 收到 control.rollback 事件
rollback_event = ControlRollbackEvent(
    trace_id="trace_abc",
    payload=ControlRollbackPayload(
        target_timestamp=1678900000300.0,
        reason="用户回退操作"
    )
)

# TraceEventAggregator 自动处理：
# 1. 截断 history（丢弃 timestamp > target_timestamp 的记录）
# 2. 重放剩余 history 重建派生状态
tree.on_event(rollback_event)
```

### 0.8 与 Store 落库的协同

聚合器与 Store 的职责边界：

| 职责 | 聚合器（Aggregator） | Store 落库层 |
|------|---------------------|-------------|
| 事件累积 | ✓ 内存中实时聚合 | ✗ 不处理事件流 |
| 快照导出 | ✓ snapshot() / to_state() | ✗ 仅接收转换后的状态 |
| 幂等控制 | ✓ watermark_timestamp 字段 | ✓ 读取 watermark 并过滤事件 |
| 持久化 | ✗ 不写数据库 | ✓ 将 state 写入数据库 |
| 回滚截断 | ✓ 截断 history 并重放 | ✓ 执行数据库回滚（可选） |
| 树结构路由 | ✓ 基于 trace_path 构建树 | ✗ 仅存储扁平化节点 |

建议的落库流程：

1. **事件消费侧**：使用聚合器实时累积事件
2. **落库触发点**：当 `is_closed=True`（run 完成）或定时检查点时，调用 `to_state()`
3. **Store 层**：
   - 接收转换后的 `Message` / `ToolCallRecord` / `Action` 等状态对象
   - 检查 `is_stored` 标记，避免重复落库
   - 写入数据库后，回调聚合器的 `set_stored(True, timestamp)`
4. **watermark 推进**：Store 维护全局 watermark，用于断线重连过滤

---

## 1. Store 层职责与数据模型

### 1.1 职责边界

在当前架构下，**Store 层不直接消费事件流**，而是接收聚合器转换后的状态对象并持久化：

- **接收状态对象**：从聚合器的 `to_state()` 方法获取 `Message` / `ToolCallRecord` / `Action` 等
- **持久化存储**：将状态对象写入数据库（Postgres / SQLite / Manticore Search）
- **幂等控制**：检查 `is_stored` 标记，避免重复落库
- **watermark 维护**：维护全局 watermark，用于断线重连时过滤重复事件
- **查询服务**：提供状态查询接口，支持 UI 渲染和断线重连恢复

### 1.2 核心数据模型

Store 层存储的数据结构与 `turbo_agent_core.schema.states` 保持一致：

#### Message（智能体执行结果）
```python
class Message(BaseModel):
    id: str  # trace_id
    role: MessageRole
    content: Optional[Union[str, List[Union[str, Dict[str, Any]]]]]
    reasoning_content: Optional[str]
    reactRounds: List[ReActRound]
    actions: List[Action]
    status: MessageStatus
    tokenCost: Optional[float]
    moneyCost: Optional[float]
    modelUsed: Optional[str]
    created_at: datetime
    updated_at: datetime
    ancestors: List[str]  # [parent_trace_id] if exists
    children: List[str]   # children trace_ids
```

#### ToolCallRecord（工具调用结果）
```python
class ToolCallRecord(BaseModel):
    id: str  # trace_id
    tool_project_id: Optional[str]
    tool_name_id: Optional[str]
    tool_type: Literal["API", "LLM", "Agent"]
    input: Optional[JSON]
    result: Optional[JSON]
    error_message: Optional[JSON]
    status: Literal["pending", "running", "succeeded", "failed"]
    time_cost: Optional[float]
    created_at: datetime
    updated_at: datetime
```

#### ReActRound（推理-行动轮次）
```python
class ReActRound(BaseModel):
    id: str  # react_id
    thought: Optional[str]
    content: Optional[Union[str, Dict[str, Any]]]
    belongToMessageId: str
    actions: List[Action]
```

#### Action（动作记录）
```python
class Action(BaseModel):
    id: str  # action_id
    name: Optional[str]
    intent: Optional[str]
    input: Optional[Union[str, Dict[str, Any]]]
    observation: Optional[Union[str, Dict[str, Any]]]
    status: ActionStatus
    records: List[ToolCallRecord]  # records[0].id = result_record_id (子 trace_id)
    created_at: datetime
    updated_at: datetime
```

### 1.3 扁平化 Trace 树存储

为避免深层嵌套，Store 采用**扁平化存储**：

- 所有 trace 节点（root + 子 trace）存储在同一层级
- 父子关系通过 `Message.ancestors` 和 `Message.children` 表达
- 子 trace 的 `id` 来自事件的 `trace_path` 末尾元素

存储结构示例：

```
RootTrace (trace_abc)
├── Message(id=trace_abc, children=[trace_def, trace_ghi])
├── Message(id=trace_def, ancestors=[trace_abc], children=[trace_jkl])
└── Message(id=trace_ghi, ancestors=[trace_abc])
```

---

## 2. 断线重连与幂等消费

### 2.1 重连流程

1. **拉取已落库状态**：从 Store 获取 `trace_id` 对应的最新状态与 `watermark_timestamp`
2. **恢复聚合器**：可选地从已落库状态重建聚合器内部状态
3. **监听新事件**：订阅事件流，按 `timestamp > watermark` 过滤
4. **推进 watermark**：每次落库成功后更新 watermark

### 2.2 幂等保证

- **聚合器层**：通过 `watermark_timestamp` 过滤重复事件
- **Store 层**：通过 `is_stored` 标记避免重复写库
- **时间戳严格性**：必须使用事件信封的 `timestamp`（UTC 毫秒浮点），不得使用消费时间

---

## 3. 回滚支持

### 3.1 回滚语义

当收到 `control.rollback` 事件时，聚合器自动执行：

1. **截断 history**：丢弃所有 `timestamp > target_timestamp` 的历史记录
2. **重放重建**：按 timestamp 排序回放剩余历史，重建派生状态
3. **更新 watermark**：将 watermark 回退到 `target_timestamp`

### 3.2 Store 层配合

Store 层可选地执行数据库回滚：

- **软回滚**：标记被回滚的记录为"已作废"，保留审计历史
- **硬回滚**：物理删除 `timestamp > target_timestamp` 的记录（不推荐）

---

## 4. 线上监听策略

### 4.1 trace_id 发现

Server 下发任务时通常还不知道 `trace_id`（trace_id 在执行者开始流式后才生成）。

建议流程：

1. **监听全局 `run.lifecycle.created` 流**：发现新的 root trace_id
2. **业务 ID 过滤**：根据 `input_data` 中的绑定信息（如 `conversation_id` / `task_id`）识别归属
3. **拉起子监听**：拿到 root `trace_id` 后，订阅该 trace 的事件流

### 4.2 子 trace 识别

- 事件信封 `trace_id` 恒为 root
- 事件携带 `trace_path` 时，Store 需要确保所有节点存在并建立父子关系：
  - 叶子节点：`leaf_trace_id = trace_path[-1]`
  - 父节点：若 `len(trace_path)==1` 则父为 root；否则父为 `trace_path[-2]`

### 4.3 路由规则

- 若 `trace_path` 为空或缺失：事件属于 root trace 节点
- 若 `trace_path` 非空：事件属于叶子 trace 节点（`trace_path[-1]`）

---

## 5. 附录：与旧版协议的兼容性说明

### 5.1 已废弃的概念

- **DataFlowAggregate**：不再作为独立存储单元，已整合到 `Message.reactRounds` 和 `Message.actions`
- **RunRecord**：不再作为中间层，直接存储 `Message` 或 `ToolCallRecord`
- **control_history**：控制流信息整合到 `Message` 的 `status` 和业务逻辑中

### 5.2 迁移指南

如果系统中存在旧版 Store 实现（直接消费事件流），建议迁移路径：

1. **引入聚合器层**：在 Store 前加入 `EventTreeAggregator`
2. **修改 Store 接口**：从 `on_event(event)` 改为 `save_state(state)`
3. **清理事件处理逻辑**：删除 Store 内部的累积/合并逻辑
4. **保留幂等机制**：继续使用 `watermark_timestamp` 和 `is_stored` 标记


