# Agent Execution API - 前端对接文档 > 版本:v4.0 > 更新日期:2026-02-04 --- ## 概览 本 API 提供 Agent 执行过程的实时可视化能力: - **REST API** - 查询 Trace 和 GoalTree - **WebSocket** - 实时推送 Goal/Message 更新(支持断线续传) **核心概念**: - **Trace** - 一次完整的 Agent 执行(主 Agent 和 Sub-Agent 都是 Trace) - **GoalTree** - 每个 Trace 的目标树 - **Goal** - 一个目标节点,包含 self_stats(自身统计)和 cumulative_stats(含后代统计) - **Message** - 执行消息,对应 LLM API 的 assistant/tool 消息 - **Sub-Trace** - 子 Agent(通过 explore/delegate 工具启动) **统一的 Trace 模型**: ``` 主 Trace 和 Sub-Trace 使用相同的数据结构: - 每个 Trace 有独立的 GoalTree - 每个 Trace 有独立的 Message List - Sub-Trace 通过 parent_trace_id 和 parent_goal_id 关联父 Trace - Trace ID 采用层级命名(abc123, abc123.A, abc123.A.1) ``` **数据结构**: ``` 后端存储: 1. 每个 Trace 独立存储(主 Trace 和 Sub-Traces) 2. GoalTree - 目标树结构 + 聚合统计 3. Messages - 执行记录,通过 goal_id 关联 Goal 关系: - Goal.stats 从关联的 Messages 聚合计算 - Sub-Trace 通过 parent_trace_id 关联 - Goal 通过 sub_trace_ids 关联启动的 Sub-Traces ``` **DAG 可视化**(前端负责): - 从 GoalTree 生成 DAG 视图 - 节点 = Goal 完成后的里程碑 - 边 = 相邻节点之间的执行过程 - Sub-Trace 可以折叠(显示为单个节点)或展开(显示内部 Goals) - 边的统计数据从 target Goal 的 stats 获取 --- ## REST API ### 基础信息 - **Base URL**: `http://localhost:8000` - **Content-Type**: `application/json` --- ### 1. 列出 Traces ```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_messages": 15, "total_tokens": 5000, "total_cost": 0.05, "current_goal_id": "2.1", "created_at": "2026-02-04T15:30:00" } ], "total": 1 } ``` --- ### 2. 获取 Trace + GoalTree ```http GET /api/traces/{trace_id} ``` **响应示例**(主 Trace): ```json { "trace_id": "abc123", "mode": "agent", "task": "实现用户认证功能", "status": "running", "parent_trace_id": null, "parent_goal_id": null, "agent_type": "main", "total_messages": 15, "total_tokens": 5000, "total_cost": 0.05, "created_at": "2026-02-04T15:30:00", "completed_at": null, "goal_tree": { "mission": "实现用户认证功能", "current_id": "2", "goals": [ { "id": "1", "parent_id": null, "type": "normal", "description": "分析代码", "reason": "了解现有结构", "status": "completed", "summary": "用户模型在 models/user.py", "self_stats": { "message_count": 5, "total_tokens": 2300, "total_cost": 0.03, "preview": "glob → read × 2" }, "cumulative_stats": { "message_count": 5, "total_tokens": 2300, "total_cost": 0.03, "preview": "glob → read × 2" } }, { "id": "2", "parent_id": null, "type": "agent_call", "description": "并行探索认证方案", "reason": "评估不同技术选型", "status": "in_progress", "agent_call_mode": "explore", "sub_trace_ids": ["abc123.A", "abc123.B"], "self_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null }, "cumulative_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null } }, { "id": "3", "parent_id": null, "type": "normal", "description": "完善实现", "reason": "基于选定方案完成实现", "status": "pending" } ] }, "sub_traces": { "abc123.A": { "trace_id": "abc123.A", "parent_trace_id": "abc123", "parent_goal_id": "2", "agent_type": "explore", "task": "JWT 方案", "status": "completed", "total_messages": 8, "total_tokens": 4000, "total_cost": 0.05, "created_at": "2026-02-04T15:31:00", "completed_at": "2026-02-04T15:35:00" }, "abc123.B": { "trace_id": "abc123.B", "parent_trace_id": "abc123", "parent_goal_id": "2", "agent_type": "explore", "task": "Session 方案", "status": "completed", "total_messages": 10, "total_tokens": 5000, "total_cost": 0.06 } } } ``` --- ### 3. 获取 Sub-Trace 详情 获取 Sub-Trace 的完整信息(包括 GoalTree)。 ```http GET /api/traces/abc123.A ``` **响应示例**: ```json { "trace_id": "abc123.A", "parent_trace_id": "abc123", "parent_goal_id": "2", "agent_type": "explore", "task": "JWT 方案", "status": "completed", "total_messages": 8, "total_tokens": 4000, "total_cost": 0.05, "created_at": "2026-02-04T15:31:00", "completed_at": "2026-02-04T15:35:00", "goal_tree": { "mission": "JWT 方案", "current_id": null, "goals": [ { "id": "1", "parent_id": null, "type": "normal", "description": "JWT 设计", "status": "completed", "summary": "设计完成", "self_stats": { "message_count": 3, "total_tokens": 1500, "total_cost": 0.02, "preview": "read → edit" }, "cumulative_stats": { "message_count": 3, "total_tokens": 1500, "total_cost": 0.02, "preview": "read → edit" } }, { "id": "2", "parent_id": null, "type": "normal", "description": "JWT 实现", "status": "completed", "summary": "实现完成", "self_stats": { "message_count": 5, "total_tokens": 2500, "total_cost": 0.03, "preview": "edit × 3 → bash" }, "cumulative_stats": { "message_count": 5, "total_tokens": 2500, "total_cost": 0.03, "preview": "edit × 3 → bash" } } ] }, "sub_traces": {} } ``` ### 4. 获取 Messages(边详情) 查询指定 Trace 的 Messages(用于查看边的详细执行内容)。 ```http GET /api/traces/abc123/messages?goal_id=1 GET /api/traces/abc123.A/messages?goal_id=2 ``` **查询参数**: | 参数 | 类型 | 必填 | 说明 | |------|------|------|------| | `goal_id` | string | 否 | 过滤指定 Goal 的 Messages(内部 ID,如 "1", "2")| **响应示例**: ```json { "trace_id": "abc123", "messages": [ { "message_id": "msg-001", "trace_id": "abc123", "role": "assistant", "sequence": 1, "goal_id": "1", "content": { "text": "让我先读取现有的 API 设计...", "tool_calls": [ { "id": "call_abc", "name": "read_file", "arguments": { "path": "api/routes.py" } } ] }, "description": "让我先读取现有的 API 设计...", "tokens": 150, "cost": 0.002, "created_at": "2026-02-04T15:31:00" }, { "message_id": "msg-002", "trace_id": "abc123", "role": "tool", "sequence": 2, "goal_id": "1", "tool_call_id": "call_abc", "content": "# API Routes\n...", "description": "read_file", "tokens": null, "cost": null, "created_at": "2026-02-04T15:31:01" } ], "total": 2 } ``` --- ## 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` = 补发所有历史 | --- ### 事件类型 #### 1. connected(连接成功) 连接后推送完整 Trace 信息,前端据此初始化 DAG。 ```json { "event": "connected", "trace_id": "abc123", "current_event_id": 15, "trace": { "trace_id": "abc123", "status": "running", "goal_tree": { "mission": "实现用户认证功能", "current_id": "2", "goals": [...] }, "sub_traces": {} } } ``` **前端处理**: ```javascript if (data.event === 'connected') { initDAG(data.goal_tree) localStorage.setItem('last_event_id', data.current_event_id) } ``` --- #### 2. goal_added(新增 Goal) ```json { "event": "goal_added", "event_id": 16, "goal": { "id": "6", "parent_id": "2", "branch_id": null, "type": "normal", "description": "编写测试", "reason": "确保代码质量", "status": "pending", "summary": null, "self_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null }, "cumulative_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null } }, "parent_id": "2" } ``` **前端处理**: ```javascript if (data.event === 'goal_added') { insertGoal(data.goal, data.parent_id) regenerateDAG() } ``` --- #### 3. goal_updated(Goal 状态变化) 包含级联完成场景:当所有子 Goal 完成时,父 Goal 自动 completed。 ```json { "event": "goal_updated", "event_id": 17, "goal_id": "3", "updates": { "status": "completed", "summary": "接口设计完成" }, "affected_goals": [ { "goal_id": "3", "cumulative_stats": { "message_count": 3, "total_tokens": 1500, "total_cost": 0.02, "preview": "read → edit" } }, { "goal_id": "2", "status": "completed", "summary": "功能实现完成", "cumulative_stats": { "message_count": 8, "total_tokens": 4200, "total_cost": 0.05, "preview": "..." } } ] } ``` **前端处理**: ```javascript if (data.event === 'goal_updated') { updateGoal(data.goal_id, data.updates) for (const g of data.affected_goals) { updateGoalStats(g.goal_id, g) } regenerateDAG() } ``` --- #### 4. message_added(新 Message) 后端更新统计后推送,包含受影响的所有 Goals。 ```json { "event": "message_added", "event_id": 18, "message": { "message_id": "msg-018", "role": "assistant", "goal_id": "4", "branch_id": null, "content": { "text": "...", "tool_calls": [...] }, "tokens": 500, "cost": 0.005 }, "affected_goals": [ { "goal_id": "4", "self_stats": { "message_count": 6, "total_tokens": 3200, "total_cost": 0.035, "preview": "edit × 3 → bash × 2" }, "cumulative_stats": { "message_count": 6, "total_tokens": 3200, "total_cost": 0.035, "preview": "edit × 3 → bash × 2" } }, { "goal_id": "2", "cumulative_stats": { "message_count": 9, "total_tokens": 4700, "total_cost": 0.055, "preview": "read → edit × 4 → bash × 2" } } ] } ``` **说明**: - `affected_goals[0]` 是直接关联的 Goal,更新 `self_stats` + `cumulative_stats` - 后续是所有祖先 Goal,仅更新 `cumulative_stats` **前端处理**: ```javascript if (data.event === 'message_added') { for (const g of data.affected_goals) { updateGoalStats(g.goal_id, g) } // 根据当前展开状态更新对应边 rerenderEdge(data.message.goal_id) } ``` --- #### 5. trace_completed(任务完成) ```json { "event": "trace_completed", "event_id": 50, "trace_id": "abc123", "total_messages": 50, "total_tokens": 25000, "total_cost": 0.25 } ``` **前端处理**: ```javascript if (data.event === 'trace_completed') { markTraceCompleted() ws.close() } ``` --- #### 6. sub_trace_started(Sub-Trace 开始) explore 或 delegate 工具启动 Sub-Trace 时触发。 ```json { "event": "sub_trace_started", "event_id": 20, "parent_trace_id": "abc123", "parent_goal_id": "2", "sub_trace": { "trace_id": "abc123.A", "parent_trace_id": "abc123", "parent_goal_id": "2", "agent_type": "explore", "task": "JWT 方案", "status": "running", "total_messages": 0, "total_tokens": 0, "total_cost": 0.0 } } ``` **前端处理**: ```javascript if (data.event === 'sub_trace_started') { insertSubTrace(data.parent_trace_id, data.sub_trace) regenerateDAG() } ``` --- #### 7. sub_trace_completed(Sub-Trace 完成) Sub-Trace 执行完成后触发。 ```json { "event": "sub_trace_completed", "event_id": 35, "trace_id": "abc123.A", "parent_trace_id": "abc123", "parent_goal_id": "2", "summary": "JWT 方案实现完成,无状态但 token 较大", "total_messages": 8, "total_tokens": 4000, "total_cost": 0.05 } ``` **前端处理**: ```javascript if (data.event === 'sub_trace_completed') { updateSubTrace(data.trace_id, { status: 'completed', summary: data.summary, total_messages: data.total_messages, total_tokens: data.total_tokens, total_cost: data.total_cost }) regenerateDAG() } ``` --- ## 数据模型 ### Trace | 字段 | 类型 | 说明 | |------|------|------| | `trace_id` | string | 层级化 ID(如 "abc123", "abc123.A", "abc123.A.1")| | `mode` | string | `call` - 单次调用 / `agent` - Agent 模式 | | `task` | string | 任务描述 | | `parent_trace_id` | string \| null | 父 Trace ID(Sub-Trace 才有)| | `parent_goal_id` | string \| null | 哪个 Goal 启动的(Sub-Trace 才有)| | `agent_type` | string \| null | "main" / "explore" / "delegate" / "compaction" | | `status` | string | `running` / `completed` / `failed` | | `total_messages` | int | Message 总数 | | `total_tokens` | int | Token 总消耗 | | `total_cost` | float | 成本总和 | | `created_at` | string | 创建时间(ISO 8601)| | `completed_at` | string \| null | 完成时间 | **Trace ID 规则**: - 主 Trace:短随机 ID(如 "abc123") - Sub-Trace:父 ID + 后缀(如 "abc123.A", "abc123.task1") - 嵌套:继续追加(如 "abc123.A.1") --- ### GoalTree | 字段 | 类型 | 说明 | |------|------|------| | `mission` | string | 总任务描述(来自 Trace.task)| | `current_id` | string \| null | 当前焦点 Goal 的内部 ID | | `goals` | Goal[] | 顶层目标列表 | --- ### Goal | 字段 | 类型 | 说明 | |------|------|------| | `id` | string | 内部 ID,每个 Trace 独立编号("1", "2", "3"...)| | `parent_id` | string \| null | 父 Goal ID(层级关系)| | `type` | string | `normal` / `agent_call` | | `description` | string | 目标描述(做什么)| | `reason` | string | 创建理由(为什么做)| | `status` | string | `pending` / `in_progress` / `completed` / `abandoned` | | `summary` | string \| null | 完成/放弃时的总结 | | `sub_trace_ids` | string[] \| null | 启动的 Sub-Trace IDs(仅 agent_call)| | `agent_call_mode` | string \| null | "explore" / "delegate" / "sequential"(仅 agent_call)| | `self_stats` | GoalStats | 自身统计 | | `cumulative_stats` | GoalStats | 累计统计 | **ID 设计**: - 每个 Trace 内部独立编号("1", "2", "3") - 层级关系通过 `parent_id` 维护 - 显示序号由前端/后端动态生成 --- ### GoalStats | 字段 | 类型 | 说明 | |------|------|------| | `message_count` | int | 消息数量 | | `total_tokens` | int | Token 总数 | | `total_cost` | float | 总成本 | | `preview` | string \| null | 工具调用摘要(如 `"read → edit × 2"`)| --- ### Message | 字段 | 类型 | 说明 | |------|------|------| | `message_id` | string | 唯一 ID | | `trace_id` | string | 所属 Trace ID(可能是主 Trace 或 Sub-Trace)| | `role` | string | `assistant` / `tool` | | `sequence` | int | 当前 Trace 内的顺序 | | `goal_id` | string | 关联的 Goal 内部 ID(如 "1", "2")| | `tool_call_id` | string \| null | tool 消息关联的 tool_call ID | | `content` | any | 消息内容(和 LLM API 格式一致)| | `description` | string | 消息描述(系统自动生成)| | `tokens` | int \| null | Token 消耗 | | `cost` | float \| null | 成本 | | `created_at` | string | 创建时间 | --- ## DAG 可视化(前端实现) 后端提供 Trace(包含 GoalTree 和 Sub-Traces),前端负责生成 DAG 视图。 ### 核心逻辑 **普通 Goal(子目标)**: 1. 未展开:显示父 Goal 2. 已展开:父 Goal 被子 Goal **替代**(顺序) **agent_call Goal(Sub-Trace)**: 1. 未展开:显示为单个节点(代表整个 Sub-Trace) 2. 已展开:加载 Sub-Trace 的 GoalTree,显示内部 Goals ### 边数据 从 target 节点的 stats 获取: - 折叠边(父 Goal)→ `target.cumulative_stats` - 展开边(子 Goal)→ `target.self_stats` - Sub-Trace 折叠边 → Sub-Trace 的 total_tokens/total_cost - Sub-Trace 展开边 → Sub-Trace 内部 Goal 的 stats ### 示例:普通展开(子目标) ```javascript // GoalTree(扁平列表,通过 parent_id 构建层级) const goalTree = { goals: [ { id: "1", parent_id: null, description: "分析代码" }, { id: "2", parent_id: null, description: "实现功能" }, { id: "3", parent_id: "2", description: "设计接口" }, // 2 的子目标 { id: "4", parent_id: "2", description: "实现代码" }, // 2 的子目标 { id: "5", parent_id: null, description: "测试" } ] } // 构建层级视图 function buildHierarchy(goals) { const map = new Map(goals.map(g => [g.id, { ...g, children: [] }])) const roots = [] for (const goal of goals) { const node = map.get(goal.id) if (goal.parent_id) { map.get(goal.parent_id)?.children.push(node) } else { roots.push(node) } } return roots } // 展开状态 const expanded = new Set() // 空 = 全部折叠 // 生成可见节点序列 function getVisibleGoals(nodes, expanded) { const result = [] for (const node of nodes) { if (expanded.has(node.id) && node.children.length > 0) { // 展开:递归处理子节点 result.push(...getVisibleGoals(node.children, expanded)) } else { // 折叠:显示自己 result.push(node) } } return result } // 折叠视图: [1] → [2] → [5] // 展开 "2" 后: [1] → [3] → [4] → [5] // 生成边 function generateEdges(visibleGoals) { const edges = [] for (let i = 0; i < visibleGoals.length; i++) { const source = i === 0 ? null : visibleGoals[i - 1] const target = visibleGoals[i] edges.push({ source: source?.id ?? null, target: target.id, // 边数据来自 target 的 stats // 如果 target 有子节点且未展开,用 cumulative_stats // 否则用 self_stats stats: hasUnexpandedChildren(target, expanded) ? target.cumulative_stats : target.self_stats }) } return edges } ``` ### 示例:Sub-Trace 展开 ```javascript // 主 Trace 的 GoalTree const mainTrace = { trace_id: "abc123", goal_tree: { goals: [ { id: "1", type: "normal", description: "分析问题" }, { id: "2", type: "agent_call", agent_call_mode: "explore", sub_trace_ids: ["abc123.A", "abc123.B"] }, { id: "3", type: "normal", description: "完善实现" } ] }, sub_traces: { "abc123.A": { trace_id: "abc123.A", task: "JWT 方案", status: "completed", total_tokens: 4000 }, "abc123.B": { trace_id: "abc123.B", task: "Session 方案", status: "completed", total_tokens: 5000 } } } // 折叠视图:[1] → [2:并行探索] → [3] // 展开 Sub-Traces 后的视图: // ┌→ [abc123.A] ────┐ // [1] ──────┼ ├──→ [3] // └→ [abc123.B] ────┘ // 继续展开 Sub-Trace abc123.A 内部 async function loadSubTrace(traceId) { const resp = await fetch(`/api/traces/${traceId}`) return await resp.json() // 返回完整 Trace,含 goal_tree } const subTraceA = await loadSubTrace("abc123.A") // { // trace_id: "abc123.A", // goal_tree: { // goals: [ // { id: "1", description: "JWT 设计" }, // { id: "2", description: "JWT 实现" } // ] // } // } // 展开后显示 Sub-Trace 内部 Goals: // ┌→ [A.1:JWT设计] → [A.2:JWT实现] ──┐ // [1] ──────┼ ├──→ [3] // └→ [abc123.B] ─────────────────────┘ // 注意:前端显示为 "A.1",实际查询是 GET /api/traces/abc123.A/messages?goal_id=1 ``` ### 视觉区分 | 边类型 | 说明 | 样式建议 | |--------|------|---------| | 普通边 | 顺序执行 | 实线 | | 分叉边 | explore 分支开始 | 虚线或带标记 | | 汇合边 | 分支合并到 merge 节点 | 虚线或带标记 | | 废弃边 | abandoned 分支 | 灰色 | --- ## 断线续传 ```javascript let lastEventId = 0 function connect(traceId) { const ws = new WebSocket( `ws://localhost:8000/api/traces/${traceId}/watch?since_event_id=${lastEventId}` ) ws.onmessage = (event) => { const data = JSON.parse(event.data) if (data.event_id) { lastEventId = data.event_id localStorage.setItem(`trace_${traceId}_event_id`, lastEventId) } // 处理事件... } ws.onclose = () => { setTimeout(() => connect(traceId), 3000) } } ``` --- ## 错误处理 ### HTTP 错误码 | 状态码 | 说明 | |--------|------| | 200 | 成功 | | 404 | Trace 不存在 | | 400 | 参数错误 | | 500 | 服务器错误 | ### WebSocket 错误 ```json { "event": "error", "message": "Too many missed events, please reload via REST API" } ``` --- ## 相关文档 - [Context 管理设计](../docs/context-management.md) - Goal 机制完整设计 - [Trace 模块说明](../docs/trace-api.md) - 后端实现细节