Qoder Cloud Agents 使用 Server-Sent Events (SSE) 协议将 Agent 的实时执行状态推送到客户端。你无需轮询——建立一次连接即可持续接收所有事件。
连接 URL
GET https://api.qoder.com.cn/api/v1/cloud/sessions/{session_id}/events/stream请求头:
Authorization: Bearer $QODER_PAT
Accept: text/event-streamSSE 格式
每个事件由三行组成,以空行分隔:
id: evt_01abc123
event: agent.message
data: {"content":[{"type":"text","text":"Hello, I'm ready to help."}]}
字段 | 说明 |
| 事件唯一标识,用于断线重连 |
| 事件类型,决定 |
| JSON 格式的事件负载 |
完整事件类型
事件类型 | 含义 | 触发时机 | data 结构 |
| 用户发送消息 | 客户端 POST 事件后 |
|
| 用户中断当前执行 | 客户端发送中断事件 |
|
| 用户定义预期结果 | 客户端设定目标 |
|
| Session 开始执行 | 收到消息后开始处理 |
|
| 模型请求开始 | Agent 发起 LLM 调用 |
|
| Agent 思考中 | 模型推理过程中 |
|
| Agent 回复消息 | 模型产出文本 |
|
| Agent 发起的工具调用(Bash/Read/Write/Edit/Glob/Grep/WebFetch/WebSearch) | 模型决定调用工具 | 见下文「工具调用对」 |
| 工具执行回执,通过 | 工具执行完成后 | 见下文「工具调用对」 |
| Session 进入空闲 | 一轮对话完成 | 见下文「status_idle 完整 schema」 |
| 模型请求结束 | LLM 调用完成 |
|
| Session 出错 | 运行时异常 |
|
| Session 终止 | Session 被关闭或超时 |
|
每个事件的 data JSON 除上表列出的特有字段外,还包含以下通用字段:id、type、turn_id(user.define_outcome 不含此字段)、session_id、created_at、processed_at、schema_version。
典型事件生命周期
一次完整的对话轮次,事件按以下顺序触发:
user.message ← 用户输入
session.status_running ← 开始执行
span.model_request_start ← 模型请求
agent.thinking ← 推理中(可选)
agent.message ← 回复内容(可能多次 delta)
session.status_idle ← 空闲,等待下一轮
span.model_request_end ← 模型请求收尾
工具调用对(pair)
每个 agent.tool_use 必有同 turn_id 的 agent.tool_result 配对,靠 tool_use_id 关联。
agent.tool_use 关键字段:
{
"type": "agent.tool_use",
"id": "evt_1d4c9b3c873b...",
"name": "Bash",
"input": { "command": "...", "description": "..." },
"tool_use_id": "toolu_bdrk_01Kj...",
"turn_id": "turn_...",
"session_id": "sess_...",
"requires_confirmation": false
}
此事件的 id 与其他事件一样使用 evt_ 前缀;tool_use_id 字段使用外部模型方格式 toolu_bdrk_<24>。name 首字母大写。
agent.tool_result 关键字段:
{
"type": "agent.tool_result",
"id": "evt_<32hex>",
"name": "Bash",
"content": [{ "type": "text", "text": "..." }],
"tool_use_id": "toolu_bdrk_01Kj...",
"turn_id": "turn_...",
"is_error": false
}
排序建议:按 created_at → 类型(tool_use 在 tool_result 前)→ id 三级排序。不要单靠 id 排序——平台生成的 tool_result id 可能字典序小于 tool_use id。
status_idle 完整 schema
session.status_idle 事件除 status 外还携带 stop_reason、usage、turn_id 与 session_id:
{
"type": "session.status_idle",
"id": "evt_840b8dc4b1534e52a2b858bf26b7de00",
"status": "idle",
"stop_reason": { "type": "end_turn" },
"usage": {
"input_tokens": 23487,
"output_tokens": 1024,
"cache_read_input_tokens": 11223,
"cache_creation_input_tokens": 0
},
"turn_id": "turn_...",
"session_id": "sess_..."
}
stop_reason.type 当前观察值为 end_turn;其他枚举值(cancel / max_turns / error 等)按实际场景出现。
断线重连与 after_id
SSE 连接建立后,默认从头重放所有历史事件。若只需增量事件,传入 after_id 参数:
GET /sessions/{session_id}/events/stream?after_id=evt_01abc123
浏览器原生 EventSource 会在断线重连时自动携带 Last-Event-ID 请求头,服务端据此返回增量事件。
长时间保持连接时建议客户端记录最后收到的 id,断线后用 after_id 恢复。
delta_flush_interval_ms
通过查询参数控制 agent.message delta 事件的刷新间隔:
GET /sessions/{session_id}/events/stream?delta_flush_interval_ms=100
默认值为 50ms。增大该值可减少事件频率、降低客户端渲染压力。
curl 示例
curl -N \
-H "Authorization: Bearer $QODER_PAT" \
-H "Accept: text/event-stream" \
"https://api.qoder.com.cn/api/v1/cloud/sessions/sess_abc123/events/stream"
带 after_id 的增量连接:
curl -N \
-H "Authorization: Bearer $QODER_PAT" \
-H "Accept: text/event-stream" \
"https://api.qoder.com.cn/api/v1/cloud/sessions/sess_abc123/events/stream?after_id=evt_01abc123"
JavaScript EventSource 示例
const url = new URL('https://api.qoder.com.cn/api/v1/cloud/sessions/sess_abc123/events/stream');
// 如需增量:url.searchParams.set('after_id', lastEventId);
const es = new EventSource(url, {
headers: { 'Authorization': `Bearer ${QODER_PAT}` }
});
es.addEventListener('agent.message', (e) => {
const data = JSON.parse(e.data);
console.log('Agent:', data.content);
});
es.addEventListener('session.status_idle', () => {
console.log('轮次完成,等待下一轮输入');
});
es.addEventListener('session.error', (e) => {
const data = JSON.parse(e.data);
console.error('错误:', data.error);
es.close();
});
es.onerror = () => {
console.warn('连接断开,浏览器将自动重连');
};
客户端实现建议
记录 last event id —— 每收到事件就更新,用于重连恢复。
处理 delta 拼接 ——
agent.message可能以流式 delta 到达,客户端需拼接为完整文本。监听 terminated —— 收到后主动关闭连接,不再重连。
超时重试 —— 若 30 秒无事件,主动断开并重连(避免静默断连)。
过滤事件 —— 仅监听业务关心的事件类型,忽略 span 级别事件可减少处理开销。
常见问题
Q:连接后收到大量历史事件怎么办? A:传入 after_id 参数,指向最后处理过的事件 ID,服务端只返回该 ID 之后的事件。
Q:SSE 连接会自动关闭吗? A:Session 终止时服务端发送 terminated 事件后关闭连接。空闲超时由 Session 配置决定。
Q:能否用 WebSocket 替代? A:当前仅支持 SSE。SSE 对单向推送场景更轻量,且天然支持断线重连。
Q:delta_flush_interval_ms 设太大会丢事件吗? A:不会丢失,只是批量合并后发送。最终内容完整性不受影响。