# Trace 模块 - 执行记录存储 > 执行轨迹记录和存储的后端实现 ## 运行边界 Trace API 当前只面向单用户本地开发,不提供账号、租户或 Token 鉴权。 - API 默认仅监听 `127.0.0.1:8000`,前端仅监听 `127.0.0.1:3000`。 - HTTP 只接受 `localhost` 和 `127.0.0.1` Host;CORS 只允许这两个本地前端 Origin。 - Trace 和日志 WebSocket 同样要求本地前端 Origin。 - 默认禁用 API 热重载;确需时显式设置 `TRACE_API_RELOAD=true`。 不应将当前 API 通过公网 IP、容器端口映射或反向代理对外暴露。 ## 工具执行确认 当一批 tool calls 中任意一项需要人工确认时,Trace 进入 `waiting_confirmation`,整批调用都不会提前执行。确认状态持久化在该 Trace 目录的 `tool_approvals.json`。 - `GET /api/traces/{trace_id}/tool-approvals/pending`:读取待决策批次。 - `POST /api/traces/{trace_id}/tool-approvals/{batch_id}`:一次提交全部待决策 调用的 `approve` / `reject`;只能修改工具声明的 `editable_params`。 - 待确认时普通 `/run` 返回 409;`/stop` 取消整批并停止 Trace。 - 重启时,未决策批次恢复为等待确认;已决策但尚未开始执行的 批次从持久化 assistant tool-call 安全恢复。 - 重复决策返回 409。如进程在已开始的副作用期间中断,批次进入 `execution_unknown`,Trace 失败关闭且不会自动重试。 新 Trace 在 `context.run_config_snapshot` 保存带哈希的完整运行配置。 续跑、回溯、反思和压缩只从该快照恢复行为配置,不接受本次覆盖。 无快照的历史 Recursive Trace 拒绝续跑;Legacy Trace 使用安全默认值推导 一次并标记 `legacy_inferred=true`。 --- ## 架构概览 **职责定位**:`cyber_agent/trace` 模块负责所有 Trace/Message 相关功能 ``` cyber_agent/trace/ ├── models.py # Trace/Message 数据模型 ├── goal_models.py # Goal/GoalTree 数据模型 ├── protocols.py # TraceStore 存储接口 ├── store.py # 文件系统存储实现 ├── trace_id.py # Trace ID 生成工具 ├── api.py # RESTful 查询 API ├── run_api.py # 控制 API(run/stop/reflect) ├── websocket.py # WebSocket 实时推送 ├── goal_tool.py # goal 工具(计划管理) └── compaction.py # Context 压缩 ``` **设计原则**: - **高内聚**:所有 Trace 相关代码在一个模块 - **松耦合**:核心模型不依赖 FastAPI - **可扩展**:易于添加 PostgreSQL 等存储实现 - **统一模型**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构 --- ## 核心模型 ### Trace - 执行轨迹 一次完整的 LLM 交互(单次调用或 Agent 任务)。每个 Sub-Agent 都是独立的 Trace。 ```python # 主 Trace main_trace = Trace.create(mode="agent", task="探索代码库") # Sub-Trace(由统一 agent() 工具创建) sub_trace = Trace( trace_id="2f8d3a1c...@explore-20260204220012-001", mode="agent", task="探索 JWT 认证方案", parent_trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", parent_goal_id="3", agent_type="explore", status="running" ) # 字段说明 trace.trace_id # UUID(主 Trace)或 {parent}@{mode}-{timestamp}-{seq}(Sub-Trace) trace.mode # "call" | "agent" trace.task # 任务描述 trace.parent_trace_id # 父 Trace ID(Sub-Trace 专用) trace.parent_goal_id # 触发的父 Goal ID(Sub-Trace 专用) trace.agent_type # Agent 类型:explore, delegate 等 trace.status # "running" | "completed" | "failed" | "stopped" trace.total_messages # Message 总数 trace.total_tokens # Token 总数 trace.total_cost # 总成本 trace.current_goal_id # 当前焦点 goal trace.head_sequence # 当前主路径头节点 sequence(用于 build_llm_messages) trace.context # 扩展上下文;Recursive 会固化模式、血缘、协议和预算快照 ``` Recursive 的关键 `context` 字段: | 字段 | 说明 | |------|------| | `agent_mode` / `agent_mode_revision` | 创建时固化的 `recursive` 模式及协议版本 | | `root_trace_id` / `agent_depth` | 根 Trace ID 和业务 Agent 深度(根为 0) | | `created_by_tool` | `agent` 表示业务子 Agent,`validator` 表示独立验收 Trace | | `root_task_anchor` / `root_task_anchor_hash` | Recursive 根任务的不可变目标、完成标准、硬约束及内容哈希 | | `task_protocol` | TaskBrief、报告、审核、待审核项和下一步动作等权威协议状态 | | `context_access` | 当前 Trace 本地被授权的不可变协议引用及静态字符用量 | | `resource_budget` | 根 Trace 固化的树级资源预算快照;动态用量单独保存 | 旧 Trace 缺少 `agent_mode` 时按 Legacy 处理。详细语义见 [Agent 运行模式](./agent-modes.md) 和 [Recursive 任务协议](./recursive-task-protocol.md)。 **Trace ID 格式**: - **主 Trace**:标准 UUID,例如 `2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d` - **Sub-Trace**:`{parent_uuid}@{mode}-{timestamp}-{seq}`,例如 `2f8d3a1c...@explore-20260204220012-001` **实现**:`cyber_agent/trace/models.py:Trace` ### Message - 执行消息 对应 LLM API 消息,加上元数据。通过 `goal_id` 关联 GoalTree 中的目标。通过 `parent_sequence` 形成消息树。 ```python # assistant 消息(模型返回,可能含 text + tool_calls) assistant_msg = Message.create( trace_id=trace.trace_id, role="assistant", goal_id="3", # Goal ID(Trace 内部自增) content={"text": "...", "tool_calls": [...]}, parent_sequence=5, # 父消息的 sequence ) # tool 消息 tool_msg = Message.create( trace_id=trace.trace_id, role="tool", goal_id="5", tool_call_id="call_abc123", content="工具执行结果", parent_sequence=6, ) ``` **parent_sequence**:指向父消息的 sequence,构成消息树。主路径 = 从 `trace.head_sequence` 沿 parent chain 回溯到 root。 **description 字段**(系统自动生成): - `assistant` 消息:优先取 content 中的 text,若无 text 则生成 "tool call: XX, XX" - `tool` 消息:使用 tool name **实现**:`cyber_agent/trace/models.py:Message` --- ## 存储接口 ### TraceStore Protocol ```python class TraceStore(Protocol): # Trace 操作 async def create_trace(self, trace: Trace) -> str: ... async def get_trace(self, trace_id: str) -> Optional[Trace]: ... async def update_trace(self, trace_id: str, **updates) -> None: ... async def list_traces( self, ..., parent_trace_id=None, created_by_tool=None ) -> List[Trace]: ... # Recursive 根 Trace 的树级动态用量 async def get_resource_usage(self, root_trace_id: str) -> Optional[Dict]: ... async def replace_resource_usage(self, root_trace_id: str, usage: Dict) -> None: ... # GoalTree 操作(每个 Trace 有独立的 GoalTree) async def get_goal_tree(self, trace_id: str) -> Optional[GoalTree]: ... async def update_goal_tree(self, trace_id: str, tree: GoalTree) -> None: ... async def add_goal(self, trace_id: str, goal: Goal) -> None: ... async def update_goal(self, trace_id: str, goal_id: str, **updates) -> None: ... # Message 操作 async def add_message(self, message: Message) -> str: ... async def get_message(self, message_id: str) -> Optional[Message]: ... async def get_trace_messages(self, trace_id: str) -> List[Message]: ... async def get_main_path_messages(self, trace_id: str, head_sequence: int) -> List[Message]: ... async def get_messages_by_goal(self, trace_id: str, goal_id: str) -> List[Message]: ... async def update_message(self, message_id: str, **updates) -> None: ... # 事件流(WebSocket 断线续传) async def get_events(self, trace_id: str, since_event_id: int) -> List[Dict]: ... async def append_event(self, trace_id: str, event_type: str, payload: Dict) -> int: ... ``` **实现**:`cyber_agent/trace/protocols.py` `list_traces()` 先应用 `parent_trace_id` 和 `created_by_tool` 过滤,再应用 `limit`。因此可以准确查询某 Trace 由 `agent` 创建的直接业务孩子,Validator Trace 不会混入六孩子计数。 ### FileSystemTraceStore ```python from cyber_agent.trace import FileSystemTraceStore store = FileSystemTraceStore(base_path=".trace") ``` **目录结构**: ``` .trace/ ├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/ # 主 Trace │ ├── meta.json # Trace 元数据 │ ├── goal.json # GoalTree(扁平 JSON) │ ├── resource_usage.json # Recursive 根 Trace 的树级资源用量 │ ├── messages/ # Messages │ │ ├── {message_id}.json │ │ └── ... │ └── events.jsonl # 事件流 │ ├── 2f8d3a1c...@explore-20260204220012-001/ # Sub-Trace A │ ├── meta.json # parent_trace_id 指向主 Trace │ ├── goal.json # 独立的 GoalTree │ ├── messages/ │ └── events.jsonl │ └── 2f8d3a1c...@explore-20260204220012-002/ # Sub-Trace B └── ... ``` **关键变化**(相比旧设计): - ❌ 不再有 `branches/` 子目录 - ✅ 每个 Sub-Trace 是顶层独立目录 - ✅ Sub-Trace 有完整的 Trace 结构(meta + goal + messages + events) **实现**:`cyber_agent/trace/store.py` --- ## REST API 端点 ### 查询端点 #### 1. 列出 Traces ```http GET /api/traces?mode=agent&status=running&limit=20 ``` 返回所有 Traces(包括主 Trace 和 Sub-Traces)。 #### 2. 获取 Trace + GoalTree + Sub-Traces ```http GET /api/traces/{trace_id} ``` 返回: - `trace`:Trace 元数据(包含 `context`) - GoalTree(该 Trace 的完整 Goal 树) - `sub_traces`:所有 `parent_trace_id == trace_id` 的直接子 Trace 元数据,包括业务子 Agent 和 Validator Trace 每个有效验收 Scope 都会生成一个 Validator Trace,使用 `agent_type="validator"`、 `context.created_by_tool="validator"` 和 `context.validation_scope`。它不增加业务 `agent_depth`,也不计入每父六个业务孩子。如果只需业务子 Agent,存储层查询应同时使用 `parent_trace_id` 和 `created_by_tool="agent"`。 #### 3. 获取 Messages ```http GET /api/traces/{trace_id}/messages?mode=main_path&head=15&goal_id=3 ``` 返回指定 Trace 的 Messages。参数: - `mode`: `main_path`(默认)| `all` — 返回主路径消息或全部消息 - `head`: 可选 sequence 值 — 指定主路径的 head(默认用 trace.head_sequence,仅 mode=main_path 有效) - `goal_id`: 可选,按 Goal 过滤 **实现**:`cyber_agent/trace/api.py` ### 控制端点 需在 `api_server.py` 中配置 Runner。执行在后台异步进行,通过 WebSocket 监听进度。 #### 4. 新建 Trace 并执行 ```http POST /api/traces Content-Type: application/json { "messages": [ {"role": "system", "content": "自定义 system prompt(可选,不传则从 skills 自动构建)"}, {"role": "user", "content": "分析项目架构"} ], "model": "gpt-4o", "temperature": 0.3, "max_iterations": 200, "tools": null, "name": "任务名称", "uid": "user_id", "project_name": null, "root_task_anchor": { "objective": "分析项目架构", "completion_criteria": ["列出模块与核心调用链"], "constraints": ["结论必须来自实际代码"] } } ``` `root_task_anchor` 在 Recursive 模式下必填,`objective` 和至少一项 `completion_criteria` 不能为空;Legacy 模式忽略该字段。旧字段 `root_completion_criteria` 会明确报错,不静默兼容。 #### 5. 运行(统一续跑 + 回溯) ```http POST /api/traces/{trace_id}/run Content-Type: application/json { "messages": [{"role": "user", "content": "..."}], "after_message_id": null } ``` - `after_message_id: null`(或省略)→ 从末尾续跑 - `after_message_id: ""`(主路径上且 < head)→ 回溯到该消息后运行 - `messages: []` + `after_message_id: ""` → 重新生成 Runner 根据解析出的 sequence 与 `head_sequence` 的关系自动判断续跑/回溯行为。 #### 6. 停止运行中的 Trace ```http POST /api/traces/{trace_id}/stop ``` 设置取消信号,agent loop 在下一个检查点退出,Trace 状态置为 `stopped`。 Legacy 只停止请求的 Trace;Recursive 会在当前进程内向活跃的直接孩子和后代传播停止信号。 Recursive 为协作式停止:不强制中断已发出的 LLM 请求,但请求返回后不再执行 Tool Call 或开始下一轮。尚未启动的孩子直接写为 `stopped`;已停止子任务以 `outcome=failed` 的 TaskReport 返回父级。当前不支持跨 Worker 取消。 #### 7. 列出正在运行的 Trace ```http GET /api/traces/running ``` #### 8. 反思(提取经验) ```http POST /api/traces/{trace_id}/reflect Content-Type: application/json { "focus": "可选,反思重点" } ``` 以 `force_side_branch=["reflection"]` 启动反思侧分支,使用当前 Agent 循环进行多轮思考,必要时可调用知识工具。 默认提取结果以 `extraction_pending` 写入 `cognition_log`,再通过提取审核 CLI/API 审核和提交;反思侧分支不改变主路径的 `head_sequence`。 ### 经验端点 #### 9. 读取经验文件 ```http GET /api/experiences ``` 返回 `./.cache/experiences.md` 的文件内容。 **实现**:`cyber_agent/trace/run_api.py` --- ## WebSocket 事件 ### 连接 ``` ws://43.106.118.91:8000/api/traces/{trace_id}/watch?since_event_id=0 ``` ### 事件类型 | 事件 | 触发时机 | payload | |------|---------|---------| | `connected` | WebSocket 连接成功 | trace_id, current_event_id, trace_status, is_running, goal_tree, sub_traces | | `goal_added` | 新增 Goal | goal 完整数据(含 stats, parent_id, type) | | `goal_updated` | Goal 状态变化 | goal_id, patch(历史补发为 updates), affected_goals(Legacy 级联完成时可包含父节点) | | `message_added` | 新 Message | message 数据(含 goal_id),affected_goals | | `sub_trace_started` | Sub-Trace 开始执行 | trace_id, parent_goal_id, agent_type, task | | `sub_trace_completed` | Sub-Trace 完成 | trace_id, status, summary, stats | | `rewind` | 回溯执行 | after_sequence, head_sequence, goal_tree_snapshot | | `trace_completed` | 执行完成 | trace_id, total_messages | | `trace_status_changed` | 停止或续跑导致 Trace 状态变化 | trace_id, status | ### Stats 更新逻辑 每次添加 Message 时,后端执行: 1. 更新对应 Goal 的 `self_stats` 2. 沿 `parent_id` 链向上更新所有祖先的 `cumulative_stats` 3. 在 `message_added` 事件的 `affected_goals` 中推送所有受影响的 Goal 及其最新 stats ### 级联完成(Cascade Completion) 该自动行为只用于 Legacy:当所有子 Goals 都完成时,自动完成父 Goal: 1. 检测子 Goals 全部 `status == "completed"` 2. 自动设置父 Goal 的 `status = "completed"` 3. 在 `goal_updated` 事件的 `affected_goals` 中包含级联完成的父节点 Recursive 会显式关闭级联完成和自动切换下一个 sibling:子 Agent 返回后,父 Goal 从 `waiting_children` 进入 `pending_review`,由父 Agent 审核后再决定重新规划、修订、完成或失败。 **实现**:`cyber_agent/trace/goal_models.py`、`cyber_agent/trace/goal_tool.py`、`cyber_agent/trace/store.py` --- ## Sub-Trace 工具 ### agent() 工具 本地 Sub-Agent 统一由 `agent()` 工具创建。`delegate` 和 `explore` 只是内部运行模式,不是可单独导入或调用的工具: ```python # Legacy:单任务进入 delegate 模式 agent(task="实现用户登录功能") # Legacy:多任务进入 explore 模式 agent(task=["JWT 方案", "Session 方案"]) # Recursive:使用结构化 TaskBrief,单个对象或对象列表 agent(task_brief={ "objective": "实现用户登录功能", "reason": "将身份认证从父任务中独立验证", "completion_criteria": ["登录成功和失败路径均通过验证"], "expected_outputs": ["实现代码", "验证结果"] }) ``` - 单任务使用内部 `delegate` 模式,多任务使用内部 `explore` 模式并汇总结果。 - Legacy 的多任务整批并行;Recursive 默认串行,显式配置并行时最多同时执行 2 个孩子。 - Trace/Goal 上下文由框架注入;Recursive 子 Agent 的工具权限不会超过父 Agent 的实际权限。 --- ## 使用场景 ### Agent 执行时记录 ```python from cyber_agent import AgentRunner from cyber_agent.trace import FileSystemTraceStore store = FileSystemTraceStore(base_path=".trace") runner = AgentRunner(trace_store=store, llm_call=my_llm_fn) async for event in runner.run([ {"role": "user", "content": "探索代码库"}, ]): print(event) # Trace 或 Message ``` ### 查询 Sub-Traces ```python # 获取主 Trace 的直接 Sub-Traces sub_traces = await store.list_traces( parent_trace_id=main_trace_id, created_by_tool="agent", limit=1000, ) ``` Recursive 根 Trace 的 `resource_usage.json` 记录 Agent 数、LLM 调用、Token、成本、起始时间及超限原因。它由 TraceStore 供框架内部读写,不是 Trace 详情响应的独立顶层字段。停止、回溯和续跑不退还历史用量。 --- ## 相关文档 - [frontend/API.md](../../frontend/API.md) - 前端对接 API 文档 - [Agent 运行模式](./agent-modes.md) - Legacy/Recursive 运行边界、调度和预算 - [Recursive 任务协议](./recursive-task-protocol.md) - TaskBrief、TaskReport、Validator 和审核状态机 - [context-management.md](./context-management.md) - Context 管理完整设计 - [trace/goal_models.py](../trace/goal_models.py) - GoalTree 模型定义