|
|
@@ -1,6 +1,6 @@
|
|
|
# Context 管理与执行计划
|
|
|
|
|
|
-> 本文档描述 Agent 的 Context 管理、执行计划和探索机制。
|
|
|
+> 本文档描述 Agent 的 Context 管理、执行计划和 Sub-Agent 机制。
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -9,7 +9,8 @@
|
|
|
1. **自主长程执行**:Agent 能独立执行复杂任务,无需人工频繁干预
|
|
|
2. **有效的 Context 管理**:长任务中保持关键信息,压缩次要细节
|
|
|
3. **支持探索和回溯**:能尝试多种方案,失败时能有效回溯
|
|
|
-4. **简单的工具接口**:LLM 只需理解少量简单工具,复杂逻辑由系统处理
|
|
|
+4. **统一的 Agent 模型**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构
|
|
|
+5. **简单的工具接口**:LLM 只需理解少量简单工具,复杂逻辑由系统处理
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -130,57 +131,137 @@ Todo.Info = {
|
|
|
### 核心思路
|
|
|
|
|
|
```
|
|
|
-基于 OpenCode 方案,增强三个能力:
|
|
|
+基于 OpenCode 方案,但采用更统一的架构:
|
|
|
1. 结构化 Plan(goal 工具)
|
|
|
-2. 并行探索-合并(explore 工具)
|
|
|
-3. 精确回溯(abandon + context 压缩)
|
|
|
+2. 统一的 Trace 模型(主 Agent 和 Sub-Agent 都是 Trace)
|
|
|
+3. 并行探索-合并(explore 工具,启动独立的 Sub-Traces)
|
|
|
+4. 精确回溯(abandon + context 压缩)
|
|
|
```
|
|
|
|
|
|
### 架构
|
|
|
|
|
|
```
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
-│ GoalTree (嵌套 JSON) │
|
|
|
-│ 层级目标,LLM 通过 goal 工具维护 │
|
|
|
-│ 注入 LLM 时过滤废弃目标,重新生成连续显示序号 │
|
|
|
+│ 每个 Agent = 一个 Trace │
|
|
|
+│ 主 Trace 和 Sub-Trace 使用相同的数据结构 │
|
|
|
└─────────────────────────────────────────────┘
|
|
|
│
|
|
|
┌────────────┴────────────┐
|
|
|
↓ ↓
|
|
|
┌─────────────────┐ ┌─────────────────┐
|
|
|
-│ Messages │ │ 并行探索 │
|
|
|
-│ (扁平列表, │ │ (explore 工具) │
|
|
|
-│ goal_id 关联) │ │ 多个独立分支 │
|
|
|
+│ GoalTree │ │ Messages │
|
|
|
+│ (层级目标) │ │ (扁平列表) │
|
|
|
+│ goal 工具维护 │ │ goal_id 关联 │
|
|
|
└─────────────────┘ └─────────────────┘
|
|
|
│ │
|
|
|
↓ ↓
|
|
|
┌─────────────────┐ ┌─────────────────┐
|
|
|
-│ 完成/回溯 │ │ 合并评估 │
|
|
|
-│ done/abandon │ │ 返回主会话 │
|
|
|
-│ 触发 context │ └─────────────────┘
|
|
|
-│ 压缩 │
|
|
|
-└─────────────────┘
|
|
|
+│ 工具调用 │ │ Sub-Traces │
|
|
|
+│ explore/ │ │ 完全独立的 │
|
|
|
+│ delegate │──────▶ Trace 实例 │
|
|
|
+└─────────────────┘ └─────────────────┘
|
|
|
│
|
|
|
↓
|
|
|
┌─────────────────────────────────────────────┐
|
|
|
│ DAG 可视化(派生视图) │
|
|
|
│ 从 GoalTree + Messages 生成 │
|
|
|
│ 节点 = 结果/里程碑,边 = 动作/执行过程 │
|
|
|
-│ 边可展开/折叠,对应目标的层级展开 │
|
|
|
+│ 边可展开/折叠,Sub-Trace 作为折叠边显示 │
|
|
|
└─────────────────────────────────────────────┘
|
|
|
```
|
|
|
|
|
|
-### 数据结构
|
|
|
+### 核心概念:统一的 Trace 模型
|
|
|
|
|
|
-#### 两层数据
|
|
|
+**关键设计**:每个 Agent(主 Agent 或 Sub-Agent)都是一个完整的 Trace。
|
|
|
+
|
|
|
+```python
|
|
|
+# 主 Agent
|
|
|
+Trace(trace_id="abc123", mode="agent", task="实现用户认证")
|
|
|
+
|
|
|
+# Sub-Agent(并行探索)
|
|
|
+Trace(trace_id="abc123.A", parent_trace_id="abc123", agent_type="explore", task="JWT 方案")
|
|
|
+Trace(trace_id="abc123.B", parent_trace_id="abc123", agent_type="explore", task="Session 方案")
|
|
|
+
|
|
|
+# Sub-Agent(单线委托)
|
|
|
+Trace(trace_id="abc123.task1", parent_trace_id="abc123", agent_type="delegate", task="实现具体功能")
|
|
|
+```
|
|
|
+
|
|
|
+**优点**:
|
|
|
+- **概念统一**:不需要额外的 SubAgentContext/BranchContext 概念
|
|
|
+- **ID 简洁**:每个 Trace 内部独立编号(1, 2, 3),不需要前缀
|
|
|
+- **完全隔离**:每个 Trace 有独立的 GoalTree、Message List、LLM Context
|
|
|
+- **自然分布式**:每个 Trace 可以独立运行、迁移、存储
|
|
|
+- **层级清晰**:从 trace_id 可以直接解析出父子关系(`abc123.A` 的父是 `abc123`)
|
|
|
+
|
|
|
+### 数据结构
|
|
|
|
|
|
后端存储两类数据,可视化的 DAG 是派生视图:
|
|
|
|
|
|
-1. **GoalTree**(嵌套 JSON):层级目标,注入 LLM
|
|
|
-2. **Messages**(扁平列表):执行记录,通过 `goal_id` 关联 Goal
|
|
|
+1. **GoalTree**:层级目标,注入 LLM
|
|
|
+2. **Messages**:执行记录,通过 `goal_id` 关联 Goal
|
|
|
|
|
|
不存在独立的"边"数据结构,边在可视化时从 Messages 聚合生成。
|
|
|
|
|
|
+#### Trace
|
|
|
+
|
|
|
+```python
|
|
|
+@dataclass
|
|
|
+class Trace:
|
|
|
+ """
|
|
|
+ 执行轨迹 - 一次完整的 Agent 运行
|
|
|
+
|
|
|
+ 主 Agent 和 Sub-Agent 使用相同的 Trace 结构
|
|
|
+ """
|
|
|
+ trace_id: str # 层级化 ID:"abc123", "abc123.A", "abc123.A.1"
|
|
|
+ mode: Literal["call", "agent"]
|
|
|
+ task: Optional[str] = None
|
|
|
+
|
|
|
+ # 父子关系
|
|
|
+ parent_trace_id: Optional[str] = None # 父 Trace ID
|
|
|
+ parent_goal_id: Optional[str] = None # 哪个 Goal 启动的
|
|
|
+
|
|
|
+ # 类型标记(语义)
|
|
|
+ agent_type: Optional[str] = None # "main", "explore", "delegate", "compaction"
|
|
|
+
|
|
|
+ # 权限和配置
|
|
|
+ context: Dict[str, Any] = field(default_factory=dict)
|
|
|
+ # 可以包含:
|
|
|
+ # - allowed_tools: ["read", "grep"] # 工具白名单
|
|
|
+ # - denied_tools: ["bash"] # 工具黑名单
|
|
|
+ # - max_turns: 10 # 最大对话轮数
|
|
|
+ # - timeout: 300 # 超时(秒)
|
|
|
+
|
|
|
+ status: Literal["running", "completed", "failed"] = "running"
|
|
|
+
|
|
|
+ # 统计
|
|
|
+ total_messages: int = 0
|
|
|
+ total_tokens: int = 0
|
|
|
+ total_cost: float = 0.0
|
|
|
+
|
|
|
+ created_at: datetime
|
|
|
+ completed_at: Optional[datetime] = None
|
|
|
+```
|
|
|
+
|
|
|
+**实现**:`agent/execution/models.py:Trace`
|
|
|
+
|
|
|
+**Trace ID 命名规则**:
|
|
|
+- **主 Trace**:短随机 ID(如 `"abc123"`)
|
|
|
+- **Sub-Trace**:`parent_id.suffix`(如 `"abc123.A"`, `"abc123.task1"`)
|
|
|
+- **嵌套 Sub-Trace**:继续追加(如 `"abc123.A.1"`, `"abc123.A.task2"`)
|
|
|
+
|
|
|
+**从 trace_id 解析父子关系**:
|
|
|
+```python
|
|
|
+def parse_parent_trace_id(trace_id: str) -> Optional[str]:
|
|
|
+ """从 trace_id 解析出 parent_trace_id"""
|
|
|
+ parts = trace_id.rsplit('.', 1)
|
|
|
+ return parts[0] if len(parts) > 1 else None
|
|
|
+
|
|
|
+# 示例
|
|
|
+parse_parent_trace_id("abc123.A") # → "abc123"
|
|
|
+parse_parent_trace_id("abc123.A.1") # → "abc123.A"
|
|
|
+parse_parent_trace_id("abc123") # → None
|
|
|
+```
|
|
|
+
|
|
|
#### Goal
|
|
|
|
|
|
```python
|
|
|
@@ -192,13 +273,12 @@ class GoalStats:
|
|
|
preview: Optional[str] = None # 工具调用摘要,如 "read_file → edit_file → bash"
|
|
|
|
|
|
GoalStatus = Literal["pending", "in_progress", "completed", "abandoned"]
|
|
|
-GoalType = Literal["normal", "explore_start", "explore_merge"]
|
|
|
+GoalType = Literal["normal", "agent_call"] # agent_call: 启动了 Sub-Agent
|
|
|
|
|
|
@dataclass
|
|
|
class Goal:
|
|
|
id: str # 内部唯一 ID,纯自增("1", "2", "3"...)
|
|
|
parent_id: Optional[str] = None # 父 Goal ID(层级关系)
|
|
|
- branch_id: Optional[str] = None # 所属分支 ID(分支关系,null=主线)
|
|
|
type: GoalType = "normal" # Goal 类型
|
|
|
|
|
|
description: str # 目标描述(做什么)
|
|
|
@@ -206,13 +286,9 @@ class Goal:
|
|
|
status: GoalStatus # pending | in_progress | completed | abandoned
|
|
|
summary: Optional[str] = None # 完成/放弃时的总结
|
|
|
|
|
|
- # explore_start 特有
|
|
|
- branch_ids: Optional[List[str]] = None # 关联的分支 ID 列表
|
|
|
-
|
|
|
- # explore_merge 特有
|
|
|
- explore_start_id: Optional[str] = None # 关联的 explore_start Goal
|
|
|
- merge_summary: Optional[str] = None # 各分支汇总结果
|
|
|
- selected_branch: Optional[str] = None # 选中的分支(可选)
|
|
|
+ # agent_call 特有
|
|
|
+ sub_trace_ids: Optional[List[str]] = None # 启动的 Sub-Trace IDs
|
|
|
+ agent_call_mode: Optional[str] = None # "explore" | "delegate" | "sequential"
|
|
|
|
|
|
# 统计(后端维护,用于可视化边的数据)
|
|
|
self_stats: GoalStats # 自身统计(仅直接关联的 messages)
|
|
|
@@ -222,9 +298,8 @@ class Goal:
|
|
|
**实现**:`agent/goal/models.py:Goal`
|
|
|
|
|
|
**ID 设计**:
|
|
|
-- **内部 ID**:纯自增数字("1", "2", "3", "4"...),不管层级、分支、废弃
|
|
|
+- **内部 ID**:每个 Trace 独立编号("1", "2", "3"),简单自增
|
|
|
- **层级关系**:通过 `parent_id` 字段维护
|
|
|
-- **分支关系**:通过 `branch_id` 字段维护(null 表示主线,"A"/"B" 表示分支)
|
|
|
- **显示序号**:`to_prompt()` 时动态生成连续有意义的编号("1", "2", "2.1", "2.2"...)
|
|
|
|
|
|
**统计更新逻辑**:
|
|
|
@@ -236,24 +311,24 @@ class Goal:
|
|
|
class GoalTree:
|
|
|
mission: str # 总任务描述
|
|
|
current_id: Optional[str] = None # 当前焦点(内部 ID)
|
|
|
- goals: List[Goal] # 顶层目标(扁平列表,通过 parent_id 构建层级)
|
|
|
+ goals: List[Goal] # 扁平列表,通过 parent_id 构建层级
|
|
|
+ _next_id: int = 1 # 内部 ID 计数器
|
|
|
```
|
|
|
|
|
|
**实现**:`agent/goal/models.py:GoalTree`
|
|
|
|
|
|
#### Message
|
|
|
|
|
|
-Message 对应 LLM API 的消息,加上元数据。每条 Message 通过 `goal_id` 和 `branch_id` 关联所属 Goal。
|
|
|
+Message 对应 LLM API 的消息,加上元数据。每条 Message 通过 `goal_id` 关联所属 Goal。
|
|
|
|
|
|
```python
|
|
|
@dataclass
|
|
|
class Message:
|
|
|
message_id: str
|
|
|
- trace_id: str
|
|
|
- branch_id: Optional[str] = None # 所属分支(null=主线, "A"/"B"=分支)
|
|
|
+ trace_id: str # 所属 Trace ID
|
|
|
role: Literal["assistant", "tool"] # 和 LLM API 一致
|
|
|
- sequence: int # 全局顺序
|
|
|
- goal_id: str # 关联的 Goal 内部 ID
|
|
|
+ sequence: int # 当前 Trace 内的顺序
|
|
|
+ goal_id: str # 关联的 Goal 内部 ID(如 "1", "2")
|
|
|
tool_call_id: Optional[str] # tool 消息关联对应的 tool_call
|
|
|
content: Any # 消息内容(和 LLM API 格式一致)
|
|
|
description: str # 消息描述(系统自动生成)
|
|
|
@@ -274,6 +349,15 @@ class Message:
|
|
|
- `role="assistant"`:模型的一次返回,可能同时包含文本和多个 tool_calls
|
|
|
- `role="tool"`:一个工具的执行结果,通过 `tool_call_id` 关联对应的 tool_call
|
|
|
|
|
|
+**查询 Message**:
|
|
|
+```python
|
|
|
+# 查询主 Trace 的 Messages
|
|
|
+GET /api/traces/abc123/messages?goal_id=2
|
|
|
+
|
|
|
+# 查询 Sub-Trace 的 Messages
|
|
|
+GET /api/traces/abc123.A/messages?goal_id=1
|
|
|
+```
|
|
|
+
|
|
|
### 工具设计
|
|
|
|
|
|
#### goal 工具:计划管理
|
|
|
@@ -328,7 +412,7 @@ pending ──focus──→ in_progress ──done──→ completed
|
|
|
|
|
|
#### explore 工具:并行探索
|
|
|
|
|
|
-基于 sub-agent 机制实现。
|
|
|
+启动多个独立的 Sub-Traces 并行执行。
|
|
|
|
|
|
```python
|
|
|
@tool
|
|
|
@@ -339,11 +423,152 @@ def explore(
|
|
|
"""
|
|
|
并行探索多个方向,汇总结果。
|
|
|
|
|
|
- - background 有值:用它初始化各分支的 context
|
|
|
- - background 为空:继承主 message list
|
|
|
+ 每个方向会启动一个独立的 Sub-Trace(agent_type="explore")。
|
|
|
+
|
|
|
+ - background 有值:用它初始化各 Sub-Trace 的 context
|
|
|
+ - background 为空:继承主 Trace 的 message list
|
|
|
+
|
|
|
+ 返回:各分支的汇总结果
|
|
|
"""
|
|
|
```
|
|
|
|
|
|
+**内部实现**:
|
|
|
+```python
|
|
|
+async def explore_tool(branches: List[str], background: Optional[str] = None) -> str:
|
|
|
+ current_trace_id = get_current_trace_id()
|
|
|
+ current_goal_id = get_current_goal_id()
|
|
|
+
|
|
|
+ # 1. 创建 agent_call Goal
|
|
|
+ goal = Goal(
|
|
|
+ id=next_id(),
|
|
|
+ type="agent_call",
|
|
|
+ description=f"并行探索 {len(branches)} 个方案",
|
|
|
+ agent_call_mode="explore",
|
|
|
+ sub_trace_ids=[],
|
|
|
+ )
|
|
|
+
|
|
|
+ # 2. 为每个分支创建 Sub-Trace
|
|
|
+ sub_traces = []
|
|
|
+ for i, desc in enumerate(branches):
|
|
|
+ suffix = chr(ord('A') + i) # A, B, C...
|
|
|
+ sub_trace = Trace.create(
|
|
|
+ trace_id=f"{current_trace_id}.{suffix}",
|
|
|
+ parent_trace_id=current_trace_id,
|
|
|
+ parent_goal_id=current_goal_id,
|
|
|
+ agent_type="explore",
|
|
|
+ task=desc,
|
|
|
+ context=get_explore_context(), # 从配置获取权限设置
|
|
|
+ )
|
|
|
+ sub_traces.append(sub_trace)
|
|
|
+ goal.sub_trace_ids.append(sub_trace.trace_id)
|
|
|
+
|
|
|
+ # 3. 并行执行
|
|
|
+ results = await asyncio.gather(
|
|
|
+ *[run_agent(st, background) for st in sub_traces]
|
|
|
+ )
|
|
|
+
|
|
|
+ # 4. 汇总返回
|
|
|
+ summary = format_explore_results(results)
|
|
|
+ return summary
|
|
|
+```
|
|
|
+
|
|
|
+**权限配置**:
|
|
|
+```python
|
|
|
+# 示例:系统配置中定义 Sub-Trace 的默认权限
|
|
|
+# 可以根据 agent_type 或具体场景调整
|
|
|
+
|
|
|
+def get_explore_context() -> Dict[str, Any]:
|
|
|
+ """获取 explore 模式的 context 配置"""
|
|
|
+ return {
|
|
|
+ # 工具权限:根据实际需求配置
|
|
|
+ # 选项 1:受限权限(只读,快速探索)
|
|
|
+ # "allowed_tools": ["read", "grep", "glob"],
|
|
|
+
|
|
|
+ # 选项 2:完整权限(需要实际实现来评估)
|
|
|
+ # "allowed_tools": None, # None = 所有工具
|
|
|
+
|
|
|
+ # 步数限制
|
|
|
+ "max_turns": 20,
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+#### delegate 工具:单线委托
|
|
|
+
|
|
|
+将大任务委托给独立的 Sub-Trace 执行。
|
|
|
+
|
|
|
+```python
|
|
|
+@tool
|
|
|
+def delegate(task: str) -> str:
|
|
|
+ """
|
|
|
+ 将大任务委托给独立的 sub-agent。
|
|
|
+
|
|
|
+ 创建一个独立的 Sub-Trace(agent_type="delegate"),
|
|
|
+ 有独立的 context,权限由配置决定。
|
|
|
+
|
|
|
+ 返回:任务执行结果摘要
|
|
|
+ """
|
|
|
+```
|
|
|
+
|
|
|
+**内部实现**:
|
|
|
+```python
|
|
|
+async def delegate_tool(task: str) -> str:
|
|
|
+ current_trace_id = get_current_trace_id()
|
|
|
+ current_goal_id = get_current_goal_id()
|
|
|
+
|
|
|
+ # 1. 创建 agent_call Goal
|
|
|
+ goal = Goal(
|
|
|
+ id=next_id(),
|
|
|
+ type="agent_call",
|
|
|
+ description=f"委托任务: {task}",
|
|
|
+ agent_call_mode="delegate",
|
|
|
+ sub_trace_ids=[],
|
|
|
+ )
|
|
|
+
|
|
|
+ # 2. 创建 Sub-Trace
|
|
|
+ suffix = f"task{next_task_num()}" # task1, task2...
|
|
|
+ sub_trace = Trace.create(
|
|
|
+ trace_id=f"{current_trace_id}.{suffix}",
|
|
|
+ parent_trace_id=current_trace_id,
|
|
|
+ parent_goal_id=current_goal_id,
|
|
|
+ agent_type="delegate",
|
|
|
+ task=task,
|
|
|
+ context=get_delegate_context(), # 从配置获取权限设置
|
|
|
+ )
|
|
|
+ goal.sub_trace_ids.append(sub_trace.trace_id)
|
|
|
+
|
|
|
+ # 3. 执行
|
|
|
+ result = await run_agent(sub_trace)
|
|
|
+
|
|
|
+ # 4. 返回摘要
|
|
|
+ return result.summary
|
|
|
+```
|
|
|
+
|
|
|
+**权限配置**:
|
|
|
+```python
|
|
|
+def get_delegate_context() -> Dict[str, Any]:
|
|
|
+ """获取 delegate 模式的 context 配置"""
|
|
|
+ return {
|
|
|
+ # 工具权限:根据实际需求配置
|
|
|
+ # 通常 delegate 用于完整任务,给完整权限
|
|
|
+ "allowed_tools": None, # None = 所有工具
|
|
|
+
|
|
|
+ # 或者也可以限制:
|
|
|
+ # "allowed_tools": ["read", "write", "edit", "bash"],
|
|
|
+ # "denied_tools": ["危险工具"],
|
|
|
+
|
|
|
+ # 步数限制
|
|
|
+ "max_turns": 50,
|
|
|
+ }
|
|
|
+```
|
|
|
+
|
|
|
+**注意**:
|
|
|
+- `explore` 和 `delegate` 的权限配置是独立的,可以根据需求调整
|
|
|
+- 不是工具本身决定权限,而是通过 `Trace.context` 配置
|
|
|
+- 典型配置:
|
|
|
+ - `explore`:可能受限(快速探索)或完整(需要实现验证)
|
|
|
+ - `delegate`:通常完整权限(执行完整任务)
|
|
|
+- 但这些都不是固定的,可以根据具体场景调整
|
|
|
+
|
|
|
### Context 管理
|
|
|
|
|
|
#### 1. Plan 注入
|
|
|
@@ -611,174 +836,226 @@ ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0
|
|
|
### 存储结构
|
|
|
|
|
|
```
|
|
|
-.trace/{trace_id}/
|
|
|
-├── goal.json # GoalTree(嵌套 JSON,含 abandoned 目标)
|
|
|
-├── messages/ # Messages(每条独立文件)
|
|
|
-│ ├── {message_id}.json
|
|
|
+.trace/
|
|
|
+├── abc123/ # 主 Trace
|
|
|
+│ ├── meta.json # Trace 元数据
|
|
|
+│ ├── goal.json # GoalTree
|
|
|
+│ ├── messages/ # Messages
|
|
|
+│ │ ├── {message_id}.json
|
|
|
+│ │ └── ...
|
|
|
+│ └── events.jsonl # 事件流
|
|
|
+│
|
|
|
+├── abc123.A/ # Sub-Trace A(并行探索)
|
|
|
+│ ├── meta.json # parent_trace_id: "abc123"
|
|
|
+│ ├── goal.json # 独立的 GoalTree
|
|
|
+│ ├── messages/
|
|
|
+│ └── events.jsonl
|
|
|
+│
|
|
|
+├── abc123.B/ # Sub-Trace B(并行探索)
|
|
|
│ └── ...
|
|
|
-├── events.jsonl # 事件流(WebSocket 断线续传)
|
|
|
-└── meta.json # Trace 元数据
|
|
|
+│
|
|
|
+└── abc123.task1/ # Sub-Trace task1(单线委托)
|
|
|
+ └── ...
|
|
|
```
|
|
|
|
|
|
+**关键点**:
|
|
|
+- 每个 Trace(主/子)都是完全独立的目录
|
|
|
+- 从 trace_id 可以直接找到对应目录
|
|
|
+- 通过 `parent_trace_id` 可以追溯父子关系
|
|
|
+
|
|
|
---
|
|
|
|
|
|
-## 分支-合并设计(explore 工具)
|
|
|
+## 并行探索设计(explore 工具)
|
|
|
|
|
|
### 场景
|
|
|
|
|
|
```
|
|
|
-主线 Agent:
|
|
|
+主 Trace (abc123):
|
|
|
[1] 分析问题
|
|
|
- [2] explore_start: 启动并行探索 (type=explore_start)
|
|
|
- │
|
|
|
- ├── 分支A (sub-agent): [1]设计 → [2]实现 → 完成
|
|
|
- └── 分支B (sub-agent): [1]设计 → [2]实现 → [3]测试 → 完成
|
|
|
- │
|
|
|
- ↓ (工具返回汇总结果)
|
|
|
- [3] explore_merge: 评估选择JWT (type=explore_merge)
|
|
|
- [4] 完善实现
|
|
|
+ [2] 并行探索认证方案 (type=agent_call, mode=explore)
|
|
|
+ → 启动 Sub-Traces: abc123.A, abc123.B
|
|
|
+ [3] 完善实现
|
|
|
+
|
|
|
+Sub-Trace A (abc123.A):
|
|
|
+ [1] JWT 设计
|
|
|
+ [2] JWT 实现
|
|
|
+ → 返回摘要:"JWT 方案实现完成"
|
|
|
+
|
|
|
+Sub-Trace B (abc123.B):
|
|
|
+ [1] Session 设计
|
|
|
+ [2] Session 实现
|
|
|
+ [3] Session 测试
|
|
|
+ → 返回摘要:"Session 方案实现完成"
|
|
|
+
|
|
|
+explore 工具返回:
|
|
|
+ 汇总两个分支的结果,主 Agent 继续决策
|
|
|
```
|
|
|
|
|
|
**核心原则**:
|
|
|
-- 每个分支是独立的 sub-agent,有自己的 GoalTree 和 Message List
|
|
|
-- 模型在分支内看到的是简单的连续编号 "1", "2", "3"(独立于主线)
|
|
|
-- `explore_start` 和 `explore_merge` 是主线 GoalTree 中的特殊 Goal 类型
|
|
|
-- 分支数据独立存储,不直接嵌入主线 GoalTree
|
|
|
-- explore 工具返回时自动汇总各分支 summary
|
|
|
+- 每个分支是独立的 Trace,有自己的 GoalTree 和 Message List
|
|
|
+- 分支内的 Goal ID 简单编号("1", "2", "3"),独立于主 Trace
|
|
|
+- 主 Trace 的 Goal 通过 `sub_trace_ids` 字段关联分支
|
|
|
+- 分支完全独立存储,可以并行运行
|
|
|
+- explore 工具自动汇总各分支 summary
|
|
|
|
|
|
### 数据结构
|
|
|
|
|
|
-**主线 GoalTree**(不含分支内部 Goals):
|
|
|
+**主 Trace 的 GoalTree**:
|
|
|
|
|
|
```python
|
|
|
-# Goal 类型在前面已定义,这里展示主线 GoalTree 示例
|
|
|
+# Trace: abc123
|
|
|
goals = [
|
|
|
- Goal(id="1", type="normal", description="分析问题", ...),
|
|
|
- Goal(id="2", type="explore_start", description="探索认证方案",
|
|
|
- branch_ids=["A", "B"], ...),
|
|
|
- Goal(id="3", type="explore_merge", description="选择JWT方案",
|
|
|
- explore_start_id="2", merge_summary="...", selected_branch="A", ...),
|
|
|
- Goal(id="4", type="normal", description="完善实现", ...),
|
|
|
+ Goal(id="1", type="normal", description="分析问题"),
|
|
|
+ Goal(
|
|
|
+ id="2",
|
|
|
+ type="agent_call",
|
|
|
+ description="并行探索认证方案",
|
|
|
+ agent_call_mode="explore",
|
|
|
+ sub_trace_ids=["abc123.A", "abc123.B"],
|
|
|
+ ),
|
|
|
+ Goal(id="3", type="normal", description="完善实现"),
|
|
|
]
|
|
|
```
|
|
|
|
|
|
-**分支上下文**(独立存储):
|
|
|
+**Sub-Trace A 的 GoalTree**(独立编号):
|
|
|
|
|
|
```python
|
|
|
-@dataclass
|
|
|
-class BranchContext:
|
|
|
- """分支执行上下文(独立的 sub-agent 环境)"""
|
|
|
- id: str # 分支 ID,如 "A", "B"
|
|
|
- explore_start_id: str # 关联的 explore_start Goal ID
|
|
|
- description: str # 探索方向描述
|
|
|
- status: BranchStatus # exploring | completed | abandoned
|
|
|
+# Trace: abc123.A
|
|
|
+goals = [
|
|
|
+ Goal(id="1", type="normal", description="JWT 设计"),
|
|
|
+ Goal(id="2", type="normal", description="JWT 实现"),
|
|
|
+]
|
|
|
+```
|
|
|
|
|
|
- # 独立的执行环境
|
|
|
- goal_tree: GoalTree # 分支自己的 GoalTree(简单编号 1, 2, 3...)
|
|
|
+**Sub-Trace B 的 GoalTree**(独立编号):
|
|
|
|
|
|
- summary: Optional[str] # 完成时的总结
|
|
|
- cumulative_stats: GoalStats # 累计统计
|
|
|
- last_message: Optional[Message] # 最新消息(用于可视化预览)
|
|
|
+```python
|
|
|
+# Trace: abc123.B
|
|
|
+goals = [
|
|
|
+ Goal(id="1", type="normal", description="Session 设计"),
|
|
|
+ Goal(id="2", type="normal", description="Session 实现"),
|
|
|
+ Goal(id="3", type="normal", description="Session 测试"),
|
|
|
+]
|
|
|
```
|
|
|
|
|
|
### 存储结构
|
|
|
|
|
|
```
|
|
|
-.trace/{trace_id}/
|
|
|
-├── meta.json # Trace 元数据
|
|
|
-├── goal.json # 主线 GoalTree
|
|
|
-├── messages/ # 主线 Messages
|
|
|
-│ └── ...
|
|
|
-├── branches/ # 分支数据(独立存储)
|
|
|
-│ ├── A/
|
|
|
-│ │ ├── meta.json # BranchContext 元数据
|
|
|
-│ │ ├── goal.json # 分支 A 的 GoalTree
|
|
|
-│ │ └── messages/ # 分支 A 的 Messages
|
|
|
-│ └── B/
|
|
|
-│ └── ...
|
|
|
-└── events.jsonl # 事件流
|
|
|
+.trace/
|
|
|
+├── abc123/ # 主 Trace
|
|
|
+│ ├── meta.json
|
|
|
+│ ├── goal.json
|
|
|
+│ └── messages/
|
|
|
+│
|
|
|
+├── abc123.A/ # Sub-Trace A
|
|
|
+│ ├── meta.json # parent_trace_id: "abc123", parent_goal_id: "2"
|
|
|
+│ ├── goal.json # 独立的 GoalTree
|
|
|
+│ └── messages/
|
|
|
+│
|
|
|
+└── abc123.B/ # Sub-Trace B
|
|
|
+ └── ...
|
|
|
```
|
|
|
|
|
|
### DAG 可视化
|
|
|
|
|
|
-**折叠视图**(explore 区域显示为 start → merge):
|
|
|
+**折叠视图**(Sub-Trace 作为单个节点):
|
|
|
```
|
|
|
-[1:分析] ──→ [2:explore_start] ──→ [3:explore_merge] ──→ [4:完善]
|
|
|
- │ │
|
|
|
- (启动2分支) (汇总评估)
|
|
|
+[1:分析] ──→ [2:并行探索] ──→ [3:完善]
|
|
|
+ │
|
|
|
+ (启动2个Sub-Trace)
|
|
|
```
|
|
|
|
|
|
**展开分支视图**(显示并行路径):
|
|
|
```
|
|
|
┌──→ [A:JWT方案] ────┐
|
|
|
-[1:分析] ──→ [2] ─┤ ├──→ [3:合并] ──→ [4:完善]
|
|
|
+[1:分析] ──→ [2] ─┤ ├──→ [3:完善]
|
|
|
└──→ [B:Session方案] ┘
|
|
|
```
|
|
|
|
|
|
-**继续展开分支 A 内部**:
|
|
|
+**继续展开分支 A 内部**(加载 Sub-Trace abc123.A 的 GoalTree):
|
|
|
```
|
|
|
- ┌──→ [A.1:设计] → [A.2:实现] ──┐
|
|
|
-[1:分析] ──→ [2] ─┤ ├──→ [3:合并] ──→ [4:完善]
|
|
|
- └──→ [B:Session方案] ──────────┘
|
|
|
+ ┌──→ [A.1:JWT设计] → [A.2:JWT实现] ──┐
|
|
|
+[1:分析] ──→ [2] ─┤ ├──→ [3:完善]
|
|
|
+ └──→ [B:Session方案] ────────────────┘
|
|
|
```
|
|
|
|
|
|
-注意:`[A.1]`, `[A.2]` 是**前端显示格式**,后端存储的是 `(branch_id="A", goal_id="1")`。
|
|
|
+**注意**:
|
|
|
+- `[A:JWT方案]` 是折叠视图,代表整个 Sub-Trace abc123.A
|
|
|
+- `[A.1]`, `[A.2]` 是展开后显示 Sub-Trace 内部的 Goals
|
|
|
+- 前端显示为 "A.1",但后端查询是 `GET /api/traces/abc123.A/messages?goal_id=1`
|
|
|
|
|
|
### 前端 API
|
|
|
|
|
|
-**REST**:返回主线 GoalTree + 分支元数据(不含分支内部 Goals),按需加载分支详情。
|
|
|
+**REST**:返回主 Trace 的 GoalTree + Sub-Trace 列表(元数据)。
|
|
|
|
|
|
```http
|
|
|
-GET /api/traces/{trace_id}
|
|
|
+GET /api/traces/abc123
|
|
|
```
|
|
|
|
|
|
响应:
|
|
|
```json
|
|
|
{
|
|
|
+ "trace_id": "abc123",
|
|
|
+ "status": "running",
|
|
|
"goal_tree": {
|
|
|
"goals": [
|
|
|
- {"id": "1", "type": "normal", "description": "分析问题", ...},
|
|
|
- {"id": "2", "type": "explore_start", "branch_ids": ["A", "B"], ...},
|
|
|
- {"id": "3", "type": "explore_merge", "explore_start_id": "2", ...},
|
|
|
- {"id": "4", "type": "normal", ...}
|
|
|
+ {"id": "1", "type": "normal", "description": "分析问题"},
|
|
|
+ {
|
|
|
+ "id": "2",
|
|
|
+ "type": "agent_call",
|
|
|
+ "description": "并行探索认证方案",
|
|
|
+ "agent_call_mode": "explore",
|
|
|
+ "sub_trace_ids": ["abc123.A", "abc123.B"]
|
|
|
+ },
|
|
|
+ {"id": "3", "type": "normal", "description": "完善实现"}
|
|
|
]
|
|
|
},
|
|
|
- "branches": {
|
|
|
- "A": {
|
|
|
- "id": "A",
|
|
|
- "explore_start_id": "2",
|
|
|
- "description": "JWT方案",
|
|
|
+ "sub_traces": {
|
|
|
+ "abc123.A": {
|
|
|
+ "trace_id": "abc123.A",
|
|
|
+ "parent_trace_id": "abc123",
|
|
|
+ "parent_goal_id": "2",
|
|
|
+ "agent_type": "explore",
|
|
|
+ "task": "JWT 方案",
|
|
|
"status": "completed",
|
|
|
- "summary": "JWT方案实现完成,无状态但token较大",
|
|
|
- "cumulative_stats": {"message_count": 8, "total_tokens": 4000, ...},
|
|
|
- "goal_count": 2,
|
|
|
- "last_message": {"role": "assistant", "content": "JWT实现完成...", ...}
|
|
|
+ "total_messages": 8,
|
|
|
+ "total_tokens": 4000,
|
|
|
+ "total_cost": 0.05
|
|
|
},
|
|
|
- "B": {...}
|
|
|
+ "abc123.B": {
|
|
|
+ "trace_id": "abc123.B",
|
|
|
+ "agent_type": "explore",
|
|
|
+ "task": "Session 方案",
|
|
|
+ "status": "completed",
|
|
|
+ ...
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
-**按需加载分支详情**:
|
|
|
+**按需加载 Sub-Trace 详情**:
|
|
|
|
|
|
```http
|
|
|
-GET /api/traces/{trace_id}/branches/{branch_id}
|
|
|
+GET /api/traces/abc123.A
|
|
|
```
|
|
|
|
|
|
响应:
|
|
|
```json
|
|
|
{
|
|
|
- "id": "A",
|
|
|
- "description": "JWT方案",
|
|
|
+ "trace_id": "abc123.A",
|
|
|
+ "parent_trace_id": "abc123",
|
|
|
+ "parent_goal_id": "2",
|
|
|
+ "agent_type": "explore",
|
|
|
+ "task": "JWT 方案",
|
|
|
"status": "completed",
|
|
|
- "summary": "...",
|
|
|
"goal_tree": {
|
|
|
"goals": [
|
|
|
- {"id": "1", "description": "JWT设计", ...},
|
|
|
- {"id": "2", "description": "JWT实现", ...}
|
|
|
+ {"id": "1", "description": "JWT 设计", ...},
|
|
|
+ {"id": "2", "description": "JWT 实现", ...}
|
|
|
]
|
|
|
},
|
|
|
- "cumulative_stats": {...}
|
|
|
+ "total_tokens": 4000,
|
|
|
+ "total_cost": 0.05
|
|
|
}
|
|
|
```
|
|
|
|
|
|
@@ -786,62 +1063,62 @@ GET /api/traces/{trace_id}/branches/{branch_id}
|
|
|
|
|
|
| 事件 | 触发时机 | payload |
|
|
|
|------|---------|---------|
|
|
|
-| `branch_started` | 分支开始探索 | explore_start_id, branch 元数据 |
|
|
|
-| `branch_goal_added` | 分支内新增 Goal | branch_id, goal |
|
|
|
-| `branch_message_added` | 分支内新 Message | branch_id, message, affected_goals |
|
|
|
-| `branch_completed` | 分支完成 | branch_id, summary, cumulative_stats, last_message |
|
|
|
-| `explore_completed` | 所有分支完成 | explore_start_id, merge_summary |
|
|
|
+| `sub_trace_started` | Sub-Trace 开始 | trace_id, parent_trace_id, parent_goal_id, agent_type |
|
|
|
+| `goal_added` | 任何 Trace 新增 Goal | trace_id, goal |
|
|
|
+| `message_added` | 任何 Trace 新 Message | trace_id, message, affected_goals |
|
|
|
+| `sub_trace_completed` | Sub-Trace 完成 | trace_id, summary, stats |
|
|
|
+| `trace_completed` | 主 Trace 完成 | trace_id, stats |
|
|
|
|
|
|
-### explore 工具流程
|
|
|
+### explore 工具执行流程
|
|
|
|
|
|
-```python
|
|
|
-@tool
|
|
|
-def explore(branches: List[str]) -> str:
|
|
|
- """并行探索多个方向"""
|
|
|
+详细流程见前面"工具设计"部分,这里展示关键步骤:
|
|
|
|
|
|
- # 1. 创建 explore_start Goal
|
|
|
- start_goal = Goal(
|
|
|
- id=next_id(),
|
|
|
- type="explore_start",
|
|
|
- description=f"探索 {len(branches)} 个方案",
|
|
|
- branch_ids=[chr(ord('A') + i) for i in range(len(branches))],
|
|
|
+```python
|
|
|
+async def explore_tool(branches: List[str]) -> str:
|
|
|
+ current_trace_id = "abc123"
|
|
|
+ current_goal_id = "2"
|
|
|
+
|
|
|
+ # 1. 创建 agent_call Goal
|
|
|
+ goal = Goal(
|
|
|
+ id="2",
|
|
|
+ type="agent_call",
|
|
|
+ description=f"并行探索 {len(branches)} 个方案",
|
|
|
+ agent_call_mode="explore",
|
|
|
+ sub_trace_ids=[],
|
|
|
)
|
|
|
|
|
|
- # 2. 为每个方向创建 sub-agent
|
|
|
+ # 2. 创建多个 Sub-Traces
|
|
|
+ sub_traces = []
|
|
|
for i, desc in enumerate(branches):
|
|
|
- branch_id = chr(ord('A') + i)
|
|
|
- create_branch_context(
|
|
|
- branch_id=branch_id,
|
|
|
- explore_start_id=start_goal.id,
|
|
|
- description=desc,
|
|
|
+ suffix = chr(ord('A') + i)
|
|
|
+ sub_trace = Trace.create(
|
|
|
+ trace_id=f"{current_trace_id}.{suffix}",
|
|
|
+ parent_trace_id=current_trace_id,
|
|
|
+ parent_goal_id=current_goal_id,
|
|
|
+ agent_type="explore",
|
|
|
+ task=desc,
|
|
|
+ context={"allowed_tools": ["read", "grep", "glob"]},
|
|
|
)
|
|
|
- spawn_sub_agent(branch_id)
|
|
|
-
|
|
|
- # 3. 等待所有分支完成
|
|
|
- results = await gather_branch_results()
|
|
|
+ sub_traces.append(sub_trace)
|
|
|
+ goal.sub_trace_ids.append(sub_trace.trace_id)
|
|
|
|
|
|
- # 4. 创建 explore_merge Goal
|
|
|
- merge_summary = format_exploration_results(results)
|
|
|
- merge_goal = Goal(
|
|
|
- id=next_id(),
|
|
|
- type="explore_merge",
|
|
|
- description="评估探索结果",
|
|
|
- explore_start_id=start_goal.id,
|
|
|
- merge_summary=merge_summary,
|
|
|
+ # 3. 并行执行
|
|
|
+ results = await asyncio.gather(
|
|
|
+ *[run_agent(st) for st in sub_traces]
|
|
|
)
|
|
|
|
|
|
- # 5. 返回汇总给主线 Agent
|
|
|
- return merge_summary
|
|
|
+ # 4. 汇总返回
|
|
|
+ return format_results(results)
|
|
|
```
|
|
|
|
|
|
-**汇总结果示例**(作为 explore 工具的返回值):
|
|
|
+**汇总结果示例**:
|
|
|
```markdown
|
|
|
## 探索结果
|
|
|
|
|
|
-### 分支 A: JWT 方案
|
|
|
+### 方案 A (abc123.A): JWT 方案
|
|
|
实现完成。优点:无状态,易扩展。缺点:token 较大,无法主动失效。
|
|
|
|
|
|
-### 分支 B: Session 方案
|
|
|
+### 方案 B (abc123.B): Session 方案
|
|
|
实现完成。优点:token 小,可主动失效。缺点:需要 Redis 存储。
|
|
|
|
|
|
---
|
|
|
@@ -850,11 +1127,11 @@ def explore(branches: List[str]) -> str:
|
|
|
|
|
|
### Context 压缩
|
|
|
|
|
|
-分支完成后的压缩策略:
|
|
|
+Sub-Trace 完成后的压缩策略:
|
|
|
|
|
|
-1. **分支完成时**:分支的详细 context 压缩为 summary,存储在 BranchContext.summary
|
|
|
-2. **explore 完成后**:所有分支的 summary 汇总为 merge_summary
|
|
|
-3. **主线 context**:explore 工具调用被压缩为一条包含 merge_summary 的消息
|
|
|
+1. **Sub-Trace 完成时**:Sub-Trace 的详细 context 压缩为 summary(存储在 Trace.summary)
|
|
|
+2. **explore 完成后**:所有 Sub-Traces 的 summary 汇总到主 Trace 的 tool result
|
|
|
+3. **主 Trace context**:explore 工具调用被压缩为一条包含汇总结果的 tool message
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -862,14 +1139,17 @@ def explore(branches: List[str]) -> str:
|
|
|
|
|
|
| 方面 | OpenCode | 我们的方案 |
|
|
|
|------|----------|-----------|
|
|
|
-| Plan 格式 | 纯文本 (plan.md) | 结构化 (GoalTree 嵌套 JSON) |
|
|
|
+| Plan 格式 | 纯文本 (plan.md) | 结构化 (GoalTree JSON) |
|
|
|
| Plan 与执行关联 | 无 | 通过 goal_id 关联 |
|
|
|
| 执行记录 | Message List | Message List(加 goal_id 元数据) |
|
|
|
+| Sub-Agent 模型 | SubagentPart(嵌入式) | 独立 Trace(统一模型) |
|
|
|
+| Sub-Agent ID | 复杂(需要前缀) | 简单(每个 Trace 独立编号) |
|
|
|
| 压缩时机 | 事后(context 满时) | 增量(goal 完成/放弃时) |
|
|
|
| 并行探索 | Sub-agent(手动管理) | explore 工具(自动汇总) |
|
|
|
+| 权限控制 | 在 Sub-Agent 类型上 | 在 Trace.context 上(更灵活) |
|
|
|
| 回溯能力 | 有限 | 精确(基于 goal 压缩 + 废弃分支 summary) |
|
|
|
| 可视化 | 无 | DAG(边可展开/折叠) |
|
|
|
-| 工具复杂度 | todoread/todowrite | goal/explore |
|
|
|
+| 分布式支持 | 困难 | 自然支持(每个 Trace 独立) |
|
|
|
|
|
|
---
|
|
|
|
|
|
@@ -877,14 +1157,16 @@ def explore(branches: List[str]) -> str:
|
|
|
|
|
|
| 功能 | 文件路径 | 状态 |
|
|
|
|------|---------|------|
|
|
|
-| Goal 数据模型 | `agent/goal/models.py` | 待调整(ID 映射) |
|
|
|
+| Trace 数据模型 | `agent/execution/models.py` | 待调整(增加父子关系、context) |
|
|
|
+| Goal 数据模型 | `agent/goal/models.py` | 待调整(简化,移除 branch_id) |
|
|
|
| goal 工具 | `agent/goal/tool.py` | 待调整 |
|
|
|
| explore 工具 | `agent/goal/explore.py` | 待实现 |
|
|
|
+| delegate 工具 | `agent/goal/delegate.py` | 待实现 |
|
|
|
+| Trace ID 生成 | `agent/execution/trace_id.py` | 待实现 |
|
|
|
| Context 压缩 | `agent/goal/compaction.py` | 待调整 |
|
|
|
-| Message 数据模型 | `agent/execution/models.py` | 待调整(Step→Message) |
|
|
|
-| TraceStore 协议 | `agent/execution/protocols.py` | 待调整 |
|
|
|
-| DAG 可视化 API | `agent/execution/api.py` | 待调整(tree→DAG) |
|
|
|
-| WebSocket 推送 | `agent/execution/websocket.py` | 待调整(新增 goal 事件) |
|
|
|
+| TraceStore 协议 | `agent/execution/protocols.py` | 待调整(支持嵌套 Trace) |
|
|
|
+| DAG 可视化 API | `agent/execution/api.py` | 待调整(支持 Sub-Trace) |
|
|
|
+| WebSocket 推送 | `agent/execution/websocket.py` | 待调整(统一事件格式) |
|
|
|
| Plan 注入 | `agent/core/runner.py` | 待调整 |
|
|
|
|
|
|
---
|