docs: 更新 Agent 协议文档与部署配置
- 更新 Agent API 端点文档 - 更新 SSE 事件与输入输出文档 - 新增 deploy/.env.prod.example 配置模板
This commit is contained in:
+180
-337
@@ -1,370 +1,213 @@
|
||||
# Agent SSE Events
|
||||
|
||||
本文档记录 Agent Runtime 产生的所有 Server-Sent Events (SSE) 事件,用于前端实时展示。
|
||||
本文档描述 `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>
|
||||
|
||||
```
|
||||
pipeline.emit()
|
||||
↓
|
||||
AgentScopeEventPipeline.emit()
|
||||
├─→ store.persist() → 持久化到数据库
|
||||
└─→ bus.publish() → 发布到 Redis Stream
|
||||
↓
|
||||
前端通过 GET /runs/{thread_id}/events 读取
|
||||
```
|
||||
|
||||
## 事件统一格式
|
||||
- `id` 可用于断点续流(`Last-Event-ID`)
|
||||
- `event` 与 JSON 内 `type` 一致(例如 `RUN_STARTED`)
|
||||
- 空闲时可能出现 keep-alive 注释帧:
|
||||
|
||||
所有事件在 Redis 中传输时都包含以下字段:
|
||||
```text
|
||||
: keep-alive
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: string, // 事件类型
|
||||
threadId: string, // 会话 ID
|
||||
runId: string, // 运行 ID
|
||||
data: object // 事件数据
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 1. Orchestrator 生命周期事件
|
||||
## 3) 事件类型(当前实现)
|
||||
|
||||
### run.started
|
||||
### 3.1 Run 生命周期
|
||||
|
||||
Agent 开始运行时触发。
|
||||
#### `RUN_STARTED`
|
||||
|
||||
```typescript
|
||||
```json
|
||||
{
|
||||
type: "run.started",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {}
|
||||
"type": "RUN_STARTED",
|
||||
"threadId": "...",
|
||||
"runId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### run.finished
|
||||
#### `RUN_FINISHED`
|
||||
|
||||
Agent 成功完成时触发。
|
||||
|
||||
```typescript
|
||||
```json
|
||||
{
|
||||
type: "run.finished",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {}
|
||||
"type": "RUN_FINISHED",
|
||||
"threadId": "...",
|
||||
"runId": "..."
|
||||
}
|
||||
```
|
||||
|
||||
### run.error
|
||||
#### `RUN_ERROR`
|
||||
|
||||
Agent 运行出错时触发。
|
||||
|
||||
```typescript
|
||||
```json
|
||||
{
|
||||
type: "run.error",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
message: "runtime execution failed"
|
||||
}
|
||||
"type": "RUN_ERROR",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"message": "runtime execution failed",
|
||||
"code": null
|
||||
}
|
||||
```
|
||||
|
||||
### 3.2 阶段事件
|
||||
|
||||
#### `STEP_STARTED`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "STEP_STARTED",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"stepName": "router" | "worker"
|
||||
}
|
||||
```
|
||||
|
||||
#### `STEP_FINISHED`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "STEP_FINISHED",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"stepName": "router" | "worker"
|
||||
}
|
||||
```
|
||||
|
||||
### 3.3 Tool 事件
|
||||
|
||||
#### `TOOL_CALL_START`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TOOL_CALL_START",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
#### `TOOL_CALL_ARGS`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TOOL_CALL_ARGS",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"args": {},
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
#### `TOOL_CALL_END`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TOOL_CALL_END",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"toolCallId": "...",
|
||||
"toolCallName": "...",
|
||||
"stage": "worker"
|
||||
}
|
||||
```
|
||||
|
||||
#### `TOOL_CALL_RESULT`
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TOOL_CALL_RESULT",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"role": "tool",
|
||||
"stage": "worker",
|
||||
"tool_name": "...",
|
||||
"tool_call_id": "...",
|
||||
"tool_call_args": {},
|
||||
"status": "success" | "failed",
|
||||
"result_summary": "...",
|
||||
"ui_schema": {},
|
||||
"error": null
|
||||
}
|
||||
```
|
||||
|
||||
### 3.4 文本完成事件
|
||||
|
||||
#### `TEXT_MESSAGE_END`
|
||||
|
||||
当前实现仅在 worker 输出完成后发送完整结果,不发送 token delta 事件。
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "TEXT_MESSAGE_END",
|
||||
"threadId": "...",
|
||||
"runId": "...",
|
||||
"messageId": "...",
|
||||
"role": "assistant",
|
||||
"stage": "worker",
|
||||
"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 对外协议中暴露。
|
||||
|
||||
### 3.5 快照事件
|
||||
|
||||
编码器支持以下 AG-UI 类型映射:
|
||||
|
||||
- `STATE_SNAPSHOT`
|
||||
- `MESSAGES_SNAPSHOT`
|
||||
|
||||
当前 `/runs/{thread_id}/events` 主流程通常不主动产出这两类事件;历史查询请使用 `/history`。
|
||||
|
||||
---
|
||||
|
||||
## 2. Step 阶段事件
|
||||
## 4) 字段命名约定
|
||||
|
||||
### step.start
|
||||
- 事件顶层通用字段使用 AG-UI 风格:`type`、`threadId`、`runId`
|
||||
- 部分业务字段沿运行时模型历史命名保留下划线:
|
||||
- `tool_name`
|
||||
- `tool_call_id`
|
||||
- `tool_call_args`
|
||||
- `ui_schema`
|
||||
|
||||
阶段开始时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "step.start",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
stepName: "router" | "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### step.finish
|
||||
|
||||
阶段结束时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "step.finish",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
stepName: "router" | "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. Worker 运行时事件
|
||||
|
||||
### 3.1 文本消息事件
|
||||
|
||||
#### text.start
|
||||
|
||||
文本消息开始时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "text.start",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
role: "assistant",
|
||||
stage: "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### text.delta
|
||||
|
||||
文本内容增量更新时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "text.delta",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
delta: "这是新增的文本内容",
|
||||
stage: "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### text.end
|
||||
|
||||
文本消息结束时触发,包含完整输出和使用统计。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "text.end",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
role: "assistant",
|
||||
stage: "worker",
|
||||
workerAgentOutput: {
|
||||
status: "success" | "partial_success" | "failed",
|
||||
answer: "主回复文本",
|
||||
key_points: ["要点1", "要点2"],
|
||||
result_type: "execution_report" | "clarification" | "error_report" | "unknown",
|
||||
suggested_actions: ["建议操作1"],
|
||||
error: null | { code: string, message: string },
|
||||
ui_hints: { ... } | null // 仅在 ui_mode 非空时存在
|
||||
},
|
||||
model: "gpt-4o",
|
||||
inputTokens: 1000,
|
||||
outputTokens: 500,
|
||||
cost: 0.025,
|
||||
latencyMs: 1500
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**workerAgentOutput 详细结构:**
|
||||
|
||||
```typescript
|
||||
// WorkerAgentOutputLite
|
||||
{
|
||||
status: "success" | "partial_success" | "failed",
|
||||
answer: string,
|
||||
key_points: string[],
|
||||
result_type: "execution_report" | "clarification" | "error_report" | "unknown",
|
||||
suggested_actions: string[],
|
||||
error: { code: string, message: string } | null
|
||||
}
|
||||
|
||||
// WorkerAgentOutputRich (当 ui.ui_mode 非空时)
|
||||
{
|
||||
// ... WorkerAgentOutputLite 字段
|
||||
ui_hints: {
|
||||
ui_mode: string,
|
||||
ui_state: object,
|
||||
actions: UiHintAction[]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 3.2 工具调用事件
|
||||
|
||||
#### tool.start
|
||||
|
||||
工具调用开始时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "tool.start",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
toolCallId: "call-abc",
|
||||
toolName: "calendar_read",
|
||||
stage: "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.args
|
||||
|
||||
工具调用参数时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "tool.args",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
toolCallId: "call-abc",
|
||||
toolName: "calendar_read",
|
||||
args: { start_date: "2024-01-01", end_date: "2024-01-07" },
|
||||
stage: "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.end
|
||||
|
||||
工具调用结束时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "tool.end",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
toolCallId: "call-abc",
|
||||
toolName: "calendar_read",
|
||||
stage: "worker"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### tool.result
|
||||
|
||||
工具执行结果时触发。
|
||||
|
||||
```typescript
|
||||
{
|
||||
type: "tool.result",
|
||||
threadId: "xxx",
|
||||
runId: "yyy",
|
||||
data: {
|
||||
messageId: "msg-xxx",
|
||||
toolCallId: "call-abc",
|
||||
toolName: "calendar_read",
|
||||
stage: "worker",
|
||||
toolAgentOutput: {
|
||||
tool_name: "calendar_read",
|
||||
tool_call_id: "call-abc",
|
||||
tool_call_args: { start_date: "2024-01-01", end_date: "2024-01-07" },
|
||||
status: "success" | "failed",
|
||||
result_summary: "找到3个事件...",
|
||||
ui_hints: null,
|
||||
attachments: null
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
**toolAgentOutput 详细结构:**
|
||||
|
||||
```typescript
|
||||
{
|
||||
tool_name: string,
|
||||
tool_call_id: string,
|
||||
tool_call_args: object | null,
|
||||
status: "success" | "failed",
|
||||
result_summary: string,
|
||||
ui_hints: object | null,
|
||||
attachments: Array<{
|
||||
bucket: string,
|
||||
path: string,
|
||||
mime_type: string,
|
||||
url: string
|
||||
}> | null
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 使用统计字段
|
||||
|
||||
`text.end` 事件中包含使用统计:
|
||||
|
||||
| 字段 | 类型 | 说明 |
|
||||
|------|------|------|
|
||||
| `model` | string | 使用的模型名称 |
|
||||
| `inputTokens` | number | 输入 token 数量 |
|
||||
| `outputTokens` | number | 输出 token 数量 |
|
||||
| `cost` | number | 花费(美元) |
|
||||
| `latencyMs` | number | 延迟(毫秒) |
|
||||
|
||||
---
|
||||
|
||||
## 前端接收示例
|
||||
|
||||
```javascript
|
||||
const eventSource = new EventSource(`/runs/${threadId}/events`);
|
||||
|
||||
eventSource.addEventListener('run.started', (e) => {
|
||||
console.log('Agent started:', JSON.parse(e.data));
|
||||
});
|
||||
|
||||
eventSource.addEventListener('step.start', (e) => {
|
||||
console.log('Step started:', JSON.parse(e.data));
|
||||
});
|
||||
|
||||
eventSource.addEventListener('text.delta', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
console.log('Text delta:', data.data.delta);
|
||||
});
|
||||
|
||||
eventSource.addEventListener('tool.start', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
console.log('Tool called:', data.data.toolName);
|
||||
});
|
||||
|
||||
eventSource.addEventListener('tool.result', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
console.log('Tool result:', data.data.toolAgentOutput);
|
||||
});
|
||||
|
||||
eventSource.addEventListener('text.end', (e) => {
|
||||
const data = JSON.parse(e.data);
|
||||
console.log('Worker output:', data.data.workerAgentOutput);
|
||||
console.log('Usage:', {
|
||||
inputTokens: data.data.inputTokens,
|
||||
outputTokens: data.data.outputTokens,
|
||||
cost: data.data.cost
|
||||
});
|
||||
});
|
||||
|
||||
eventSource.addEventListener('run.finished', (e) => {
|
||||
console.log('Agent finished:', JSON.parse(e.data));
|
||||
});
|
||||
|
||||
eventSource.addEventListener('run.error', (e) => {
|
||||
console.log('Agent error:', JSON.parse(e.data));
|
||||
});
|
||||
```
|
||||
这部分命名属于当前后端实现约束,文档与实现保持一致。
|
||||
|
||||
Reference in New Issue
Block a user