|
|
@@ -13,6 +13,7 @@ agent/execution/
|
|
|
├── models.py # Trace/Message 数据模型
|
|
|
├── protocols.py # TraceStore 存储接口
|
|
|
├── fs_store.py # 文件系统存储实现
|
|
|
+├── trace_id.py # Trace ID 生成工具
|
|
|
├── api.py # RESTful API
|
|
|
└── websocket.py # WebSocket 实时推送
|
|
|
```
|
|
|
@@ -21,6 +22,7 @@ agent/execution/
|
|
|
- **高内聚**:所有 Trace 相关代码在一个模块
|
|
|
- **松耦合**:核心模型不依赖 FastAPI
|
|
|
- **可扩展**:易于添加 PostgreSQL 等存储实现
|
|
|
+- **统一模型**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -28,14 +30,30 @@ agent/execution/
|
|
|
|
|
|
### Trace - 执行轨迹
|
|
|
|
|
|
-一次完整的 LLM 交互(单次调用或 Agent 任务)
|
|
|
+一次完整的 LLM 交互(单次调用或 Agent 任务)。每个 Sub-Agent 都是独立的 Trace。
|
|
|
|
|
|
```python
|
|
|
-trace = Trace.create(mode="agent", task="探索代码库")
|
|
|
+# 主 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.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 总数
|
|
|
@@ -43,28 +61,30 @@ 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` 和 `branch_id` 关联 GoalTree 中的目标。
|
|
|
+对应 LLM API 消息,加上元数据。通过 `goal_id` 关联 GoalTree 中的目标。
|
|
|
|
|
|
```python
|
|
|
# assistant 消息(模型返回,可能含 text + tool_calls)
|
|
|
assistant_msg = Message.create(
|
|
|
trace_id=trace.trace_id,
|
|
|
role="assistant",
|
|
|
- goal_id="3", # 内部 ID(纯自增)
|
|
|
- branch_id=None, # 主线消息
|
|
|
+ goal_id="3", # Goal ID(Trace 内部自增)
|
|
|
content={"text": "...", "tool_calls": [...]},
|
|
|
)
|
|
|
|
|
|
-# 分支内的 tool 消息
|
|
|
+# tool 消息
|
|
|
tool_msg = Message.create(
|
|
|
trace_id=trace.trace_id,
|
|
|
role="tool",
|
|
|
- goal_id="1", # 分支内的 goal ID(分支内独立编号)
|
|
|
- branch_id="A", # 分支 A
|
|
|
+ goal_id="5",
|
|
|
tool_call_id="call_abc123",
|
|
|
content="工具执行结果",
|
|
|
)
|
|
|
@@ -90,25 +110,17 @@ class TraceStore(Protocol):
|
|
|
async def update_trace(self, trace_id: str, **updates) -> None: ...
|
|
|
async def list_traces(self, ...) -> List[Trace]: ...
|
|
|
|
|
|
- # GoalTree 操作
|
|
|
+ # 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: ...
|
|
|
|
|
|
- # Branch 操作(分支独立存储)
|
|
|
- async def create_branch(self, trace_id: str, branch: BranchContext) -> None: ...
|
|
|
- async def get_branch(self, trace_id: str, branch_id: str) -> Optional[BranchContext]: ...
|
|
|
- async def get_branch_detail(self, trace_id: str, branch_id: str) -> Optional[BranchDetail]: ...
|
|
|
- async def update_branch(self, trace_id: str, branch_id: str, **updates) -> None: ...
|
|
|
- async def list_branches(self, trace_id: str) -> Dict[str, BranchContext]: ...
|
|
|
-
|
|
|
# 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 get_messages_by_branch(self, trace_id: str, branch_id: str) -> List[Message]: ...
|
|
|
async def update_message(self, message_id: str, **updates) -> None: ...
|
|
|
|
|
|
# 事件流(WebSocket 断线续传)
|
|
|
@@ -128,22 +140,30 @@ store = FileSystemTraceStore(base_path=".trace")
|
|
|
|
|
|
**目录结构**:
|
|
|
```
|
|
|
-.trace/{trace_id}/
|
|
|
-├── meta.json # Trace 元数据
|
|
|
-├── goal.json # 主线 GoalTree(扁平 JSON,通过 parent_id 构建层级)
|
|
|
-├── messages/ # 主线 Messages(每条独立文件)
|
|
|
-│ ├── {message_id}.json
|
|
|
-│ └── ...
|
|
|
-├── branches/ # 分支数据(独立存储)
|
|
|
-│ ├── A/
|
|
|
-│ │ ├── meta.json # BranchContext 元数据
|
|
|
-│ │ ├── goal.json # 分支 A 的 GoalTree
|
|
|
-│ │ └── messages/ # 分支 A 的 Messages
|
|
|
-│ └── B/
|
|
|
-│ └── ...
|
|
|
-└── events.jsonl # 事件流(WebSocket 续传)
|
|
|
+.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`
|
|
|
|
|
|
---
|
|
|
@@ -156,30 +176,26 @@ store = FileSystemTraceStore(base_path=".trace")
|
|
|
GET /api/traces?mode=agent&status=running&limit=20
|
|
|
```
|
|
|
|
|
|
-### 2. 获取 Trace + GoalTree + 分支元数据
|
|
|
+返回所有 Traces(包括主 Trace 和 Sub-Traces)。
|
|
|
+
|
|
|
+### 2. 获取 Trace + GoalTree + Sub-Traces
|
|
|
|
|
|
```http
|
|
|
GET /api/traces/{trace_id}
|
|
|
```
|
|
|
|
|
|
-返回 Trace 元数据、主线 GoalTree(扁平列表,含所有 Goal 及其 stats)、分支元数据(不含分支内部 GoalTree)。
|
|
|
+返回:
|
|
|
+- Trace 元数据
|
|
|
+- GoalTree(该 Trace 的完整 Goal 树)
|
|
|
+- Sub-Traces 元数据(查询所有 `parent_trace_id == trace_id` 的 Traces)
|
|
|
|
|
|
### 3. 获取 Messages
|
|
|
|
|
|
```http
|
|
|
GET /api/traces/{trace_id}/messages?goal_id=3
|
|
|
-GET /api/traces/{trace_id}/messages?branch_id=A
|
|
|
-```
|
|
|
-
|
|
|
-返回指定 Goal 或分支关联的所有 Messages(用于查看执行详情)。
|
|
|
-
|
|
|
-### 4. 获取分支详情(按需加载)
|
|
|
-
|
|
|
-```http
|
|
|
-GET /api/traces/{trace_id}/branches/{branch_id}
|
|
|
```
|
|
|
|
|
|
-返回分支完整详情,包括分支内的 GoalTree(用于展开分支时加载)。
|
|
|
+返回指定 Trace 的 Messages,可选按 Goal 过滤。
|
|
|
|
|
|
**实现**:`agent/execution/api.py`
|
|
|
|
|
|
@@ -197,14 +213,12 @@ ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0
|
|
|
|
|
|
| 事件 | 触发时机 | payload |
|
|
|
|------|---------|---------|
|
|
|
-| `connected` | WebSocket 连接成功 | trace_id, current_event_id, goal_tree, branches(分支元数据) |
|
|
|
-| `goal_added` | 新增 Goal | goal 完整数据(含 stats, parent_id, branch_id, type) |
|
|
|
-| `goal_updated` | Goal 状态变化(含级联完成) | goal_id, updates, affected_goals(含 cumulative_stats) |
|
|
|
-| `message_added` | 新 Message | message 数据(含 goal_id, branch_id),affected_goals,affected_branches |
|
|
|
-| `branch_started` | 分支开始探索 | explore_start_id, branch 元数据 |
|
|
|
-| `branch_goal_added` | 分支内新增 Goal | branch_id, goal |
|
|
|
-| `branch_completed` | 分支完成 | explore_start_id, branch_id, summary, cumulative_stats, last_message |
|
|
|
-| `explore_completed` | 所有分支完成 | explore_start_id, merge_summary |
|
|
|
+| `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 更新逻辑
|
|
|
@@ -212,17 +226,62 @@ ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0
|
|
|
每次添加 Message 时,后端执行:
|
|
|
1. 更新对应 Goal 的 `self_stats`
|
|
|
2. 沿 `parent_id` 链向上更新所有祖先的 `cumulative_stats`
|
|
|
-3. 如果是分支内 Message(branch_id 非空),还需更新所属 Branch 的 `cumulative_stats`
|
|
|
-4. 在 `message_added` 事件的 `affected_goals` 和 `affected_branches` 中推送所有受影响的最新 stats
|
|
|
+3. 在 `message_added` 事件的 `affected_goals` 中推送所有受影响的 Goal 及其最新 stats
|
|
|
|
|
|
-**分支统计**:
|
|
|
-- Branch 的 `cumulative_stats`:分支内所有 Goals 的累计统计
|
|
|
-- explore_start Goal 的 `cumulative_stats`:所有关联分支的 cumulative_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 执行时记录
|
|
|
@@ -238,6 +297,14 @@ 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]
|
|
|
+```
|
|
|
+
|
|
|
---
|
|
|
|
|
|
## 相关文档
|
|
|
@@ -245,3 +312,4 @@ async for event in runner.run(task="探索代码库"):
|
|
|
- [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) - 重构总结
|