# 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