Files
social-app/docs/protocols/agent/sse-events.md
T
qzl 641d847008 docs: update agent and UI protocol documentation
- Update api-endpoints.md with new agent endpoints
- Update run-agent-input.md with new input schemas
- Update sse-events.md with new event types
- Update ui-schema.md with schema changes
2026-03-19 18:42:45 +08:00

332 lines
8.2 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Agent SSE Events
本文档描述 `GET /api/v1/agent/runs/{thread_id}/events` 的事件协议。
---
## 1) 事件管道
后端事件流转如下:
1. Runtime 直接产出 AG-UI 事件(如 `RUN_STARTED``TOOL_CALL_RESULT`
2. `agui_codec` 仅做协议对齐与字段净化(例如移除仅后端内部统计字段)
3. 事件同时:
- 持久化到数据库(用于 history)
- 发布到 Redis Stream(用于 SSE
4. `/runs/{thread_id}/events` 从 Redis Stream 读取并输出 SSE
---
## 2) SSE 帧格式
每条事件遵循标准 SSE
```text
id: <redis-stream-id>
event: <AG-UI-EVENT-TYPE>
data: <json>
```
- `id` 可用于断点续流(`Last-Event-ID`
- `event` 与 JSON 内 `type` 一致(例如 `RUN_STARTED`
- 空闲时可能出现 keep-alive 注释帧:
```text
: keep-alive
```
---
## 3) 事件类型(当前实现)
### 3.1 Run 生命周期
#### `RUN_STARTED`
```json
{
"type": "RUN_STARTED",
"threadId": "...",
"runId": "..."
}
```
#### `RUN_FINISHED`
```json
{
"type": "RUN_FINISHED",
"threadId": "...",
"runId": "..."
}
```
#### `RUN_ERROR`
```json
{
"type": "RUN_ERROR",
"threadId": "...",
"runId": "...",
"message": "runtime execution failed",
"code": null
}
```
### 3.2 阶段事件
#### `STEP_STARTED`
```json
{
"type": "STEP_STARTED",
"threadId": "...",
"runId": "...",
"stepName": "router" | "worker" | "memory"
}
```
#### `STEP_FINISHED`
```json
{
"type": "STEP_FINISHED",
"threadId": "...",
"runId": "...",
"stepName": "router" | "worker" | "memory"
}
```
### 3.3 Tool 事件
#### `TOOL_CALL_START`
```json
{
"type": "TOOL_CALL_START",
"threadId": "...",
"runId": "...",
"messageId": "...",
"toolCallId": "...",
"toolCallName": "...",
"stage": "worker" | "memory"
}
```
#### `TOOL_CALL_ARGS`
```json
{
"type": "TOOL_CALL_ARGS",
"threadId": "...",
"runId": "...",
"messageId": "...",
"toolCallId": "...",
"toolCallName": "...",
"args": {},
"stage": "worker" | "memory"
}
```
#### `TOOL_CALL_END`
```json
{
"type": "TOOL_CALL_END",
"threadId": "...",
"runId": "...",
"messageId": "...",
"toolCallId": "...",
"toolCallName": "...",
"stage": "worker" | "memory"
}
```
#### `TOOL_CALL_RESULT`
```json
{
"type": "TOOL_CALL_RESULT",
"threadId": "...",
"runId": "...",
"messageId": "...",
"role": "tool",
"stage": "worker" | "memory",
"tool_name": "...",
"tool_call_id": "...",
"tool_call_args": {},
"status": "success" | "failure" | "partial",
"result": "...",
"error": null
}
```
说明:`TOOL_CALL_RESULT` 不再携带 `ui_schema`。tool 结果通过 `result` 字段提供紧凑、结构化、可执行的信息(优先包含 id/status/count 等关键事实),用于 agent 后续推理与工具编排。
补充约束:
- `tool_call_id` 必须与同次调用的 `TOOL_CALL_START/ARGS/END.toolCallId` 一致,并在每次工具调用中保持唯一。
- `tool_call_args` 仅表示输入参数快照。
- `result` 仅表示执行输出事实,不重复 `tool_call_args` 已包含的输入参数。
### 3.4 文本完成事件
#### `TEXT_MESSAGE_END`
当前实现仅在 worker 输出完成后发送完整结果,不发送 token delta 事件。
```json
{
"type": "TEXT_MESSAGE_END",
"threadId": "...",
"runId": "...",
"messageId": "...",
"role": "assistant",
"stage": "worker" | "memory",
"status": "success" | "partial_success" | "failed",
"answer": "...",
"key_points": [],
"result_type": "execution_report" | "clarification" | "error_report" | "unknown",
"suggested_actions": [],
"error": null,
"ui_schema": {}
}
```
`inputTokens``outputTokens``cost``latencyMs``model` 属于后端内部统计字段,不在 SSE 对外协议中暴露。
当前实现补充说明:
- `TEXT_MESSAGE_END` 在 wire payload 中会包含 `totalTokens``cachedPromptTokens``promptCacheHitTokens``promptCacheMissTokens``reasoningTokens``costSource``usageComplete` 等 usage 摘要字段,供前端观测面板使用。
- 这些字段来自后端 usage 归一化层,属于 AG-UI 事件数据的一部分,不改变 `TEXT_MESSAGE_END` 主结构。
---
## 5) Usage 审计协议(后端内部)
本节描述后端对 LLM usage 的内部审计与计费策略。该协议用于数据库持久化、成本统计与运行观测,不对 SSE 外部协议直接暴露。
### 5.1 当前厂商范围
- DashScopeQwen
- DeepSeek
当前实现仅针对上述两家做深度适配。
### 5.2 原始字段采集(Provider -> Runtime
`TrackingChatModel` 会优先读取 provider 直接字段,读取不到时再从 metadata 补齐。
优先级如下:
1. 直接字段(优先)
- `usage.input_tokens`
- `usage.output_tokens`
- `usage.total_tokens`
- `usage.time`(秒)
- `usage.cost`(若存在)
2. metadata 字段(补齐)
- `metadata.prompt_tokens`
- `metadata.completion_tokens`
- `metadata.total_tokens`
- `metadata.prompt_tokens_details.cached_tokens`
- `metadata.prompt_cache_hit_tokens`
- `metadata.prompt_cache_miss_tokens`
- `metadata.completion_tokens_details.reasoning_tokens`
- `metadata.cost` / `metadata.total_cost`(若存在)
### 5.3 归一化后的内部 usage_summary 字段
`TrackingChatModel.usage_summary()` 当前输出:
- `input_tokens`
- `output_tokens`
- `total_tokens`
- `latency_ms`(由 `usage.time * 1000` 转换)
- `cached_prompt_tokens`
- `prompt_cache_hit_tokens`
- `prompt_cache_miss_tokens`
- `reasoning_tokens`
- `direct_cost`
- `direct_cost_observed`0/1
- `direct_cost_complete`0/1
- `model_call_records`
- `usage_records`
- `direct_cost_records`
- `cost_source``provider` | `catalog_fallback`
### 5.4 成本计算策略(严谨优先)
核心原则:**能直接用 provider 返回就直接用;缺失才 fallback。**
`LiteLLMService.build_usage_metadata()` 执行规则:
1. 仅当以下条件同时满足时使用 provider 直出成本:
- `usageComplete == true``model_call_records == usage_records`
- `direct_cost_observed == 1`
- `direct_cost_complete == 1`
- `direct_cost` 为有效非负数
2. 否则使用 catalog 价格回退计算(`calculate_cost`
### 5.5 Fallback 计费细节
- 档位选择:按 `prompt_tokens` 命中 `pricing_tiers.max_prompt_tokens`
- 公式:
```text
cost = uncached_prompt_tokens * input_cost_per_token
+ cached_prompt_tokens * cached_token_rate
+ completion_tokens * output_cost_per_token
```
- `cached_token_rate` 规则:
- 若 tier 配置了 `cache_hit_cost_per_token` 且 > 0,使用该值
- 否则回退为 `input_cost_per_token`
### 5.6 内部 costSource 语义
- `provider`: 使用 provider 直接成本
- `catalog_fallback`: 正常使用价格表回退
- `catalog_fallback_incomplete_provider_cost`: provider 返回了部分 direct cost,但不完整,回退价格表
- `incomplete_usage_fallback`: usage 本身不完整,回退价格表
### 5.7 DeepSeek / DashScope 当前观测到的返回特征
根据当前线上探针与运行结果:
- 两家都稳定返回:`input_tokens``output_tokens``time`
- `usage.total_tokens` 顶层可能为空,但 `metadata.total_tokens` 可用
- DeepSeek 常见 `prompt_tokens_details.cached_tokens``prompt_cache_hit_tokens``prompt_cache_miss_tokens`
- DashScope 常见 `completion_tokens_details.reasoning_tokens`(可能为 `null`
- 两家当前都未稳定提供直接 `cost` 字段,因此多数场景为 catalog fallback
## 6) 快照事件
编码器支持以下 AG-UI 类型映射:
- `STATE_SNAPSHOT`
- `MESSAGES_SNAPSHOT`
当前 `/runs/{thread_id}/events` 主流程通常不主动产出这两类事件;历史查询请使用 `/history`
---
## 7) 字段命名约定
- 事件顶层通用字段使用 AG-UI 风格:`type``threadId``runId`
- 部分业务字段沿运行时模型历史命名保留下划线:
- `tool_name`
- `tool_call_id`
- `tool_call_args`
- `ui_schema`
这部分命名属于当前后端实现约束,文档与实现保持一致。
## 8) 可见性与上下文装载说明
- 持久化消息使用单字段 `visibility_mask`(位掩码)控制 consumer 可见性。
- `/history` 仅投影 `ui.history` 可见消息。
- 运行时上下文按当前 stage 对应 consumer 位过滤装载,不依赖前端展示可见性。