# 前后端数据流通指南（Agent Chat）

本文档仅描述**当前后端实现**的 runs/events/history 数据流，不定义视觉细节。

---

## 1) 总体流程

1. 客户端 `POST /api/v1/agent/runs` 提交 `RunAgentInput`
2. 后端返回 `202` + `taskId/threadId/runId/created`
3. 客户端 `GET /api/v1/agent/runs/{threadId}/events` 订阅 SSE
4. 后端输出 AG-UI 事件（如 `RUN_STARTED`、`TOOL_CALL_RESULT`、`TEXT_MESSAGE_END`）
5. 客户端按需 `GET /api/v1/agent/history` 拉取历史快照（按天）

---

## 2) `/runs` 请求与响应

### 请求

- Body: `RunAgentInput`
- user message 可为纯文本，也可为文本+binary（图片 URL）

### 响应

```json
{
  "taskId": "...",
  "threadId": "...",
  "runId": "...",
  "created": true
}
```

`created` 语义：是否在本次请求中创建了新会话。

---

## 3) `/runs/{threadId}/events` 事件流

### SSE 形式

```text
id: <stream-id>
event: <EVENT_TYPE>
data: <json>

```

### 事件类型

以 `docs/protocols/agent/sse-events.md` 为准。当前重点是：

- 运行生命周期：`RUN_STARTED` / `RUN_FINISHED` / `RUN_ERROR`
- 阶段：`STEP_STARTED` / `STEP_FINISHED`
- 工具：`TOOL_CALL_START` / `TOOL_CALL_ARGS` / `TOOL_CALL_END` / `TOOL_CALL_RESULT`
- 文本完成：`TEXT_MESSAGE_END`

### 文本流策略

当前后端不提供 token 级 `TEXT_MESSAGE_CONTENT` 增量流作为主路径；
而是在 worker 完成后通过 `TEXT_MESSAGE_END` 一次性携带完整语义结果。

---

## 4) `/history` 快照

`GET /api/v1/agent/history` 返回 `HistorySnapshotResponse`：

```json
{
  "scope": "history_day",
  "threadId": "...",
  "day": "2026-03-16",
  "hasMore": false,
  "messages": []
}
```

说明：

- 这是普通 JSON 响应，不是 SSE 事件包装。
- `messages` 已按 seq 升序组织。
- `before` 采用 `YYYY-MM-DD`，语义是向更早日期翻页。

---

## 5) events 与 history 的一致性机制

### 5.1 语义来源一致

两条链路都来自同一运行时输出（worker/tool output）及其持久化元数据。

### 5.2 UI 编译器一致

两条链路都使用后端 `ui_compiler.compile(...)` 将 **worker** 的 `ui_hints` 编译为可渲染结构：

- events：在 runtime 发送事件前编译，字段名为 `ui_schema`
- history：在历史转换时编译，字段名为 `ui_schema`

tool 结果不再走 UI 编译链路：`TOOL_CALL_RESULT` 提供 `tool_call_args` + `result` 组合。

- `metadata.tool_agent_output` 是 tool 消息的完整信源（用于 runtime observation 与 history replay）。
- `message.content` 保持轻量摘要（当前等于 `result`）。
- `tool_call_args` 记录输入参数，`result` 记录输出事实，二者不做冗余重复。

### 5.3 当前命名差异（实现现状）

两条链路字段命名已统一：

- events: `ui_schema`（snake_case）
- history: `ui_schema`（snake_case）

---

## 6) 推荐消费顺序（面向客户端重构）

1. 先以 `/history` 获取首屏快照
2. 再接入 `/events` 处理后续增量
3. 以 `runId` + `messageId/toolCallId` 做去重与合并
4. 统一消费 `ui_schema`

---

## 7) Navigation Action 数据流（ui_schema.actions）

### 7.1 后端生成

- runtime 使用 `ui_hints.action.type = navigation` 产出导航动作。
- 编译后在 `ui_schema` 中保持 `action.type = navigation`、`action.path`、`action.params`。
- 路由来源应受后端静态路由目录约束：
  - `backend/src/core/config/static/route/frontend_routes.yaml`

### 7.2 前端消费（统一解析规则）

- 对 `type = navigation`，前端仅走一条解析路径：
  1. 读取 `path` 作为内部路由目标；
  2. 将 `params` 仅视为 query 参数（不用于 path 模板替换）；
  3. 执行 GoRouter 跳转（建议 `context.go(...)`）。
- `path` 必须是已落地页面路由，且应是已实参化路径（如 `/todo/123`，而非 `/todo/:id`）。

### 7.3 约束建议

- 为了让前端只保留一种解析逻辑，推荐强约束：
  - `path` 只接受内部路由；
  - `params` 只接受标量值（string/number/boolean）；
  - 禁止在 `params` 里放嵌套对象数组。
