# Trace 模块 - 执行记录存储 > 执行轨迹记录和存储的后端实现 --- ## 架构概览 **职责定位**:`agent/execution` 模块负责所有 Trace/Message 相关功能 ``` agent/execution/ ├── models.py # Trace/Message 数据模型 ├── protocols.py # TraceStore 存储接口 ├── fs_store.py # 文件系统存储实现 ├── trace_id.py # Trace ID 生成工具 ├── api.py # RESTful API └── websocket.py # WebSocket 实时推送 ``` **设计原则**: - **高内聚**:所有 Trace 相关代码在一个模块 - **松耦合**:核心模型不依赖 FastAPI - **可扩展**:易于添加 PostgreSQL 等存储实现 - **统一模型**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构 --- ## 核心模型 ### Trace - 执行轨迹 一次完整的 LLM 交互(单次调用或 Agent 任务)。每个 Sub-Agent 都是独立的 Trace。 ```python # 主 Trace main_trace = Trace.create(mode="agent", task="探索代码库") # Sub-Trace(由 delegate 或 explore 工具创建) 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" trace.total_messages # Message 总数 trace.total_tokens # Token 总数 trace.total_cost # 总成本 trace.current_goal_id # 当前焦点 goal ``` **Trace ID 格式**: - **主 Trace**:标准 UUID,例如 `2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d` - **Sub-Trace**:`{parent_uuid}@{mode}-{timestamp}-{seq}`,例如 `2f8d3a1c...@explore-20260204220012-001` **实现**:`agent/execution/models.py:Trace` ### Message - 执行消息 对应 LLM API 消息,加上元数据。通过 `goal_id` 关联 GoalTree 中的目标。 ```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": [...]}, ) # tool 消息 tool_msg = Message.create( trace_id=trace.trace_id, role="tool", goal_id="5", tool_call_id="call_abc123", content="工具执行结果", ) ``` **description 字段**(系统自动生成): - `assistant` 消息:优先取 content 中的 text,若无 text 则生成 "tool call: XX, XX" - `tool` 消息:使用 tool name **实现**:`agent/execution/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, ...) -> List[Trace]: ... # 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_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: ... ``` **实现**:`agent/execution/protocols.py` ### FileSystemTraceStore ```python from agent.execution import FileSystemTraceStore store = FileSystemTraceStore(base_path=".trace") ``` **目录结构**: ``` .trace/ ├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/ # 主 Trace │ ├── meta.json # Trace 元数据 │ ├── goal.json # GoalTree(扁平 JSON) │ ├── 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) **实现**:`agent/execution/fs_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 元数据 - GoalTree(该 Trace 的完整 Goal 树) - Sub-Traces 元数据(查询所有 `parent_trace_id == trace_id` 的 Traces) ### 3. 获取 Messages ```http GET /api/traces/{trace_id}/messages?goal_id=3 ``` 返回指定 Trace 的 Messages,可选按 Goal 过滤。 **实现**:`agent/execution/api.py` --- ## WebSocket 事件 ### 连接 ``` ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0 ``` ### 事件类型 | 事件 | 触发时机 | payload | |------|---------|---------| | `connected` | WebSocket 连接成功 | trace_id, current_event_id, goal_tree, sub_traces | | `goal_added` | 新增 Goal | goal 完整数据(含 stats, parent_id, type) | | `goal_updated` | Goal 状态变化(含级联完成) | goal_id, updates, affected_goals(含级联完成的父节点) | | `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 | | `trace_completed` | 执行完成 | 统计信息 | ### Stats 更新逻辑 每次添加 Message 时,后端执行: 1. 更新对应 Goal 的 `self_stats` 2. 沿 `parent_id` 链向上更新所有祖先的 `cumulative_stats` 3. 在 `message_added` 事件的 `affected_goals` 中推送所有受影响的 Goal 及其最新 stats ### 级联完成(Cascade Completion) 当所有子 Goals 都完成时,自动完成父 Goal: 1. 检测子 Goals 全部 `status == "completed"` 2. 自动设置父 Goal 的 `status = "completed"` 3. 在 `goal_updated` 事件的 `affected_goals` 中包含级联完成的父节点 **实现**:`agent/execution/websocket.py` --- ## Sub-Trace 工具 ### explore 工具 并行探索多个方向: ```python from agent.goal.explore import explore_tool result = await explore_tool( current_trace_id="main_trace_id", current_goal_id="3", branches=["JWT 方案", "Session 方案"], store=store, run_agent=run_agent_func ) ``` - 为每个 branch 创建独立的 Sub-Trace - 并行执行所有 Sub-Traces - 汇总结果返回 ### delegate 工具 将大任务委托给独立 Sub-Agent: ```python from agent.goal.delegate import delegate_tool result = await delegate_tool( current_trace_id="main_trace_id", current_goal_id="3", task="实现用户登录功能", store=store, run_agent=run_agent_func ) ``` - 创建单个 Sub-Trace,拥有完整权限 - 执行任务并返回结果 --- ## 使用场景 ### Agent 执行时记录 ```python from agent import AgentRunner from agent.execution import FileSystemTraceStore store = FileSystemTraceStore(base_path=".trace") runner = AgentRunner(trace_store=store, llm_call=my_llm_fn) async for event in runner.run(task="探索代码库"): print(event) # Trace 或 Message ``` ### 查询 Sub-Traces ```python # 获取主 Trace 的所有 Sub-Traces all_traces = await store.list_traces(limit=1000) sub_traces = [t for t in all_traces if t.parent_trace_id == main_trace_id] ``` --- ## 相关文档 - [frontend/API.md](../frontend/API.md) - 前端对接 API 文档 - [docs/context-management.md](./context-management.md) - Context 管理完整设计 - [agent/goal/models.py](../agent/goal/models.py) - GoalTree 模型定义 - [docs/REFACTOR_SUMMARY.md](./REFACTOR_SUMMARY.md) - 重构总结