版本:v4.0 更新日期:2026-02-04
本 API 提供 Agent 执行过程的实时可视化能力:
核心概念:
统一的 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 可视化(前端负责):
http://localhost:8000application/jsonGET /api/traces?status=running&limit=20
查询参数:
| 参数 | 类型 | 必填 | 说明 |
|------|------|------|------|
| status | string | 否 | 过滤状态:running / completed / failed |
| mode | string | 否 | 过滤模式:call / agent |
| limit | int | 否 | 返回数量(默认 50,最大 100)|
响应示例:
{
"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
}
GET /api/traces/{trace_id}
响应示例(主 Trace):
{
"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
}
}
}
获取 Sub-Trace 的完整信息(包括 GoalTree)。
GET /api/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",
"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": {}
}
查询指定 Trace 的 Messages(用于查看边的详细执行内容)。
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")|
响应示例:
{
"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
}
const ws = new WebSocket(
'ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0'
)
查询参数:
| 参数 | 类型 | 默认值 | 说明 |
|------|------|--------|------|
| since_event_id | int | 0 | 从哪个事件 ID 开始。0 = 补发所有历史 |
连接后推送完整 Trace 信息,前端据此初始化 DAG。
{
"event": "connected",
"trace_id": "abc123",
"current_event_id": 15,
"trace": {
"trace_id": "abc123",
"status": "running",
"goal_tree": {
"mission": "实现用户认证功能",
"current_id": "2",
"goals": [...]
},
"sub_traces": {}
}
}
前端处理:
if (data.event === 'connected') {
initDAG(data.goal_tree)
localStorage.setItem('last_event_id', data.current_event_id)
}
{
"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"
}
前端处理:
if (data.event === 'goal_added') {
insertGoal(data.goal, data.parent_id)
regenerateDAG()
}
包含级联完成场景:当所有子 Goal 完成时,父 Goal 自动 completed。
{
"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": "..." }
}
]
}
前端处理:
if (data.event === 'goal_updated') {
updateGoal(data.goal_id, data.updates)
for (const g of data.affected_goals) {
updateGoalStats(g.goal_id, g)
}
regenerateDAG()
}
后端更新统计后推送,包含受影响的所有 Goals。
{
"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_statscumulative_stats前端处理:
if (data.event === 'message_added') {
for (const g of data.affected_goals) {
updateGoalStats(g.goal_id, g)
}
// 根据当前展开状态更新对应边
rerenderEdge(data.message.goal_id)
}
{
"event": "trace_completed",
"event_id": 50,
"trace_id": "abc123",
"total_messages": 50,
"total_tokens": 25000,
"total_cost": 0.25
}
前端处理:
if (data.event === 'trace_completed') {
markTraceCompleted()
ws.close()
}
explore 或 delegate 工具启动 Sub-Trace 时触发。
{
"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
}
}
前端处理:
if (data.event === 'sub_trace_started') {
insertSubTrace(data.parent_trace_id, data.sub_trace)
regenerateDAG()
}
Sub-Trace 执行完成后触发。
{
"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
}
前端处理:
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_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 规则:
| 字段 | 类型 | 说明 |
|---|---|---|
mission |
string | 总任务描述(来自 Trace.task) |
current_id |
string | null | 当前焦点 Goal 的内部 ID |
goals |
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 设计:
parent_id 维护| 字段 | 类型 | 说明 |
|---|---|---|
message_count |
int | 消息数量 |
total_tokens |
int | Token 总数 |
total_cost |
float | 总成本 |
preview |
string | null | 工具调用摘要(如 "read → edit × 2") |
| 字段 | 类型 | 说明 |
|---|---|---|
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 | 创建时间 |
后端提供 Trace(包含 GoalTree 和 Sub-Traces),前端负责生成 DAG 视图。
普通 Goal(子目标):
agent_call Goal(Sub-Trace):
从 target 节点的 stats 获取:
target.cumulative_statstarget.self_stats// 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
}
// 主 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 分支 | 灰色 |
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)
}
}
| 状态码 | 说明 |
|---|---|
| 200 | 成功 |
| 404 | Trace 不存在 |
| 400 | 参数错误 |
| 500 | 服务器错误 |
{
"event": "error",
"message": "Too many missed events, please reload via REST API"
}