# SSE 进度事件结构文档 ## 📖 概览 本文档说明如何在 OpenAPI Swagger 文档中查看和理解 SSE (Server-Sent Events) 进度流的事件结构。 ## 🔍 在 OpenAPI 文档中查看 ### 1. 访问 Swagger UI 启动服务后访问: ``` http://localhost:8087/api/docs ``` ### 2. 查看 SSE 接口 在 Swagger UI 中找到以下接口: - **Agent 进度流**: `GET /agent/progress/{session_id}` - **Computer Agent 进度流**: `GET /computer/agent/progress/{session_id}` ### 3. 查看事件结构 在接口文档的 "Responses" 部分: 1. 点击 **200 响应** 展开详情 2. 在 **description** 中查看: - 📡 SSE 事件格式说明 - 🔄 各种事件类型示例 - 💡 JavaScript 使用示例 3. 在 **Schemas** 部分查找 `ProgressEventDoc` 获取完整的字段定义 ## 📊 事件结构说明 ### ProgressEventDoc 通过 SSE 流推送的核心事件结构: ```typescript interface ProgressEventDoc { // 消息主类型 message_type: "SessionPromptStart" | "SessionPromptEnd" | "AgentSessionUpdate" | "Heartbeat"; // 消息子类型(作为 SSE 的 event 字段) sub_type: string; // agent_message_chunk, tool_call, end_turn 等 // ACP 消息的完整 JSON 载荷(作为 SSE 的 data 字段) payload: object; // 可选的请求 ID request_id?: string; // 时间戳(Unix 毫秒) timestamp: number; } ``` ### 常见子类型 (sub_type) | sub_type | 说明 | payload 示例 | |----------|------|-------------| | `agent_message_chunk` | AI 响应文本片段 | `{"content":{"type":"text","text":"Hello"},"index":0}` | | `agent_thought_chunk` | AI 思考过程片段 | `{"thinking":"正在分析...","is_complete":false}` | | `tool_call` | 工具调用事件 | `{"tool_name":"read_file","tool_input":{"path":"test.rs"},"status":"started"}` | | `tool_result` | 工具执行结果 | `{"tool_name":"read_file","tool_output":"...","status":"success"}` | | `plan` | 执行计划 | `{"steps":[...],"current_step":1}` | | `end_turn` | 对话轮次结束 | `{"reason":"complete","final_message":"Done"}` | | `cancelled` | 任务被取消 | `{"reason":"user_request"}` | | `error` | 错误事件 | `{"code":"EXECUTION_ERROR","message":"..."}` | ## 📡 SSE 原始格式 实际通过网络传输的 SSE 消息格式: ``` event: agent_message_chunk data: {"content":{"type":"text","text":"正在分析您的请求..."},"index":0} event: tool_call data: {"tool_name":"read_file","tool_input":{"path":"src/main.rs"},"status":"started"} event: tool_result data: {"tool_name":"read_file","tool_output":"fn main() {...}","status":"success"} event: end_turn data: {"reason":"complete","final_message":"任务已完成"} ``` ## 💻 客户端使用示例 ### JavaScript / TypeScript ```typescript // 1. 建立 SSE 连接 const eventSource = new EventSource('/agent/progress/session_123'); // 2. 监听特定事件类型 eventSource.addEventListener('agent_message_chunk', (event) => { const data = JSON.parse(event.data); console.log('AI 响应:', data.content.text); // 更新 UI 显示 AI 响应 }); eventSource.addEventListener('tool_call', (event) => { const data = JSON.parse(event.data); console.log(`工具调用: ${data.tool_name}`, data.tool_input); // 显示工具调用状态 }); eventSource.addEventListener('tool_result', (event) => { const data = JSON.parse(event.data); console.log(`工具结果: ${data.tool_name}`, data.tool_output); // 显示工具执行结果 }); eventSource.addEventListener('end_turn', (event) => { const data = JSON.parse(event.data); console.log('任务完成:', data.final_message); eventSource.close(); // 关闭连接 }); // 3. 错误处理 eventSource.onerror = (error) => { console.error('连接错误:', error); eventSource.close(); }; // 4. 监听所有消息(可选) eventSource.onmessage = (event) => { console.log('收到消息:', event.data); }; ``` ### Python ```python import sseclient import requests import json # 1. 建立 SSE 连接 response = requests.get( 'http://localhost:8087/agent/progress/session_123', stream=True, headers={'Accept': 'text/event-stream'} ) client = sseclient.SSEClient(response) # 2. 处理事件 for event in client.events(): if event.event == 'agent_message_chunk': data = json.loads(event.data) print(f"AI 响应: {data['content']['text']}") elif event.event == 'tool_call': data = json.loads(event.data) print(f"工具调用: {data['tool_name']}") elif event.event == 'end_turn': data = json.loads(event.data) print(f"任务完成: {data['final_message']}") break ``` ### Rust ```rust use eventsource_stream::Eventsource; use futures_util::StreamExt; #[tokio::main] async fn main() -> Result<(), Box> { let client = reqwest::Client::new(); let mut stream = client .get("http://localhost:8087/agent/progress/session_123") .send() .await? .bytes_stream() .eventsource(); while let Some(event) = stream.next().await { match event { Ok(event) => { match event.event.as_str() { "agent_message_chunk" => { let data: serde_json::Value = serde_json::from_str(&event.data)?; println!("AI 响应: {}", data["content"]["text"]); } "tool_call" => { let data: serde_json::Value = serde_json::from_str(&event.data)?; println!("工具调用: {}", data["tool_name"]); } "end_turn" => { println!("任务完成"); break; } _ => {} } } Err(e) => { eprintln!("错误: {}", e); break; } } } Ok(()) } ``` ## 🎯 完整工作流程 ``` 1. 发起对话 POST /chat ↓ 返回 { session_id: "session_123" } 2. 建立 SSE 连接 GET /agent/progress/session_123 ↓ EventSource 连接建立 3. 接收进度事件 event: agent_message_chunk data: {...} ↓ event: tool_call data: {...} ↓ event: tool_result data: {...} ↓ event: end_turn data: {...} 4. 关闭连接 eventSource.close() ``` ## 🔧 在 Swagger UI 中测试 虽然 Swagger UI 本身不支持直接测试 SSE 流,但你可以: 1. 使用浏览器开发者工具的 Network 标签 2. 使用 `curl` 命令: ```bash curl -N http://localhost:8087/agent/progress/session_123 ``` 3. 使用专门的 SSE 测试工具 ## 📚 相关文档 - [OpenAPI 规范](http://localhost:8087/api/docs) - [ACP (Agent Client Protocol) 协议文档](../specs/) - [gRPC 架构文档](./grpc-architecture.md) ## ❓ FAQ ### Q: 为什么在 OpenAPI 中看不到具体的 data 字段? A: 因为 SSE 是流式响应,OpenAPI 无法像普通 JSON 响应那样定义结构。我们通过以下方式提供文档: 1. **ProgressEventDoc** schema:定义了事件的完整结构 2. **接口 description**:提供详细的事件格式说明和示例 3. **本文档**:提供完整的使用指南 ### Q: 如何知道有哪些可能的 sub_type? A: 查看: 1. Swagger UI 中的接口描述部分 2. `ProgressEventDoc` schema 的 `sub_type` 字段注释 3. 本文档的"常见子类型"表格 ### Q: payload 的具体结构在哪里定义? A: `payload` 是透传的 ACP (Agent Client Protocol) 消息,具体结构取决于 `sub_type`。每种 `sub_type` 的 payload 结构在接口文档的示例中都有说明。 ### Q: 如何处理错误? A: SSE 流可能会发送 `error` 事件,同时监听 `EventSource.onerror` 来处理连接错误: ```javascript eventSource.addEventListener('error', (event) => { const data = JSON.parse(event.data); console.error('业务错误:', data.code, data.message); }); eventSource.onerror = (error) => { console.error('连接错误:', error); }; ``` ## 📝 总结 - ✅ **在 Swagger UI 中查看**: `ProgressEventDoc` schema 和接口描述 - ✅ **事件格式**: SSE 标准格式,event = sub_type, data = payload - ✅ **客户端支持**: JavaScript, Python, Rust 等都有成熟的 SSE 客户端库 - ✅ **类型安全**: 可以根据 `ProgressEventDoc` 定义生成 TypeScript 类型