# Agent Execution API - 前端对接文档 > 版本:v2.0 > 更新日期:2026-02-03 --- ## 📋 概览 本 API 提供 Agent 执行过程的实时可视化能力,包括: - **REST API** - 查询历史数据、获取完整 Step 树 - **WebSocket** - 实时推送 Step 更新(支持断线续传) **核心概念**: - **Trace** - 一次完整的任务执行(如一次 Agent 运行) - **Step** - 执行过程中的一个原子操作,形成树结构 - **Event** - Step 的变更事件(用于 WebSocket 推送和断线续传) 可以运行 python3 examples/feature_extract/run.py 来生成数据。 --- ## 🌐 REST API ### 基础信息 - **Base URL**: `http://localhost:8000` - **Content-Type**: `application/json` --- ### 1. 列出 Traces 获取 Trace 列表(支持过滤) ```http GET /api/traces?status=running&limit=20 ``` **查询参数**: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `status` | string | 否 | 过滤状态:`running` / `completed` / `failed` | | `mode` | string | 否 | 过滤模式:`call` / `agent` | | `limit` | int | 否 | 返回数量(默认 50,最大 100)| **响应示例**: ```json { "traces": [ { "trace_id": "abc123", "mode": "agent", "task": "分析代码库结构", "status": "running", "total_steps": 15, "total_tokens": 5000, "total_cost": 0.05, "created_at": "2026-02-03T15:30:00" } ], "total": 1 } ``` --- ### 2. 获取 Trace 元数据 ```http GET /api/traces/{trace_id} ``` **响应示例**: ```json { "trace_id": "abc123", "mode": "agent", "task": "分析代码库结构", "status": "running", "total_steps": 15, "total_tokens": 5000, "total_cost": 0.05, "total_duration_ms": 12345, "last_sequence": 15, "last_event_id": 15, "created_at": "2026-02-03T15:30:00", "completed_at": null } ``` --- ### 3. 获取完整 Step 树 ⭐ 重要 获取 Trace 的完整 Step 树(适合小型 Trace,<100 个 Step) ```http GET /api/traces/{trace_id}/tree?view=compact&max_depth=10 ``` **查询参数**: | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `view` | string | `compact` | `compact` - 精简视图 / `full` - 完整视图 | | `max_depth` | int | 无限 | 最大树深度 | **响应示例**: ```json { "trace_id": "abc123", "root_steps": [ { "step_id": "step-001", "step_type": "thought", "status": "completed", "sequence": 1, "parent_id": null, "description": "分析项目结构...", "has_children": true, "children_count": 2, "duration_ms": 1234, "tokens": 500, "cost": 0.005, "created_at": "2026-02-03T15:30:01", "data": { "content": "让我先看看项目的目录结构...", "model": "claude-sonnet-4.5" }, "children": [ { "step_id": "step-002", "step_type": "action", "status": "completed", "parent_id": "step-001", "description": "glob_files(**/*.py)", "data": { "tool_name": "glob_files", "arguments": {"pattern": "**/*.py"} }, "children": [ { "step_id": "step-003", "step_type": "result", "status": "completed", "parent_id": "step-002", "data": { "tool_name": "glob_files", "output": ["src/main.py", "src/utils.py"] } } ] } ] } ] } ``` **注意**: - `children` 字段包含嵌套的子节点(递归结构) - `compact` 视图:`data` 中的大字段(如 `messages`)会被省略 - `full` 视图:返回所有字段(数据量可能很大) --- ### 4. 懒加载单个节点 适用于大型 Trace(>100 Step),按需加载子树 ```http GET /api/traces/{trace_id}/node/{step_id}?expand=true&max_depth=2 ``` **查询参数**: | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `expand` | bool | `false` | 是否展开子节点 | | `max_depth` | int | 1 | 展开深度 | | `view` | string | `compact` | 视图类型 | **响应**:与 `/tree` 格式相同,但只返回指定节点及其子树。 --- ## ⚡ WebSocket API ### 连接 ```javascript const ws = new WebSocket( 'ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0' ) ``` **查询参数**: | 参数 | 类型 | 默认值 | 说明 | |------|------|--------|------| | `since_event_id` | int | `0` | 从哪个事件 ID 开始
`0` = 补发所有历史
`>0` = 只补发指定 ID 之后的 | --- ### 事件类型 #### 1. connected(连接成功) ```json { "event": "connected", "trace_id": "abc123", "current_event_id": 15 } ``` **说明**:连接建立后的第一条消息,包含当前最新的 event_id **前端处理**: ```javascript if (data.event === 'connected') { // 保存 event_id 用于断线重连 localStorage.setItem('last_event_id', data.current_event_id) } ``` --- #### 2. step_added(新增 Step)⭐ 最常用 ```json { "event": "step_added", "event_id": 16, "ts": "2026-02-03T15:30:10.123456", "step": { "step_id": "step-016", "step_type": "action", "status": "completed", "sequence": 16, "parent_id": "step-001", "description": "read_file(config.yaml)", "has_children": false, "children_count": 0, "duration_ms": 50, "data": { "tool_name": "read_file", "arguments": {"file_path": "config.yaml"} } } } ``` **前端处理**: ```javascript if (data.event === 'step_added') { // 添加到树结构 addStepToTree(data.step) // 更新 event_id localStorage.setItem('last_event_id', data.event_id) } ``` --- #### 3. step_updated(Step 更新) ```json { "event": "step_updated", "event_id": 17, "ts": "2026-02-03T15:30:15.123456", "step_id": "step-016", "patch": { "status": "completed", "duration_ms": 1234 } } ``` **说明**:`patch` 是增量更新(只包含变化的字段) **前端处理**: ```javascript if (data.event === 'step_updated') { const step = findStepById(data.step_id) Object.assign(step, data.patch) updateUI() } ``` --- #### 4. trace_completed(任务完成) ```json { "event": "trace_completed", "event_id": 18, "ts": "2026-02-03T15:35:00.123456", "trace_id": "abc123", "total_steps": 18 } ``` **前端处理**: ```javascript if (data.event === 'trace_completed') { console.log('Task completed!') ws.close() } ``` --- #### 5. error(错误) ```json { "event": "error", "message": "Too many missed events (150), please reload full tree via REST API" } ``` **说明**: - 如果断线期间产生超过 100 条事件,会收到此错误 - 此时应该用 REST API 重新加载完整树 --- ### 断线续传 **场景**:网络断开后重新连接,不丢失中间的更新 **实现方式**: ```javascript let lastEventId = 0 // 初次连接 const ws = new WebSocket( `ws://localhost:8000/api/traces/abc123/watch?since_event_id=0` ) ws.onmessage = (event) => { const data = JSON.parse(event.data) // 保存最新 event_id if (data.event_id) { lastEventId = data.event_id localStorage.setItem('trace_abc123_event_id', lastEventId) } } ws.onclose = () => { // 3 秒后重连 setTimeout(() => { // 从上次的 event_id 继续 const ws2 = new WebSocket( `ws://localhost:8000/api/traces/abc123/watch?since_event_id=${lastEventId}` ) // 服务器会补发 lastEventId 之后的所有事件 }, 3000) } ``` **注意**: - 每条消息都有 `event_id` 和 `ts` 字段 - 重连时传入 `since_event_id`,服务器自动补发缺失的事件(最多 100 条) - 超过 100 条会返回错误,需要用 REST API 重新加载 --- ### 心跳检测 保持连接活跃,检测僵尸连接 ```javascript // 每 30 秒发送心跳 const heartbeat = setInterval(() => { if (ws.readyState === WebSocket.OPEN) { ws.send('ping') } }, 30000) ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.event === 'pong') { console.log('Connection alive') } } ``` --- ## 📊 数据模型 ### Trace | 字段 | 类型 | 说明 | |------|------|------| | `trace_id` | string | 唯一 ID | | `mode` | string | `call` - 单次调用 / `agent` - Agent 模式 | | `task` | string | 任务描述(Agent 模式)| | `status` | string | `running` / `completed` / `failed` | | `total_steps` | int | Step 总数 | | `total_tokens` | int | Token 总消耗 | | `total_cost` | float | 成本总和 | | `total_duration_ms` | int | 总耗时(毫秒)| | `last_sequence` | int | 最新 Step 的 sequence | | `last_event_id` | int | 最新事件 ID | | `created_at` | string | 创建时间(ISO 8601)| | `completed_at` | string \| null | 完成时间 | --- ### Step #### 顶层字段(所有 Step 共有) | 字段 | 类型 | 说明 | |------|------|------| | `step_id` | string | 唯一 ID | | `trace_id` | string | 所属 Trace ID | | `step_type` | string | 步骤类型(见下表)| | `status` | string | 状态(见下表)| | `sequence` | int | 在 Trace 中的顺序(递增)| | `parent_id` | string \| null | 父节点 ID | | `description` | string | 简短描述 | | `summary` | string \| null | 总结(仅 `evaluation` 类型)| | `has_children` | bool | 是否有子节点 | | `children_count` | int | 子节点数量 | | `duration_ms` | int \| null | 耗时(毫秒)| | `tokens` | int \| null | Token 消耗 | | `cost` | float \| null | 成本 | | `created_at` | string | 创建时间 | | `data` | object | 类型相关的详细数据 | #### step_type(步骤类型) | 类型 | 说明 | 来源 | |------|------|------| | `goal` | 目标/计划 | LLM | | `thought` | 思考/分析 | LLM | | `evaluation` | 评估总结 | LLM | | `response` | 最终回复 | LLM | | `action` | 工具调用 | System | | `result` | 工具结果 | System | | `memory_read` | 读取记忆 | System | | `memory_write` | 写入记忆 | System | #### status(步骤状态) | 状态 | 说明 | |------|------| | `planned` | 计划中(未执行)| | `in_progress` | 执行中 | | `awaiting_approval` | 等待用户确认 | | `completed` | 已完成 | | `failed` | 失败 | | `skipped` | 跳过 | #### data 字段(按 step_type) **thought / response**: ```json { "model": "claude-sonnet-4.5", "content": "让我先分析...", "messages": [...], // full 视图才有 "tool_calls": [...] // 如果有工具调用 } ``` **action**: ```json { "tool_name": "read_file", "arguments": { "file_path": "config.yaml" } } ``` **result**: ```json { "tool_name": "read_file", "output": "file content...", "error": null } ``` **memory_read**: ```json { "experiences_count": 5, "skills_count": 3 } ``` --- ## 🎯 推荐的实现方案 ### 方案 1:纯 WebSocket(简单场景) 适用于:实时监控进行中的任务,Step 数量 < 100 ```javascript // 只用 WebSocket,自动获取历史 const ws = new WebSocket( 'ws://localhost:8000/api/traces/abc123/watch?since_event_id=0' ) ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.event === 'step_added') { // 历史 + 新增的 Step 都会收到 addStepToTree(data.step) } } ``` **优点**:代码简单 **缺点**:超过 100 个 Step 会失败 --- ### 方案 2:REST + WebSocket(生产推荐) 适用于:查看历史任务,或 Step 数量 > 100 ```javascript // 1. 先用 REST API 获取完整树 const response = await fetch( `/api/traces/${traceId}/tree?view=compact` ) const treeData = await response.json() renderTree(treeData.root_steps) // 2. 连接 WebSocket 监听增量更新 const ws = new WebSocket( `ws://localhost:8000/api/traces/${traceId}/watch?since_event_id=0` ) ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.event === 'step_added') { addStepToTree(data.step) // 只处理新增的 } } ``` **优点**:可靠,支持大型 Trace **缺点**:略复杂 --- ## 🐛 错误处理 ### HTTP 错误码 | 状态码 | 说明 | |--------|------| | 200 | 成功 | | 404 | Trace/Step 不存在 | | 400 | 参数错误 | | 500 | 服务器错误 | ### WebSocket 错误 ```javascript ws.onerror = (error) => { console.error('WebSocket error:', error) // 重连 } ws.onclose = (event) => { console.log('Connection closed:', event.code, event.reason) // 自动重连 } ``` --- ## 💡 最佳实践 ### 1. 保存 event_id 用于断线重连 ```javascript ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.event_id) { localStorage.setItem( `trace_${traceId}_event_id`, data.event_id ) } } ``` ### 2. 实现自动重连 ```javascript function connectWebSocket(traceId) { const lastEventId = localStorage.getItem(`trace_${traceId}_event_id`) || 0 const ws = new WebSocket( `ws://localhost:8000/api/traces/${traceId}/watch?since_event_id=${lastEventId}` ) ws.onclose = () => { setTimeout(() => connectWebSocket(traceId), 3000) } return ws } ``` ### 3. 使用 compact 视图减少流量 ```javascript // ✅ 推荐 const response = await fetch(`/api/traces/${id}/tree?view=compact`) // ❌ 避免(数据量可能很大) const response = await fetch(`/api/traces/${id}/tree?view=full`) ``` ### 4. 按需懒加载(大型 Trace) ```javascript // 初次只加载第一层 const tree = await fetch( `/api/traces/${id}/tree?max_depth=1` ).then(r => r.json()) // 用户点击展开时,懒加载子树 async function onExpand(stepId) { const node = await fetch( `/api/traces/${id}/node/${stepId}?expand=true&max_depth=1` ).then(r => r.json()) appendChildren(stepId, node.children) } ``` --- ## 🔗 相关文档 - [Step 树结构详解](./step-tree.md) - [API 接口规范](./trace-api.md) - [架构设计文档](./README.md) --- ## 📞 问题反馈 如有问题请提 Issue:https://github.com/anthropics/agent/issues