# 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 开始<br>`0` = 补发所有历史<br>`>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
