256 lines
6.2 KiB
Markdown
256 lines
6.2 KiB
Markdown
# 事件流说明(轮询返回 events)
|
||
|
||
`GET /api/v1/tasks/<task_id>` 返回的 `events` 字段,是一个按时间顺序(`idx` 递增)的**事件流**。
|
||
|
||
本项目的目标是:**与网页端 WebSocket 事件保持同一粒度**。因此你会看到:
|
||
|
||
- token 级别的 `text_chunk`/`thinking_chunk`
|
||
- 工具调用生命周期事件(准备/意图/开始/状态更新)
|
||
- 工具执行结果的状态更新与系统消息
|
||
|
||
> 提示:客户端应只依赖 `type` 字符串与 `data` 字段,并对未知事件类型保持兼容(忽略或记录)。
|
||
|
||
## 1. 事件统一包裹格式(envelope)
|
||
|
||
每个事件的结构:
|
||
|
||
```json
|
||
{
|
||
"idx": 12,
|
||
"type": "text_chunk",
|
||
"data": { "content": "你好", "index": 5, "elapsed": 0.003 },
|
||
"ts": 1769182968.154797
|
||
}
|
||
```
|
||
|
||
字段含义:
|
||
|
||
- `idx`:事件序号(从 0 开始递增),用于轮询 offset 与去重
|
||
- `type`:事件类型(字符串)
|
||
- `data`:事件载荷(不同 type 不同结构)
|
||
- `ts`:服务端记录的 UNIX 时间戳(秒,float)
|
||
|
||
## 2. offset 与去重
|
||
|
||
轮询接口支持 `from` 参数:
|
||
|
||
- `GET /api/v1/tasks/<task_id>?from=0`:从头拉取
|
||
- `GET /api/v1/tasks/<task_id>?from=next_offset`:增量拉取
|
||
|
||
正确做法:
|
||
|
||
1. 客户端本地保存 `offset`(初始 0)
|
||
2. 轮询请求带上 `from=offset`
|
||
3. 处理返回 `events`
|
||
4. 将 `offset` 更新为返回的 `next_offset`
|
||
|
||
## 3. 常见事件类型与载荷
|
||
|
||
以下为**常见**事件类型(实际可能随版本增加更多 type)。
|
||
|
||
### 3.1 AI 消息生命周期
|
||
|
||
#### `ai_message_start`
|
||
|
||
AI 新一轮回复开始(一次用户消息可能触发多次迭代,但 UI 通常以此作为“助手消息开始”的信号)。
|
||
|
||
```json
|
||
{ "type": "ai_message_start", "data": {} }
|
||
```
|
||
|
||
### 3.2 思考(reasoning)
|
||
|
||
#### `thinking_start`
|
||
|
||
```json
|
||
{ "type": "thinking_start", "data": {} }
|
||
```
|
||
|
||
#### `thinking_chunk`
|
||
|
||
逐段思考内容(细粒度流式)。
|
||
|
||
```json
|
||
{ "type": "thinking_chunk", "data": { "content": "..." } }
|
||
```
|
||
|
||
#### `thinking_end`
|
||
|
||
思考结束(带完整思考文本)。
|
||
|
||
```json
|
||
{ "type": "thinking_end", "data": { "full_content": "..." } }
|
||
```
|
||
|
||
建议客户端处理方式:
|
||
|
||
- 若你想还原“实时思考流”,就持续 append `thinking_chunk.data.content`;
|
||
- 若你只想最终值,可忽略 chunk,只在 `thinking_end` 读取 `full_content`。
|
||
|
||
### 3.3 正文输出(token 级)
|
||
|
||
#### `text_start`
|
||
|
||
```json
|
||
{ "type": "text_start", "data": {} }
|
||
```
|
||
|
||
#### `text_chunk`
|
||
|
||
逐 token/子串输出。字段:
|
||
|
||
- `content`:本次增量内容
|
||
- `index`:从 1 开始的 chunk 序号(仅用于调试/对齐)
|
||
- `elapsed`:与上一 chunk 的时间间隔(秒)
|
||
|
||
```json
|
||
{
|
||
"type": "text_chunk",
|
||
"data": { "content": "你好", "index": 5, "elapsed": 0.003 }
|
||
}
|
||
```
|
||
|
||
#### `text_end`
|
||
|
||
正文结束,包含 `full_content`(完整文本)。
|
||
|
||
```json
|
||
{ "type": "text_end", "data": { "full_content": "..." } }
|
||
```
|
||
|
||
建议客户端处理方式:
|
||
|
||
- 实时显示:append `text_chunk.data.content`
|
||
- 最终落盘:以 `text_end.data.full_content` 为准(如果你做了实时 append,最终可用 full_content 校验/纠偏)
|
||
|
||
> 注意:若模型在工具调用后没有再次输出总结文本,则可能出现“最后一轮事件是工具相关,没有 text_end”的情况;这属于模型行为/迭代次数限制导致,并不代表任务异常。
|
||
|
||
### 3.4 工具调用(tool)相关
|
||
|
||
工具链路通常会出现如下事件:
|
||
|
||
1) `tool_hint`(可选,提前猜测意图)
|
||
2) `tool_preparing`(模型开始输出 tool_calls 时)
|
||
3) `tool_intent`(从增量 arguments 中抽取 intent 字段)
|
||
4) `tool_start`(真正执行工具时)
|
||
5) `update_action`(执行进度与结果状态更新)
|
||
|
||
#### `tool_hint`(可选)
|
||
|
||
```json
|
||
{
|
||
"type": "tool_hint",
|
||
"data": {
|
||
"id": "early_web_search_...",
|
||
"name": "web_search",
|
||
"message": "检测到可能需要调用 web_search...",
|
||
"confidence": "low",
|
||
"conversation_id": "conv_..."
|
||
}
|
||
}
|
||
```
|
||
|
||
#### `tool_preparing`
|
||
|
||
```json
|
||
{
|
||
"type": "tool_preparing",
|
||
"data": {
|
||
"id": "web_search:0",
|
||
"name": "web_search",
|
||
"message": "准备调用 web_search...",
|
||
"intent": "搜索明日方舟终末地游戏信息",
|
||
"conversation_id": "conv_..."
|
||
}
|
||
}
|
||
```
|
||
|
||
#### `tool_intent`
|
||
|
||
```json
|
||
{
|
||
"type": "tool_intent",
|
||
"data": {
|
||
"id": "web_search:0",
|
||
"name": "web_search",
|
||
"intent": "搜索明日方舟终末地游戏信息",
|
||
"conversation_id": "conv_..."
|
||
}
|
||
}
|
||
```
|
||
|
||
#### `tool_start`
|
||
|
||
```json
|
||
{
|
||
"type": "tool_start",
|
||
"data": {
|
||
"id": "tool_0_web_search_...",
|
||
"name": "web_search",
|
||
"arguments": { "...": "..." },
|
||
"preparing_id": "web_search:0",
|
||
"monitor_snapshot": null,
|
||
"conversation_id": "conv_..."
|
||
}
|
||
}
|
||
```
|
||
|
||
说明:
|
||
|
||
- `preparing_id` 用于把“模型输出的 tool_call”与“真实执行的工具卡片”关联起来
|
||
- `arguments` 为工具参数(通常是 JSON 对象)
|
||
|
||
#### `update_action`
|
||
|
||
该事件用于更新工具卡片(或其他 action)的状态。字段随工具不同可能略有差异,但通常包含:
|
||
|
||
- `id`:与 `tool_start.data.id` 对应
|
||
- `status`:`running/completed/failed/...`
|
||
- `message`:可读描述
|
||
- 可能还带 `result/summary/monitor_snapshot/...`
|
||
|
||
客户端建议:
|
||
|
||
- 把 `update_action` 当作通用的“状态更新”事件处理;
|
||
- 对未知字段保持兼容(直接展示或忽略)。
|
||
|
||
### 3.5 系统消息与错误
|
||
|
||
#### `system_message`
|
||
|
||
```json
|
||
{ "type": "system_message", "data": { "content": "..." } }
|
||
```
|
||
|
||
#### `error`
|
||
|
||
任务内错误(不同于 HTTP 层错误)。出现后通常任务会进入 `failed`。
|
||
|
||
```json
|
||
{ "type": "error", "data": { "message": "..." } }
|
||
```
|
||
|
||
### 3.6 任务结束
|
||
|
||
#### `task_complete`
|
||
|
||
```json
|
||
{
|
||
"type": "task_complete",
|
||
"data": {
|
||
"total_iterations": 2,
|
||
"total_tool_calls": 1,
|
||
"auto_fix_attempts": 0
|
||
}
|
||
}
|
||
```
|
||
|
||
> 结束条件以轮询返回的 `status != running` 为准;`task_complete` 是一个“完成事件”,但客户端仍应读取 `status`。
|
||
|
||
## 4. 事件缓冲与性能建议
|
||
|
||
- 服务端每个任务保存最近 **1000** 条事件(队列会丢弃更早的数据)
|
||
- 建议轮询间隔 `0.5s ~ 2s`
|
||
- 长任务建议客户端把 `text_chunk` 与关键工具事件持久化到本地,避免错过
|
||
|