max_liu před 1 měsícem
rodič
revize
f14463c935
61 změnil soubory, kde provedl 2301 přidání a 5280 odebrání
  1. 2 1
      .claude/settings.local.json
  2. 17 7
      agent/__init__.py
  3. 0 51
      agent/core/README.md
  4. 7 2
      agent/core/__init__.py
  5. 0 26
      agent/core/config.py
  6. 60 0
      agent/core/presets.py
  7. 179 44
      agent/core/runner.py
  8. 0 70
      agent/execution/__init__.py
  9. 0 26
      agent/goal/__init__.py
  10. 0 176
      agent/goal/delegate.py
  11. 0 248
      agent/goal/explore.py
  12. 0 91
      agent/memory/README.md
  13. 0 0
      agent/memory/skills/core.md
  14. 0 137
      agent/subagents/default.json
  15. 0 94
      agent/tools/README.md
  16. 4 2
      agent/tools/builtin/__init__.py
  17. 18 2
      agent/tools/builtin/browser/baseClass.py
  18. 19 0
      agent/tools/builtin/file/__init__.py
  19. 0 0
      agent/tools/builtin/file/edit.py
  20. 108 0
      agent/tools/builtin/file/glob.py
  21. 0 0
      agent/tools/builtin/file/grep.py
  22. 0 0
      agent/tools/builtin/file/read.py
  23. 0 0
      agent/tools/builtin/file/write.py
  24. 0 75
      agent/tools/builtin/goal.py
  25. 2 2
      agent/tools/builtin/skill.py
  26. 606 0
      agent/tools/builtin/subagent.py
  27. 38 0
      agent/trace/__init__.py
  28. 1 1
      agent/trace/api.py
  29. 1 1
      agent/trace/compaction.py
  30. 0 0
      agent/trace/goal_models.py
  31. 74 2
      agent/trace/goal_tool.py
  32. 0 0
      agent/trace/models.py
  33. 2 2
      agent/trace/protocols.py
  34. 2 2
      agent/trace/store.py
  35. 0 0
      agent/trace/trace_id.py
  36. 1 1
      agent/trace/tree_dump.py
  37. 1 1
      agent/trace/websocket.py
  38. 3 3
      api_server.py
  39. 433 407
      docs/README.md
  40. 0 306
      docs/REFACTOR_SUMMARY.md
  41. 0 618
      docs/cloud_browser_guide.md
  42. 0 1476
      docs/context-management.md
  43. 77 0
      docs/decisions.md
  44. 0 215
      docs/dependencies.md
  45. 0 129
      docs/project-structure.md
  46. 0 0
      docs/ref/context-comparison.md
  47. 0 174
      docs/refactor-plan.md
  48. 0 241
      docs/sub-agents.md
  49. 0 109
      docs/testing.md
  50. 0 232
      docs/tools-adapters.md
  51. 41 0
      docs/tools.md
  52. 1 1
      examples/feature_extract/run.py
  53. 76 27
      examples/research/run.py
  54. 7 2
      examples/research/test.prompt
  55. 0 276
      examples/test_plan.py
  56. 61 0
      examples/test_subagent_real/README.md
  57. 216 0
      examples/test_subagent_real/run.py
  58. 28 0
      examples/test_subagent_real/test.prompt
  59. 28 0
      examples/test_subagent_real/test_continue.prompt
  60. 187 0
      examples/test_subagent_real/visualize_trace.py
  61. 1 0
      vendor/browser-use

+ 2 - 1
.claude/settings.local.json

@@ -10,7 +10,8 @@
       "Bash(pip install:*)",
       "Bash(timeout 60 python:*)",
       "Bash(timeout 240 python:*)",
-      "Bash(curl:*)"
+      "Bash(curl:*)",
+      "Bash(tree:*)"
     ],
     "deny": [],
     "ask": []

+ 17 - 7
agent/__init__.py

@@ -4,19 +4,21 @@ Reson Agent - 模块化、可扩展的 Agent 框架
 核心导出:
 - AgentRunner: Agent 执行引擎
 - AgentConfig: Agent 配置
-- Trace, Step: 执行追踪
+- Trace, Message, Goal: 执行追踪
 - Experience, Skill: 记忆模型
 - tool: 工具装饰器
 - TraceStore, MemoryStore: 存储接口
 """
 
 # 核心引擎
-from agent.core.runner import AgentRunner
-from agent.core.config import AgentConfig, CallResult
+from agent.core.runner import AgentRunner, AgentConfig, CallResult
+from agent.core.presets import AgentPreset, AGENT_PRESETS, get_preset
 
 # 执行追踪
-from agent.execution.models import Trace, Step, StepType, StepStatus
-from agent.execution.protocols import TraceStore
+from agent.trace.models import Trace, Message, Step, StepType, StepStatus
+from agent.trace.goal_models import Goal, GoalTree, GoalStatus
+from agent.trace.protocols import TraceStore
+from agent.trace.store import FileSystemTraceStore
 
 # 记忆系统
 from agent.memory.models import Experience, Skill
@@ -27,19 +29,27 @@ from agent.memory.stores import MemoryMemoryStore, MemoryStateStore
 from agent.tools import tool, ToolRegistry, get_tool_registry
 from agent.tools.models import ToolResult, ToolContext
 
-__version__ = "0.2.0"
+__version__ = "0.3.0"
 
 __all__ = [
     # Core
     "AgentRunner",
     "AgentConfig",
     "CallResult",
-    # Execution
+    "AgentPreset",
+    "AGENT_PRESETS",
+    "get_preset",
+    # Trace
     "Trace",
+    "Message",
     "Step",
     "StepType",
     "StepStatus",
+    "Goal",
+    "GoalTree",
+    "GoalStatus",
     "TraceStore",
+    "FileSystemTraceStore",
     # Memory
     "Experience",
     "Skill",

+ 0 - 51
agent/core/README.md

@@ -1,51 +0,0 @@
-# Agent Core - 核心引擎模块
-
-## 职责
-
-核心引擎是框架的"心脏",负责:
-
-1. **Agent 主循环逻辑** (`runner.py`)
-   - 单次调用模式:`call()` - 简单的 LLM 调用
-   - Agent 模式:`run()` - 循环执行 + 记忆 + 工具调用
-
-2. **配置数据类** (`config.py`)
-   - `AgentConfig` - Agent 配置参数
-   - `CallResult` - 单次调用返回结果
-
-3. **事件定义** (`events.py`)
-   - `AgentEvent` - Agent 事件数据结构
-   - `AgentEventType` - 事件类型枚举
-
-## 特点
-
-- **最小依赖**:核心模块只依赖 execution, memory, tools
-- **最稳定**:核心 API 变更频率最低
-- **可扩展**:通过插件化的 tools, execution, memory 扩展功能
-
-## 使用示例
-
-```python
-from agent.core import AgentRunner, AgentConfig
-
-# 创建 Runner
-runner = AgentRunner(
-    llm_call=my_llm_function,
-    config=AgentConfig(max_iterations=10)
-)
-
-# 单次调用
-result = await runner.call(
-    messages=[{"role": "user", "content": "Hello"}]
-)
-
-# Agent 模式
-async for event in runner.run(task="Complete this task"):
-    print(event)
-```
-
-## 文件说明
-
-- `runner.py` - AgentRunner 类,核心执行逻辑
-- `config.py` - 配置类定义
-- `events.py` - 事件系统
-- `__init__.py` - 模块导出

+ 7 - 2
agent/core/__init__.py

@@ -4,14 +4,19 @@ Agent Core - 核心引擎模块
 职责:
 1. Agent 主循环逻辑(call() 和 run())
 2. 配置数据类(AgentConfig, CallResult)
+3. Agent 预设(AgentPreset)
 """
 
-from agent.core.runner import AgentRunner, BUILTIN_TOOLS
-from agent.core.config import AgentConfig, CallResult
+from agent.core.runner import AgentRunner, BUILTIN_TOOLS, AgentConfig, CallResult
+from agent.core.presets import AgentPreset, AGENT_PRESETS, get_preset, register_preset
 
 __all__ = [
     "AgentRunner",
     "BUILTIN_TOOLS",
     "AgentConfig",
     "CallResult",
+    "AgentPreset",
+    "AGENT_PRESETS",
+    "get_preset",
+    "register_preset",
 ]

+ 0 - 26
agent/core/config.py

@@ -1,26 +0,0 @@
-"""
-Agent 配置类
-"""
-
-from dataclasses import dataclass
-from typing import Optional, List, Dict
-
-
-@dataclass
-class AgentConfig:
-    """Agent 配置"""
-    agent_type: str = "default"
-    max_iterations: int = 10
-    enable_memory: bool = True
-    auto_execute_tools: bool = True
-
-
-@dataclass
-class CallResult:
-    """单次调用结果"""
-    reply: str
-    tool_calls: Optional[List[Dict]] = None
-    trace_id: Optional[str] = None
-    step_id: Optional[str] = None
-    tokens: Optional[Dict[str, int]] = None
-    cost: float = 0.0

+ 60 - 0
agent/core/presets.py

@@ -0,0 +1,60 @@
+"""
+Agent Presets - Agent 类型预设配置
+
+定义不同类型 Agent 的工具权限和运行参数。
+用户可通过 .agent/presets.json 覆盖或添加预设。
+"""
+
+from dataclasses import dataclass, field
+from typing import Optional, List
+
+
+@dataclass
+class AgentPreset:
+    """Agent 预设配置"""
+
+    # 工具权限
+    allowed_tools: Optional[List[str]] = None  # None 表示允许全部
+    denied_tools: Optional[List[str]] = None   # 黑名单
+
+    # 运行参数
+    max_iterations: int = 30
+    temperature: Optional[float] = None
+
+    # 描述
+    description: Optional[str] = None
+
+
+# 内置预设
+AGENT_PRESETS = {
+    "default": AgentPreset(
+        allowed_tools=None,
+        max_iterations=30,
+        description="默认 Agent,拥有全部工具权限",
+    ),
+    "explore": AgentPreset(
+        allowed_tools=["read", "glob", "grep", "list_files"],
+        denied_tools=["write", "edit", "bash", "task"],
+        max_iterations=15,
+        description="探索型 Agent,只读权限,用于代码分析",
+    ),
+    "analyst": AgentPreset(
+        allowed_tools=["read", "glob", "grep", "web_search", "webfetch"],
+        denied_tools=["write", "edit", "bash", "task"],
+        temperature=0.3,
+        max_iterations=25,
+        description="分析型 Agent,用于深度分析和研究",
+    ),
+}
+
+
+def get_preset(name: str) -> AgentPreset:
+    """获取预设配置"""
+    if name not in AGENT_PRESETS:
+        raise ValueError(f"Unknown preset: {name}. Available: {list(AGENT_PRESETS.keys())}")
+    return AGENT_PRESETS[name]
+
+
+def register_preset(name: str, preset: AgentPreset) -> None:
+    """注册自定义预设"""
+    AGENT_PRESETS[name] = preset

+ 179 - 44
agent/core/runner.py

@@ -10,14 +10,13 @@ Agent Runner - Agent 执行引擎
 """
 
 import logging
+from dataclasses import dataclass
 from datetime import datetime
 from typing import AsyncIterator, Optional, Dict, Any, List, Callable, Literal, Union
 
-from agent.core.config import AgentConfig, CallResult
-from agent.execution.models import Trace, Message
-from agent.execution.protocols import TraceStore
-from agent.goal.models import GoalTree
-from agent.goal.tool import goal_tool
+from agent.trace.models import Trace, Message
+from agent.trace.protocols import TraceStore
+from agent.trace.goal_models import GoalTree
 from agent.memory.models import Experience, Skill
 from agent.memory.protocols import MemoryStore, StateStore
 from agent.memory.skill_loader import load_skills_from_dir
@@ -26,6 +25,26 @@ from agent.tools import ToolRegistry, get_tool_registry
 logger = logging.getLogger(__name__)
 
 
+@dataclass
+class AgentConfig:
+    """Agent 配置"""
+    agent_type: str = "default"
+    max_iterations: int = 10
+    enable_memory: bool = True
+    auto_execute_tools: bool = True
+
+
+@dataclass
+class CallResult:
+    """单次调用结果"""
+    reply: str
+    tool_calls: Optional[List[Dict]] = None
+    trace_id: Optional[str] = None
+    step_id: Optional[str] = None
+    tokens: Optional[Dict[str, int]] = None
+    cost: float = 0.0
+
+
 # 内置工具列表(始终自动加载)
 BUILTIN_TOOLS = [
     # 文件操作工具
@@ -42,6 +61,7 @@ BUILTIN_TOOLS = [
     "skill",
     "list_skills",
     "goal",
+    "subagent",
 
     # 搜索工具
     "search_posts",
@@ -166,7 +186,6 @@ class AgentRunner:
             for tool in tools:
                 if tool not in tool_names:
                     tool_names.append(tool)
-
         tool_schemas = self.tools.get_schemas(tool_names)
 
         # 创建 Trace
@@ -224,6 +243,78 @@ class AgentRunner:
 
     # ===== Agent 模式 =====
 
+    async def run_result(
+        self,
+        task: str,
+        messages: Optional[List[Dict]] = None,
+        system_prompt: Optional[str] = None,
+        model: str = "gpt-4o",
+        tools: Optional[List[str]] = None,
+        agent_type: Optional[str] = None,
+        uid: Optional[str] = None,
+        max_iterations: Optional[int] = None,
+        enable_memory: Optional[bool] = None,
+        auto_execute_tools: Optional[bool] = None,
+        trace_id: Optional[str] = None,
+        **kwargs
+    ) -> Dict[str, Any]:
+        """
+        Agent 结果模式执行。
+
+        消费 run() 的流式事件,返回结构化结果(最后一条有文本的 assistant + trace 统计)。
+        """
+        last_assistant_text = ""
+        final_trace: Optional[Trace] = None
+
+        async for item in self.run(
+            task=task,
+            messages=messages,
+            system_prompt=system_prompt,
+            model=model,
+            tools=tools,
+            agent_type=agent_type,
+            uid=uid,
+            max_iterations=max_iterations,
+            enable_memory=enable_memory,
+            auto_execute_tools=auto_execute_tools,
+            trace_id=trace_id,
+            **kwargs
+        ):
+            if isinstance(item, Message) and item.role == "assistant":
+                content = item.content
+                text = ""
+                if isinstance(content, dict):
+                    text = content.get("text", "") or ""
+                elif isinstance(content, str):
+                    text = content
+                if text and text.strip():
+                    last_assistant_text = text
+            elif isinstance(item, Trace):
+                final_trace = item
+
+        if not final_trace and trace_id and self.trace_store:
+            final_trace = await self.trace_store.get_trace(trace_id)
+
+        status = final_trace.status if final_trace else "unknown"
+        error = final_trace.error_message if final_trace else None
+        summary = last_assistant_text
+
+        if not summary:
+            status = "failed"
+            error = error or "Sub-Agent 没有产生 assistant 文本结果"
+
+        return {
+            "status": status,
+            "summary": summary,
+            "trace_id": final_trace.trace_id if final_trace else trace_id,
+            "error": error,
+            "stats": {
+                "total_messages": final_trace.total_messages if final_trace else 0,
+                "total_tokens": final_trace.total_tokens if final_trace else 0,
+                "total_cost": final_trace.total_cost if final_trace else 0.0,
+            },
+        }
+
     async def run(
         self,
         task: str,
@@ -236,6 +327,7 @@ class AgentRunner:
         max_iterations: Optional[int] = None,
         enable_memory: Optional[bool] = None,
         auto_execute_tools: Optional[bool] = None,
+        trace_id: Optional[str] = None,
         **kwargs
     ) -> AsyncIterator[Union[Trace, Message]]:
         """
@@ -252,6 +344,7 @@ class AgentRunner:
             max_iterations: 最大迭代次数
             enable_memory: 是否启用记忆
             auto_execute_tools: 是否自动执行工具
+            trace_id: Trace ID(可选,传入时复用已有 Trace)
             **kwargs: 其他参数
 
         Yields:
@@ -274,26 +367,44 @@ class AgentRunner:
                     tool_names.append(tool)
         tool_schemas = self.tools.get_schemas(tool_names)
 
-        # 创建 Trace
-        trace_id = self._generate_id()
-        trace_obj = Trace(
-            trace_id=trace_id,
-            mode="agent",
-            task=task,
-            agent_type=agent_type,
-            uid=uid,
-            model=model,
-            tools=tool_schemas,  # 保存工具定义
-            llm_params=kwargs,  # 保存 LLM 参数
-            status="running"
-        )
+        # 创建或复用 Trace
+        if trace_id:
+            if self.trace_store:
+                trace_obj = await self.trace_store.get_trace(trace_id)
+                if not trace_obj:
+                    raise ValueError(f"Trace not found: {trace_id}")
+            else:
+                trace_obj = Trace(
+                    trace_id=trace_id,
+                    mode="agent",
+                    task=task,
+                    agent_type=agent_type,
+                    uid=uid,
+                    model=model,
+                    tools=tool_schemas,
+                    llm_params=kwargs,
+                    status="running"
+                )
+        else:
+            trace_id = self._generate_id()
+            trace_obj = Trace(
+                trace_id=trace_id,
+                mode="agent",
+                task=task,
+                agent_type=agent_type,
+                uid=uid,
+                model=model,
+                tools=tool_schemas,  # 保存工具定义
+                llm_params=kwargs,  # 保存 LLM 参数
+                status="running"
+            )
 
-        if self.trace_store:
-            await self.trace_store.create_trace(trace_obj)
+            if self.trace_store:
+                await self.trace_store.create_trace(trace_obj)
 
-            # 初始化 GoalTree
-            goal_tree = self.goal_tree or GoalTree(mission=task)
-            await self.trace_store.update_goal_tree(trace_id, goal_tree)
+                # 初始化 GoalTree
+                goal_tree = self.goal_tree or GoalTree(mission=task)
+                await self.trace_store.update_goal_tree(trace_id, goal_tree)
 
         # 返回 Trace(表示开始)
         yield trace_obj
@@ -319,13 +430,34 @@ class AgentRunner:
                     logger.info(f"加载 {len(skills)} 个内置 skills")
 
             # 构建初始消息
+            sequence = 1
             if messages is None:
-                messages = []
-
+                if trace_id and self.trace_store:
+                    existing_messages = await self.trace_store.get_trace_messages(trace_id)
+                    messages = []
+                    for msg in existing_messages:
+                        msg_dict = {"role": msg.role}
+                        if isinstance(msg.content, dict):
+                            if msg.content.get("text"):
+                                msg_dict["content"] = msg.content["text"]
+                            if msg.content.get("tool_calls"):
+                                msg_dict["tool_calls"] = msg.content["tool_calls"]
+                        else:
+                            msg_dict["content"] = msg.content
+
+                        if msg.role == "tool" and msg.tool_call_id:
+                            msg_dict["tool_call_id"] = msg.tool_call_id
+                            msg_dict["name"] = msg.description or "unknown"
+
+                        messages.append(msg_dict)
+
+                    if existing_messages:
+                        sequence = existing_messages[-1].sequence + 1
+                else:
+                    messages = []
             # 记录初始 system 和 user 消息到 trace
-            sequence = 1
 
-            if system_prompt:
+            if system_prompt and not any(m.get("role") == "system" for m in messages):
                 # 注入记忆和 skills 到 system prompt
                 full_system = system_prompt
                 if skills_text:
@@ -348,21 +480,22 @@ class AgentRunner:
                     yield system_msg
                     sequence += 1
 
-            # 添加任务描述
-            messages.append({"role": "user", "content": task})
+            # 添加任务描述(支持 continue_from 场景再次追加)
+            if task:
+                messages.append({"role": "user", "content": task})
 
-            # 保存 user 消息(任务描述)
-            if self.trace_store:
-                user_msg = Message.create(
-                    trace_id=trace_id,
-                    role="user",
-                    sequence=sequence,
-                    goal_id=None,  # 初始消息没有 goal
-                    content=task,
-                )
-                await self.trace_store.add_message(user_msg)
-                yield user_msg
-                sequence += 1
+                # 保存 user 消息(任务描述)
+                if self.trace_store:
+                    user_msg = Message.create(
+                        trace_id=trace_id,
+                        role="user",
+                        sequence=sequence,
+                        goal_id=None,  # 初始消息没有 goal
+                        content=task,
+                    )
+                    await self.trace_store.add_message(user_msg)
+                    yield user_msg
+                    sequence += 1
 
             # 获取 GoalTree
             goal_tree = None
@@ -370,7 +503,7 @@ class AgentRunner:
                 goal_tree = await self.trace_store.get_goal_tree(trace_id)
 
                 # 设置 goal_tree 到 goal 工具(供 LLM 调用)
-                from agent.tools.builtin.goal import set_goal_tree
+                from agent.trace.goal_tool import set_goal_tree
                 set_goal_tree(goal_tree)
 
             # 执行循环
@@ -451,7 +584,9 @@ class AgentRunner:
                             uid=uid or "",
                             context={
                                 "store": self.trace_store,
-                                "trace_id": trace_id
+                                "trace_id": trace_id,
+                                "goal_id": current_goal_id,
+                                "runner": self,
                             }
                         )
 

+ 0 - 70
agent/execution/__init__.py

@@ -1,70 +0,0 @@
-"""
-Execution - 执行追踪系统
-
-核心职责:
-1. Trace/Message 模型定义(新架构)
-2. 存储接口和实现(文件系统)
-3. GoalTree 集成(计划管理)
-4. RESTful API(可视化查询)
-5. WebSocket 推送(实时更新,支持断线续传)
-"""
-
-# 模型(核心,无依赖)
-from agent.execution.models import Trace, Message
-
-# 向后兼容:保留 Step 导出(已废弃)
-from agent.execution.models import Step, StepType, StepStatus
-
-# 存储接口(核心,无依赖)
-from agent.execution.protocols import TraceStore
-
-# 文件系统存储实现(跨进程 + 持久化)
-from agent.execution.fs_store import FileSystemTraceStore
-
-
-# API 路由(可选,需要 FastAPI)
-def _get_api_router():
-    """延迟导入 API Router(避免强制依赖 FastAPI)"""
-    from agent.execution.api import router
-    return router
-
-
-def _get_ws_router():
-    """延迟导入 WebSocket Router(避免强制依赖 FastAPI)"""
-    from agent.execution.websocket import router
-    return router
-
-
-# WebSocket 广播函数(可选,需要 FastAPI)
-def _get_broadcast_functions():
-    """延迟导入 WebSocket 广播函数"""
-    from agent.execution.websocket import (
-        broadcast_goal_added,
-        broadcast_goal_updated,
-        broadcast_branch_started,
-        broadcast_branch_completed,
-        broadcast_explore_completed,
-        broadcast_trace_completed,
-    )
-    return (
-        broadcast_goal_added,
-        broadcast_goal_updated,
-        broadcast_branch_started,
-        broadcast_branch_completed,
-        broadcast_explore_completed,
-        broadcast_trace_completed,
-    )
-
-
-__all__ = [
-    # 模型
-    "Trace",
-    "Message",
-    # 向后兼容(已废弃)
-    "Step",
-    "StepType",
-    "StepStatus",
-    # 存储
-    "TraceStore",
-    "FileSystemTraceStore",
-]

+ 0 - 26
agent/goal/__init__.py

@@ -1,26 +0,0 @@
-"""
-Goal 模块 - 执行计划管理
-
-提供 Goal 和 GoalTree 数据模型,以及 goal 工具。
-"""
-
-from agent.goal.models import (
-    Goal,
-    GoalTree,
-    GoalStatus,
-    GoalType,
-    GoalStats,
-)
-from agent.goal.tool import goal_tool, create_goal_tool_schema
-
-__all__ = [
-    # Models
-    "Goal",
-    "GoalTree",
-    "GoalStatus",
-    "GoalType",
-    "GoalStats",
-    # Tool
-    "goal_tool",
-    "create_goal_tool_schema",
-]

+ 0 - 176
agent/goal/delegate.py

@@ -1,176 +0,0 @@
-"""
-Delegate 工具 - 委托任务给子 Agent
-
-将大任务委托给独立的 Sub-Trace 执行,获得完整权限。
-"""
-
-from typing import Optional, Dict, Any
-from datetime import datetime
-
-from agent.execution.models import Trace, Message
-from agent.execution.trace_id import generate_sub_trace_id
-from agent.goal.models import Goal
-
-
-async def delegate_tool(
-    current_trace_id: str,
-    current_goal_id: str,
-    task: str,
-    store=None,
-    run_agent=None
-) -> str:
-    """
-    将任务委托给独立的 Sub-Agent
-
-    Args:
-        current_trace_id: 当前主 Trace ID
-        current_goal_id: 当前 Goal ID
-        task: 委托的任务描述
-        store: TraceStore 实例
-        run_agent: 运行 Agent 的函数
-
-    Returns:
-        任务执行结果摘要
-
-    Example:
-        >>> result = await delegate_tool(
-        ...     current_trace_id="abc123",
-        ...     current_goal_id="3",
-        ...     task="实现用户登录功能",
-        ...     store=store,
-        ...     run_agent=run_agent_func
-        ... )
-    """
-    if not store:
-        raise ValueError("store parameter is required")
-    if not run_agent:
-        raise ValueError("run_agent parameter is required")
-
-    # 1. 创建 agent_call Goal
-    await store.update_goal(current_trace_id, current_goal_id,
-                           type="agent_call",
-                           agent_call_mode="delegate",
-                           status="in_progress")
-
-    # 2. 生成 Sub-Trace ID
-    sub_trace_id = generate_sub_trace_id(current_trace_id, "delegate")
-
-    # 3. 创建 Sub-Trace
-    sub_trace = Trace(
-        trace_id=sub_trace_id,
-        mode="agent",
-        task=task,
-        parent_trace_id=current_trace_id,
-        parent_goal_id=current_goal_id,
-        agent_type="delegate",
-        context={
-            # delegate 模式:完整权限
-            "allowed_tools": None,  # None = 所有工具
-            "max_turns": 50
-        },
-        status="running",
-        created_at=datetime.now()
-    )
-
-    # 保存 Sub-Trace
-    await store.create_trace(sub_trace)
-
-    # 更新主 Goal 的 sub_trace_ids
-    await store.update_goal(current_trace_id, current_goal_id, sub_trace_ids=[sub_trace_id])
-
-    # 推送 sub_trace_started 事件
-    await store.append_event(current_trace_id, "sub_trace_started", {
-        "trace_id": sub_trace_id,
-        "parent_trace_id": current_trace_id,
-        "parent_goal_id": current_goal_id,
-        "agent_type": "delegate",
-        "task": task
-    })
-
-    # 4. 执行 Sub-Trace
-    try:
-        result = await run_agent(sub_trace)
-
-        # 获取 Sub-Trace 的最终状态
-        updated_trace = await store.get_trace(sub_trace_id)
-
-        if isinstance(result, dict):
-            summary = result.get("summary", "任务完成")
-        else:
-            summary = "任务完成"
-
-        # 推送 sub_trace_completed 事件
-        await store.append_event(current_trace_id, "sub_trace_completed", {
-            "trace_id": sub_trace_id,
-            "status": "completed",
-            "summary": summary,
-            "stats": {
-                "total_messages": updated_trace.total_messages if updated_trace else 0,
-                "total_tokens": updated_trace.total_tokens if updated_trace else 0,
-                "total_cost": updated_trace.total_cost if updated_trace else 0
-            }
-        })
-
-        # 5. 完成主 Goal
-        await store.update_goal(current_trace_id, current_goal_id,
-                               status="completed",
-                               summary=f"已委托完成: {task}")
-
-        # 格式化返回结果
-        return f"""## 委托任务完成
-
-**任务**: {task}
-
-**结果**: {summary}
-
-**统计**:
-- 消息数: {updated_trace.total_messages if updated_trace else 0}
-- Tokens: {updated_trace.total_tokens if updated_trace else 0}
-- 成本: ${updated_trace.total_cost if updated_trace else 0:.4f}
-"""
-
-    except Exception as e:
-        # 推送失败事件
-        await store.append_event(current_trace_id, "sub_trace_completed", {
-            "trace_id": sub_trace_id,
-            "status": "failed",
-            "error": str(e)
-        })
-
-        # 更新主 Goal 为失败
-        await store.update_goal(current_trace_id, current_goal_id,
-                               status="failed",
-                               summary=f"委托任务失败: {str(e)}")
-
-        return f"""## 委托任务失败
-
-**任务**: {task}
-
-**错误**: {str(e)}
-"""
-
-
-def create_delegate_tool_schema() -> Dict[str, Any]:
-    """
-    创建 delegate 工具的 JSON Schema
-
-    Returns:
-        工具的 JSON Schema
-    """
-    return {
-        "type": "function",
-        "function": {
-            "name": "delegate",
-            "description": "将大任务委托给独立的 Sub-Agent 执行。Sub-Agent 拥有完整权限,适合执行复杂的、需要多步骤的任务。",
-            "parameters": {
-                "type": "object",
-                "properties": {
-                    "task": {
-                        "type": "string",
-                        "description": "要委托的任务描述,应该清晰具体"
-                    }
-                },
-                "required": ["task"]
-            }
-        }
-    }

+ 0 - 248
agent/goal/explore.py

@@ -1,248 +0,0 @@
-"""
-Explore 工具 - 并行探索多个方案
-
-启动多个 Sub-Trace 并行执行不同的探索方向,汇总结果返回。
-"""
-
-import asyncio
-from typing import List, Optional, Dict, Any
-from datetime import datetime
-
-from agent.execution.models import Trace, Message
-from agent.execution.trace_id import generate_sub_trace_id
-from agent.goal.models import Goal
-
-
-async def explore_tool(
-    current_trace_id: str,
-    current_goal_id: str,
-    branches: List[str],
-    background: Optional[str] = None,
-    store=None,
-    run_agent=None
-) -> str:
-    """
-    并行探索多个方向,汇总结果
-
-    Args:
-        current_trace_id: 当前主 Trace ID
-        current_goal_id: 当前 Goal ID
-        branches: 探索方向列表(每个元素是一个探索任务描述)
-        background: 可选,背景信息(如果提供则用作各 Sub-Trace 的初始 context)
-        store: TraceStore 实例
-        run_agent: 运行 Agent 的函数
-
-    Returns:
-        汇总结果字符串
-
-    Example:
-        >>> result = await explore_tool(
-        ...     current_trace_id="abc123",
-        ...     current_goal_id="2",
-        ...     branches=["JWT 方案", "Session 方案"],
-        ...     store=store,
-        ...     run_agent=run_agent_func
-        ... )
-    """
-    if not store:
-        raise ValueError("store parameter is required")
-    if not run_agent:
-        raise ValueError("run_agent parameter is required")
-
-    # 1. 创建 agent_call Goal
-    goal = Goal(
-        id=current_goal_id,
-        type="agent_call",
-        description=f"并行探索 {len(branches)} 个方案",
-        reason="探索多个可行方案",
-        agent_call_mode="explore",
-        sub_trace_ids=[],
-        status="in_progress"
-    )
-
-    # 更新 Goal(标记为 agent_call)
-    await store.update_goal(current_trace_id, current_goal_id,
-                           type="agent_call",
-                           agent_call_mode="explore",
-                           status="in_progress")
-
-    # 2. 为每个分支创建 Sub-Trace
-    sub_traces = []
-    sub_trace_ids = []
-
-    for i, desc in enumerate(branches):
-        # 生成 Sub-Trace ID
-        sub_trace_id = generate_sub_trace_id(current_trace_id, "explore")
-
-        # 创建 Sub-Trace
-        sub_trace = Trace(
-            trace_id=sub_trace_id,
-            mode="agent",
-            task=desc,
-            parent_trace_id=current_trace_id,
-            parent_goal_id=current_goal_id,
-            agent_type="explore",
-            context={
-                "allowed_tools": ["read", "grep", "glob"],  # 探索模式:只读权限
-                "max_turns": 20,
-                "background": background
-            },
-            status="running",
-            created_at=datetime.now()
-        )
-
-        # 保存 Sub-Trace
-        await store.create_trace(sub_trace)
-
-        sub_traces.append(sub_trace)
-        sub_trace_ids.append(sub_trace_id)
-
-        # 推送 sub_trace_started 事件
-        await store.append_event(current_trace_id, "sub_trace_started", {
-            "trace_id": sub_trace_id,
-            "parent_trace_id": current_trace_id,
-            "parent_goal_id": current_goal_id,
-            "agent_type": "explore",
-            "task": desc
-        })
-
-    # 更新主 Goal 的 sub_trace_ids
-    await store.update_goal(current_trace_id, current_goal_id, sub_trace_ids=sub_trace_ids)
-
-    # 3. 并行执行所有 Sub-Traces
-    results = await asyncio.gather(
-        *[run_agent(st, background=background) for st in sub_traces],
-        return_exceptions=True
-    )
-
-    # 4. 收集元数据并汇总结果
-    sub_trace_metadata = {}
-    summary_parts = ["## 探索结果\n"]
-
-    for i, (sub_trace, result) in enumerate(zip(sub_traces, results), 1):
-        branch_name = chr(ord('A') + i - 1)  # A, B, C...
-
-        if isinstance(result, Exception):
-            # 处理异常情况
-            summary_parts.append(f"### 方案 {branch_name}: {sub_trace.task}")
-            summary_parts.append(f"⚠️ 执行出错: {str(result)}\n")
-
-            sub_trace_metadata[sub_trace.trace_id] = {
-                "task": sub_trace.task,
-                "status": "failed",
-                "summary": f"执行出错: {str(result)}",
-                "last_message": None,
-                "stats": {
-                    "message_count": 0,
-                    "total_tokens": 0,
-                    "total_cost": 0.0
-                }
-            }
-        else:
-            # 获取 Sub-Trace 的最终状态
-            updated_trace = await store.get_trace(sub_trace.trace_id)
-
-            # 获取最后一条 assistant 消息
-            messages = await store.get_trace_messages(sub_trace.trace_id)
-            last_message = None
-            for msg in reversed(messages):
-                if msg.role == "assistant":
-                    last_message = msg
-                    break
-
-            # 构建元数据
-            # 优先使用 result 中的 summary,否则使用最后一条消息的内容
-            summary_text = None
-            if isinstance(result, dict) and result.get("summary"):
-                summary_text = result.get("summary")
-            elif last_message and last_message.content:
-                # 使用最后一条消息的内容作为 summary(截断至 200 字符)
-                content_text = last_message.content
-                if isinstance(content_text, dict) and "text" in content_text:
-                    content_text = content_text["text"]
-                elif not isinstance(content_text, str):
-                    content_text = str(content_text)
-                summary_text = content_text[:200] if content_text else "执行完成"
-            else:
-                summary_text = "执行完成"
-
-            sub_trace_metadata[sub_trace.trace_id] = {
-                "task": sub_trace.task,
-                "status": updated_trace.status if updated_trace else "unknown",
-                "summary": summary_text,
-                "last_message": {
-                    "role": last_message.role,
-                    "description": last_message.description,
-                    "content": last_message.content[:500] if last_message.content else None,
-                    "created_at": last_message.created_at.isoformat()
-                } if last_message else None,
-                "stats": {
-                    "message_count": updated_trace.total_messages if updated_trace else 0,
-                    "total_tokens": updated_trace.total_tokens if updated_trace else 0,
-                    "total_cost": updated_trace.total_cost if updated_trace else 0.0
-                }
-            }
-
-            # 组装摘要文本
-            summary_parts.append(f"### 方案 {branch_name}: {sub_trace.task}")
-
-            if updated_trace and updated_trace.status == "completed":
-                summary_parts.append(f"{summary_text}\n")
-                summary_parts.append(f"📊 统计: {updated_trace.total_messages} 条消息, "
-                                   f"{updated_trace.total_tokens} tokens, "
-                                   f"成本 ${updated_trace.total_cost:.4f}\n")
-            else:
-                summary_parts.append(f"未完成\n")
-
-        # 推送 sub_trace_completed 事件
-        await store.append_event(current_trace_id, "sub_trace_completed", {
-            "trace_id": sub_trace.trace_id,
-            "status": "completed" if not isinstance(result, Exception) else "failed",
-            "summary": result.get("summary", "") if isinstance(result, dict) else ""
-        })
-
-    summary_parts.append("\n---")
-    summary_parts.append(f"已完成 {len(branches)} 个方案的探索,请根据结果选择继续的方向。")
-
-    summary = "\n".join(summary_parts)
-
-    # 5. 完成主 Goal,保存元数据
-    await store.update_goal(current_trace_id, current_goal_id,
-                           status="completed",
-                           summary=f"探索了 {len(branches)} 个方案",
-                           sub_trace_metadata=sub_trace_metadata)
-
-    return summary
-
-
-def create_explore_tool_schema() -> Dict[str, Any]:
-    """
-    创建 explore 工具的 JSON Schema
-
-    Returns:
-        工具的 JSON Schema
-    """
-    return {
-        "type": "function",
-        "function": {
-            "name": "explore",
-            "description": "并行探索多个方向,汇总结果。用于需要对比多个方案或尝试不同实现方式的场景。",
-            "parameters": {
-                "type": "object",
-                "properties": {
-                    "branches": {
-                        "type": "array",
-                        "items": {"type": "string"},
-                        "description": "探索方向列表,每个元素是一个探索任务的描述",
-                        "minItems": 2,
-                        "maxItems": 5
-                    },
-                    "background": {
-                        "type": "string",
-                        "description": "可选的背景信息,用于初始化各 Sub-Trace 的上下文"
-                    }
-                },
-                "required": ["branches"]
-            }
-        }
-    }

+ 0 - 91
agent/memory/README.md

@@ -1,91 +0,0 @@
-# Agent Memory - 记忆系统
-
-## 职责
-
-记忆系统管理 Agent 的长期记忆和技能:
-
-1. **数据模型** (`models.py`)
-   - `Experience` - 经验记录(从执行中提取的知识)
-   - `Skill` - 技能定义(领域知识和最佳实践)
-
-2. **存储接口** (`protocols.py`, `stores.py`)
-   - `MemoryStore` - 经验和技能存储接口
-   - `StateStore` - 状态存储接口
-   - `MemoryMemoryStore` - 内存实现
-   - `MemoryStateStore` - 内存实现
-
-3. **技能加载** (`skill_loader.py`)
-   - 从 Markdown 文件加载技能
-   - 支持 YAML frontmatter 和行内元数据
-
-## 模块边界
-
-- **只依赖**:无(纯数据模型和接口)
-- **被依赖**:core.runner(加载和注入记忆)
-- **独立开发**:记忆检索、经验提取算法可独立迭代
-
-## 使用示例
-
-```python
-from agent.memory import (
-    MemoryMemoryStore,
-    Experience,
-    Skill,
-    load_skills_from_dir
-)
-
-# 创建存储
-memory_store = MemoryMemoryStore()
-
-# 添加经验
-exp = Experience.create(
-    scope="coding",
-    context="Python type hints",
-    pattern="Use Optional[T] for nullable types",
-    outcome="success"
-)
-await memory_store.add_experience(exp)
-
-# 加载技能
-skills = load_skills_from_dir("./config/skills")
-for skill in skills:
-    await memory_store.add_skill(skill)
-
-# 检索记忆
-experiences = await memory_store.search_experiences(
-    scope="coding",
-    context="type hints",
-    limit=5
-)
-```
-
-## 技能文件格式
-
-技能文件使用 Markdown + YAML frontmatter:
-
-```markdown
----
-name: browser-automation
-description: Browser automation best practices
-category: web-automation
-scope: agent:*
----
-
-## When to use
-
-- Scraping dynamic web pages
-- Testing web applications
-
-## Guidelines
-
-- Always check if element exists before clicking
-- Use explicit waits instead of sleep()
-```
-
-## 文件说明
-
-- `models.py` - Experience 和 Skill 数据模型
-- `protocols.py` - MemoryStore 和 StateStore 接口
-- `stores.py` - 内存存储实现
-- `skill_loader.py` - 技能加载器
-- `__init__.py` - 模块导出

+ 0 - 0
agent/skills/core.md → agent/memory/skills/core.md


+ 0 - 137
agent/subagents/default.json

@@ -1,137 +0,0 @@
-{
-  "agents": {
-    "code-reviewer": {
-      "description": "代码审查专家,专注于代码质量和最佳实践",
-      "mode": "subagent",
-      "allowed_tools": [
-        "read_file",
-        "search_code",
-        "list_files"
-      ],
-      "denied_tools": [
-        "write_file",
-        "edit_file",
-        "execute_bash",
-        "task"
-      ],
-      "max_iterations": 15,
-      "temperature": 0.2,
-      "system_prompt": "你是一个代码审查专家。专注于:\n\n1. 代码质量\n   - 可读性和可维护性\n   - 命名规范\n   - 注释质量\n\n2. 最佳实践\n   - 设计模式的正确使用\n   - SOLID 原则\n   - DRY 原则\n\n3. 潜在问题\n   - 性能问题\n   - 内存泄漏\n   - 边界条件\n\n输出格式:\n- **文件**: [路径]\n- **问题**: [描述]\n- **严重程度**: [高/中/低]\n- **建议**: [改进建议]\n- **示例**: [可选的代码示例]",
-      "can_spawn_subagent": false
-    },
-
-    "security-scanner": {
-      "description": "安全扫描专家,查找安全漏洞和敏感信息泄露",
-      "mode": "subagent",
-      "allowed_tools": [
-        "read_file",
-        "search_code",
-        "list_files"
-      ],
-      "denied_tools": [
-        "write_file",
-        "edit_file",
-        "execute_bash",
-        "task"
-      ],
-      "max_iterations": 20,
-      "temperature": 0.1,
-      "system_prompt": "你是一个安全扫描专家。专注于:\n\n1. 常见漏洞\n   - SQL 注入\n   - XSS 攻击\n   - CSRF 漏洞\n   - 不安全的反序列化\n\n2. 敏感信息\n   - 硬编码的密钥\n   - API tokens\n   - 数据库凭据\n\n3. 配置安全\n   - 不安全的默认配置\n   - 过于宽松的权限\n\n输出格式:\n- **漏洞类型**: [类型]\n- **位置**: [文件:行号]\n- **严重程度**: [高/中/低]\n- **描述**: [详细说明]\n- **修复建议**: [如何修复]",
-      "can_spawn_subagent": false,
-      "permissions": {
-        "paths": {
-          "/etc": "deny",
-          "~/.ssh": "deny",
-          "/tmp": "allow"
-        }
-      }
-    },
-
-    "doc-writer": {
-      "description": "文档编写专家,生成和改进项目文档",
-      "mode": "subagent",
-      "allowed_tools": [
-        "read_file",
-        "search_code",
-        "list_files",
-        "write_file"
-      ],
-      "denied_tools": [
-        "execute_bash",
-        "task"
-      ],
-      "max_iterations": 15,
-      "temperature": 0.5,
-      "system_prompt": "你是一个文档编写专家。专注于:\n\n1. 文档结构\n   - 清晰的层次\n   - 合理的章节划分\n   - 完整的目录\n\n2. 内容质量\n   - 准确性\n   - 示例代码\n   - 使用场景\n\n3. 文档类型\n   - README\n   - API 文档\n   - 教程\n   - 架构设计文档\n\n输出格式:使用 Markdown",
-      "can_spawn_subagent": false
-    },
-
-    "performance-analyzer": {
-      "description": "性能分析专家,识别性能瓶颈和优化机会",
-      "mode": "subagent",
-      "allowed_tools": [
-        "read_file",
-        "search_code",
-        "list_files",
-        "execute_bash"
-      ],
-      "denied_tools": [
-        "write_file",
-        "edit_file",
-        "task"
-      ],
-      "max_iterations": 25,
-      "temperature": 0.2,
-      "system_prompt": "你是一个性能分析专家。专注于:\n\n1. 性能问题识别\n   - N+1 查询\n   - 不必要的计算\n   - 内存泄漏\n   - 阻塞操作\n\n2. 优化建议\n   - 缓存策略\n   - 数据库索引\n   - 并发处理\n   - 批量操作\n\n3. 基准测试\n   - 关键路径分析\n   - 性能指标\n\n输出格式:\n- **问题**: [描述]\n- **位置**: [文件:函数]\n- **影响**: [响应时间/内存/CPU]\n- **优化建议**: [具体方案]\n- **预期收益**: [性能提升估算]",
-      "can_spawn_subagent": false
-    },
-
-    "test-writer": {
-      "description": "测试编写专家,生成单元测试和集成测试",
-      "mode": "subagent",
-      "allowed_tools": [
-        "read_file",
-        "search_code",
-        "list_files",
-        "write_file"
-      ],
-      "denied_tools": [
-        "execute_bash",
-        "task"
-      ],
-      "max_iterations": 20,
-      "temperature": 0.3,
-      "system_prompt": "你是一个测试编写专家。专注于:\n\n1. 测试覆盖\n   - 核心功能\n   - 边界条件\n   - 错误处理\n\n2. 测试质量\n   - 可读性\n   - 独立性\n   - 可维护性\n\n3. 测试类型\n   - 单元测试\n   - 集成测试\n   - 端到端测试\n\n遵循 AAA 模式:Arrange, Act, Assert",
-      "can_spawn_subagent": false
-    },
-
-    "my-primary-agent": {
-      "description": "自定义主 Agent,具有完整权限",
-      "mode": "primary",
-      "can_spawn_subagent": true,
-      "max_iterations": 30,
-      "temperature": 0.7
-    }
-  },
-
-  "default_agent": "my-primary-agent",
-
-  "permissions": {
-    "global": {
-      "tools": {
-        "execute_bash": "ask",
-        "write_file": "ask"
-      },
-      "paths": {
-        "/etc": "deny",
-        "~/.ssh": "deny",
-        "/tmp": "allow"
-      },
-      "network": {
-        "blocked_domains": [
-          "*.evil.com"
-        ]
-      }
-    }
-  }
-}

+ 0 - 94
agent/tools/README.md

@@ -1,94 +0,0 @@
-# Agent Tools - 工具系统
-
-## 职责
-
-工具系统提供 Agent 可调用的各种能力:
-
-1. **工具注册** (`registry.py`)
-   - 工具装饰器 `@tool`
-   - 全局工具注册表
-   - 工具发现和调用
-
-2. **Schema 生成** (`schema.py`)
-   - 将 Python 函数转换为 OpenAI function calling schema
-   - 支持类型注解和文档字符串
-
-3. **工具模型** (`models.py`)
-   - `ToolResult` - 工具执行结果
-   - `ToolContext` - 工具执行上下文
-
-4. **内置工具** (`builtin/`)
-   - `read_file` - 读取文件
-   - `edit_file` - 编辑文件
-   - `write_file` - 写入文件
-   - `glob_files` - 文件搜索
-   - `grep_content` - 内容搜索
-   - `bash_command` - 执行 Shell 命令
-   - `skill` - 加载技能文档
-
-5. **高级工具** (`advanced/`)
-   - LSP - Language Server Protocol 集成
-   - WebFetch - 网页抓取
-
-6. **适配器** (`adapters/`)
-   - Browser-use - 浏览器自动化
-
-## 工具开发
-
-创建自定义工具:
-
-```python
-from agent.tools import tool, ToolResult
-
-@tool(description="Calculate sum of two numbers")
-async def add(a: int, b: int, uid: str = "") -> ToolResult:
-    """
-    Add two numbers
-
-    Args:
-        a: First number
-        b: Second number
-        uid: User ID (auto-injected)
-
-    Returns:
-        ToolResult with sum
-    """
-    return ToolResult(
-        success=True,
-        data={"sum": a + b}
-    )
-```
-
-## 文件说明
-
-- `registry.py` - 工具注册表和装饰器
-- `schema.py` - OpenAI schema 生成
-- `models.py` - ToolResult 等数据模型
-- `sensitive.py` - 敏感数据处理
-- `url_matcher.py` - URL 模式匹配
-- `builtin/` - 核心工具
-- `advanced/` - 高级工具
-- `adapters/` - 外部集成
-
-## 目录结构
-
-```
-tools/
-├── __init__.py          # 工具装饰器和注册表
-├── registry.py          # 核心注册逻辑
-├── schema.py            # Schema 生成
-├── models.py            # 数据模型
-├── builtin/             # 核心工具(必需)
-│   ├── read.py
-│   ├── edit.py
-│   ├── write.py
-│   ├── glob.py
-│   ├── grep.py
-│   ├── bash.py
-│   └── skill.py
-├── advanced/            # 高级工具(可选)
-│   ├── lsp.py
-│   └── webfetch.py
-└── adapters/            # 外部集成(可选)
-    └── browser_use/
-```

+ 4 - 2
agent/tools/builtin/__init__.py

@@ -14,7 +14,7 @@ from agent.tools.builtin.glob_tool import glob_files
 from agent.tools.builtin.grep import grep_content
 from agent.tools.builtin.bash import bash_command
 from agent.tools.builtin.skill import skill, list_skills
-from agent.tools.builtin.goal import goal
+from agent.tools.builtin.subagent import subagent
 from agent.tools.builtin.search import search_posts, get_search_suggestions
 from agent.tools.builtin.sandbox import (sandbox_create_environment, sandbox_run_shell,
                                          sandbox_rebuild_with_ports,sandbox_destroy_environment)
@@ -23,15 +23,17 @@ from agent.tools.builtin.sandbox import (sandbox_create_environment, sandbox_run
 import agent.tools.builtin.browser  # noqa: F401
 
 __all__ = [
+    # 文件操作
     "read_file",
     "edit_file",
     "write_file",
     "glob_files",
     "grep_content",
+    # 系统工具
     "bash_command",
     "skill",
     "list_skills",
-    "goal",
+    "subagent",
     "search_posts",
     "get_search_suggestions",
     "sandbox_create_environment",

+ 18 - 2
agent/tools/builtin/browser/baseClass.py

@@ -1250,8 +1250,24 @@ async def browser_get_selector_map() -> ToolResult:
     try:
         browser, tools = await get_browser_session()
 
-        # 获取选择器映射
-        selector_map = await browser.get_selector_map()
+        # 关键修复:先触发 BrowserStateRequestEvent 来更新 DOM 状态
+        # 这会触发 DOM watchdog 重新构建 DOM 树并更新 selector_map
+        from browser_use.browser.events import BrowserStateRequestEvent
+
+        # 触发事件并等待结果
+        event = browser.event_bus.dispatch(
+            BrowserStateRequestEvent(
+                include_dom=True,
+                include_screenshot=False,  # 不需要截图,节省时间
+                include_recent_events=False
+            )
+        )
+
+        # 等待 DOM 更新完成
+        browser_state = await event.event_result(raise_if_none=True, raise_if_any=True)
+
+        # 从更新后的状态中获取 selector_map
+        selector_map = browser_state.dom_state.selector_map if browser_state.dom_state else {}
 
         # 构建输出信息
         elements_info = []

+ 19 - 0
agent/tools/builtin/file/__init__.py

@@ -0,0 +1,19 @@
+"""
+File tools - 文件操作工具
+
+包含:read, write, edit, glob, grep
+"""
+
+from .read import read_file
+from .write import write_file
+from .edit import edit_file
+from .glob import glob_files
+from .grep import grep_content
+
+__all__ = [
+    "read_file",
+    "write_file",
+    "edit_file",
+    "glob_files",
+    "grep_content",
+]

+ 0 - 0
agent/tools/builtin/edit.py → agent/tools/builtin/file/edit.py


+ 108 - 0
agent/tools/builtin/file/glob.py

@@ -0,0 +1,108 @@
+"""
+Glob Tool - 文件模式匹配工具
+
+参考:vendor/opencode/packages/opencode/src/tool/glob.ts
+
+核心功能:
+- 使用 glob 模式匹配文件
+- 按修改时间排序
+- 限制返回数量
+"""
+
+import glob as glob_module
+from pathlib import Path
+from typing import Optional
+
+from agent.tools import tool, ToolResult, ToolContext
+
+# 常量
+LIMIT = 100  # 最大返回数量(参考 opencode glob.ts:35)
+
+
+@tool(description="使用 glob 模式匹配文件")
+async def glob_files(
+    pattern: str,
+    path: Optional[str] = None,
+    context: Optional[ToolContext] = None
+) -> ToolResult:
+    """
+    使用 glob 模式匹配文件
+
+    参考 OpenCode 实现
+
+    Args:
+        pattern: glob 模式(如 "*.py", "src/**/*.ts")
+        path: 搜索目录(默认当前目录)
+        context: 工具上下文
+
+    Returns:
+        ToolResult: 匹配的文件列表
+    """
+    # 确定搜索路径
+    search_path = Path(path) if path else Path.cwd()
+    if not search_path.is_absolute():
+        search_path = Path.cwd() / search_path
+
+    if not search_path.exists():
+        return ToolResult(
+            title="目录不存在",
+            output=f"搜索目录不存在: {path}",
+            error="Directory not found"
+        )
+
+    # 执行 glob 搜索
+    try:
+        # 使用 pathlib 的 glob(支持 ** 递归)
+        if "**" in pattern:
+            matches = list(search_path.glob(pattern))
+        else:
+            # 使用标准 glob(更快)
+            pattern_path = search_path / pattern
+            matches = [Path(p) for p in glob_module.glob(str(pattern_path))]
+
+        # 过滤掉目录,只保留文件
+        file_matches = [m for m in matches if m.is_file()]
+
+        # 按修改时间排序(参考 opencode:47-56)
+        file_matches_with_mtime = []
+        for file_path in file_matches:
+            try:
+                mtime = file_path.stat().st_mtime
+                file_matches_with_mtime.append((file_path, mtime))
+            except Exception:
+                file_matches_with_mtime.append((file_path, 0))
+
+        # 按修改时间降序排序(最新的在前)
+        file_matches_with_mtime.sort(key=lambda x: x[1], reverse=True)
+
+        # 限制数量
+        truncated = len(file_matches_with_mtime) > LIMIT
+        file_matches_with_mtime = file_matches_with_mtime[:LIMIT]
+
+        # 格式化输出
+        if not file_matches_with_mtime:
+            output = "未找到匹配的文件"
+        else:
+            file_paths = [str(f[0]) for f in file_matches_with_mtime]
+            output = "\n".join(file_paths)
+
+            if truncated:
+                output += f"\n\n(结果已截断。考虑使用更具体的路径或模式。)"
+
+        return ToolResult(
+            title=f"匹配: {pattern}",
+            output=output,
+            metadata={
+                "count": len(file_matches_with_mtime),
+                "truncated": truncated,
+                "pattern": pattern,
+                "search_path": str(search_path)
+            }
+        )
+
+    except Exception as e:
+        return ToolResult(
+            title="Glob 错误",
+            output=f"glob 匹配失败: {str(e)}",
+            error=str(e)
+        )

+ 0 - 0
agent/tools/builtin/grep.py → agent/tools/builtin/file/grep.py


+ 0 - 0
agent/tools/builtin/read.py → agent/tools/builtin/file/read.py


+ 0 - 0
agent/tools/builtin/write.py → agent/tools/builtin/file/write.py


+ 0 - 75
agent/tools/builtin/goal.py

@@ -1,75 +0,0 @@
-"""
-Goal 工具 - 执行计划管理
-
-提供 LLM 可调用的 goal 工具,用于管理执行计划(GoalTree)。
-"""
-
-from typing import Optional
-from agent.tools import tool
-
-
-# 全局 GoalTree 引用(由 AgentRunner 注入)
-_current_goal_tree = None
-
-
-def set_goal_tree(tree):
-    """设置当前 GoalTree(由 AgentRunner 调用)"""
-    global _current_goal_tree
-    _current_goal_tree = tree
-
-
-def get_goal_tree():
-    """获取当前 GoalTree"""
-    return _current_goal_tree
-
-
-@tool(description="管理执行计划,添加/完成/放弃目标,切换焦点")
-async def goal(
-    add: Optional[str] = None,
-    reason: Optional[str] = None,
-    done: Optional[str] = None,
-    abandon: Optional[str] = None,
-    focus: Optional[str] = None,
-    context: Optional[dict] = None
-) -> str:
-    """
-    管理执行计划,添加/完成/放弃目标,切换焦点。
-
-    Args:
-        add: 添加目标(逗号分隔多个)。添加到当前 focus 的 goal 下作为子目标。
-        reason: 创建理由(逗号分隔多个,与 add 一一对应)。说明为什么要做这些目标。
-        done: 完成当前目标,值为 summary
-        abandon: 放弃当前目标,值为原因(会触发 context 压缩)
-        focus: 切换焦点到指定 ID(如 "1", "2.1", "2.2")
-        context: 工具执行上下文(包含 store 和 trace_id)
-
-    Examples:
-        goal(add="分析代码, 实现功能, 测试", reason="了解现有结构, 完成需求, 确保质量")
-        goal(focus="2", add="设计接口, 实现代码", reason="明确API规范, 编写核心逻辑")
-        goal(done="发现用户模型在 models/user.py")
-        goal(done="已完成调研", focus="2")
-        goal(abandon="方案A需要Redis,环境没有", add="实现方案B", reason="使用现有技术栈")
-
-    Returns:
-        str: 更新后的计划状态文本
-    """
-    from agent.goal.tool import goal_tool
-
-    tree = get_goal_tree()
-    if tree is None:
-        return "错误:GoalTree 未初始化"
-
-    # 从 context 获取 store 和 trace_id
-    store = context.get("store") if context else None
-    trace_id = context.get("trace_id") if context else None
-
-    return await goal_tool(
-        tree=tree,
-        store=store,
-        trace_id=trace_id,
-        add=add,
-        reason=reason,
-        done=done,
-        abandon=abandon,
-        focus=focus
-    )

+ 2 - 2
agent/tools/builtin/skill.py

@@ -15,7 +15,7 @@ from agent.memory.skill_loader import SkillLoader
 # 默认 skills 目录(优先级:项目 skills > 框架 skills)
 DEFAULT_SKILLS_DIRS = [
     os.getenv("SKILLS_DIR", "./skills"),      # 项目特定 skills(优先)
-    "./agent/skills"                           # 框架内置 skills
+    "./agent/memory/skills"                    # 框架内置 skills
 ]
 
 # 默认单一目录(用于 list_skills)
@@ -36,7 +36,7 @@ def _check_skill_setup(skill_name: str) -> Optional[str]:
     if skill_name in ["browser-use", "browser_use"]:
         try:
             # 动态导入 browser-use skill 的 setup 模块
-            from agent.skills.browser_use.setup import (
+            from agent.memory.skills.browser_use.setup import (
                 _check_browser_use_cli,
                 _check_chromium_installed
             )

+ 606 - 0
agent/tools/builtin/subagent.py

@@ -0,0 +1,606 @@
+"""
+Sub-Agent 工具 - 统一 explore/delegate/evaluate
+
+作为普通工具运行:创建(或继承)子 Trace,执行并返回结构化结果。
+"""
+
+import asyncio
+from datetime import datetime
+from typing import Any, Dict, List, Optional
+
+from agent.tools import tool
+from agent.trace.models import Trace
+from agent.trace.trace_id import generate_sub_trace_id
+from agent.trace.goal_models import GoalTree
+from agent.trace.websocket import broadcast_sub_trace_started, broadcast_sub_trace_completed
+
+
+def _build_explore_prompt(branches: List[str], background: Optional[str]) -> str:
+    lines = ["# 探索任务", ""]
+    if background:
+        lines.extend([background, ""])
+    lines.append("请探索以下方案:")
+    for i, branch in enumerate(branches, 1):
+        lines.append(f"{i}. {branch}")
+    return "\n".join(lines)
+
+
+async def _build_evaluate_prompt(
+    store,
+    trace_id: str,
+    target_goal_id: str,
+    evaluation_input: Dict[str, Any],
+    requirements: Optional[str],
+) -> str:
+    goal_tree = await store.get_goal_tree(trace_id)
+    target_desc = ""
+    if goal_tree:
+        target_goal = goal_tree.find(target_goal_id)
+        if target_goal:
+            target_desc = target_goal.description
+
+    goal_description = evaluation_input.get("goal_description") or target_desc or f"Goal {target_goal_id}"
+    actual_result = evaluation_input.get("actual_result", "(无执行结果)")
+
+    lines = [
+        "# 评估任务",
+        "",
+        "请评估以下任务的执行结果是否满足要求。",
+        "",
+        "## 目标描述",
+        "",
+        str(goal_description),
+        "",
+        "## 执行结果",
+        "",
+        str(actual_result),
+        "",
+    ]
+
+    if requirements:
+        lines.extend(["## 评估要求", "", requirements, ""])
+
+    lines.extend(
+        [
+            "## 输出格式",
+            "",
+            "## 评估结论",
+            "[通过/不通过]",
+            "",
+            "## 评估理由",
+            "[详细说明通过或不通过原因]",
+            "",
+            "## 修改建议(如果不通过)",
+            "1. [建议1]",
+            "2. [建议2]",
+        ]
+    )
+    return "\n".join(lines)
+
+
+# ===== 辅助函数 =====
+
+async def _update_goal_start(
+    store, trace_id: str, goal_id: str, mode: str, sub_trace_ids: List[str]
+) -> None:
+    """标记 Goal 开始执行"""
+    if not goal_id:
+        return
+    await store.update_goal(
+        trace_id, goal_id,
+        type="agent_call",
+        agent_call_mode=mode,
+        status="in_progress",
+        sub_trace_ids=sub_trace_ids
+    )
+
+
+async def _update_goal_complete(
+    store, trace_id: str, goal_id: str,
+    status: str, summary: str, sub_trace_ids: List[str]
+) -> None:
+    """标记 Goal 完成"""
+    if not goal_id:
+        return
+    await store.update_goal(
+        trace_id, goal_id,
+        status=status,
+        summary=summary,
+        sub_trace_ids=sub_trace_ids
+    )
+
+
+def _format_explore_results(
+    branches: List[str], results: List[Dict[str, Any]]
+) -> str:
+    """格式化 explore 模式的汇总结果(Markdown)"""
+    lines = ["## 探索结果\n"]
+
+    successful = 0
+    failed = 0
+    total_tokens = 0
+    total_cost = 0.0
+
+    for i, (branch, result) in enumerate(zip(branches, results)):
+        branch_name = chr(ord('A') + i)  # A, B, C...
+        lines.append(f"### 方案 {branch_name}: {branch}")
+
+        if isinstance(result, dict):
+            status = result.get("status", "unknown")
+            if status == "completed":
+                lines.append("**状态**: ✓ 完成")
+                successful += 1
+            else:
+                lines.append("**状态**: ✗ 失败")
+                failed += 1
+
+            summary = result.get("summary", "")
+            if summary:
+                lines.append(f"**摘要**: {summary[:200]}...")  # 限制长度
+
+            stats = result.get("stats", {})
+            if stats:
+                messages = stats.get("total_messages", 0)
+                tokens = stats.get("total_tokens", 0)
+                cost = stats.get("total_cost", 0.0)
+                lines.append(f"**统计**: {messages} messages, {tokens} tokens, ${cost:.4f}")
+                total_tokens += tokens
+                total_cost += cost
+        else:
+            lines.append("**状态**: ✗ 异常")
+            failed += 1
+
+        lines.append("")
+
+    lines.append("---\n")
+    lines.append("## 总结")
+    lines.append(f"- 总分支数: {len(branches)}")
+    lines.append(f"- 成功: {successful}")
+    lines.append(f"- 失败: {failed}")
+    lines.append(f"- 总 tokens: {total_tokens}")
+    lines.append(f"- 总成本: ${total_cost:.4f}")
+
+    return "\n".join(lines)
+
+
+def _format_delegate_result(result: Dict[str, Any]) -> str:
+    """格式化 delegate 模式的详细结果"""
+    lines = ["## 委托任务完成\n"]
+
+    summary = result.get("summary", "")
+    if summary:
+        lines.append(summary)
+        lines.append("")
+
+    lines.append("---\n")
+    lines.append("**执行统计**:")
+
+    stats = result.get("stats", {})
+    if stats:
+        lines.append(f"- 消息数: {stats.get('total_messages', 0)}")
+        lines.append(f"- Tokens: {stats.get('total_tokens', 0)}")
+        lines.append(f"- 成本: ${stats.get('total_cost', 0.0):.4f}")
+
+    return "\n".join(lines)
+
+
+def _format_evaluate_result(result: Dict[str, Any]) -> str:
+    """格式化 evaluate 模式的评估结果"""
+    summary = result.get("summary", "")
+    return summary  # evaluate 的 summary 已经是格式化的评估结果
+
+
+def _get_allowed_tools_for_mode(mode: str, context: dict) -> Optional[List[str]]:
+    """获取模式对应的允许工具列表"""
+    if mode == "explore":
+        return ["read_file", "grep_content", "glob_files"]
+    elif mode in ["delegate", "evaluate"]:
+        # 获取所有工具,排除 subagent
+        runner = context.get("runner")
+        if runner and hasattr(runner, "tools") and hasattr(runner.tools, "registry"):
+            all_tools = list(runner.tools.registry.keys())
+            return [t for t in all_tools if t != "subagent"]
+    return None  # 使用默认(所有工具)
+
+
+def _aggregate_stats(results: List[Dict[str, Any]]) -> Dict[str, Any]:
+    """聚合多个结果的统计信息"""
+    total_messages = 0
+    total_tokens = 0
+    total_cost = 0.0
+
+    for result in results:
+        if isinstance(result, dict) and "stats" in result:
+            stats = result["stats"]
+            total_messages += stats.get("total_messages", 0)
+            total_tokens += stats.get("total_tokens", 0)
+            total_cost += stats.get("total_cost", 0.0)
+
+    return {
+        "total_messages": total_messages,
+        "total_tokens": total_tokens,
+        "total_cost": total_cost
+    }
+
+
+# ===== 模式处理函数 =====
+
+async def _handle_explore_mode(
+    branches: List[str],
+    background: Optional[str],
+    continue_from: Optional[str],
+    store, current_trace_id: str, current_goal_id: str, runner
+) -> Dict[str, Any]:
+    """Explore 模式:并行探索多个方案"""
+
+    # 1. 检查 continue_from(不支持)
+    if continue_from:
+        return {
+            "status": "failed",
+            "error": "explore mode does not support continue_from parameter"
+        }
+
+    # 2. 创建所有 Sub-Traces
+    sub_trace_ids = []
+    tasks = []
+
+    for i, branch in enumerate(branches):
+        # 生成唯一的 sub_trace_id
+        sub_trace_id = generate_sub_trace_id(current_trace_id, f"explore-{i+1:03d}")
+        sub_trace_ids.append(sub_trace_id)
+
+        # 创建 Sub-Trace
+        parent_trace = await store.get_trace(current_trace_id)
+        sub_trace = Trace(
+            trace_id=sub_trace_id,
+            mode="agent",
+            task=branch,
+            parent_trace_id=current_trace_id,
+            parent_goal_id=current_goal_id,
+            agent_type="explore",
+            uid=parent_trace.uid if parent_trace else None,
+            model=parent_trace.model if parent_trace else None,
+            status="running",
+            context={"subagent_mode": "explore", "created_by_tool": "subagent"},
+            created_at=datetime.now(),
+        )
+        await store.create_trace(sub_trace)
+        await store.update_goal_tree(sub_trace_id, GoalTree(mission=branch))
+
+        # 广播 sub_trace_started
+        await broadcast_sub_trace_started(
+            current_trace_id, sub_trace_id, current_goal_id or "",
+            "explore", branch
+        )
+
+        # 创建执行任务
+        task_coro = runner.run_result(
+            task=branch,
+            trace_id=sub_trace_id,
+            agent_type="explore",
+            tools=["read_file", "grep_content", "glob_files"]
+        )
+        tasks.append(task_coro)
+
+    # 3. 更新主 Goal 为 in_progress
+    await _update_goal_start(store, current_trace_id, current_goal_id, "explore", sub_trace_ids)
+
+    # 4. 并行执行所有分支
+    results = await asyncio.gather(*tasks, return_exceptions=True)
+
+    # 5. 处理结果并广播完成事件
+    processed_results = []
+
+    for i, result in enumerate(results):
+        if isinstance(result, Exception):
+            # 异常处理
+            error_result = {
+                "status": "failed",
+                "summary": f"执行出错: {str(result)}",
+                "stats": {"total_messages": 0, "total_tokens": 0, "total_cost": 0.0}
+            }
+            processed_results.append(error_result)
+            await broadcast_sub_trace_completed(
+                current_trace_id, sub_trace_ids[i],
+                "failed", str(result), {}
+            )
+        else:
+            processed_results.append(result)
+            await broadcast_sub_trace_completed(
+                current_trace_id, sub_trace_ids[i],
+                result.get("status", "completed"),
+                result.get("summary", ""),
+                result.get("stats", {})
+            )
+
+    # 6. 格式化汇总结果
+    aggregated_summary = _format_explore_results(branches, processed_results)
+
+    # 7. 更新主 Goal 为 completed
+    overall_status = "completed" if any(
+        r.get("status") == "completed" for r in processed_results if isinstance(r, dict)
+    ) else "failed"
+
+    await _update_goal_complete(
+        store, current_trace_id, current_goal_id,
+        overall_status, aggregated_summary, sub_trace_ids
+    )
+
+    # 8. 返回结果
+    return {
+        "mode": "explore",
+        "status": overall_status,
+        "summary": aggregated_summary,
+        "sub_trace_ids": sub_trace_ids,
+        "branches": branches,
+        "stats": _aggregate_stats(processed_results)
+    }
+
+
+async def _handle_delegate_mode(
+    task: str,
+    continue_from: Optional[str],
+    store, current_trace_id: str, current_goal_id: str, runner, context: dict
+) -> Dict[str, Any]:
+    """Delegate 模式:委托单个任务"""
+
+    # 1. 处理 continue_from 或创建新 Sub-Trace
+    if continue_from:
+        existing_trace = await store.get_trace(continue_from)
+        if not existing_trace:
+            return {"status": "failed", "error": f"Continue-from trace not found: {continue_from}"}
+        sub_trace_id = continue_from
+        sub_trace_ids = [sub_trace_id]
+    else:
+        parent_trace = await store.get_trace(current_trace_id)
+        sub_trace_id = generate_sub_trace_id(current_trace_id, "delegate")
+        sub_trace = Trace(
+            trace_id=sub_trace_id,
+            mode="agent",
+            task=task,
+            parent_trace_id=current_trace_id,
+            parent_goal_id=current_goal_id,
+            agent_type="delegate",
+            uid=parent_trace.uid if parent_trace else None,
+            model=parent_trace.model if parent_trace else None,
+            status="running",
+            context={"subagent_mode": "delegate", "created_by_tool": "subagent"},
+            created_at=datetime.now(),
+        )
+        await store.create_trace(sub_trace)
+        await store.update_goal_tree(sub_trace_id, GoalTree(mission=task))
+        sub_trace_ids = [sub_trace_id]
+
+        # 广播 sub_trace_started
+        await broadcast_sub_trace_started(
+            current_trace_id, sub_trace_id, current_goal_id or "",
+            "delegate", task
+        )
+
+    # 2. 更新主 Goal 为 in_progress
+    await _update_goal_start(store, current_trace_id, current_goal_id, "delegate", sub_trace_ids)
+
+    # 3. 执行任务
+    try:
+        allowed_tools = _get_allowed_tools_for_mode("delegate", context)
+        result = await runner.run_result(
+            task=task,
+            trace_id=sub_trace_id,
+            agent_type="delegate",
+            tools=allowed_tools
+        )
+
+        # 4. 广播 sub_trace_completed
+        await broadcast_sub_trace_completed(
+            current_trace_id, sub_trace_id,
+            result.get("status", "completed"),
+            result.get("summary", ""),
+            result.get("stats", {})
+        )
+
+        # 5. 格式化结果
+        formatted_summary = _format_delegate_result(result)
+
+        # 6. 更新主 Goal 为 completed
+        await _update_goal_complete(
+            store, current_trace_id, current_goal_id,
+            result.get("status", "completed"), formatted_summary, sub_trace_ids
+        )
+
+        # 7. 返回结果
+        return {
+            "mode": "delegate",
+            "sub_trace_id": sub_trace_id,
+            "continue_from": bool(continue_from),
+            **result,
+            "summary": formatted_summary
+        }
+
+    except Exception as e:
+        # 错误处理
+        error_msg = str(e)
+        await broadcast_sub_trace_completed(
+            current_trace_id, sub_trace_id,
+            "failed", error_msg, {}
+        )
+
+        await _update_goal_complete(
+            store, current_trace_id, current_goal_id,
+            "failed", f"委托任务失败: {error_msg}", sub_trace_ids
+        )
+
+        return {
+            "mode": "delegate",
+            "status": "failed",
+            "error": error_msg,
+            "sub_trace_id": sub_trace_id
+        }
+
+
+async def _handle_evaluate_mode(
+    target_goal_id: str,
+    evaluation_input: Dict[str, Any],
+    requirements: Optional[str],
+    continue_from: Optional[str],
+    store, current_trace_id: str, current_goal_id: str, runner, context: dict
+) -> Dict[str, Any]:
+    """Evaluate 模式:评估任务结果"""
+
+    # 1. 构建评估 prompt
+    task_prompt = await _build_evaluate_prompt(
+        store, current_trace_id, target_goal_id,
+        evaluation_input, requirements
+    )
+
+    # 2. 处理 continue_from 或创建新 Sub-Trace
+    if continue_from:
+        existing_trace = await store.get_trace(continue_from)
+        if not existing_trace:
+            return {"status": "failed", "error": f"Continue-from trace not found: {continue_from}"}
+        sub_trace_id = continue_from
+        sub_trace_ids = [sub_trace_id]
+    else:
+        parent_trace = await store.get_trace(current_trace_id)
+        sub_trace_id = generate_sub_trace_id(current_trace_id, "evaluate")
+        sub_trace = Trace(
+            trace_id=sub_trace_id,
+            mode="agent",
+            task=task_prompt,
+            parent_trace_id=current_trace_id,
+            parent_goal_id=current_goal_id,
+            agent_type="evaluate",
+            uid=parent_trace.uid if parent_trace else None,
+            model=parent_trace.model if parent_trace else None,
+            status="running",
+            context={"subagent_mode": "evaluate", "created_by_tool": "subagent"},
+            created_at=datetime.now(),
+        )
+        await store.create_trace(sub_trace)
+        await store.update_goal_tree(sub_trace_id, GoalTree(mission=task_prompt))
+        sub_trace_ids = [sub_trace_id]
+
+        # 广播 sub_trace_started
+        await broadcast_sub_trace_started(
+            current_trace_id, sub_trace_id, current_goal_id or "",
+            "evaluate", task_prompt
+        )
+
+    # 3. 更新主 Goal 为 in_progress
+    await _update_goal_start(store, current_trace_id, current_goal_id, "evaluate", sub_trace_ids)
+
+    # 4. 执行评估
+    try:
+        allowed_tools = _get_allowed_tools_for_mode("evaluate", context)
+        result = await runner.run_result(
+            task=task_prompt,
+            trace_id=sub_trace_id,
+            agent_type="evaluate",
+            tools=allowed_tools
+        )
+
+        # 5. 广播 sub_trace_completed
+        await broadcast_sub_trace_completed(
+            current_trace_id, sub_trace_id,
+            result.get("status", "completed"),
+            result.get("summary", ""),
+            result.get("stats", {})
+        )
+
+        # 6. 格式化结果
+        formatted_summary = _format_evaluate_result(result)
+
+        # 7. 更新主 Goal 为 completed
+        await _update_goal_complete(
+            store, current_trace_id, current_goal_id,
+            result.get("status", "completed"), formatted_summary, sub_trace_ids
+        )
+
+        # 8. 返回结果
+        return {
+            "mode": "evaluate",
+            "sub_trace_id": sub_trace_id,
+            "continue_from": bool(continue_from),
+            **result,
+            "summary": formatted_summary
+        }
+
+    except Exception as e:
+        # 错误处理
+        error_msg = str(e)
+        await broadcast_sub_trace_completed(
+            current_trace_id, sub_trace_id,
+            "failed", error_msg, {}
+        )
+
+        await _update_goal_complete(
+            store, current_trace_id, current_goal_id,
+            "failed", f"评估任务失败: {error_msg}", sub_trace_ids
+        )
+
+        return {
+            "mode": "evaluate",
+            "status": "failed",
+            "error": error_msg,
+            "sub_trace_id": sub_trace_id
+        }
+
+
+@tool(description="创建 Sub-Agent 执行任务(evaluate/delegate/explore)")
+async def subagent(
+    mode: str,
+    task: Optional[str] = None,
+    target_goal_id: Optional[str] = None,
+    evaluation_input: Optional[Dict[str, Any]] = None,
+    requirements: Optional[str] = None,
+    branches: Optional[List[str]] = None,
+    background: Optional[str] = None,
+    continue_from: Optional[str] = None,
+    context: Optional[dict] = None,
+) -> Dict[str, Any]:
+    # 1. 验证 context
+    if not context:
+        return {"status": "failed", "error": "context is required"}
+
+    store = context.get("store")
+    current_trace_id = context.get("trace_id")
+    current_goal_id = context.get("goal_id")
+    runner = context.get("runner")
+
+    missing = []
+    if not store:
+        missing.append("store")
+    if not current_trace_id:
+        missing.append("trace_id")
+    if not runner:
+        missing.append("runner")
+    if missing:
+        return {"status": "failed", "error": f"Missing required context: {', '.join(missing)}"}
+
+    # 2. 验证 mode
+    if mode not in {"evaluate", "delegate", "explore"}:
+        return {"status": "failed", "error": "Invalid mode: must be evaluate/delegate/explore"}
+
+    # 3. 验证模式特定参数
+    if mode == "delegate" and not task:
+        return {"status": "failed", "error": "delegate mode requires task"}
+    if mode == "explore" and not branches:
+        return {"status": "failed", "error": "explore mode requires branches"}
+    if mode == "evaluate" and (not target_goal_id or evaluation_input is None):
+        return {"status": "failed", "error": "evaluate mode requires target_goal_id and evaluation_input"}
+
+    # 4. 路由到模式处理函数
+    if mode == "explore":
+        return await _handle_explore_mode(
+            branches, background, continue_from,
+            store, current_trace_id, current_goal_id, runner
+        )
+    elif mode == "delegate":
+        return await _handle_delegate_mode(
+            task, continue_from,
+            store, current_trace_id, current_goal_id, runner, context
+        )
+    else:  # evaluate
+        return await _handle_evaluate_mode(
+            target_goal_id, evaluation_input, requirements, continue_from,
+            store, current_trace_id, current_goal_id, runner, context
+        )

+ 38 - 0
agent/trace/__init__.py

@@ -0,0 +1,38 @@
+"""
+Trace module - 执行追踪与计划管理
+
+包含:
+- Trace, Goal, Message 数据模型
+- TraceStore 存储接口和实现
+- goal 工具(计划管理)
+- Context 压缩
+- REST/WebSocket API
+"""
+
+from .models import Trace, Message
+from .goal_models import Goal, GoalTree, GoalStatus, GoalType, GoalStats
+from .protocols import TraceStore
+from .store import FileSystemTraceStore
+from .trace_id import generate_trace_id, generate_sub_trace_id, parse_parent_trace_id
+from .goal_tool import set_goal_tree, get_goal_tree
+
+__all__ = [
+    # Models
+    "Trace",
+    "Message",
+    "Goal",
+    "GoalTree",
+    "GoalStatus",
+    "GoalType",
+    "GoalStats",
+    # Store
+    "TraceStore",
+    "FileSystemTraceStore",
+    # Utils
+    "generate_trace_id",
+    "generate_sub_trace_id",
+    "parse_parent_trace_id",
+    # Goal tool
+    "set_goal_tree",
+    "get_goal_tree",
+]

+ 1 - 1
agent/execution/api.py → agent/trace/api.py

@@ -8,7 +8,7 @@ from typing import List, Optional, Dict, Any
 from fastapi import APIRouter, HTTPException, Query
 from pydantic import BaseModel
 
-from agent.execution.protocols import TraceStore
+from .protocols import TraceStore
 
 
 router = APIRouter(prefix="/api/traces", tags=["traces"])

+ 1 - 1
agent/goal/compaction.py → agent/trace/compaction.py

@@ -6,7 +6,7 @@ Context 压缩
 """
 
 from typing import List, Dict, Any, Optional
-from agent.goal.models import GoalTree, Goal
+from .goal_models import GoalTree, Goal
 
 
 def compress_messages_for_goal(

+ 0 - 0
agent/goal/models.py → agent/trace/goal_models.py


+ 74 - 2
agent/goal/tool.py → agent/trace/goal_tool.py

@@ -6,9 +6,81 @@ Goal 工具 - 计划管理
 
 from typing import Optional, List, TYPE_CHECKING
 
+from agent.tools import tool
+
 if TYPE_CHECKING:
-    from agent.goal.models import GoalTree
-    from agent.execution.protocols import TraceStore
+    from .goal_models import GoalTree
+    from .protocols import TraceStore
+
+
+# ===== 全局 GoalTree 状态管理 =====
+
+_current_goal_tree = None
+
+
+def set_goal_tree(tree):
+    """设置当前 GoalTree(由 AgentRunner 调用)"""
+    global _current_goal_tree
+    _current_goal_tree = tree
+
+
+def get_goal_tree():
+    """获取当前 GoalTree"""
+    return _current_goal_tree
+
+
+# ===== LLM 可调用的 goal 工具 =====
+
+@tool(description="管理执行计划,添加/完成/放弃目标,切换焦点")
+async def goal(
+    add: Optional[str] = None,
+    reason: Optional[str] = None,
+    after: Optional[str] = None,
+    under: Optional[str] = None,
+    done: Optional[str] = None,
+    abandon: Optional[str] = None,
+    focus: Optional[str] = None,
+    context: Optional[dict] = None
+) -> str:
+    """
+    管理执行计划,添加/完成/放弃目标,切换焦点。
+
+    Args:
+        add: 添加目标(逗号分隔多个)
+        reason: 创建理由(逗号分隔多个,与 add 一一对应)
+        after: 在指定目标后面添加(同层级)
+        under: 为指定目标添加子目标
+        done: 完成当前目标,值为 summary
+        abandon: 放弃当前目标,值为原因
+        focus: 切换焦点到指定 ID
+        context: 工具执行上下文(包含 store 和 trace_id)
+
+    Returns:
+        str: 更新后的计划状态文本
+    """
+    tree = get_goal_tree()
+    if tree is None:
+        return "错误:GoalTree 未初始化"
+
+    # 从 context 获取 store 和 trace_id
+    store = context.get("store") if context else None
+    trace_id = context.get("trace_id") if context else None
+
+    return await goal_tool(
+        tree=tree,
+        store=store,
+        trace_id=trace_id,
+        add=add,
+        reason=reason,
+        after=after,
+        under=under,
+        done=done,
+        abandon=abandon,
+        focus=focus
+    )
+
+
+# ===== 核心逻辑函数 =====
 
 
 async def goal_tool(

+ 0 - 0
agent/execution/models.py → agent/trace/models.py


+ 2 - 2
agent/execution/protocols.py → agent/trace/protocols.py

@@ -6,8 +6,8 @@ Trace Storage Protocol - Trace 存储接口定义
 
 from typing import Protocol, List, Optional, Dict, Any, runtime_checkable
 
-from agent.execution.models import Trace, Message
-from agent.goal.models import GoalTree, Goal
+from .models import Trace, Message
+from .goal_models import GoalTree, Goal
 
 
 @runtime_checkable

+ 2 - 2
agent/execution/fs_store.py → agent/trace/store.py

@@ -26,8 +26,8 @@ from pathlib import Path
 from typing import Dict, List, Optional, Any
 from datetime import datetime
 
-from agent.execution.models import Trace, Message
-from agent.goal.models import GoalTree, Goal, GoalStats
+from .models import Trace, Message
+from .goal_models import GoalTree, Goal, GoalStats
 
 
 class FileSystemTraceStore:

+ 0 - 0
agent/execution/trace_id.py → agent/trace/trace_id.py


+ 1 - 1
agent/execution/tree_dump.py → agent/trace/tree_dump.py

@@ -11,7 +11,7 @@ Step 树 Debug 输出
        code .trace/tree.txt
 
     3. 代码中使用:
-       from agent.execution import dump_tree
+       from agent.trace import dump_tree
        dump_tree(trace, steps)
 """
 

+ 1 - 1
agent/execution/websocket.py → agent/trace/websocket.py

@@ -8,7 +8,7 @@ from typing import Dict, Set, Any
 from datetime import datetime
 from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
 
-from agent.execution.protocols import TraceStore
+from .protocols import TraceStore
 
 
 router = APIRouter(prefix="/api/traces", tags=["websocket"])

+ 3 - 3
api_server.py

@@ -9,9 +9,9 @@ from fastapi import FastAPI
 from fastapi.middleware.cors import CORSMiddleware
 import uvicorn
 
-from agent.execution import FileSystemTraceStore
-from agent.execution.api import router as api_router, set_trace_store as set_api_trace_store
-from agent.execution.websocket import router as ws_router, set_trace_store as set_ws_trace_store
+from agent.trace import FileSystemTraceStore
+from agent.trace.api import router as api_router, set_trace_store as set_api_trace_store
+from agent.trace.websocket import router as ws_router, set_trace_store as set_ws_trace_store
 
 
 # ===== 日志配置 =====

+ 433 - 407
docs/README.md

@@ -1,47 +1,91 @@
 # Agent 功能需求与架构设计文档
 
-> **可执行规格书**:本文档是系统的核心设计。代码修改必须同步更新此文档。
-> 如文档与代码冲突,以代码为准,并立即修复文档。
-
----
-
 ## 文档维护规范
 
-**维护原则**:
-1. **谁改代码谁更新文档** - 功能变更后,相关文档必须同步修改
-2. **保持结构稳定** - 只增删内容,不随意调整层级结构
-3. **流程优先** - 新功能先写入核心流程,再补充模块详情
-4. **链接代码** - 关键实现标注文件路径,格式:`module/file.py:function_name`
-5. **简洁原则** - 只记录最重要的、与代码准确对应的或者明确的已完成的设计的信息,避免推测、建议,或大量代码
-6. **文档分层** - 每层文档是不同层次的overview,在上层文档对应位置引用下层详细文档
+0. **先改文档,再动代码** - 新功能或重大修改需先完成文档更新、并完成审阅后,再进行代码实现;除非改动较小、不被文档涵盖
+1. **文档分层,链接代码** - 重要或复杂设计可以另有详细文档;关键实现需标注代码文件路径;格式:`module/file.py:function_name`
+2. **简洁快照,日志分离** - 只记录最重要的、与代码准确对应的或者明确的已完成的设计的信息,避免推测、建议,或大量代码;决策依据或修改日志若有必要,可在`docs/decisions.md`另行记录
 
 ---
 
 ## 系统概览
 
-**单次调用是 Agent 的特例**:
+**核心理念:所有 Agent 都是 Trace**
 
-| 特性 | 单次调用 | Agent 模式 | Sub-Agent 模式 |
-|------|---------|-----------|--------------|
-| 循环次数 | 1 | N (可配置) | N (可配置,受限) |
-| 工具调用 | 可选 | 常用 | 受限工具集 |
-| 状态管理 | 无 | 有 (Trace) | 有 (独立 Trace + 父子关系) |
-| 记忆检索 | 无 | 有 (Experience/Skill) | 有 (继承主 Agent) |
-| 执行图 | 1 条 Message | N 条 Messages 的 DAG | 嵌套 DAG(多个 Trace) |
-| 触发方式 | 直接调用 | 直接调用 | 通过 Task 工具 |
-| 权限范围 | 完整 | 完整 | 受限(可配置) |
+| 类型 | 创建方式 | 父子关系 | 状态 |
+|------|---------|---------|------|
+| 主 Agent | 直接调用 `runner.run()` | 无 parent | 正常执行 |
+| 子 Agent | 通过 `subagent` 工具 | `parent_trace_id` / `parent_goal_id` 指向父 | 正常执行 |
+| 人类协助 | 通过 `ask_human` 工具 | `parent_trace_id` 指向父 | 阻塞等待 |
 
 ---
 
 ## 核心架构
 
+### 模块结构
+
+```
+agent/
+├── core/                  # 核心引擎
+│   ├── runner.py          # AgentRunner + 运行时配置
+│   └── presets.py         # Agent 预设(explore、analyst 等)
+│
+├── trace/                 # 执行追踪(含计划管理)
+│   ├── models.py          # Trace, Message
+│   ├── goal_models.py     # Goal, GoalTree, GoalStats
+│   ├── protocols.py       # TraceStore 接口
+│   ├── store.py           # FileSystemTraceStore 实现
+│   ├── goal_tool.py       # goal 工具(计划管理)
+│   ├── compaction.py      # Context 压缩
+│   ├── api.py             # REST API
+│   ├── websocket.py       # WebSocket API
+│   └── trace_id.py        # Trace ID 生成工具
+│
+├── tools/                 # 外部交互工具
+│   ├── registry.py        # 工具注册表
+│   ├── schema.py          # Schema 生成器
+│   ├── models.py          # ToolResult, ToolContext
+│   └── builtin/
+│       ├── file/          # 文件操作(read, write, edit, glob, grep)
+│       ├── browser/       # 浏览器自动化
+│       ├── bash.py        # 命令执行
+│       ├── sandbox.py     # 沙箱环境
+│       ├── search.py      # 网络搜索
+│       ├── webfetch.py    # 网页抓取
+│       ├── skill.py       # 技能加载
+│       └── subagent.py    # 子 Agent 统一入口(evaluate/delegate/explore)
+│
+├── memory/                # 跨会话记忆
+│   ├── models.py          # Experience, Skill
+│   ├── protocols.py       # MemoryStore 接口
+│   ├── stores.py          # 存储实现
+│   ├── skill_loader.py    # Skill 加载器
+│   └── skills/            # 内置 Skills
+│       └── core.md        # Core Skill(自动加载)
+│
+├── llm/                   # LLM 集成
+│   ├── gemini.py          # Gemini Provider
+│   ├── openrouter.py      # OpenRouter Provider
+│   └── prompts/           # Prompt 工具
+```
+
+### 职责划分
+
+| 模块 | 职责 |
+|-----|------|
+| **core/** | Agent 执行引擎 + 预设配置 |
+| **trace/** | 执行追踪 + 计划管理 |
+| **tools/** | 与外部世界交互(文件、命令、网络、浏览器) |
+| **memory/** | 跨会话知识(Skills、Experiences) |
+| **llm/** | LLM Provider 适配 |
+
 ### 三层记忆模型
 
 ```
 ┌─────────────────────────────────────────────────────────────┐
 │ Layer 3: Skills(技能库)                                     │
-│ - Markdown 文件,存储详细的能力描述                            │
-│ - 通过 skill 工具按需加载                                     │
+│ - Markdown 文件,存储领域知识和能力描述
+│ - 通过 skill 工具按需加载到对话历史
 └─────────────────────────────────────────────────────────────┘
                               │ 归纳
@@ -53,43 +97,39 @@
                               │ 提取
 ┌─────────────────────────────────────────────────────────────┐
-│ Layer 1: Task State(任务状态)
+│ Layer 1: Trace(任务状态)     
 │ - 当前任务的工作记忆                                          │
 │ - Trace + Messages 记录执行过程                               │
-│ - GoalTree 管理执行计划
+│ - Goals 管理执行计划   
 └─────────────────────────────────────────────────────────────┘
 ```
 
-**注入方式**:
-- **Skills**:通过 `skill` 工具动态加载到对话历史
-- **Experiences**:检索后注入到 system prompt
-
 ---
 
 ## 核心流程:Agent Loop
 
 ```python
-async def run(task: str, max_steps: int = 50) -> AsyncIterator[Union[Trace, Message]]:
+async def run(task: str, agent_type: str = "default") -> AsyncIterator[Union[Trace, Message]]:
     # 1. 创建 Trace
-    trace = Trace.create(mode="agent", task=task, status="in_progress")
-    await trace_store.create_trace(trace)
-    yield trace  # 返回 Trace(表示开始)
+    trace = Trace.create(
+        mode="agent",
+        task=task,
+        agent_type=agent_type,
+        model=config.model
+    )
+    await store.create_trace(trace)
+    yield trace
 
-    # 2. 加载 Skills(内置 + 自定义)
+    # 2. 加载 Skills,构建 system prompt
     skills = load_skills_from_dir(skills_dir)
-    skills_text = format_skills(skills)
-
-    # 3. 检索 Experiences,构建 system prompt
-    experiences = await search_experiences(task)
-    system_prompt = build_system_prompt(experiences, skills_text)
+    system_prompt = build_system_prompt(skills)
 
-    # 4. 初始化消息和 GoalTree
+    # 3. 初始化
     messages = [{"role": "user", "content": task}]
-    goal_tree = GoalTree(mission=task)
 
-    # 5. ReAct 循环
-    for step in range(max_steps):
-        # 注入当前计划到 system prompt
+    # 4. ReAct 循环
+    for step in range(max_iterations):
+        # 注入当前计划
         plan_text = goal_tree.to_prompt()
 
         # 调用 LLM
@@ -100,67 +140,42 @@ async def run(task: str, max_steps: int = 50) -> AsyncIterator[Union[Trace, Mess
         )
 
         # 记录 assistant Message
-        assistant_msg = Message.create(
+        await store.add_message(Message.create(
             trace_id=trace.trace_id,
             role="assistant",
-            goal_id=goal_tree.current_id,
-            content=response.content,  # text + tool_calls
-        )
-        await trace_store.add_message(assistant_msg)
+            sequence=next_seq,
+            content=response
+        ))
         yield assistant_msg
 
         # 没有工具调用,完成
         if not response.tool_calls:
             break
 
-        # 执行工具
+        # 执行工具,记录 tool Message
         for tool_call in response.tool_calls:
             result = await execute_tool(tool_call)
-
-            # 记录 tool Message
-            tool_msg = Message.create(
+            await store.add_message(Message.create(
                 trace_id=trace.trace_id,
                 role="tool",
-                goal_id=goal_tree.current_id,
-                tool_call_id=tool_call.id,
-                content=result,
-            )
-            await trace_store.add_message(tool_msg)
+                sequence=next_seq,
+                content=result
+            ))
             yield tool_msg
 
-            # 添加到消息历史
-            messages.append({"role": "assistant", "tool_calls": [tool_call]})
-            messages.append({"role": "tool", "tool_call_id": tool_call.id, "content": result})
-
-    # 6. 完成
+    # 5. 完成
     trace.status = "completed"
-    await trace_store.update_trace(trace.trace_id, status="completed")
     yield trace
 ```
 
-**关键机制**:
-- **统一返回类型**:`AsyncIterator[Union[Trace, Message]]` - 实时返回执行状态
-- **GoalTree 注入**:每次 LLM 调用前注入当前计划(过滤废弃目标,连续编号)
-- **Message 关联 Goal**:每条 Message 通过 `goal_id` 关联所属 Goal
-- **Doom Loop 检测**:跟踪最近 3 次工具调用,如果都是同一个工具且参数相同,中断循环
-
-### Sub-Agent 执行流程
-
-主 Agent 通过 `task` 工具启动 Sub-Agent。
+**实现**:`agent/core/runner.py:AgentRunner`
 
-**层级关系**:
-```
-Main Trace (primary agent)
-  └─▶ Sub Trace (parent_trace_id 指向主 Trace)
-        └─▶ Steps...
-```
+### Runner 两种调用形态
 
-**关键字段**:
-- `Trace.parent_trace_id` - 指向父 Trace
-- `Trace.agent_definition` - Sub-Agent 类型(如 "explore")
-- Sub-Agent 有独立的工具权限配置
+- `run(...)`:流式事件模式,返回 `AsyncIterator[Union[Trace, Message]]`
+- `run_result(...)`:结果模式,内部消费 `run(...)`,返回结构化结果(`status/summary/trace_id/stats/error`)
 
-**实现位置**:`agent/tools/builtin/task.py`(待实现)
+`subagent` 工具默认使用 `run_result(...)`,并通过 `trace_id` 复用已创建或继承的子 Trace。
 
 ---
 
@@ -168,270 +183,358 @@ Main Trace (primary agent)
 
 ### Trace(任务执行)
 
+一次完整的 Agent 执行。所有 Agent(主、子、人类协助)都是 Trace。
+
 ```python
 @dataclass
 class Trace:
     trace_id: str
-    mode: Literal["call", "agent"]
+    mode: Literal["call", "agent"]           # 单次调用 or Agent 模式
+
+    # Prompt 标识
+    prompt_name: Optional[str] = None
+
+    # Agent 模式特有
     task: Optional[str] = None
     agent_type: Optional[str] = None
-    status: Literal["running", "completed", "failed"] = "running"
 
-    # Sub-Agent 支持
-    parent_trace_id: Optional[str] = None      # 父 Trace ID
-    agent_definition: Optional[str] = None     # Agent 类型名称
-    spawned_by_tool: Optional[str] = None      # 启动此 Sub-Agent 的 Message ID
+    # 父子关系(Sub-Trace 特有)
+    parent_trace_id: Optional[str] = None    # 父 Trace ID
+    parent_goal_id: Optional[str] = None     # 哪个 Goal 启动的
+
+    # 状态
+    status: Literal["running", "completed", "failed"] = "running"
 
     # 统计
     total_messages: int = 0
-    total_tokens: int = 0
+    total_tokens: int = 0                    # 总 tokens(prompt + completion)
+    total_prompt_tokens: int = 0
+    total_completion_tokens: int = 0
     total_cost: float = 0.0
+    total_duration_ms: int = 0
+
+    # 进度追踪
+    last_sequence: int = 0                   # 最新 message 的 sequence
+    last_event_id: int = 0                   # 最新事件 ID(用于 WS 续传)
 
-    # 上下文
+    # 配置
     uid: Optional[str] = None
-    context: Dict[str, Any] = field(default_factory=dict)
-    current_goal_id: Optional[str] = None      # 当前焦点 goal
+    model: Optional[str] = None              # 默认模型
+    tools: Optional[List[Dict]] = None       # 工具定义(OpenAI 格式)
+    llm_params: Dict[str, Any] = {}          # LLM 参数(temperature 等)
+    context: Dict[str, Any] = {}             # 其他元数据
+
+    # 当前焦点
+    current_goal_id: Optional[str] = None
+
+    # 结果
+    result_summary: Optional[str] = None
+    error_message: Optional[str] = None
+
+    # 时间
+    created_at: datetime
+    completed_at: Optional[datetime] = None
 ```
 
-**实现**:`agent/execution/models.py:Trace`
+**实现**:`agent/trace/models.py`
+
+### Goal(目标节点)
+
+计划中的一个目标,支持层级结构。单独存储于 `goal.json`。
+
+```python
+@dataclass
+class Goal:
+    id: str                                  # 内部 ID("1", "2"...)
+    description: str
+    reason: str = ""                         # 创建理由
+    parent_id: Optional[str] = None          # 父 Goal ID
+    type: GoalType = "normal"                # normal | agent_call
+    status: GoalStatus = "pending"           # pending | in_progress | completed | abandoned
+    summary: Optional[str] = None            # 完成/放弃时的总结
+
+    # agent_call 特有(启动 Sub-Trace)
+    sub_trace_ids: Optional[List[str]] = None
+    agent_call_mode: Optional[str] = None    # explore | delegate | evaluate
+    sub_trace_metadata: Optional[Dict] = None
+
+    # 统计
+    self_stats: GoalStats                    # 自身 Messages 统计
+    cumulative_stats: GoalStats              # 包含子孙的累计统计
+
+    created_at: datetime
+```
+
+**Goal 类型**:
+- `normal` - 普通目标,由 Agent 直接执行
+- `agent_call` - 通过 subagent 工具创建的目标,会启动 Sub-Trace
+
+**agent_call 类型的 Goal**:
+- 调用 subagent 工具时自动设置
+- `agent_call_mode` 记录使用的模式(explore/delegate/evaluate)
+- `sub_trace_ids` 记录创建的所有 Sub-Trace ID
+- 状态转换:pending → in_progress(Sub-Trace 启动)→ completed(Sub-Trace 完成)
+- `summary` 包含格式化的汇总结果(explore 模式会汇总所有分支)
+
+**Goal 操作**(通过 goal 工具):
+- `add` - 添加顶层目标
+- `under` - 在指定目标下添加子目标
+- `after` - 在指定目标后添加兄弟目标
+- `focus` - 切换焦点到指定目标
+- `done` - 完成当前目标(附带 summary)
+- `abandon` - 放弃当前目标(附带原因)
+
+**实现**:`agent/trace/goal_models.py`, `agent/trace/goal_tool.py`
 
 ### Message(执行消息)
 
-对应 LLM API 的消息,加上元数据。不再有 parent_id 树结构。
+对应 LLM API 的消息,每条 Message 关联一个 Goal
 
 ```python
 @dataclass
 class Message:
-    message_id: str
+    message_id: str                          # 格式:{trace_id}-{sequence:04d}
     trace_id: str
-    role: Literal["assistant", "tool"]   # 和 LLM API 一致
-    sequence: int                        # 全局顺序
-    goal_id: str                         # 关联的 Goal 内部 ID
-    tool_call_id: Optional[str] = None   # tool 消息关联对应的 tool_call
-    content: Any = None                  # 消息内容(和 LLM API 格式一致)
-    description: str = ""                # 系统自动提取的摘要
-
-    # 元数据
-    tokens: Optional[int] = None
+    role: Literal["system", "user", "assistant", "tool"]
+    sequence: int                            # 全局顺序
+    goal_id: Optional[str] = None            # 关联的 Goal ID
+    description: str = ""                    # 系统自动生成的摘要
+    tool_call_id: Optional[str] = None
+    content: Any = None
+
+    # 统计
+    prompt_tokens: Optional[int] = None
+    completion_tokens: Optional[int] = None
     cost: Optional[float] = None
-```
+    duration_ms: Optional[int] = None
+
+    # LLM 响应信息(仅 role="assistant")
+    finish_reason: Optional[str] = None
 
-**实现**:`agent/execution/models.py:Message`
+    created_at: datetime
+```
 
-**Message 类型**:
-- `role="assistant"`:模型的一次返回(可能同时包含文本和多个 tool_calls)
-- `role="tool"`:一个工具的执行结果(通过 `tool_call_id` 关联)
+**实现**:`agent/trace/models.py`
 
 ---
 
-## 模块详情
-
-详细的模块文档请参阅:
-
-### [项目结构](./project-structure.md)
-- 框架与应用分层设计
-- 框架内置预设(agent/presets/, agent/skills/)
-- 项目层配置(./skills/, ./subagents/, ./tools/)
-- 加载优先级和覆盖机制
-
-### [Sub-Agent 机制](./sub-agents.md)
-- 数据模型:AgentDefinition、Trace 扩展
-- 内置 Sub-Agent:general、explore、analyst
-- Task Tool 实现
-- Agent Registry 和权限控制
-- 配置文件格式
-
-**使用示例**:`examples/subagent_example.py`
-
-### [Context 管理与可视化](./context-management.md)
-- GoalTree:层级目标管理(嵌套 JSON,注入 LLM)
-- Goal ID 设计:内部 ID(稳定)vs 显示序号(连续,给 LLM)
-- goal 工具:计划管理(add, after, under, done, abandon, focus)
-- 位置控制:after(同层级追加)、under(子任务拆解)
-- Plan 注入策略:完整展示当前目标及其父链
-- explore 工具:并行探索-合并,收集每个 Sub-Trace 的最后消息作为元数据
-- 回溯机制:未执行直接修改 / 已执行标记废弃+新分支
-- DAG 可视化:节点=结果,边=动作,边可展开/折叠
-- 数据结构:GoalTree + Messages(扁平列表,goal_id 关联)
-- Sub-Trace 元数据:last_message, summary, stats(用于辅助决策和可视化)
-
-### [工具系统](./tools.md)
-- 工具定义和注册
-- 双层记忆管理
-- 域名过滤、敏感数据处理
-- 最佳实践
-
-**工具接入规范**:
-- **高频&简单工具**:Python 原生实现 → `agent/tools/builtin/`
-- **复杂&低频工具**:第三方仓库 → `vendor/` + 适配器 → `agent/tools/advanced/`
-- **CLI 命令行工具**:pip 安装 → `bash_command` 调用(如 browser-use)
-
-**内置工具**(`builtin/`):
-- `read_file`, `edit_file`, `write_file` - 文件操作
-- `bash_command` - 命令执行
-- `glob_files`, `grep_content` - 文件搜索
-- `skill`, `list_skills` - 技能库管理
-
-**高级工具**(`advanced/`):
-- `webfetch` - 网页抓取(调用 opencode)
-- `lsp_diagnostics` - LSP 诊断(调用 opencode)
-
-**Skills**(`agent/skills/`):
-- `browser_use` - 浏览器自动化(包含环境配置工具)
-
-**详细设计**:参考 [`docs/tools-adapters.md`](./tools-adapters.md)
-
-**核心特性**:
-```python
-from reson_agent import tool, ToolResult, ToolContext
+## Agent 预设
 
-@tool(
-    url_patterns=["*.google.com"],
-    requires_confirmation=True
-)
-async def my_tool(arg: str, ctx: ToolContext) -> ToolResult:
-    return ToolResult(
-        title="Success",
-        output="Result content",
-        long_term_memory="Short summary"
-    )
+不同类型 Agent 的配置模板,控制工具权限和参数。
+
+```python
+@dataclass
+class AgentPreset:
+    allowed_tools: Optional[List[str]] = None  # None 表示允许全部
+    denied_tools: Optional[List[str]] = None   # 黑名单
+    max_iterations: int = 30
+    temperature: Optional[float] = None
+    description: Optional[str] = None
+
+
+AGENT_PRESETS = {
+    "default": AgentPreset(
+        allowed_tools=None,
+        max_iterations=30,
+        description="默认 Agent,拥有全部工具权限",
+    ),
+    "explore": AgentPreset(
+        allowed_tools=["read", "glob", "grep", "list_files"],
+        denied_tools=["write", "edit", "bash", "task"],
+        max_iterations=15,
+        description="探索型 Agent,只读权限,用于代码分析",
+    ),
+    "analyst": AgentPreset(
+        allowed_tools=["read", "glob", "grep", "web_search", "webfetch"],
+        denied_tools=["write", "edit", "bash", "task"],
+        temperature=0.3,
+        max_iterations=25,
+        description="分析型 Agent,用于深度分析和研究",
+    ),
+}
 ```
 
-### [多模态支持](./multimodal.md)
-- Prompt 层多模态消息构建
-- OpenAI 格式消息规范
-- Gemini Provider 适配
-- 图片资源处理
+**实现**:`agent/core/presets.py`
 
-**实现**:
-- `agent/llm/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
-- `agent/llm/gemini.py:_convert_messages_to_gemini` - 格式转换
+**用户自定义**:项目级配置 `.agent/presets.json` 可覆盖或添加预设。
+
+---
 
-**使用示例**:`examples/feature_extract/run.py`
+## 子 Trace 机制
 
-### Prompt Loader(提示加载器)
+通过 `subagent` 工具创建子 Agent 执行任务,支持三种模式。
 
-**职责**:加载和处理 `.prompt` 文件,支持多模态消息构建
+### explore 模式
+
+并行探索多个分支,适合技术选型、方案对比等场景。
+
+- 使用 `asyncio.gather()` 并行执行所有分支
+- 每个分支创建独立的 Sub-Trace
+- 只读工具权限(read_file, grep_content, glob_files)
+- 汇总所有分支结果返回
+
+### delegate 模式
+
+委派单个任务给子 Agent 执行,适合代码分析、文档生成等场景。
+
+- 创建单个 Sub-Trace
+- 完整工具权限(除 subagent 外,防止递归)
+- 支持 `continue_from` 参数继续执行
+
+### evaluate 模式
+
+评估指定 Goal 的执行结果,提供质量评估和改进建议。
+
+- 访问目标 Goal 的执行结果
+- 完整工具权限
+- 返回评估结论和建议
+
+**实现位置**:`agent/tools/builtin/subagent.py`
+
+**详细文档**:[工具系统 - Subagent 工具](./tools.md#subagent-工具)
+
+### ask_human 工具
+
+创建阻塞式 Trace,等待人类通过 IM/邮件等渠道回复。
+
+**注意**:此功能规划中,暂未实现。
+**注意**:此功能规划中,暂未实现。
 
-**文件格式**:
-```yaml
----
-model: gemini-2.5-flash
-temperature: 0.3
 ---
 
-$system$
-系统提示...
+## 工具系统
 
-$user$
-用户提示:%variable%
+### 核心概念
+
+```python
+@tool()
+async def my_tool(arg: str, ctx: ToolContext) -> ToolResult:
+    return ToolResult(
+        title="Success",
+        output="Result content",
+        long_term_memory="Short summary"  # 可选:压缩后保留的摘要
+    )
 ```
 
-**核心功能**:
-- YAML frontmatter 解析(配置)
-- `$section$` 分节语法
-- `%variable%` 参数替换
-- 多模态消息支持(图片等)
+| 类型 | 作用 |
+|------|------|
+| `@tool` | 装饰器,自动注册工具并生成 Schema |
+| `ToolResult` | 工具执行结果,支持双层记忆 |
+| `ToolContext` | 工具执行上下文,依赖注入 |
 
-**实现**:
-- `agent/llm/prompts/loader.py:load_prompt()` - 文件解析
-- `agent/llm/prompts/loader.py:get_message()` - 参数替换
-- `agent/llm/prompts/wrapper.py:SimplePrompt` - Prompt 包装器
+### 工具分类
+
+| 目录 | 工具 | 说明 |
+|-----|------|------|
+| `trace/` | goal | Agent 内部计划管理 |
+| `builtin/` | subagent | 子 Trace 创建(explore/delegate/evaluate) |
+| `builtin/file/` | read, write, edit, glob, grep | 文件操作 |
+| `builtin/browser/` | browser actions | 浏览器自动化 |
+| `builtin/` | bash, sandbox, search, webfetch, skill, ask_human | 其他工具 |
+
+### 双层记忆管理
+
+大输出(如网页抓取)只传给 LLM 一次,之后用摘要替代:
 
-**使用**:
 ```python
-prompt = SimplePrompt("task.prompt")
-messages = prompt.build_messages(text="...", images="img.png")
+ToolResult(
+    output="<10K tokens 的完整内容>",
+    long_term_memory="Extracted 10000 chars from amazon.com",
+    include_output_only_once=True
+)
 ```
 
-### Skills(技能库)
+**详细文档**:[工具系统](./tools.md)
+
+---
+
+## Skills 系统
 
-**分类**:
+### 分类
 
 | 类型 | 加载位置 | 加载时机 |
 |------|---------|---------|
-| **Core Skill** | System Prompt | Agent 启动时自动 |
+| **Core Skill** | System Prompt | Agent 启动时自动加载 |
 | **普通 Skill** | 对话消息 | 模型调用 `skill` 工具时 |
 
-**目录结构**:
+### 目录结构
 
 ```
-./agent/skills/
-├── core.md                   # Core Skill(自动加载到 System Prompt)
-└── browser_use/              # 普通 Skill(按需加载到对话消息)
-    ├── browser-use.md
-    ├── setup.py
-    └── __init__.py
-```
-
-**Core Skill**(`agent/skills/core.md`):
-- 核心系统功能:Step 管理、进度追踪
-- 框架自动注入到 System Prompt
-
-**普通 Skill**:通过 `skill` 工具动态加载
+agent/memory/skills/
+├── core.md              # Core Skill(自动加载到 System Prompt)
+└── browser_use/         # 普通 Skill(按需加载)
 
-```python
-# Agent 运行时调用
-await tools.execute("skill", {"skill_name": "browser-use"})
-# 内容注入到对话历史
+./skills/                # 项目自定义 Skills(按需加载)
 ```
 
-**实现**:
-- `agent/memory/skill_loader.py:SkillLoader` - Markdown 解析器
-- `agent/memory/skill_loader.py:load_skills_from_dir()` - Skills 自动加载(内置 + 自定义)
-- `agent/tools/builtin/skill.py:skill()` - skill 工具实现
-- `agent/tools/builtin/skill.py:list_skills()` - 列出可用 skills
-
-**详细文档**:参考 [`docs/skills.md`](./skills.md)
+**实现**:`agent/memory/skill_loader.py`
 
-### Experiences(经验库)
+**详细文档**:[Skills 使用指南](./skills.md)
 
-**存储**:PostgreSQL + pgvector
+---
 
-```sql
-CREATE TABLE experiences (
-    exp_id TEXT PRIMARY KEY,
-    scope TEXT,           -- "agent:executor" 或 "user:123"
-    condition TEXT,       -- "当遇到数据库连接超时"
-    rule TEXT,            -- "增加重试次数到5次"
-    evidence JSONB,       -- 证据(step_ids)
+## Experiences 系统
 
-    source TEXT,          -- "execution", "feedback", "manual"
-    confidence FLOAT,
-    usage_count INT,
-    success_rate FLOAT,
+从执行历史中提取的经验规则,用于指导未来任务。
 
-    embedding vector(1536),  -- 向量检索
+### 数据结构
 
-    created_at TIMESTAMP,
-    updated_at TIMESTAMP
-);
+```python
+@dataclass
+class Experience:
+    id: str
+    scope: str           # "agent:executor" 或 "user:123"
+    condition: str       # "当遇到数据库连接超时"
+    rule: str            # "增加重试次数到5次"
+    evidence: Dict       # 证据(trace_ids)
+    confidence: float
+    usage_count: int
+    success_rate: float
+    embedding: List[float]  # 向量,用于检索
 ```
 
-**检索和注入**:
+### 检索和注入
 
 ```python
 # 1. 检索相关 Experiences
 experiences = await db.query(
-    """
-    SELECT condition, rule, success_rate
-    FROM experiences
-    WHERE scope = $1
-    ORDER BY embedding <-> $2
-    LIMIT 10
-    """,
-    f"agent:{agent_type}",
-    embed(task)
+    "SELECT * FROM experiences WHERE scope = $1 ORDER BY embedding <-> $2 LIMIT 10",
+    f"agent:{agent_type}", embed(task)
 )
 
 # 2. 注入到 system prompt
-system_prompt = base_prompt + "\n\n# Learned Experiences\n" + "\n".join([
-    f"- When {e.condition}, then {e.rule} (success rate: {e.success_rate:.1%})"
-    for e in experiences
-])
+system_prompt += "\n# Learned Experiences\n" + format_experiences(experiences)
 ```
 
-**实现**:`agent/memory/stores.py:ExperienceStore`(待实现 PostgreSQL 版本)
+**存储**:PostgreSQL + pgvector
+
+**实现**:`agent/memory/stores.py:ExperienceStore`
+
+---
+
+## Context 压缩
+
+### 压缩时机
+
+Goal 完成(done)或放弃(abandon)时,将详细 Messages 替换为 Summary Message。
+
+### 压缩策略
+
+```
+Goal 状态变化
+    ↓
+收集该 Goal 下的所有 Messages
+    ↓
+生成 Summary(由 LLM 提供)
+    ↓
+替换原始 Messages 为单条 Summary Message
+    ↓
+更新统计信息
+```
+
+**实现**:`agent/trace/compaction.py`
+
+**详细文档**:[Context 管理](./context-management.md)
 
 ---
 
@@ -439,81 +542,59 @@ system_prompt = base_prompt + "\n\n# Learned Experiences\n" + "\n".join([
 
 ```python
 class TraceStore(Protocol):
-    async def save(self, trace: Trace) -> None: ...
-    async def get(self, trace_id: str) -> Trace: ...
+    async def create_trace(self, trace: Trace) -> None: ...
+    async def get_trace(self, trace_id: str) -> Trace: ...
+    async def update_trace(self, trace_id: str, **updates) -> None: ...
     async def add_message(self, message: Message) -> None: ...
     async def get_messages(self, trace_id: str) -> List[Message]: ...
     async def get_messages_by_goal(self, trace_id: str, goal_id: str) -> List[Message]: ...
-
-class ExperienceStore(Protocol):
-    async def search(self, scope: str, query: str, limit: int) -> List[Dict]: ...
-    async def add(self, exp: Dict) -> None: ...
-    async def update_stats(self, exp_id: str, success: bool) -> None: ...
-
-class SkillLoader(Protocol):
-    async def scan(self) -> List[str]:  # 返回 skill names
-        """扫描并返回所有可用的 skill 名称"""
-
-    async def load(self, name: str) -> str:  # 返回内容
-        """加载指定 skill 的 Markdown 内容"""
 ```
 
 **实现**:
-- Trace/Message 协议:`agent/execution/protocols.py`
-- Memory 协议:`agent/memory/protocols.py`
-
-**实现策略**:
-- Trace/Message: 文件系统(JSON)
-  - `FileSystemTraceStore` - 文件持久化(支持跨进程)
-- Experience: PostgreSQL + pgvector
-- Skill: 文件系统(Markdown)
+- 协议定义:`agent/trace/protocols.py`
+- 文件存储:`agent/trace/store.py:FileSystemTraceStore`
 
----
-
-## 模块结构(v0.2.2)
+### 存储结构
 
 ```
-agent/
-├── __init__.py            # 公开 API
-│
-├── core/                  # 核心引擎
-│   ├── runner.py          # AgentRunner
-│   └── config.py          # AgentConfig, CallResult
-│
-├── execution/             # 执行追踪
-│   ├── models.py          # Trace, Message
-│   ├── protocols.py       # TraceStore
-│   ├── fs_store.py        # FileSystemTraceStore
-│   ├── api.py             # RESTful API(DAG 视图)
-│   └── websocket.py       # WebSocket
-│
-├── memory/                # 记忆系统
-│   ├── models.py          # Experience, Skill
-│   ├── protocols.py       # MemoryStore, StateStore
-│   ├── stores.py          # 存储实现
-│   └── skill_loader.py    # Skill 加载器(自动加载内置 skills)
-│
-├── tools/                 # 工具系统
-│   ├── registry.py        # ToolRegistry
-│   ├── models.py          # ToolResult, ToolContext
-│   ├── schema.py          # SchemaGenerator
-│   ├── url_matcher.py     # URL 模式匹配
-│   ├── sensitive.py       # 敏感数据处理
-│   ├── builtin/           # 核心工具
-│   ├── advanced/          # 高级工具
-│   └── adapters/          # 外部集成
-│
-├── llm/                   # LLM 相关
-│   ├── gemini.py
-│   ├── openrouter.py
-│   └── prompts/
-│       ├── loader.py
-│       └── wrapper.py
+.trace/
+├── {trace_id}/
+│   ├── meta.json        # Trace 元数据(含 tools 定义)
+│   ├── goal.json        # GoalTree(mission + goals 列表)
+│   ├── events.jsonl     # 事件流(goal 变更、sub_trace 生命周期等)
+│   └── messages/        # Messages
+│       ├── {trace_id}-0001.json
+│       └── ...
-├── skills/                # 内置 Skills(自动加载)
-│   └── core.md            # 核心 skill,每次运行自动加载
-│
-└── subagents/             # Sub-agent
+└── {trace_id}@explore-{序号}-{timestamp}-001/  # 子 Trace
+    └── ...
+```
+
+**events.jsonl 说明**:
+- 记录 Trace 执行过程中的关键事件
+- 每行一个 JSON 对象,包含 event_id、event 类型、时间戳等
+- 主要事件类型:goal_added, goal_updated, sub_trace_started, sub_trace_completed
+- 用于实时监控和历史回放
+
+**Sub-Trace 目录命名**:
+- Explore: `{parent}@explore-{序号:03d}-{timestamp}-001`
+- Delegate: `{parent}@delegate-{timestamp}-001`
+- Evaluate: `{parent}@evaluate-{timestamp}-001`
+
+**meta.json 示例**:
+```json
+{
+  "trace_id": "0415dc38-...",
+  "mode": "agent",
+  "task": "分析代码结构",
+  "agent_type": "default",
+  "status": "running",
+  "model": "google/gemini-2.5-flash",
+  "tools": [...],
+  "llm_params": {"temperature": 0.3},
+  "context": {},
+  "current_goal_id": "3"
+}
 ```
 
 ---
@@ -524,78 +605,23 @@ agent/
 
 **核心决策**:
 
-1. **Skills 通过工具加载**(vs 预先注入)
-   - 按需加载,Agent 自主选择
-   - 参考 OpenCode 和 Claude API 文档
+1. **所有 Agent 都是 Trace** - 主 Agent、子 Agent、人类协助统一为 Trace,通过 `parent_trace_id` 和 `spawn_tool` 区分
 
-2. **Skills 用文件系统**(vs 数据库)
-   - 易于编辑(Markdown)
-   - 版本控制(Git)
-   - 零依赖
+2. **trace/ 模块统一管理执行状态** - 合并原 execution/ 和 goal/,包含计划管理和 Agent 内部控制工具
 
-3. **Experiences 用数据库**(vs 文件)
-   - 需要向量检索
-   - 需要统计分析
-   - 数量大,动态更新
+3. **tools/ 专注外部交互** - 文件、命令、网络、浏览器等与外部世界的交互
 
-4. **Context 管理:GoalTree + Message + DAG 可视化**
-   - GoalTree 嵌套 JSON 注入 LLM,Messages 扁平存储
-   - DAG 可视化从 GoalTree + Messages 派生
-   - 详见 [`docs/context-management.md`](./context-management.md)
+4. **Agent 预设替代 Sub-Agent 配置** - 通过 `core/presets.py` 定义不同类型 Agent 的工具权限和参数
 
 ---
 
-## Debug 工具
-
-开发调试时可通过 API 查看 DAG 可视化:
-
-```bash
-# 启动 API Server
-python api_server.py
-
-# 查看 DAG
-curl http://localhost:8000/api/traces/{trace_id}/dag
-```
-
-**实现**:`agent/execution/api.py`
-
----
-
-## 测试
-
-详见 [测试指南](./testing.md)
-
-**测试分层**:
-- **单元测试**:Agent 定义、工具系统、Trace 模型
-- **集成测试**:Sub-Agent、Trace 存储、多模块协作
-- **E2E 测试**:真实 LLM 调用(需要 API Key)
-
-**运行测试**:
-```bash
-# 单元测试
-pytest tests/ -v -m "not e2e"
-
-# 覆盖率
-pytest --cov=agent tests/ -m "not e2e"
-
-# E2E 测试(可选)
-GEMINI_API_KEY=xxx pytest tests/e2e/ -v -m e2e
-```
-
----
+## 相关文档
 
-## 核心概念速查
-
-| 概念 | 定义 | 存储 | 实现 |
-|------|------|------|------|
-| **Trace** | 一次任务执行 | 文件系统(JSON) | `execution/models.py` |
-| **Message** | 执行消息(对应 LLM 消息) | 文件系统(JSON) | `execution/models.py` |
-| **GoalTree** | 层级执行计划 | goal.json | `goal/models.py` |
-| **Goal** | 计划中的目标节点 | 嵌套在 GoalTree 中 | `goal/models.py` |
-| **Sub-Agent** | 专门化的子代理 | 独立 Trace | `tools/builtin/task.py` |
-| **AgentDefinition** | Agent 类型定义 | 配置文件/代码 | `subagents/` |
-| **Skill** | 能力描述(Markdown) | 文件系统 | `memory/skill_loader.py` |
-| **Experience** | 经验规则(条件+规则) | 数据库 + 向量 | `memory/stores.py` |
-| **Tool** | 可调用的函数 | 内存(注册表) | `tools/registry.py` |
-| **Agent Loop** | ReAct 循环 | - | `core/runner.py` |
-| **Doom Loop** | 无限循环检测 | - | `core/runner.py` |
+| 文档 | 内容 |
+|-----|------|
+| [Context 管理](./context-management.md) | Goals、压缩、Plan 注入策略 |
+| [工具系统](./tools.md) | 工具定义、注册、双层记忆 |
+| [Skills 指南](./skills.md) | Skill 分类、编写、加载 |
+| [多模态支持](./multimodal.md) | 图片、PDF 处理 |
+| [设计决策](./decisions.md) | 架构决策记录 |
+| [测试指南](./testing.md) | 测试策略和命令 |

+ 0 - 306
docs/REFACTOR_SUMMARY.md

@@ -1,306 +0,0 @@
-# 重构总结:移除 Branch 概念,统一 Trace 模型
-
-> 完成时间:2026-02-04
->
-> 本次重构移除了旧的 branch 概念,采用统一的 Trace 模型,每个 Sub-Agent 都是完全独立的 Trace。
-
----
-
-## 重构目标
-
-将基于 branch 的设计重构为基于独立 Trace 的设计:
-- ❌ 旧设计:`.trace/{trace_id}/branches/{branch_id}/`
-- ✅ 新设计:`.trace/{parent_id}@{mode}-{timestamp}-{seq}/`
-
----
-
-## 已完成工作
-
-### ✅ Phase 1: 核心数据结构调整
-
-#### 1.1 Trace ID 生成器
-- ✅ 创建 `agent/execution/trace_id.py`
-  - `generate_trace_id()` - 生成主 Trace UUID
-  - `generate_sub_trace_id(parent_id, mode)` - 生成 Sub-Trace ID
-  - `parse_parent_trace_id(trace_id)` - 解析父 Trace ID
-  - `is_sub_trace(trace_id)` - 判断是否为 Sub-Trace
-  - `extract_mode(trace_id)` - 提取运行模式
-  - 线程安全的序号计数器
-- ✅ 创建单元测试 `tests/test_trace_id.py`
-- ✅ 所有测试通过
-
-#### 1.2 Trace 模型更新 (`agent/execution/models.py`)
-- ✅ 添加 `parent_trace_id: Optional[str]` 字段
-- ✅ 添加 `parent_goal_id: Optional[str]` 字段
-- ✅ 更新 `to_dict()` 方法
-- ✅ 确认 `context: Dict[str, Any]` 字段存在
-
-#### 1.3 Message 模型更新 (`agent/execution/models.py`)
-- ✅ **移除** `branch_id` 字段
-- ✅ 更新 `create()` 方法签名
-- ✅ 更新 `to_dict()` 方法
-- ✅ 文档字符串更新
-
-#### 1.4 Goal 模型更新 (`agent/goal/models.py`)
-- ✅ **移除** `branch_id` 字段
-- ✅ **移除** `branch_ids` 字段
-- ✅ 将 `GoalType` 从 `"explore_start" | "explore_merge"` 改为 `"normal" | "agent_call"`
-- ✅ 添加 `sub_trace_ids: Optional[List[str]]` 字段
-- ✅ 添加 `agent_call_mode: Optional[str]` 字段
-- ✅ **移除** `explore_start_id`, `merge_summary`, `selected_branch` 字段
-- ✅ 更新 `to_dict()` 和 `from_dict()` 方法
-
-#### 1.5 移除 BranchContext
-- ✅ 从 `agent/goal/models.py` 删除 `BranchContext` 类
-- ✅ 从 `agent/goal/__init__.py` 移除导出
-- ✅ **移除** `BranchStatus` 类型定义
-
-### ✅ Phase 2: 存储层重构
-
-#### 2.1 FileSystem Store 更新 (`agent/execution/fs_store.py`)
-
-**移除的方法(11 个)**:
-- ✅ `_get_branches_dir()`
-- ✅ `_get_branch_dir()`
-- ✅ `_get_branch_meta_file()`
-- ✅ `_get_branch_goal_file()`
-- ✅ `_get_branch_messages_dir()`
-- ✅ `create_branch()`
-- ✅ `get_branch()`
-- ✅ `get_branch_goal_tree()`
-- ✅ `update_branch_goal_tree()`
-- ✅ `update_branch()`
-- ✅ `list_branches()`
-
-**更新的方法**:
-- ✅ `create_trace()` - 不再创建 `branches/` 目录
-- ✅ `add_message()` - 移除 `branch_id` 逻辑
-- ✅ `_update_goal_stats()` - 移除 `branch_id` 逻辑
-- ✅ `_get_affected_goals()` - 移除 `branch_id` 逻辑
-- ✅ `get_trace_messages()` - 移除 `branch_id` 参数
-- ✅ `get_messages_by_goal()` - 移除 `branch_id` 参数
-- ✅ `update_message()` - 移除 `branch_id` 逻辑
-- ✅ `get_message()` - 不再扫描 `branches/` 目录
-
-**更新的导入**:
-- ✅ 从 `from agent.goal.models import GoalTree, Goal, BranchContext, GoalStats`
-  改为 `from agent.goal.models import GoalTree, Goal, GoalStats`
-
-#### 2.2 TraceStore 协议更新 (`agent/execution/protocols.py`)
-
-**移除的方法签名(6 个)**:
-- ✅ `create_branch()`
-- ✅ `get_branch()`
-- ✅ `get_branch_goal_tree()`
-- ✅ `update_branch_goal_tree()`
-- ✅ `update_branch()`
-- ✅ `list_branches()`
-
-**更新的方法签名**:
-- ✅ `get_trace_messages()` - 移除 `branch_id` 参数
-- ✅ `get_messages_by_goal()` - 移除 `branch_id` 参数
-
-**更新的导入**:
-- ✅ 从 `from agent.goal.models import GoalTree, Goal, BranchContext`
-  改为 `from agent.goal.models import GoalTree, Goal`
-
----
-
-## 新的 Trace ID 方案
-
-### 主 Trace
-```
-2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d
-```
-- 标准 UUID 格式
-- 36 字符长度
-
-### Sub-Trace
-```
-2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001
-```
-- 格式:`{parent_id}@{mode}-{timestamp}-{seq}`
-- 使用**完整 UUID**作为前缀(不截断)
-- 避免 ID 冲突风险
-- 约 65-70 字符长度
-
-### 优势
-
-✅ **零碰撞风险**:使用完整 UUID
-✅ **可精确追溯**:从 Sub-Trace ID 直接看到完整父 ID
-✅ **无需冲突检测**:实现简单,不依赖外部状态
-✅ **信息完整**:一眼看出触发者、模式、时间
-✅ **线程安全**:序号生成器使用锁保护
-
----
-
-## 新的存储结构
-
-### 旧结构(已废弃)
-```
-.trace/
-├── abc123/
-│   ├── meta.json
-│   ├── goal.json
-│   ├── messages/
-│   ├── branches/        ❌ 已移除
-│   │   ├── A/
-│   │   └── B/
-│   └── events.jsonl
-```
-
-### 新结构(当前)
-```
-.trace/
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/           # 主 Trace
-│   ├── meta.json                                   # parent_trace_id: null
-│   ├── goal.json
-│   ├── messages/
-│   └── events.jsonl
-│
-├── 2f8d3a1c...@explore-20260204220012-001/        # Sub-Trace A
-│   ├── meta.json                                   # parent_trace_id: "2f8d3a1c..."
-│   ├── goal.json                                   # 独立的 GoalTree
-│   ├── messages/
-│   └── events.jsonl
-│
-└── 2f8d3a1c...@explore-20260204220012-002/        # Sub-Trace B
-    └── ...
-```
-
----
-
-## 测试验证
-
-### ✅ 导入测试
-```bash
-python3 -c "from agent.execution.fs_store import FileSystemTraceStore"
-# ✅ 成功
-```
-
-### ✅ 功能测试
-- ✅ Trace 模型创建(主 + 子)
-- ✅ Sub-Trace ID 生成
-- ✅ Message 创建(无 branch_id)
-- ✅ Goal 创建(有 sub_trace_ids)
-- ✅ 父子关系设置
-
----
-
-## 待完成工作
-
-### 🔄 Phase 3: 添加 Goal 事件推送
-- [ ] 在 `fs_store.py` 中添加 `goal_added` 事件
-- [ ] 在 `fs_store.py` 中添加 `goal_updated` 事件
-- [ ] 在 `fs_store.py` 中添加 `goal_completed` 事件
-
-### ✅ Phase 4: 工具实现
-- ✅ 实现 `agent/goal/explore.py` - explore 工具
-- ✅ 实现 `agent/goal/delegate.py` - delegate 工具
-- ✅ 两个工具都会推送 `sub_trace_started` 和 `sub_trace_completed` 事件
-
-### ✅ Phase 5: API 层更新
-- ✅ 更新 `agent/execution/api.py` REST 端点
-  - 移除 `BranchDetailResponse` 模型
-  - 更新 `TraceDetailResponse` 使用 `sub_traces`
-  - 更新 `get_trace()` 端点查询 Sub-Traces
-  - 移除 `branch_id` 参数
-  - 移除 `/branches/{branch_id}` 端点
-- ✅ 更新 `agent/execution/websocket.py` 事件格式
-  - 更新事件类型文档(移除 branch 事件,添加 Sub-Trace 事件)
-  - 更新 `connected` 事件:查询 Sub-Traces 而非 branches
-  - 移除 `broadcast_branch_started()`、`broadcast_branch_goal_added()`、`broadcast_branch_completed()`、`broadcast_explore_completed()` 函数
-  - 添加 `broadcast_sub_trace_started()` 和 `broadcast_sub_trace_completed()` 函数
-
-### ✅ Phase 7: 清理和文档
-- ✅ 更新 `docs/trace-api.md` - 完整重写,移除所有 branch 引用
-- ✅ 更新 `docs/decisions.md` - 更新 explore 工具描述
-- ✅ 更新 `docs/context-comparison.md` - 更新执行流程描述
-- ✅ 更新 `frontend/API.md` - 更新 Trace ID 格式,移除 branch_id 字段
-- ✅ 清理 `agent/execution/protocols.py` - 移除注释中的 branch 引用
-- ✅ 代码中的 branch 引用已全部清理(explore.py 中的 branches 是合理的参数名)
-
-### ⏭️ 跳过的工作
-- **Phase 6**: 数据迁移(按用户要求跳过)
-
----
-
-## 文件变更汇总
-
-### 新增文件(4 个)
-- ✅ `agent/execution/trace_id.py` - Trace ID 生成工具
-- ✅ `tests/test_trace_id.py` - 单元测试
-- ✅ `agent/goal/explore.py` - explore 工具实现
-- ✅ `agent/goal/delegate.py` - delegate 工具实现
-
-### 更新文件(9 个)
-- ✅ `agent/execution/models.py` - Trace 和 Message 模型
-- ✅ `agent/goal/models.py` - Goal 模型
-- ✅ `agent/goal/__init__.py` - 导出列表
-- ✅ `agent/execution/fs_store.py` - 存储实现
-- ✅ `agent/execution/protocols.py` - 协议定义
-- ✅ `agent/execution/api.py` - REST API 端点
-- ✅ `agent/execution/websocket.py` - WebSocket 事件
-- ✅ `docs/context-management.md` - 设计文档
-- ✅ `docs/refactor-plan.md` - 重构计划
-
-### 删除的类/方法汇总
-- ❌ `BranchContext` 类
-- ❌ `BranchStatus` 类型
-- ❌ 11 个 branch 相关的存储方法
-- ❌ 6 个 branch 相关的协议方法
-- ❌ `Message.branch_id` 字段
-- ❌ `Goal.branch_id` 字段
-- ❌ `Goal.branch_ids` 字段
-- ❌ `Goal.explore_start_id` 字段
-- ❌ `Goal.merge_summary` 字段
-- ❌ `Goal.selected_branch` 字段
-
----
-
-## 影响范围
-
-### ✅ 已处理
-- ✅ 核心数据模型
-- ✅ 存储层接口和实现
-- ✅ Trace ID 生成工具
-- ✅ Goal 事件推送系统
-- ✅ explore 和 delegate 工具
-- ✅ REST API 端点
-- ✅ WebSocket 事件系统
-- ✅ 基本功能测试
-
-### ⚠️ 需要注意
-- 现有的 `.trace/` 目录中的旧数据(包含 `branches/`)如需使用,需要手动处理
-- 任何外部代码引用 `BranchContext` 或 `branch_id` 的地方需要更新
-- WebSocket 客户端需要更新以使用新的事件格式(`sub_trace_started`/`sub_trace_completed` 替代旧的 branch 事件)
-
----
-
-## 总结
-
-本次重构已全面完成从 branch 概念到统一 Trace 模型的迁移:
-
-1. ✅ **概念统一**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构
-2. ✅ **ID 简洁**:每个 Trace 内部独立编号(1, 2, 3...)
-3. ✅ **完全隔离**:每个 Trace 有独立的 GoalTree、Message List
-4. ✅ **零冲突**:使用完整 UUID 避免 ID 冲突
-5. ✅ **易于分布式**:每个 Trace 可以独立运行、存储
-6. ✅ **事件系统**:Goal 变更自动推送 WebSocket 事件,支持级联完成
-7. ✅ **工具完整**:explore 和 delegate 工具已实现并正常工作
-8. ✅ **API 完善**:REST 和 WebSocket API 均已更新为新格式
-
-### 已完成的 Phase(1-5)
-
-- ✅ **Phase 1**: 核心数据结构调整
-- ✅ **Phase 2**: 存储层重构
-- ✅ **Phase 3**: Goal 事件推送
-- ✅ **Phase 4**: 工具实现(explore & delegate)
-- ✅ **Phase 5**: API 层更新(REST & WebSocket)
-
-### 跳过的 Phase(按用户要求)
-
-- ⏭️ **Phase 6**: 数据迁移(用户要求跳过)
-- ⏭️ **Phase 7**: 文档清理(可选)
-
-重构已全部完成,系统已经可以正常使用新的统一 Trace 模型。

+ 0 - 618
docs/cloud_browser_guide.md

@@ -1,618 +0,0 @@
-# Browser-Use 云浏览器模式使用指南
-
-## 目录
-
-- [简介](#简介)
-- [云浏览器 vs 本地浏览器](#云浏览器-vs-本地浏览器)
-- [环境配置](#环境配置)
-- [快速开始](#快速开始)
-- [核心概念](#核心概念)
-- [示例代码](#示例代码)
-- [高级用法](#高级用法)
-- [常见问题](#常见问题)
-- [最佳实践](#最佳实践)
-
----
-
-## 简介
-
-Browser-Use 云浏览器模式允许你在云端运行浏览器自动化任务,无需在本地安装 Chrome/Chromium。这对于以下场景特别有用:
-
-- 🚀 **无头服务器部署** - 在没有图形界面的服务器上运行
-- 🌍 **分布式爬虫** - 轻松扩展到多个云浏览器实例
-- 💻 **跨平台一致性** - 避免本地环境差异
-- 🔒 **安全隔离** - 浏览器运行在隔离的云环境中
-- 📊 **资源优化** - 不占用本地计算资源
-
----
-
-## 云浏览器 vs 本地浏览器
-
-| 特性         | 云浏览器               | 本地浏览器               |
-| ------------ | ---------------------- | ------------------------ |
-| **安装要求** | 无需安装 Chrome        | 需要安装 Chrome/Chromium |
-| **运行环境** | 云端                   | 本地机器                 |
-| **资源占用** | 不占用本地资源         | 占用本地 CPU/内存        |
-| **网络延迟** | 可能有轻微延迟         | 无网络延迟               |
-| **成本**     | 需要 API 配额          | 免费                     |
-| **调试**     | 提供 Live URL 实时查看 | 可以直接看到浏览器窗口   |
-| **适用场景** | 服务器部署、分布式任务 | 本地开发、调试           |
-
----
-
-## 环境配置
-
-### 1. 安装依赖
-
-```bash
-# 安装 browser-use
-pip install browser-use
-
-# 安装云浏览器所需的额外依赖
-pip install python-socks
-```
-
-### 2. 获取 API Key
-
-1. 访问 [Browser-Use 官网](https://browser-use.com)
-2. 注册账号并获取 API Key
-3. 将 API Key 添加到 `.env` 文件
-
-### 3. 配置环境变量
-
-在项目根目录的 `.env` 文件中添加:
-
-```bash
-# Browser-Use 云浏览器 API Key
-BROWSER_USE_API_KEY=your_api_key_here
-
-# 可选:如果需要使用 LLM 功能
-GOOGLE_API_KEY=your_google_api_key
-GEMINI_API_KEY=your_gemini_api_key
-```
-
----
-
-## 快速开始
-
-### 最简单的云浏览器示例
-
-```python
-import asyncio
-import os
-from dotenv import load_dotenv
-from agent.tools.builtin.browser.baseClass import (
-    init_browser_session,
-    cleanup_browser_session,
-    navigate_to_url,
-)
-
-# 加载环境变量
-load_dotenv()
-
-async def main():
-    # 初始化云浏览器(关键:use_cloud=True)
-    browser, tools = await init_browser_session(
-        headless=True,
-        use_cloud=True,  # 启用云浏览器
-    )
-
-    print("✅ 云浏览器已启动")
-
-    # 访问网页
-    result = await navigate_to_url("https://www.baidu.com")
-    print(f"导航结果: {result.title}")
-
-    # 清理
-    await cleanup_browser_session()
-    print("🧹 浏览器已关闭")
-
-if __name__ == "__main__":
-    asyncio.run(main())
-```
-
-### 运行示例
-
-```bash
-# 运行默认示例(示例 1)
-python examples/cloud_browser_example.py
-
-# 运行指定示例
-python examples/cloud_browser_example.py --example 2
-
-# 运行所有示例
-python examples/cloud_browser_example.py --all
-```
-
----
-
-## 核心概念
-
-### 1. 初始化云浏览器会话
-
-```python
-from agent.tools.builtin.browser.baseClass import init_browser_session
-
-# 云浏览器模式
-browser, tools = await init_browser_session(
-    headless=True,      # 云浏览器通常使用无头模式
-    use_cloud=True,     # 关键参数:启用云浏览器
-)
-```
-
-**参数说明:**
-
-- `headless`: 是否使用无头模式(云浏览器推荐 True)
-- `use_cloud`: 是否使用云浏览器(True=云浏览器,False=本地浏览器)
-- `browser_profile`: 可选,预设 cookies、localStorage 等
-- `**kwargs`: 其他 BrowserSession 参数
-
-### 2. 使用 BrowserProfile 预设配置
-
-```python
-from browser_use import BrowserProfile
-
-# 创建配置文件
-profile = BrowserProfile(
-    # 预设 cookies
-    cookies=[
-        {
-            "name": "session_id",
-            "value": "abc123",
-            "domain": ".example.com",
-            "path": "/",
-        }
-    ],
-    # 预设 localStorage
-    local_storage={
-        "example.com": {
-            "token": "your_token",
-            "user_id": "12345",
-        }
-    },
-    # 自定义 User-Agent
-    user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
-)
-
-# 使用配置初始化浏览器
-browser, tools = await init_browser_session(
-    use_cloud=True,
-    browser_profile=profile,
-)
-```
-
-### 3. 可用的工具函数
-
-项目提供了丰富的浏览器操作工具,所有工具都支持云浏览器:
-
-#### 导航类工具
-
-```python
-# 导航到 URL
-await navigate_to_url("https://example.com")
-
-# 在新标签页打开
-await navigate_to_url("https://example.com", new_tab=True)
-
-# 搜索
-await search_web("Python async", engine="google")
-
-# 返回上一页
-await go_back()
-
-# 等待
-await wait(seconds=3)
-```
-
-#### 元素交互工具
-
-```python
-# 点击元素(需要先获取元素索引)
-await click_element(index=5)
-
-# 输入文本
-await input_text(index=0, text="Hello World", clear=True)
-
-# 发送按键
-await send_keys("Enter")
-await send_keys("Control+A")
-
-# 上传文件
-await upload_file(index=7, path="/path/to/file.pdf")
-```
-
-#### 页面操作工具
-
-```python
-# 滚动页面
-await scroll_page(down=True, pages=2.0)
-
-# 查找文本
-await find_text("Privacy Policy")
-
-# 截图
-await screenshot()
-
-# 获取页面 HTML
-html_result = await get_page_html()
-
-# 获取可交互元素
-selector_result = await get_selector_map()
-
-# 执行 JavaScript
-result = await evaluate("document.title")
-```
-
-#### 标签页管理
-
-```python
-# 切换标签页
-await switch_tab(tab_id="a3f2")
-
-# 关闭标签页
-await close_tab(tab_id="a3f2")
-```
-
-#### 文件操作
-
-```python
-# 写入文件
-await write_file("output.txt", "Hello World")
-
-# 读取文件
-content = await read_file("input.txt")
-
-# 替换文件内容
-await replace_file("config.txt", "old_value", "new_value")
-```
-
----
-
-## 示例代码
-
-### 示例 1: 基础导航操作
-
-```python
-async def example_basic_navigation():
-    """访问网页并获取页面信息"""
-    browser, tools = await init_browser_session(use_cloud=True)
-
-    # 导航到百度
-    await navigate_to_url("https://www.baidu.com")
-    await wait(2)
-
-    # 获取页面标题
-    title_result = await evaluate("document.title")
-    print(f"页面标题: {title_result.output}")
-
-    # 截图
-    await screenshot()
-
-    await cleanup_browser_session()
-```
-
-### 示例 2: 搜索和内容提取
-
-```python
-async def example_search():
-    """使用搜索引擎并提取内容"""
-    browser, tools = await init_browser_session(use_cloud=True)
-
-    # 搜索
-    await search_web("Python async programming", engine="google")
-    await wait(3)
-
-    # 获取页面 HTML
-    html_result = await get_page_html()
-    print(f"HTML 长度: {len(html_result.metadata.get('html', ''))} 字符")
-
-    # 获取可交互元素
-    selector_result = await get_selector_map()
-    print(selector_result.output)
-
-    await cleanup_browser_session()
-```
-
-### 示例 3: 使用 BrowserProfile
-
-```python
-async def example_with_profile():
-    """使用预设配置"""
-    from browser_use import BrowserProfile
-
-    # 创建配置
-    profile = BrowserProfile(
-        cookies=[{
-            "name": "test_cookie",
-            "value": "test_value",
-            "domain": ".example.com",
-            "path": "/",
-        }],
-        user_agent="Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7)",
-    )
-
-    # 使用配置初始化
-    browser, tools = await init_browser_session(
-        use_cloud=True,
-        browser_profile=profile,
-    )
-
-    # 访问网页
-    await navigate_to_url("https://httpbin.org/headers")
-    await wait(2)
-
-    # 检查 User-Agent
-    ua_result = await evaluate("navigator.userAgent")
-    print(f"User-Agent: {ua_result.output}")
-
-    await cleanup_browser_session()
-```
-
-### 示例 4: 表单交互
-
-```python
-async def example_form_interaction():
-    """填写表单"""
-    browser, tools = await init_browser_session(use_cloud=True)
-
-    # 访问表单页面
-    await navigate_to_url("https://httpbin.org/forms/post")
-    await wait(2)
-
-    # 获取页面元素
-    selector_result = await get_selector_map()
-    print(f"找到 {selector_result.long_term_memory}")
-
-    # 根据实际页面结构填写表单
-    # await input_text(index=0, text="用户名")
-    # await input_text(index=1, text="密码")
-    # await click_element(index=2)  # 提交按钮
-
-    await cleanup_browser_session()
-```
-
-### 示例 5: 多标签页操作
-
-```python
-async def example_multi_tab():
-    """管理多个标签页"""
-    browser, tools = await init_browser_session(use_cloud=True)
-
-    # 第一个标签页
-    await navigate_to_url("https://www.baidu.com")
-    await wait(2)
-
-    # 新标签页
-    await navigate_to_url("https://www.google.com", new_tab=True)
-    await wait(2)
-
-    # 获取当前页面信息
-    title_result = await evaluate("document.title")
-    print(f"当前标题: {title_result.output}")
-
-    await cleanup_browser_session()
-```
-
----
-
-## 高级用法
-
-### 1. 实时查看云浏览器
-
-云浏览器启动时会输出一个 Live URL,你可以在浏览器中打开这个 URL 实时查看云浏览器的操作:
-
-```
-INFO [cloud] 🔗 Live URL: https://live.browser-use.com?wss=https%3A%2F%2F...
-```
-
-复制这个 URL 到浏览器中打开,即可实时查看云浏览器的操作。
-
-### 2. 错误处理
-
-```python
-async def example_with_error_handling():
-    browser = None
-    try:
-        browser, tools = await init_browser_session(use_cloud=True)
-
-        result = await navigate_to_url("https://example.com")
-        if result.error:
-            print(f"导航失败: {result.error}")
-            return
-
-        # 其他操作...
-
-    except Exception as e:
-        print(f"发生错误: {str(e)}")
-    finally:
-        if browser:
-            await cleanup_browser_session()
-```
-
-### 3. 会话复用
-
-```python
-# 全局会话会自动复用
-# 第一次调用会创建新会话
-browser1, tools1 = await init_browser_session(use_cloud=True)
-
-# 后续调用会返回同一个会话
-browser2, tools2 = await init_browser_session(use_cloud=True)
-
-# browser1 和 browser2 是同一个对象
-assert browser1 is browser2
-```
-
-### 4. 强制终止浏览器
-
-```python
-from agent.tools.builtin.browser.baseClass import kill_browser_session
-
-# 优雅关闭(推荐)
-await cleanup_browser_session()
-
-# 强制终止(用于异常情况)
-await kill_browser_session()
-```
-
----
-
-## 常见问题
-
-### Q1: 云浏览器启动失败
-
-**问题:** `python-socks is required to use a SOCKS proxy`
-
-**解决:**
-
-```bash
-pip install python-socks
-```
-
-### Q2: API Key 无效
-
-**问题:** `未找到 BROWSER_USE_API_KEY`
-
-**解决:**
-
-1. 确保 `.env` 文件在项目根目录
-2. 确保 API Key 格式正确
-3. 确保代码中调用了 `load_dotenv()`
-
-### Q3: 云浏览器连接超时
-
-**问题:** 云浏览器启动后无法连接
-
-**解决:**
-
-1. 检查网络连接
-2. 检查防火墙设置
-3. 尝试使用代理
-
-### Q4: 如何切换回本地浏览器
-
-**解决:**
-
-```python
-# 使用本地浏览器
-browser, tools = await init_browser_session(
-    use_cloud=False,  # 或者不传这个参数,默认是 False
-)
-```
-
-### Q5: 云浏览器的配额限制
-
-**问题:** API 配额用完了怎么办
-
-**解决:**
-
-1. 查看 Browser-Use 官网的定价计划
-2. 升级到更高的配额
-3. 优化代码,减少不必要的浏览器操作
-
----
-
-## 最佳实践
-
-### 1. 合理使用 wait
-
-```python
-# ❌ 不好:固定等待时间太长
-await wait(10)
-
-# ✅ 好:根据实际需要调整等待时间
-await wait(2)  # 页面加载
-await wait(1)  # 动画完成
-```
-
-### 2. 及时清理会话
-
-```python
-# ✅ 使用 try-finally 确保清理
-try:
-    browser, tools = await init_browser_session(use_cloud=True)
-    # 操作...
-finally:
-    await cleanup_browser_session()
-```
-
-### 3. 使用 BrowserProfile 避免重复登录
-
-```python
-# ✅ 预设 cookies,避免每次都登录
-profile = BrowserProfile(
-    cookies=[
-        # 从之前的会话中保存的 cookies
-    ]
-)
-browser, tools = await init_browser_session(
-    use_cloud=True,
-    browser_profile=profile,
-)
-```
-
-### 4. 批量操作时复用会话
-
-```python
-# ✅ 一次会话处理多个任务
-browser, tools = await init_browser_session(use_cloud=True)
-
-for url in urls:
-    await navigate_to_url(url)
-    # 处理页面...
-
-await cleanup_browser_session()
-```
-
-### 5. 使用 Live URL 调试
-
-```python
-# 开发时启用 Live URL 查看
-# 云浏览器启动时会自动输出 Live URL
-# 复制到浏览器中打开即可实时查看
-```
-
----
-
-## 性能优化建议
-
-1. **减少不必要的等待**
-   - 使用最小必要的等待时间
-   - 避免固定的长时间等待
-
-2. **批量处理**
-   - 在一个会话中处理多个任务
-   - 避免频繁创建/销毁会话
-
-3. **合理使用截图**
-   - 只在必要时截图
-   - 截图会增加网络传输时间
-
-4. **优化元素定位**
-   - 使用 `get_selector_map` 一次性获取所有元素
-   - 避免重复查询相同元素
-
----
-
-## 技术支持
-
-- **Browser-Use 官方文档**: https://docs.browser-use.com
-- **GitHub Issues**: https://github.com/browser-use/browser-use
-- **项目内部文档**: 查看 `agent/tools/builtin/baseClass.py` 的注释
-
----
-
-## 更新日志
-
-### v1.0.0 (2026-01-30)
-
-- ✅ 初始版本
-- ✅ 支持云浏览器模式
-- ✅ 提供 5 个完整示例
-- ✅ 完整的使用文档
-
----
-
-## 许可证
-
-本项目遵循项目主许可证。

+ 0 - 1476
docs/context-management.md

@@ -1,1476 +0,0 @@
-# Context 管理与执行计划
-
-> 本文档描述 Agent 的 Context 管理、执行计划和 Sub-Agent 机制。
-
----
-
-## 设计目标
-
-1. **自主长程执行**:Agent 能独立执行复杂任务,无需人工频繁干预
-2. **有效的 Context 管理**:长任务中保持关键信息,压缩次要细节
-3. **支持探索和回溯**:能尝试多种方案,失败时能有效回溯
-4. **统一的 Agent 模型**:主 Agent 和 Sub-Agent 使用相同的 Trace 结构
-5. **简单的工具接口**:LLM 只需理解少量简单工具,复杂逻辑由系统处理
-
----
-
-## 参考方案:OpenCode 的 Context 管理
-
-### 核心架构
-
-```
-┌─────────────────┐
-│   plan.md       │  ← 文本格式的计划(TODO 列表)
-└─────────────────┘
-         ↓
-┌─────────────────┐
-│  线性 Message   │  ← 对话历史
-│     List        │
-└─────────────────┘
-         ↓
-┌─────────────────┐
-│  Prune + Full   │  ← 两阶段压缩
-│   Compaction    │
-└─────────────────┘
-         ↓
-┌─────────────────┐
-│   Sub-Agent     │  ← 隔离大任务
-└─────────────────┘
-```
-
-### 1. Message 管理
-
-**数据结构**:
-- User Message: 用户输入,包含 TextPart, FilePart, CompactionPart, SubtaskPart 等
-- Assistant Message: LLM 输出,包含 TextPart, ToolPart, ReasoningPart 等
-- 每个 Message 包含多个 Part,支持流式处理
-
-**存储**:
-```
-Storage Key:
-["message", sessionID, messageID] -> MessageV2.Info
-["part", messageID, partID] -> MessageV2.Part
-```
-
-### 2. Context 压缩机制
-
-**两阶段压缩**:
-
-**阶段 1: Prune(清理旧工具输出)**
-```
-参数:
-- PRUNE_MINIMUM = 20,000 tokens(最少删除量)
-- PRUNE_PROTECT = 40,000 tokens(保护阈值)
-- PRUNE_PROTECTED_TOOLS = ["skill"](不删除的工具)
-
-流程:
-1. 从后向前遍历 messages
-2. 跳过最后 2 轮 turns(保护最近交互)
-3. 跳过已有 summary 标记的 assistant 消息
-4. 收集已完成工具调用的输出
-5. 当累计 > PRUNE_PROTECT 时,标记为已 compacted
-6. 当删除量 > PRUNE_MINIMUM 时,执行删除
-```
-
-**阶段 2: Full Compaction(上下文总结)**
-```
-流程:
-1. 创建新的 assistant 消息(summary=true)
-2. 调用 "compaction" 专用 agent
-3. 提示词: "Provide a detailed prompt for continuing our conversation..."
-4. 返回 "continue" 时自动创建新的 user 消息继续
-```
-
-### 3. Plan/Todo 机制
-
-**数据结构**:
-```typescript
-Todo.Info = {
-  id: string
-  content: string      // 任务描述
-  status: string       // pending | in_progress | completed | cancelled
-  priority: string     // high | medium | low
-}
-```
-
-**存储**:文件系统(.opencode/plans/xxx.md)或 Storage
-
-### 4. Sub-Agent 机制
-
-**Agent Mode**:
-- `primary`: 主代理,执行工具
-- `subagent`: 子代理,独立 context,结果汇总回主会话
-
-**内置 Sub-Agents**:
-- `general`: 通用代理,可并行执行多个任务
-- `explore`: 代码探索专用,仅允许查询工具
-- `compaction`: 上下文总结专用
-
-**Subtask 执行**:
-1. 创建 SubtaskPart
-2. 子代理独立处理(独立 message list)
-3. 结果通过 "The following tool was executed by the user" 汇总
-
-### 5. 优缺点分析
-
-**优点**:
-- 简单成熟,经过大量验证
-- Plan 和执行分离,用户可直接编辑 plan.md
-- Sub-agent 有效隔离大任务的 context
-
-**局限**:
-- Plan 是纯文本,与执行记录无结构化关联
-- 压缩是"事后"的,等满了再压缩
-- 回溯能力有限,无法精确回到某个状态
-- 不支持并行探索-合并的模式
-
----
-
-## 我们的方案
-
-### 核心思路
-
-```
-基于 OpenCode 方案,但采用更统一的架构:
-1. 结构化 Plan(goal 工具)
-2. 统一的 Trace 模型(主 Agent 和 Sub-Agent 都是 Trace)
-3. 并行探索-合并(explore 工具,启动独立的 Sub-Traces)
-4. 精确回溯(abandon + context 压缩)
-```
-
-### 架构
-
-```
-┌─────────────────────────────────────────────┐
-│            每个 Agent = 一个 Trace            │
-│  主 Trace 和 Sub-Trace 使用相同的数据结构      │
-└─────────────────────────────────────────────┘
-                      │
-         ┌────────────┴────────────┐
-         ↓                         ↓
-┌─────────────────┐      ┌─────────────────┐
-│   GoalTree      │      │   Messages      │
-│   (层级目标)     │      │   (扁平列表)     │
-│   goal 工具维护  │      │   goal_id 关联   │
-└─────────────────┘      └─────────────────┘
-         │                         │
-         ↓                         ↓
-┌─────────────────┐      ┌─────────────────┐
-│  工具调用        │      │  Sub-Traces     │
-│  explore/       │      │  完全独立的      │
-│  delegate       │──────▶  Trace 实例      │
-└─────────────────┘      └─────────────────┘
-         │
-         ↓
-┌─────────────────────────────────────────────┐
-│              DAG 可视化(派生视图)             │
-│  从 GoalTree + Messages 生成                  │
-│  节点 = 结果/里程碑,边 = 动作/执行过程         │
-│  边可展开/折叠,Sub-Trace 作为折叠边显示        │
-└─────────────────────────────────────────────┘
-```
-
-### 核心概念:统一的 Trace 模型
-
-**关键设计**:每个 Agent(主 Agent 或 Sub-Agent)都是一个完整的 Trace。
-
-```python
-# 主 Agent
-Trace(trace_id="2f8d3a1c", mode="agent", task="实现用户认证")
-
-# Sub-Agent(并行探索)
-Trace(trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001", parent_trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", agent_type="explore", task="JWT 方案")
-Trace(trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002", parent_trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", agent_type="explore", task="Session 方案")
-
-# Sub-Agent(单线委托)
-Trace(trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220030-001", parent_trace_id="2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", agent_type="delegate", task="实现具体功能")
-```
-
-**优点**:
-- **概念统一**:不需要额外的 SubAgentContext/BranchContext 概念
-- **ID 简洁**:每个 Trace 内部独立编号(1, 2, 3),不需要前缀
-- **完全隔离**:每个 Trace 有独立的 GoalTree、Message List、LLM Context
-- **自然分布式**:每个 Trace 可以独立运行、迁移、存储
-- **层级清晰**:从 trace_id 可以直接解析出父子关系(`@` 前是父 ID)
-
-### 数据结构
-
-后端存储两类数据,可视化的 DAG 是派生视图:
-
-1. **GoalTree**:层级目标,注入 LLM
-2. **Messages**:执行记录,通过 `goal_id` 关联 Goal
-
-不存在独立的"边"数据结构,边在可视化时从 Messages 聚合生成。
-
-#### Trace
-
-```python
-@dataclass
-class Trace:
-    """
-    执行轨迹 - 一次完整的 Agent 运行
-
-    主 Agent 和 Sub-Agent 使用相同的 Trace 结构
-    """
-    trace_id: str                        # 主 Trace: UUID,Sub-Trace: parent@mode-timestamp-seq
-    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(用户直接触发):
-  {uuid}
-  例如: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d
-
-Sub-Trace(Agent 启动的子任务):
-  {parent_id}@{mode}-{timestamp}-{seq}
-  例如: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001
-  例如: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220015-002
-```
-
-**格式说明**:
-- `parent_id`:父 Trace 的**完整 UUID**(不截断,避免冲突)
-- `@`:父子关系分隔符
-- `mode`:运行模式(`explore`, `delegate`, `compaction` 等)
-- `timestamp`:创建时间戳(`YYYYMMDDHHmmss`)
-- `seq`:同一秒内的序号(`001`, `002`, ...)
-
-**优点**:
-1. **零碰撞风险**:使用完整 UUID,完全避免 ID 冲突
-2. **可精确追溯**:从 Sub-Trace ID 直接看到完整父 Trace ID
-3. **无需冲突检测**:实现简单,不依赖外部状态
-4. **信息完整**:一眼看出触发者、模式、时间
-5. **层级清晰**:`@` 分隔符明确表示父子关系
-
-**从 trace_id 解析父子关系**:
-```python
-def parse_parent_trace_id(trace_id: str) -> Optional[str]:
-    """从 trace_id 解析出 parent_trace_id"""
-    if '@' in trace_id:
-        return trace_id.split('@')[0]
-    return None
-
-# 示例
-parse_parent_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001")
-# → "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d"
-
-parse_parent_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d")
-# → None
-```
-
-**生成 Sub-Trace ID**:
-```python
-from datetime import datetime
-from threading import Lock
-from typing import Dict
-
-# 全局计数器(线程安全)
-_seq_lock = Lock()
-_seq_counter: Dict[str, int] = {}  # key: "{parent_id}@{mode}-{timestamp}"
-
-def generate_sub_trace_id(parent_id: str, mode: str) -> str:
-    """
-    生成 Sub-Trace ID
-
-    格式: {parent_id}@{mode}-{timestamp}-{seq}
-
-    Args:
-        parent_id: 父 Trace ID(完整 UUID,不截断)
-        mode: 运行模式(explore, delegate, compaction)
-
-    Returns:
-        Sub-Trace ID(完整格式,无碰撞风险)
-    """
-    # 直接使用完整 UUID,不截断
-    timestamp = datetime.now().strftime("%Y%m%d%H%M%S")
-
-    # 生成序号(同一秒内递增)
-    prefix = f"{parent_id}@{mode}-{timestamp}"
-    with _seq_lock:
-        seq = _seq_counter.get(prefix, 0) + 1
-        _seq_counter[prefix] = seq
-
-    return f"{prefix}-{seq:03d}"
-
-# 示例
-generate_sub_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", "explore")
-# → "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001"
-
-generate_sub_trace_id("2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d", "delegate")
-# → "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220030-001"
-```
-
-#### Goal
-
-```python
-@dataclass
-class GoalStats:
-    message_count: int = 0               # 消息数量
-    total_tokens: int = 0                # Token 总数
-    total_cost: float = 0.0              # 总成本
-    preview: Optional[str] = None        # 工具调用摘要,如 "read_file → edit_file → bash"
-
-GoalStatus = Literal["pending", "in_progress", "completed", "abandoned"]
-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(层级关系)
-    type: GoalType = "normal"            # Goal 类型
-
-    description: str                     # 目标描述(做什么)
-    reason: str                          # 创建理由(为什么做)
-    status: GoalStatus                   # pending | in_progress | completed | abandoned
-    summary: Optional[str] = None        # 完成/放弃时的总结
-
-    # agent_call 特有
-    sub_trace_ids: Optional[List[str]] = None  # 启动的 Sub-Trace IDs
-    agent_call_mode: Optional[str] = None      # "explore" | "delegate" | "sequential"
-    sub_trace_metadata: Optional[Dict[str, Dict[str, Any]]] = None  # Sub-Trace 元数据
-
-    # 统计(后端维护,用于可视化边的数据)
-    self_stats: GoalStats                # 自身统计(仅直接关联的 messages)
-    cumulative_stats: GoalStats          # 累计统计(自身 + 所有后代)
-```
-
-**实现**:`agent/goal/models.py:Goal`
-
-**ID 设计**:
-- **内部 ID**:每个 Trace 独立编号("1", "2", "3"),简单自增
-- **层级关系**:通过 `parent_id` 字段维护
-- **显示序号**:`to_prompt()` 时动态生成连续有意义的编号("1", "2", "2.1", "2.2"...)
-
-**sub_trace_metadata 字段**(`agent_call` 类型 Goal 专用):
-存储各 Sub-Trace 的关键信息,用于辅助决策和可视化:
-```python
-{
-    "sub_trace_id_1": {
-        "task": "JWT 方案",                    # Sub-Trace 任务描述
-        "status": "completed",                  # Sub-Trace 状态
-        "summary": "实现完成,使用 JWT token",   # Sub-Trace 总结
-        "last_message": {                       # 最后一条 assistant 消息
-            "role": "assistant",
-            "description": "生成 JWT token 并返回",
-            "content": "...",                   # 截断至 500 字符
-            "created_at": "2026-02-05T10:30:00"
-        },
-        "stats": {                              # 统计信息
-            "message_count": 8,
-            "total_tokens": 4000,
-            "total_cost": 0.05
-        }
-    },
-    # ... 其他 Sub-Trace
-}
-```
-
-**用途**:
-- 帮助主 Agent 决策:基于各分支的最终输出,决定是否需要展开查看详细信息
-- 前端可视化:在折叠视图中显示关键信息,用户快速判断是否需要展开
-- 调试追踪:快速了解每个分支的执行结果
-
-**统计更新逻辑**:
-- 每次添加 Message 时,更新对应 Goal 的 `self_stats`,并沿祖先链向上更新所有祖先的 `cumulative_stats`
-- 可视化中,折叠边使用 target Goal 的 `cumulative_stats`,展开边使用 `self_stats`
-
-```python
-@dataclass
-class GoalTree:
-    mission: str                         # 总任务描述
-    current_id: Optional[str] = None     # 当前焦点(内部 ID)
-    goals: List[Goal]                    # 扁平列表,通过 parent_id 构建层级
-    _next_id: int = 1                    # 内部 ID 计数器
-```
-
-**实现**:`agent/goal/models.py:GoalTree`
-
-#### Message
-
-Message 对应 LLM API 的消息,加上元数据。每条 Message 通过 `goal_id` 关联所属 Goal。
-
-```python
-@dataclass
-class Message:
-    message_id: str
-    trace_id: str                        # 所属 Trace ID
-    role: Literal["assistant", "tool"]   # 和 LLM API 一致
-    sequence: int                        # 当前 Trace 内的顺序
-    goal_id: Optional[str] = None        # 关联的 Goal 内部 ID(None = 还没有创建 Goal)
-    tool_call_id: Optional[str]          # tool 消息关联对应的 tool_call
-    content: Any                         # 消息内容(和 LLM API 格式一致)
-    description: str                     # 消息描述(系统自动生成)
-
-    # 元数据
-    tokens: Optional[int] = None
-    cost: Optional[float] = None
-    duration_ms: Optional[int] = None
-    created_at: datetime
-```
-
-**description 字段**(系统自动生成):
-- `assistant` 消息:优先取 content 中的 text,若无 text 则生成 "tool call: XX, XX"
-- `tool` 消息:使用 tool name
-
-**goal_id 说明**:
-- 通常关联到某个 Goal 的内部 ID(如 "1", "2")
-- 可以为 None:在 Trace 开始、还没有创建任何 Goal 时
-- 前端通过虚拟 START 节点展示 goal_id=None 的 messages
-
-**实现**:`agent/execution/models.py:Message`
-
-**Message 类型说明**:
-- `role="assistant"`:模型的一次返回,可能同时包含文本和多个 tool_calls
-- `role="tool"`:一个工具的执行结果,通过 `tool_call_id` 关联对应的 tool_call
-
-**查询 Message**:
-```python
-# 查询主 Trace 的 Messages
-GET /api/traces/2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/messages?goal_id=2
-
-# 查询 Sub-Trace 的 Messages
-GET /api/traces/2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001/messages?goal_id=1
-```
-
-### 工具设计
-
-#### goal 工具:计划管理
-
-```python
-@tool
-def goal(
-    add: Optional[str] = None,       # 添加目标(逗号分隔多个)
-    reason: Optional[str] = None,    # 创建理由(逗号分隔多个,与 add 一一对应)
-    after: Optional[str] = None,     # 在指定目标后面添加(同层级)
-    under: Optional[str] = None,     # 为指定目标添加子目标
-    done: Optional[str] = None,      # 完成当前目标,值为 summary
-    abandon: Optional[str] = None,   # 放弃当前目标,值为原因
-    focus: Optional[str] = None,     # 切换焦点到指定 ID
-) -> str:
-    """管理执行计划。"""
-```
-
-**实现**:`agent/goal/tool.py:goal_tool`
-
-**位置控制**:
-- 不指定 `after`/`under`:添加到当前 focus 的 goal 下作为子目标(无 focus 时添加到顶层)
-- `after="X"`:在目标 X 后面添加兄弟节点(同层级)
-- `under="X"`:为目标 X 添加子目标(如已有子目标,追加到最后)
-- `after` 和 `under` 互斥,不可同时指定
-
-**设计原则**:优先使用 `after` 明确指定位置,`under` 用于首次拆解或追加子任务。
-
-**示例**:
-
-```python
-# 1. 默认行为:添加顶层目标(无 focus)
-goal(add="分析代码, 实现功能, 测试")
-# [ ] 1. 分析代码
-# [ ] 2. 实现功能
-# [ ] 3. 测试
-
-# 2. 拆解子任务
-goal(add="设计接口, 实现代码", under="2")
-# [ ] 1. 分析代码
-# [ ] 2. 实现功能
-#     [ ] 2.1 设计接口
-#     [ ] 2.2 实现代码
-# [ ] 3. 测试
-
-# 3. 追加同层级任务
-goal(add="编写文档", after="3")
-# [ ] 1. 分析代码
-# [ ] 2. 实现功能
-#     [ ] 2.1 设计接口
-#     [ ] 2.2 实现代码
-# [ ] 3. 测试
-# [ ] 4. 编写文档
-
-# 4. 追加子任务(目标2已有子任务)
-goal(add="编写单元测试", under="2")
-# [ ] 1. 分析代码
-# [ ] 2. 实现功能
-#     [ ] 2.1 设计接口
-#     [ ] 2.2 实现代码
-#     [ ] 2.3 编写单元测试
-# [ ] 3. 测试
-# [ ] 4. 编写文档
-
-# 5. 使用 after 在子任务中间插入
-goal(add="代码审查", after="2.2")
-# [ ] 1. 分析代码
-# [ ] 2. 实现功能
-#     [ ] 2.1 设计接口
-#     [ ] 2.2 实现代码
-#     [ ] 2.3 代码审查
-#     [ ] 2.4 编写单元测试
-# [ ] 3. 测试
-# [ ] 4. 编写文档
-```
-
-**状态流转**:
-```
-pending ──focus──→ in_progress ──done──→ completed
-                        │                    ↓
-                        │              (压缩 context)
-                        │              (级联:若所有兄弟都 completed,父 goal 自动 completed)
-                        │
-                     abandon
-                        ↓
-                   abandoned
-                        ↓
-                  (压缩 context)
-```
-
-#### explore 工具:并行探索
-
-启动多个独立的 Sub-Traces 并行执行。
-
-```python
-@tool
-def explore(
-    branches: List[str],                  # 探索方向列表
-    background: Optional[str] = None,     # 背景概括(可选)
-) -> str:
-    """
-    并行探索多个方向,汇总结果。
-
-    每个方向会启动一个独立的 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. 收集元数据并汇总
-    sub_trace_metadata = {}
-    summary_parts = []
-
-    for sub_trace, result in zip(sub_traces, results):
-        # 获取 Sub-Trace 最新状态
-        updated_trace = await store.get_trace(sub_trace.trace_id)
-
-        # 获取最后一条 assistant 消息
-        messages = await store.get_messages(sub_trace.trace_id)
-        last_message = None
-        for msg in reversed(messages):
-            if msg.role == "assistant":
-                last_message = msg
-                break
-
-        # 构建元数据
-        sub_trace_metadata[sub_trace.trace_id] = {
-            "task": sub_trace.task,
-            "status": updated_trace.status if updated_trace else "unknown",
-            "summary": result.get("summary", "") if isinstance(result, dict) else "",
-            "last_message": {
-                "role": last_message.role,
-                "description": last_message.description,
-                "content": last_message.content[:500] if last_message.content else None,
-                "created_at": last_message.created_at.isoformat()
-            } if last_message else None,
-            "stats": {
-                "message_count": updated_trace.total_messages,
-                "total_tokens": updated_trace.total_tokens,
-                "total_cost": updated_trace.total_cost
-            }
-        }
-
-        # 组装摘要文本
-        summary_parts.append(f"### {sub_trace.task}")
-        summary_parts.append(result.get("summary", "执行完成"))
-
-    # 5. 更新 Goal,包含元数据
-    await store.update_goal(
-        current_trace_id,
-        current_goal_id,
-        status="completed",
-        summary=f"探索了 {len(branches)} 个方案",
-        sub_trace_metadata=sub_trace_metadata  # 保存元数据
-    )
-
-    # 6. 返回文本摘要(给 LLM)
-    return "\n".join(summary_parts)
-```
-
-**权限配置**:
-```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 注入
-
-每次 LLM 调用时,在 system prompt 末尾注入当前计划状态。注入时过滤掉 abandoned 目标,使用连续的显示序号。
-
-**展示策略**:
-- **完整展示**:所有顶层目标、当前 focus 目标的完整父链及其子树
-- **折叠其他**:非关键路径的子目标可折叠显示(显示节点数和状态)
-- **连续编号**:显示 ID 连续且有意义("1", "2", "2.1", "2.2")
-
-```markdown
-## Current Plan
-
-**Mission**: 实现用户认证功能
-**Current**: 2.2 实现登录接口
-
-**Progress**:
-[✓] 1. 分析代码
-    → 用户模型在 models/user.py,使用 bcrypt 加密
-[→] 2. 实现功能
-    [✓] 2.1 设计接口
-        → API 设计文档完成,使用 REST 风格
-    [→] 2.2 实现登录接口  ← current
-    [ ] 2.3 实现注册接口
-[ ] 3. 测试
-    (3 subtasks)
-```
-
-**实现**:`agent/goal/models.py:GoalTree.to_prompt`
-
-**注意**:当前 focus 目标的所有父目标及其子目标都会完整展示,确保 LLM 理解当前上下文。这样在使用 `after` 或 `under` 参数时,LLM 可以准确引用目标 ID。
-
-#### 2. 完成时压缩
-
-当调用 `goal(done="...")` 时:
-1. 找到该 goal 关联的所有 messages(通过 goal_id)
-2. 将详细 messages 替换为一条 summary message
-3. 更新 goal 状态为 completed
-
-#### 3. 回溯(Abandon)
-
-两种模式:
-
-**模式 1:需要修改的计划还没有执行**
-
-直接修改计划并继续执行。Goal 状态为 pending 时,可以直接修改 description 或删除。
-
-**模式 2:需要修改的计划已经执行**
-
-1. 将原 Goal 标记为 `abandoned`(保留在 GoalTree 数据中,但 `to_prompt()` 不展示)
-2. 将废弃分支关联的 messages 做 summary
-3. 将 summary 累积到新分支的第一条消息中(供 LLM 参考历史失败原因)
-4. 创建新的 Goal 继续执行
-
-**Before 回溯**:
-```
-GoalTree 数据:
-  [✓] 1. 分析代码               (内部ID: 1)
-  [→] 2. 实现方案 A              (内部ID: 2)
-  [ ] 3. 测试                    (内部ID: 3)
-
-Messages:
-  [分析代码的 20 条 message...]
-  [实现方案 A 的 30 条 message...]
-  [测试失败的 message...]
-```
-
-**After 回溯**:
-```
-GoalTree 数据(含废弃):
-  [✓] 1. 分析代码               (内部ID: 1)
-  [✗] 2. 实现方案 A              (内部ID: 2, abandoned)
-  [→] 3. 实现方案 B              (内部ID: 4, 新建)
-  [ ] 4. 测试                    (内部ID: 3)
-
-to_prompt() 输出(给 LLM,连续编号):
-  [✓] 1. 分析代码
-  [→] 2. 实现方案 B  ← current
-  [ ] 3. 测试
-
-Messages:
-  [分析代码的 20 条 message...]
-  [Summary: "尝试方案 A,因依赖问题失败"]     ← 原 messages 压缩为 1 条
-  [方案 B 第一条消息,包含废弃分支的 summary]  ← 供 LLM 参考
-  [方案 B 的后续 message...]
-```
-
-**实现**:`agent/goal/compaction.py`
-
-#### 4. 用户编辑计划
-
-用户可以通过编辑 Goal 内的 goal() 工具调用来修改执行计划。
-
-**核心思路**:利用现有的 GoalTree 结构和 Message sequence,无需额外追踪字段。
-
-**编辑流程**:
-1. 用户选择某个 Goal,查看其内所有 messages
-2. 找到 goal() 调用,编辑其参数
-3. 系统废弃该 message 之后的所有内容:
-   - 废弃 sequence >= 该 message 的所有 messages
-   - 根据 GoalTree 结构,废弃受影响的 Goals(当前 Goal 的子孙、后续同级 Goals)
-4. 从该 Goal 重新执行
-
-**废弃判断**:
-- 基于 Message sequence:废弃 seq >= 编辑点的所有 messages
-- 基于 GoalTree 结构:废弃当前 Goal 的所有子节点、以及后续创建的 Goals
-- 不需要追踪"哪个 message 创建了哪个 Goal",结构本身就能判断
-
-**UI 展示**:
-- Goal 详情显示其内所有 messages
-- 突出显示 goal() 调用(assistant message 中包含 tool_calls)
-- 提供"编辑并重新执行"按钮
-
-**API**:
-```
-GET /api/traces/{trace_id}/messages?goal_id=X
-  # 返回 Goal X 的所有 messages
-
-PATCH /api/traces/{trace_id}/replay
-{
-  "from_message_id": "msg_123",
-  "modified_tool_call": {
-    "tool_call_id": "call_abc",
-    "new_arguments": {"add": "新任务列表", "under": "2"}
-  }
-}
-```
-
-**实现**:`agent/goal/replay.py`
-
-**注意**:这是最通用、最简单的编辑方式,完全基于现有数据结构。
-
-### 可视化
-
-#### DAG 模型
-
-可视化展示为 DAG(有向无环图),不是树。
-
-**核心概念**:
-- **节点** = Goal 完成后的结果/里程碑
-- **边** = 从一个结果到下一个结果的执行过程(动作/策略)
-- 每个节点对应一条入边,入边的数据从该 Goal 关联的 Messages 聚合
-
-**展开/折叠**:对边操作,对应目标的层级展开。
-
-```
-折叠视图(只看顶层 Goals):
-[START] ──→ [1:分析完成] ──→ [2:实现完成] ──→ [3:测试完成]
-                              逻辑边
-
-展开 [1]→[2] 的边(显示 Goal 2 的子目标):
-[START] ──→ [1:分析完成] ──→ [2.1:设计完成] ──→ [2.2:代码完成] ──→ [3:测试完成]
-                              执行边             执行边
-```
-
-展开时,父节点 [2] 被子节点 [2.1], [2.2] **替代**。
-折叠时,子节点合并回父节点 [2]。
-
-嵌套展开:如果 2.1 也有子目标,可以继续展开 [1]→[2.1] 的边。
-
-**废弃分支**:在可视化中以灰色样式展示废弃分支。
-
-```
-[1:分析完成] ──→ [2:方案A(废弃)] ──→ ...     ← 灰色
-             ──→ [4:方案B] ──→ [3:测试]       ← 正常
-```
-
-#### API
-
-后端提供 GoalTree 数据,前端负责生成 DAG 视图。
-
-**REST 端点**:
-```
-GET /api/traces/{trace_id}           # 获取 Trace + GoalTree
-GET /api/traces/{trace_id}/messages?goal_id=2.1  # 获取 Messages(边详情)
-```
-
-**响应**(GoalTree 部分):
-```json
-{
-  "goal_tree": {
-    "mission": "实现用户认证功能",
-    "current_id": "4",
-    "goals": [
-      {
-        "id": "1",
-        "parent_id": null,
-        "branch_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,
-        "branch_id": null,
-        "type": "normal",
-        "description": "实现功能",
-        "reason": "核心任务",
-        "status": "in_progress",
-        "self_stats": {"message_count": 0, ...},
-        "cumulative_stats": {"message_count": 11, "total_tokens": 5700, ...}
-      },
-      {
-        "id": "3",
-        "parent_id": "2",
-        "branch_id": null,
-        "type": "normal",
-        "description": "设计接口",
-        "status": "completed",
-        "self_stats": {...}
-      },
-      {
-        "id": "4",
-        "parent_id": "2",
-        "branch_id": null,
-        "type": "normal",
-        "description": "实现代码",
-        "status": "in_progress",
-        "self_stats": {...}
-      },
-      {
-        "id": "5",
-        "parent_id": null,
-        "branch_id": null,
-        "type": "normal",
-        "description": "测试",
-        "status": "pending",
-        ...
-      }
-    ]
-  }
-}
-```
-
-**DAG 生成逻辑**(前端实现):
-1. 根据用户展开状态,确定可见 Goal 序列
-2. 相邻 Goal 之间形成边
-3. 边的统计数据从 target Goal 的 stats 获取(折叠用 `cumulative_stats`,展开用 `self_stats`)
-4. 边的详细内容通过 Messages API 查询
-
-**实现**:见 [frontend/API.md](../frontend/API.md)
-
-#### WebSocket 实时推送
-
-```
-ws://localhost:8000/api/traces/{trace_id}/watch?since_event_id=0
-```
-
-**事件类型**:
-
-| 事件 | 触发时机 | payload |
-|------|---------|---------|
-| `connected` | WebSocket 连接成功 | trace_id, current_event_id, goal_tree(完整 GoalTree) |
-| `goal_added` | 新增 Goal | goal 完整数据(含 self_stats, cumulative_stats) |
-| `goal_updated` | Goal 状态变化(含级联完成) | goal_id, updates(含 cumulative_stats),affected_goals |
-| `message_added` | 新 Message | message 数据(含 goal_id),affected_goals |
-| `trace_completed` | 执行完成 | 统计信息 |
-
-**事件详情**:
-
-**`connected`** - 连接时推送完整 GoalTree,前端据此初始化 DAG:
-```json
-{
-  "event": "connected",
-  "trace_id": "xxx",
-  "current_event_id": 42,
-  "goal_tree": { "mission": "...", "goals": [...] }
-}
-```
-
-**`message_added`** - 新 Message 时,后端更新统计并推送受影响的 Goals:
-```json
-{
-  "event": "message_added",
-  "message": { "message_id": "...", "role": "assistant", "goal_id": "2.1", "..." : "..." },
-  "affected_goals": [
-    { "goal_id": "2.1", "self_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"}, "cumulative_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"} },
-    { "goal_id": "2", "cumulative_stats": {"message_count": 12, "total_tokens": 6800, "total_cost": 0.08, "preview": "glob → read → edit × 5 → bash"} }
-  ]
-}
-```
-
-`affected_goals` 包含该 Message 直接关联的 Goal(更新 self_stats + cumulative_stats)以及所有祖先 Goal(仅更新 cumulative_stats)。前端根据当前展开状态选择使用哪个 stats 渲染边。
-
-**`goal_updated`** - Goal 状态变化时推送,包含级联完成场景:
-```json
-{
-  "event": "goal_updated",
-  "goal_id": "2.1",
-  "updates": { "status": "completed", "summary": "接口设计完成" },
-  "affected_goals": [
-    { "goal_id": "2.1", "cumulative_stats": {"message_count": 4, "total_tokens": 2000, "total_cost": 0.03, "preview": "read → edit × 2"} },
-    { "goal_id": "2", "status": "completed", "summary": "功能实现完成", "cumulative_stats": {"message_count": 12, "total_tokens": 6800, "total_cost": 0.08, "preview": "..."} }
-  ]
-}
-```
-
-当所有子 Goal 完成时,后端自动级联完成父 Goal,并在 `affected_goals` 中包含所有状态变更的祖先。前端收到后直接更新对应节点,无需自行计算。
-
-**`goal_added`** - 新增 Goal,携带完整 Goal 数据:
-```json
-{
-  "event": "goal_added",
-  "goal": { "id": "2.1", "description": "设计接口", "reason": "需要先定义 API", "status": "pending", "self_stats": {}, "cumulative_stats": {} },
-  "parent_id": "2"
-}
-```
-
-**实现**:`agent/execution/websocket.py`
-
-### 存储结构
-
-```
-.trace/
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/                             # 主 Trace
-│   ├── meta.json                # Trace 元数据
-│   ├── goal.json                # GoalTree
-│   ├── messages/                # Messages
-│   │   ├── {message_id}.json
-│   │   └── ...
-│   └── events.jsonl             # 事件流
-│
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001/ # Sub-Trace A(并行探索)
-│   ├── meta.json                # parent_trace_id: "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d"
-│   ├── goal.json                # 独立的 GoalTree
-│   ├── messages/
-│   └── events.jsonl
-│
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002/ # Sub-Trace B(并行探索)
-│   └── ...
-│
-└── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@delegate-20260204220030-001/ # Sub-Trace task1(单线委托)
-    └── ...
-```
-
-**关键点**:
-- 每个 Trace(主/子)都是完全独立的目录
-- 从 trace_id 可以直接找到对应目录
-- 通过 `parent_trace_id` 可以追溯父子关系
-
----
-
-## 并行探索设计(explore 工具)
-
-### 场景
-
-```
-主 Trace (2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d):
-  [1] 分析问题
-  [2] 并行探索认证方案 (type=agent_call, mode=explore)
-      → 启动 Sub-Traces(完整 ID)
-  [3] 完善实现
-
-Sub-Trace A (2f8d3a1c...@explore-20260204220012-001):
-  [1] JWT 设计
-  [2] JWT 实现
-  → 返回摘要:"JWT 方案实现完成"
-
-Sub-Trace B (2f8d3a1c...@explore-20260204220012-002):
-  [1] Session 设计
-  [2] Session 实现
-  [3] Session 测试
-  → 返回摘要:"Session 方案实现完成"
-
-explore 工具返回:
-  汇总两个分支的结果,主 Agent 继续决策
-```
-
-**核心原则**:
-- 每个分支是独立的 Trace,有自己的 GoalTree 和 Message List
-- 分支内的 Goal ID 简单编号("1", "2", "3"),独立于主 Trace
-- 主 Trace 的 Goal 通过 `sub_trace_ids` 字段关联分支
-- 分支完全独立存储,可以并行运行
-- explore 工具自动汇总各分支 summary
-
-### 数据结构
-
-**主 Trace 的 GoalTree**:
-
-```python
-# Trace: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d
-goals = [
-    Goal(id="1", type="normal", description="分析问题"),
-    Goal(
-        id="2",
-        type="agent_call",
-        description="并行探索认证方案",
-        agent_call_mode="explore",
-        sub_trace_ids=[
-            "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001",
-            "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002"
-        ],
-    ),
-    Goal(id="3", type="normal", description="完善实现"),
-]
-```
-
-**Sub-Trace A 的 GoalTree**(独立编号):
-
-```python
-# Trace: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001
-goals = [
-    Goal(id="1", type="normal", description="JWT 设计"),
-    Goal(id="2", type="normal", description="JWT 实现"),
-]
-```
-
-**Sub-Trace B 的 GoalTree**(独立编号):
-
-```python
-# Trace: 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002
-goals = [
-    Goal(id="1", type="normal", description="Session 设计"),
-    Goal(id="2", type="normal", description="Session 实现"),
-    Goal(id="3", type="normal", description="Session 测试"),
-]
-```
-
-### 存储结构
-
-```
-.trace/
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/                             # 主 Trace
-│   ├── meta.json
-│   ├── goal.json
-│   └── messages/
-│
-├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001/ # Sub-Trace A
-│   ├── meta.json            # parent_trace_id: "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d"
-│   ├── goal.json            # 独立的 GoalTree
-│   └── messages/
-│
-└── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002/ # Sub-Trace B
-    └── ...
-```
-
-### DAG 可视化
-
-**折叠视图**(Sub-Trace 作为单个节点):
-```
-[1:分析] ──→ [2:并行探索] ──→ [3:完善]
-                 │
-          (启动2个Sub-Trace)
-```
-
-**展开分支视图**(显示并行路径):
-```
-                  ┌──→ [A:JWT方案] ────┐
-[1:分析] ──→ [2] ─┤                    ├──→ [3:完善]
-                  └──→ [B:Session方案] ┘
-```
-
-**继续展开分支 A 内部**(加载 Sub-Trace 的 GoalTree):
-```
-                  ┌──→ [A.1:JWT设计] → [A.2:JWT实现] ──┐
-[1:分析] ──→ [2] ─┤                                    ├──→ [3:完善]
-                  └──→ [B:Session方案] ────────────────┘
-```
-
-**注意**:
-- `[A:JWT方案]` 是折叠视图,代表整个 Sub-Trace
-- `[A.1]`, `[A.2]` 是展开后显示 Sub-Trace 内部的 Goals
-- 前端显示为 "A.1",但后端查询使用完整 trace_id:
-  `GET /api/traces/2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001/messages?goal_id=1`
-
-### 前端 API
-
-**REST**:返回主 Trace 的 GoalTree + Sub-Trace 列表(元数据)。
-
-```http
-GET /api/traces/2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d
-```
-
-响应:
-```json
-{
-  "trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d",
-  "status": "running",
-  "goal_tree": {
-    "goals": [
-      {"id": "1", "type": "normal", "description": "分析问题"},
-      {
-        "id": "2",
-        "type": "agent_call",
-        "description": "并行探索认证方案",
-        "agent_call_mode": "explore",
-        "sub_trace_ids": [
-          "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001",
-          "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002"
-        ],
-        "sub_trace_metadata": {
-          "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001": {
-            "task": "JWT 方案",
-            "status": "completed",
-            "summary": "实现完成,使用 JWT token",
-            "last_message": {
-              "role": "assistant",
-              "description": "生成 JWT token 并返回",
-              "content": "JWT 方案实现完成。使用 jsonwebtoken 库生成 token,包含 user_id 和过期时间...",
-              "created_at": "2026-02-05T10:30:00"
-            },
-            "stats": {
-              "message_count": 8,
-              "total_tokens": 4000,
-              "total_cost": 0.05
-            }
-          },
-          "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002": {
-            "task": "Session 方案",
-            "status": "completed",
-            "summary": "实现完成,使用 Redis 存储 session",
-            "last_message": {
-              "role": "assistant",
-              "description": "配置 Redis 并实现 session 管理",
-              "content": "Session 方案实现完成。使用 Redis 存储 session,支持过期自动清理...",
-              "created_at": "2026-02-05T10:32:00"
-            },
-            "stats": {
-              "message_count": 12,
-              "total_tokens": 4000,
-              "total_cost": 0.05
-            }
-          }
-        }
-      },
-      {"id": "3", "type": "normal", "description": "完善实现"}
-    ]
-  },
-  "sub_traces": {
-    "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001": {
-      "trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001",
-      "parent_trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d",
-      "parent_goal_id": "2",
-      "agent_type": "explore",
-      "task": "JWT 方案",
-      "status": "completed",
-      "total_messages": 8,
-      "total_tokens": 4000,
-      "total_cost": 0.05
-    },
-    "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002": {
-      "trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-002",
-      "agent_type": "explore",
-      "task": "Session 方案",
-      "status": "completed",
-      ...
-    }
-  }
-}
-```
-
-**按需加载 Sub-Trace 详情**:
-
-```http
-GET /api/traces/2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001
-```
-
-响应:
-```json
-{
-  "trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d@explore-20260204220012-001",
-  "parent_trace_id": "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d",
-  "parent_goal_id": "2",
-  "agent_type": "explore",
-  "task": "JWT 方案",
-  "status": "completed",
-  "goal_tree": {
-    "goals": [
-      {"id": "1", "description": "JWT 设计", ...},
-      {"id": "2", "description": "JWT 实现", ...}
-    ]
-  },
-  "total_tokens": 4000,
-  "total_cost": 0.05
-}
-```
-
-**WebSocket 事件**:
-
-| 事件 | 触发时机 | payload |
-|------|---------|---------|
-| `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 工具执行流程
-
-详细流程见前面"工具设计"部分,这里展示关键步骤:
-
-```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-Traces
-    sub_traces = []
-    for i, desc in enumerate(branches):
-        sub_trace = Trace.create(
-            trace_id=generate_sub_trace_id(current_trace_id, "explore"),
-            parent_trace_id=current_trace_id,
-            parent_goal_id=current_goal_id,
-            agent_type="explore",
-            task=desc,
-            context={"allowed_tools": ["read", "grep", "glob"]},
-        )
-        sub_traces.append(sub_trace)
-        goal.sub_trace_ids.append(sub_trace.trace_id)
-
-    # 3. 并行执行
-    results = await asyncio.gather(
-        *[run_agent(st) for st in sub_traces]
-    )
-
-    # 4. 汇总返回
-    return format_results(results)
-```
-
-**汇总结果示例**:
-```markdown
-## 探索结果
-
-### 方案 A: JWT 方案
-实现完成。优点:无状态,易扩展。缺点:token 较大,无法主动失效。
-
-### 方案 B: Session 方案
-实现完成。优点:token 小,可主动失效。缺点:需要 Redis 存储。
-
----
-两种方案都已实现,请选择一种继续。
-```
-
-### Context 压缩
-
-Sub-Trace 完成后的压缩策略:
-
-1. **Sub-Trace 完成时**:Sub-Trace 的详细 context 压缩为 summary(存储在 Trace.summary)
-2. **explore 完成后**:所有 Sub-Traces 的 summary 汇总到主 Trace 的 tool result
-3. **主 Trace context**:explore 工具调用被压缩为一条包含汇总结果的 tool message
-
----
-
-## 与 OpenCode 方案的对比
-
-| 方面 | OpenCode | 我们的方案 |
-|------|----------|-----------|
-| 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(边可展开/折叠) |
-| 分布式支持 | 困难 | 自然支持(每个 Trace 独立) |
-
----
-
-## 实现位置
-
-| 功能 | 文件路径 | 状态 |
-|------|---------|------|
-| Trace 数据模型 | `agent/execution/models.py` | 待调整(增加父子关系、移除 branch_id) |
-| Goal 数据模型 | `agent/goal/models.py` | 待调整(移除 branch_id、BranchContext) |
-| Trace ID 生成 | `agent/execution/trace_id.py` | 待实现(generate_sub_trace_id) |
-| goal 工具 | `agent/goal/tool.py` | 待调整(新增 after/under 参数) |
-| explore 工具 | `agent/goal/explore.py` | 待实现 |
-| delegate 工具 | `agent/goal/delegate.py` | 待实现 |
-| Context 压缩 | `agent/goal/compaction.py` | 待调整 |
-| TraceStore 协议 | `agent/execution/protocols.py` | 待调整(移除 branch 相关接口) |
-| FileSystem Store | `agent/execution/fs_store.py` | 待调整(移除 branches/ 目录) |
-| DAG 可视化 API | `agent/execution/api.py` | 待调整(支持 Sub-Trace) |
-| WebSocket 推送 | `agent/execution/websocket.py` | 待调整(统一事件格式) |
-| Plan 注入 | `agent/core/runner.py` | 待调整 |
-
----
-
-## 渐进式实现计划
-
-### Phase 1: 基础 goal 工具
-- GoalTree 数据结构(含 ID 映射)
-- goal 工具(add, after, under, done, focus)
-- 位置控制逻辑:after(同层级)、under(子任务)
-- Plan 注入到 system prompt(含显示序号生成、完整展示策略)
-- Message 模型(替代 Step)
-
-### Phase 2: 回溯支持
-- abandon 操作(两种模式)
-- 废弃分支 summary 累积到新分支
-- 基于 goal 的 context 压缩
-
-### Phase 3: 可视化
-- DAG 视图 API
-- WebSocket goal/message 事件
-- 展开/折叠逻辑
-
-### Phase 4: 并行探索
-- explore 工具
-- 独立 message list 管理
-- 结果汇总机制

+ 77 - 0
docs/decisions.md

@@ -590,3 +590,80 @@ execution trace v2.0 引入了 Blob 存储系统用于处理大输出和图片
    - 不需要拆分成 preview + ref
 
 ---
+
+## 13. 架构重组:统一 Trace 模型
+
+**日期**: 2026-02-08
+
+### 问题
+
+原有架构存在以下问题:
+1. `execution/` 和 `goal/` 目录职责边界模糊
+2. `Trace.context` 命名与 `ToolContext` 等概念混淆
+3. `subagents/` 目录与 Agent 预设概念重复
+4. 文档散乱,多个重构相关文档过时
+
+### 决策
+
+**目录重组**:
+- `execution/` + `goal/` → `trace/`(统一执行追踪和计划管理)
+- 删除 `subagents/` 目录,逻辑整合到 `trace/task_tool.py`
+- 删除 `core/config.py`,合并到 `runner.py` 和 `presets.py`
+
+**命名调整**:
+- `Trace.context` → `Trace.meta`(TraceMeta 数据类)
+
+**统一 Agent 模型**:
+- 所有 Agent(主、子、人类协助)都是 Trace
+- 子 Agent = 通过 `task` 工具创建的子 Trace
+- 人类协助 = 通过 `ask_human` 工具创建的阻塞式 Trace
+
+**Agent 预设替代 Sub-Agent 配置**:
+- `core/presets.py` 定义 Agent 类型(default, explore, analyst 等)
+- 项目级 `.agent/presets.json` 可覆盖
+
+**工具目录简化**:
+- 仅对 `file/` 和 `browser/` 做子目录分类
+- 其他工具直接放 `builtin/`
+- `goal` 和 `task` 工具放 `trace/`(Agent 内部控制)
+
+### 理由
+
+1. **概念更清晰**:trace/ 统一管理"执行状态",tools/ 专注"外部交互"
+2. **减少冗余**:删除 subagents/ 目录,用统一的 Trace 机制
+3. **命名准确**:meta 比 context 更准确表达"静态元信息"
+
+---
+
+## Subagent 工具设计
+
+### 决策
+
+**统一工具,三种模式**:
+- 单一工具 `subagent` 支持三种模式:`explore`(并行探索)、`delegate`(委托执行)、`evaluate`(结果评估)
+- 实现位置:`agent/tools/builtin/subagent.py`
+
+**Explore 模式的并行执行**:
+- 使用 `asyncio.gather()` 实现真并行
+- 每个 branch 创建独立的 Sub-Trace
+- 仅允许只读工具(`read_file`, `grep_content`, `glob_files`)
+
+**权限隔离**:
+- Explore 模式:只读权限,防止副作用
+- Delegate/Evaluate 模式:除 `subagent` 外的所有工具
+- 子 Agent 不能调用 `subagent`,防止无限递归
+
+**Sub-Trace 信息存储**:
+- Sub-Trace 的元信息存储在自己的 `meta.json` 中
+- 父 Trace 的 `events.jsonl` 记录 `sub_trace_started` 和 `sub_trace_completed` 事件
+- Goal 的 `sub_trace_ids` 字段记录关联关系
+
+### 理由
+
+1. **统一接口**:三种模式共享相同的 Sub-Trace 创建和管理逻辑,减少代码重复
+2. **真并行**:Explore 模式使用 `asyncio.gather()` 充分利用 I/O 等待时间,提升效率
+3. **安全性**:只读权限确保探索不会修改系统状态;禁止递归调用防止资源耗尽
+4. **可追溯**:事件记录和 Goal 关联确保完整的执行历史,支持可视化和调试
+
+---
+

+ 0 - 215
docs/dependencies.md

@@ -1,215 +0,0 @@
-# Agent Framework 依赖管理
-
-## 📦 核心依赖
-
-### 必需依赖
-
-| 包名 | 版本 | 用途 |
-|------|------|------|
-| `httpx[socks]` | >=0.28.0 | HTTP 客户端,用于调用 Gemini REST API,支持 SOCKS 代理 |
-| `python-dotenv` | >=1.0.0 | 环境变量管理,用于加载 `.env` 配置文件 |
-
-### 可选依赖
-
-| 包名 | 版本 | 用途 | 安装建议 |
-|------|------|------|----------|
-| `docstring-parser` | >=0.15 | 工具文档解析,提供更好的类型提示和描述解析 | 推荐安装 |
-| `google-generativeai` | >=0.8.0 | Google Gemini SDK(不推荐使用) | ❌ 不推荐,使用 HTTP API 代替 |
-
-## 🚀 安装方式
-
-### 方式一:使用 requirements.txt(推荐)
-
-```bash
-pip install -r requirements.txt
-```
-
-### 方式二:手动安装
-
-```bash
-# 安装核心依赖
-pip install httpx[socks]>=0.28.0 python-dotenv>=1.0.0
-
-# 可选:安装 docstring-parser
-pip install docstring-parser>=0.15
-```
-
-### 方式三:使用 pip 直接安装
-
-```bash
-pip install httpx[socks] python-dotenv
-```
-
-## 📝 依赖说明
-
-### httpx[socks]
-- **用途**:异步 HTTP 客户端,用于调用 Gemini REST API
-- **为什么需要 `[socks]`**:支持 SOCKS 代理(很多用户需要代理访问 Google API)
-- **如果不需要代理**:可以只安装 `httpx`,但推荐安装完整版
-
-### python-dotenv
-- **用途**:从 `.env` 文件加载环境变量
-- **配置示例**:
-  ```bash
-  # .env
-  GEMINI_API_KEY=your_api_key_here
-  ```
-
-### docstring-parser(可选)
-- **用途**:解析工具函数的 docstring,提取参数类型和描述
-- **如果没安装**:框架会使用 fallback 解析器,功能稍弱但可用
-- **推荐理由**:提供更准确的工具 schema 生成
-
-### google-generativeai(不推荐)
-- **为什么不推荐**:
-  - SDK 对 JSON Schema 格式要求严格,与 OpenAI 工具格式不完全兼容
-  - 难以调试和自定义
-  - HTTP API 提供更好的控制和灵活性
-- **何时使用**:仅用于学习或简单场景
-
-## 🔧 开发依赖
-
-如果你要开发或测试框架,还需要:
-
-```bash
-# 测试框架
-pip install pytest pytest-asyncio
-
-# 代码质量
-pip install black flake8 mypy
-
-# 文档生成
-pip install mkdocs mkdocs-material
-```
-
-## 🌐 Python 版本要求
-
-- **最低版本**:Python 3.9
-- **推荐版本**:Python 3.11+
-- **兼容性**:
-  - ✅ Python 3.9+
-  - ✅ Python 3.10
-  - ✅ Python 3.11
-  - ✅ Python 3.12
-
-## 📋 依赖检查
-
-运行以下命令检查依赖是否正确安装:
-
-```bash
-python3 -c "
-import httpx
-import dotenv
-print('✅ 核心依赖安装成功')
-
-try:
-    import docstring_parser
-    print('✅ docstring-parser 已安装')
-except ImportError:
-    print('⚠️  docstring-parser 未安装(可选)')
-"
-```
-
-## 🐛 常见问题
-
-### Q1: 安装 httpx 时报错 "No module named 'socksio'"
-
-**解决方案**:
-```bash
-pip install 'httpx[socks]'
-```
-
-### Q2: ImportError: cannot import name 'AsyncClient' from 'httpx'
-
-**解决方案**:升级 httpx 到最新版本
-```bash
-pip install --upgrade httpx
-```
-
-### Q3: 代理设置
-
-如果你使用代理,在 `.env` 中配置:
-```bash
-HTTP_PROXY=socks5://127.0.0.1:7890
-HTTPS_PROXY=socks5://127.0.0.1:7890
-```
-
-或者在代码中设置:
-```python
-import httpx
-
-client = httpx.AsyncClient(
-    proxies={
-        "http://": "socks5://127.0.0.1:7890",
-        "https://": "socks5://127.0.0.1:7890"
-    }
-)
-```
-
-## 📦 生成 requirements.txt
-
-如果你修改了依赖,可以重新生成:
-
-```bash
-# 导出当前环境所有包(不推荐)
-pip freeze > requirements-full.txt
-
-# 手动维护 requirements.txt(推荐)
-# 只包含项目直接依赖,不包括子依赖
-```
-
-## 🔄 更新依赖
-
-```bash
-# 更新所有依赖到最新版本
-pip install --upgrade -r requirements.txt
-
-# 更新特定包
-pip install --upgrade httpx
-```
-
-## 🎯 最小化依赖原则
-
-本框架遵循最小化依赖原则:
-- ✅ 只依赖必需的包
-- ✅ 避免大型框架(如 Django, FastAPI)
-- ✅ 使用标准库优先(asyncio, json, logging)
-- ✅ 可选依赖明确标注
-
-## 📊 依赖关系图
-
-```
-agent/
-├── httpx (必需)
-│   ├── httpcore
-│   ├── certifi
-│   ├── idna
-│   └── socksio (SOCKS 代理支持)
-│
-├── python-dotenv (必需)
-│
-└── docstring-parser (可选)
-```
-
-## 🌟 生产环境建议
-
-生产环境推荐固定版本:
-
-```txt
-# requirements-prod.txt
-httpx[socks]==0.28.1
-python-dotenv==1.2.1
-docstring-parser==0.16
-```
-
-使用:
-```bash
-pip install -r requirements-prod.txt
-```
-
-## 📚 相关文档
-
-- [httpx 文档](https://www.python-httpx.org/)
-- [python-dotenv 文档](https://github.com/theskumar/python-dotenv)
-- [docstring-parser 文档](https://github.com/rr-/docstring_parser)
-- [Gemini API 文档](https://ai.google.dev/api/rest)

+ 0 - 129
docs/project-structure.md

@@ -1,129 +0,0 @@
-# Agent Framework 目录结构
-
-> 框架与应用分层设计
-
----
-
-## 核心架构
-
-**框架层** (`agent/`): 核心代码、内置预设,可作为独立包发布
-
-**应用层** (项目根目录): 项目特定配置,覆盖或扩展框架
-
----
-
-## 框架层(`agent/`)
-
-```
-agent/
-├── core/                       # 核心引擎
-│   ├── runner.py               # AgentRunner
-│   └── config.py               # AgentConfig, CallResult
-├── execution/                  # 执行追踪
-│   ├── models.py               # Trace, Step
-│   ├── protocols.py            # TraceStore
-│   ├── fs_store.py             # FileSystemTraceStore
-│   └── tree_dump.py            # 可视化
-├── memory/                     # 记忆系统
-│   ├── models.py               # Experience, Skill
-│   ├── protocols.py            # MemoryStore, StateStore
-│   ├── stores.py               # 存储实现
-│   └── skill_loader.py         # Skill 加载器(自动加载内置 skills)
-├── tools/                      # 工具系统
-│   ├── builtin/                # 内置基础工具
-│   ├── advanced/               # 高级工具(第三方适配)
-│   └── adapters/               # 外部集成
-├── llm/                        # LLM 相关
-│   ├── gemini.py
-│   ├── openrouter.py
-│   └── prompts/                # Prompt 工具
-├── skills/                     # 框架内置 Skills(自动加载)
-│   ├── core.md                 # 核心 skill,每次运行自动加载
-│   └── browser_use/            # 浏览器自动化 skill
-└── subagents/                  # 内置 Sub-Agent 定义
-```
-
-**实现**:
-- 核心引擎:`agent/core/runner.py`
-- 执行追踪:`agent/execution/`
-- 记忆系统:`agent/memory/`(Skills 自动加载)
-- 工具系统:`agent/tools/`
-- LLM 集成:`agent/llm/`
-- Skills:`agent/skills/core.md`(自动加载)+ `agent/skills/browser_use/`(按需加载)
-- Sub-Agent 预设:`agent/subagents/`
-
----
-
-## 应用层(项目根目录)
-
-项目特定配置(可选):
-
-```
-your-project/
-├── skills/                     # 项目特定 Skills
-├── subagents/                  # 项目特定 Sub-Agents
-└── tools/                      # 项目特定工具
-```
-
----
-
-## 加载优先级
-
-| 资源 | 框架预设 | 项目配置 | 优先级 |
-|------|---------|---------|--------|
-| Skills | `agent/skills/` | `./skills/` | 项目优先(`agent/skills/core.md` 总是自动加载) |
-| Sub-Agents | `agent/subagents/` | `./subagents/` | 项目覆盖 |
-| Tools | `agent/tools/builtin/` | `./tools/` | 手动注册 |
-
-**实现**:
-- Skills 自动加载:`agent/memory/skill_loader.py:load_skills_from_dir()`
-  - 总是自动加载 `agent/skills/` 中的所有 skills(包括 `core.md`)
-  - 可选加载 `skills_dir` 参数指定的额外自定义 skills
-- Skills 工具动态加载:`agent/tools/builtin/skill.py` - 搜索路径 `./skills/` → `./agent/skills/`
-- Sub-Agents 加载:按顺序加载配置,后加载的覆盖先加载的
-
----
-
-## 使用方式
-
-### 加载框架预设
-
-```python
-from agent import AgentRunner
-from agent.llm import create_gemini_llm_call
-
-# Skills 自动加载
-# - agent/skills/core.md 总是自动加载
-# - agent/skills/ 中的其他 skills 也会自动加载
-runner = AgentRunner(
-    llm_call=create_gemini_llm_call(),
-    # skills_dir 可选:加载额外的自定义 skills
-)
-```
-
-### 扩展框架(可选)
-
-```python
-# 加载项目特定的自定义 skills
-runner = AgentRunner(
-    llm_call=create_gemini_llm_call(),
-    skills_dir="./skills",  # 额外加载项目自定义 skills
-)
-
-# 结果:agent/skills/ + ./skills/ 中的所有 skills 都会被加载
-```
-
-**Skills 加载机制**:
-- **自动加载**:`agent/skills/` 中的所有 skills(包括 `core.md`)总是自动加载
-- **可选加载**:通过 `skills_dir` 参数额外加载项目自定义 skills
-- **动态加载**:通过 `skill` 工具运行时按需加载特定 skill
-
-详见 [`SKILLS_SYSTEM.md`](../SKILLS_SYSTEM.md)
-
----
-
-## 相关文档
-
-- [Sub-Agent 配置](./sub-agents.md) - Sub-Agent 定义格式
-- [Skills](./skills.md) - Skill 使用和编写
-- [工具系统](./tools.md) - 工具注册和使用

+ 0 - 0
docs/context-comparison.md → docs/ref/context-comparison.md


+ 0 - 174
docs/refactor-plan.md

@@ -1,174 +0,0 @@
-# 重构计划:统一 Trace 模型
-
-> 基于更新后的 context-management.md 设计,移除 branch 概念,采用独立 Trace 架构
-
-## 目标
-
-将当前基于 branch 的实现重构为统一的 Trace 模型,每个 Sub-Agent 都是独立的 Trace。
-
-## Phase 1: 核心数据结构调整
-
-### 1.1 Trace ID 生成器
-- [ ] 实现 `agent/execution/trace_id.py`
-  - [ ] `generate_sub_trace_id(parent_id, mode)` 函数
-  - [ ] `parse_parent_trace_id(trace_id)` 函数
-  - [ ] 线程安全的序号计数器
-  - [ ] 单元测试
-
-### 1.2 Trace 模型更新
-- [ ] 更新 `agent/execution/models.py:Trace`
-  - [ ] 添加 `parent_trace_id: Optional[str]` 字段
-  - [ ] 添加 `parent_goal_id: Optional[str]` 字段
-  - [ ] 确保 `context: Dict[str, Any]` 字段存在
-  - [ ] 更新 `to_dict()` 和 `from_dict()` 方法
-  - [ ] 移除任何 branch 相关字段(如果有)
-
-### 1.3 Message 模型更新
-- [ ] 更新 `agent/execution/models.py:Message`
-  - [ ] **移除** `branch_id` 字段
-  - [ ] 确保 `duration_ms` 字段存在
-  - [ ] 更新 `to_dict()` 方法
-  - [ ] 更新 `create()` 方法
-
-### 1.4 Goal 模型更新
-- [ ] 更新 `agent/goal/models.py:Goal`
-  - [ ] **移除** `branch_id` 字段
-  - [ ] **移除** `branch_ids` 字段
-  - [ ] 确保 `sub_trace_ids` 字段存在
-  - [ ] 更新 `to_dict()` 和 `from_dict()` 方法
-
-### 1.5 移除 BranchContext
-- [ ] 删除 `agent/goal/models.py:BranchContext` 类
-  - [ ] 检查所有引用
-  - [ ] 移除相关导入
-
-## Phase 2: 存储层重构
-
-### 2.1 FileSystem Store 更新
-- [ ] 重构 `agent/execution/fs_store.py`
-  - [ ] **移除** `_get_branches_dir()` 方法
-  - [ ] **移除** `_get_branch_dir()` 方法
-  - [ ] **移除** `_get_branch_meta_file()` 方法
-  - [ ] **移除** `_get_branch_goal_file()` 方法
-  - [ ] **移除** `_get_branch_messages_dir()` 方法
-  - [ ] **移除** `create_branch()` 方法
-  - [ ] **移除** `get_branch()` 方法
-  - [ ] **移除** `get_branch_goal_tree()` 方法
-  - [ ] **移除** `update_branch_goal_tree()` 方法
-  - [ ] **移除** `update_branch()` 方法
-  - [ ] **移除** `list_branches()` 方法
-  - [ ] 更新 `create_trace()` - 不再创建 branches/ 目录
-  - [ ] 更新 `add_message()` - 移除 branch_id 逻辑
-  - [ ] 更新 `get_trace_messages()` - 移除 branch_id 参数
-  - [ ] 更新 `get_messages_by_goal()` - 移除 branch_id 参数
-  - [ ] 更新 `_update_goal_stats()` - 移除 branch_id 逻辑
-  - [ ] 更新 `_get_affected_goals()` - 移除 branch_id 逻辑
-
-### 2.2 TraceStore 协议更新
-- [ ] 更新 `agent/execution/protocols.py:TraceStore`
-  - [ ] **移除** branch 相关方法签名
-  - [ ] 更新方法签名(移除 branch_id 参数)
-  - [ ] 添加查询 Sub-Trace 的方法(如果需要)
-
-### 2.3 数据迁移脚本
-- [ ] 创建 `scripts/migrate_traces.py`
-  - [ ] 读取旧的 `.trace/*/branches/` 结构
-  - [ ] 为每个 branch 生成新的 trace_id
-  - [ ] 创建独立的 Trace 目录
-  - [ ] 更新 Goal 的 sub_trace_ids
-  - [ ] 移除 branch_id 字段
-  - [ ] 备份原始数据
-
-## Phase 3: 工具实现
-
-### 3.1 explore 工具
-- [ ] 实现 `agent/goal/explore.py`
-  - [ ] `explore_tool(branches, background)` 函数
-  - [ ] 使用 `generate_sub_trace_id()` 生成 Sub-Trace ID
-  - [ ] 创建独立的 Trace 实例
-  - [ ] 并行执行 Sub-Traces
-  - [ ] 汇总结果
-  - [ ] 单元测试
-
-### 3.2 delegate 工具
-- [ ] 实现 `agent/goal/delegate.py`
-  - [ ] `delegate_tool(task)` 函数
-  - [ ] 使用 `generate_sub_trace_id()` 生成 Sub-Trace ID
-  - [ ] 创建独立的 Trace 实例
-  - [ ] 执行并返回结果
-  - [ ] 单元测试
-
-### 3.3 goal 工具更新
-- [ ] 更新 `agent/goal/tool.py`
-  - [ ] 移除任何 branch 相关逻辑
-  - [ ] 确保与新的 Goal 模型兼容
-
-## Phase 4: API 层更新
-
-### 4.1 REST API
-- [ ] 更新 `agent/execution/api.py`
-  - [ ] `GET /api/traces/{trace_id}` - 支持新的 trace_id 格式
-  - [ ] 添加查询 Sub-Traces 的端点
-  - [ ] 移除 branch 相关端点
-  - [ ] 更新响应格式(包含 sub_traces)
-
-### 4.2 WebSocket
-- [ ] 更新 `agent/execution/websocket.py`
-  - [ ] 移除 branch 相关事件
-  - [ ] 添加 `sub_trace_started` 事件
-  - [ ] 添加 `sub_trace_completed` 事件
-  - [ ] 更新事件格式
-
-## Phase 5: 清理和测试
-
-### 5.1 清理
-- [ ] 搜索代码库中所有 "branch" 引用
-  - [ ] 移除或更新相关代码
-- [ ] 删除 `.trace/*/branches/` 目录(旧数据)
-- [ ] 更新所有文档引用
-
-### 5.2 测试
-- [ ] 单元测试
-  - [ ] Trace ID 生成器
-  - [ ] explore 工具
-  - [ ] delegate 工具
-  - [ ] FileSystem Store
-- [ ] 集成测试
-  - [ ] 创建主 Trace
-  - [ ] 启动 Sub-Trace
-  - [ ] 查询跨 Trace 数据
-- [ ] 端到端测试
-  - [ ] 完整的 explore 流程
-  - [ ] 完整的 delegate 流程
-
-## Phase 6: 文档更新
-
-- [ ] 更新 README
-- [ ] 更新 API 文档
-- [ ] 添加迁移指南
-- [ ] 更新示例代码
-
-## 实施顺序建议
-
-1. **Phase 1** (数据结构) - 最重要,先打好基础
-2. **Phase 2.1-2.2** (存储层) - 核心存储逻辑
-3. **Phase 3** (工具实现) - 业务功能
-4. **Phase 4** (API 层) - 对外接口
-5. **Phase 2.3** (数据迁移) - 处理历史数据
-6. **Phase 5** (清理测试) - 确保质量
-7. **Phase 6** (文档) - 最后完善
-
-## 风险和注意事项
-
-1. **兼容性**:旧的 traces 数据需要迁移
-2. **并发安全**:`generate_sub_trace_id` 的序号生成器需要线程安全
-3. **性能**:独立 Trace 可能增加文件系统操作,需要监控
-4. **调试**:新的 trace_id 格式较长,需要确保日志和 UI 能正确显示
-
-## 完成标准
-
-- [ ] 所有测试通过
-- [ ] 文档更新完成
-- [ ] 示例代码可运行
-- [ ] 没有 branch 相关代码残留
-- [ ] 旧数据可以迁移

+ 0 - 241
docs/sub-agents.md

@@ -1,241 +0,0 @@
-# Sub-Agent 实现指南
-
-> **可执行规格书**:Sub-Agent 实现细节。代码修改必须同步更新此文档。
-
----
-
-## 数据模型
-
-### AgentDefinition
-
-```python
-# agent/subagents/agent.py
-
-@dataclass
-class AgentDefinition:
-    name: str
-    description: Optional[str] = None
-    mode: Literal["primary", "subagent", "all"] = "all"
-
-    # 权限配置
-    allowed_tools: Optional[List[str]] = None  # 白名单
-    denied_tools: Optional[List[str]] = None   # 黑名单
-    permissions: Dict[str, Any] = field(default_factory=dict)
-
-    # 模型配置
-    model: Optional[str] = None
-    temperature: Optional[float] = None
-    max_iterations: Optional[int] = None
-    system_prompt: Optional[str] = None
-
-    # 是否可以调用其他 Sub-Agent
-    can_spawn_subagent: bool = False
-```
-
-**实现位置**:`agent/subagents/agent.py:AgentDefinition`(待实现)
-
-### Trace 扩展
-
-```python
-# agent/execution/models.py (扩展现有模型)
-
-@dataclass
-class Trace:
-    # ... 原有字段
-
-    # Sub-Agent 支持(新增)
-    parent_trace_id: Optional[str] = None    # 父 Trace ID
-    agent_definition: Optional[str] = None   # Agent 类型名称
-    spawned_by_tool: Optional[str] = None    # 启动此 Sub-Agent 的 Step ID
-```
-
-**实现位置**:`agent/execution/models.py:Trace`
-
----
-
-## 内置 Sub-Agent
-
-### 1. general - 通用型
-
-```python
-GENERAL_AGENT = AgentDefinition(
-    name="general",
-    description="通用型 Sub-Agent,执行复杂的多步骤任务",
-    mode="subagent",
-    denied_tools=["task"],
-    max_iterations=20,
-)
-```
-
-**用途**:多步骤任务、数据收集、复杂分析
-
-### 2. explore - 探索型
-
-```python
-EXPLORE_AGENT = AgentDefinition(
-    name="explore",
-    description="探索型 Sub-Agent,快速查找文件和代码",
-    mode="subagent",
-    allowed_tools=["read_file", "list_files", "search_code", "search_files"],
-    denied_tools=["write_file", "edit_file", "execute_bash", "task"],
-    max_iterations=15,
-)
-```
-
-**用途**:代码库探索、文件查找、结构分析
-
-### 3. analyst - 分析型
-
-```python
-ANALYST_AGENT = AgentDefinition(
-    name="analyst",
-    description="分析型 Sub-Agent,深度分析和报告生成",
-    mode="subagent",
-    allowed_tools=["read_file", "list_files", "search_code", "web_search", "fetch_url"],
-    denied_tools=["task", "write_file", "edit_file"],
-    max_iterations=25,
-    temperature=0.3,
-)
-```
-
-**用途**:技术栈分析、性能分析、安全审计
-
-**实现位置**:`agent/builtin_agents.py`(待实现)
-
----
-
-## 核心 API
-
-### Task Tool
-
-```python
-# agent/tools/builtin/task.py
-
-@tool(name="task")
-async def task_tool(
-    subagent_type: str,      # Sub-Agent 类型
-    description: str,         # 任务简短描述(3-5词)
-    prompt: str,              # 详细任务描述
-    ctx: ToolContext
-) -> ToolResult:
-    """启动 Sub-Agent 处理子任务"""
-```
-
-**实现位置**:`agent/tools/builtin/task.py:task_tool`(待实现)
-
-### Agent Registry
-
-```python
-# agent/agent_registry.py
-
-class AgentRegistry:
-    def register(self, agent: AgentDefinition) -> None:
-        """注册 Agent"""
-
-    def get(self, name: str) -> Optional[AgentDefinition]:
-        """获取 Agent 定义"""
-
-    def list_subagents(self) -> List[AgentDefinition]:
-        """列出所有可用的 Sub-Agent"""
-
-    def load_from_config(self, config_path: str) -> None:
-        """从配置文件加载"""
-
-# 全局注册表
-def get_agent_registry() -> AgentRegistry:
-    """获取全局 Agent 注册表"""
-```
-
-**实现位置**:`agent/agent_registry.py:AgentRegistry`(待实现)
-
----
-
-## 配置文件
-
-### 格式
-
-```json
-{
-  "agents": {
-    "code-reviewer": {
-      "description": "代码审查专家",
-      "mode": "subagent",
-      "allowed_tools": ["read_file", "search_code", "list_files"],
-      "max_iterations": 15,
-      "temperature": 0.2,
-      "system_prompt": "你是代码审查专家...",
-      "can_spawn_subagent": false
-    }
-  }
-}
-```
-
-**框架预设**:`agent/subagents/default.json`
-
-**项目配置**(可选):`./subagents/custom.json`
-
-### 加载
-
-```python
-from agent.agent_registry import get_agent_registry
-
-# 加载框架预设
-registry = get_agent_registry()
-registry.load_from_config("agent/subagents/default.json")
-
-# 加载项目自定义配置(可选,覆盖框架预设)
-from pathlib import Path
-if Path("./subagents/custom.json").exists():
-    registry.load_from_config("./subagents/custom.json")
-```
-
----
-
-## 使用示例
-
-### 基本使用
-
-```python
-from agent import AgentRunner, AgentConfig
-
-runner = AgentRunner(
-    llm_call=your_llm,
-    config=AgentConfig(max_iterations=20),
-)
-
-# 主 Agent 会自动使用 task 工具启动 Sub-Agent
-async for event in runner.run(
-    task="使用 explore sub-agent 查找所有配置文件"
-):
-    if event.type == "conclusion":
-        print(event.data["content"])
-```
-
-### 自定义 Sub-Agent
-
-```python
-from agent.models.agent import AgentDefinition
-from agent.agent_registry import get_agent_registry
-
-# 定义
-custom = AgentDefinition(
-    name="security-scanner",
-    description="安全扫描专家",
-    mode="subagent",
-    allowed_tools=["read_file", "search_code"],
-)
-
-# 注册
-get_agent_registry().register(custom)
-```
-
-**完整示例**:`examples/subagent_example.py`
-
----
-
-## 注意事项
-
-1. **防止递归**:默认禁止 Sub-Agent 调用 task 工具
-2. **权限隔离**:Sub-Agent 只能访问允许的工具
-3. **Trace 完整性**:每个 Sub-Agent 有独立的 Trace
-4. **成本控制**:跟踪每个 Sub-Agent 的 token 消耗

+ 0 - 109
docs/testing.md

@@ -1,109 +0,0 @@
-# 测试指南
-
-> 项目测试策略和运行方法
-
----
-
-## 测试分层
-
-```
-E2E 测试 - 真实 LLM 调用(需要 API Key)
-    ↑
-集成测试 - 多模块协作(Mock LLM)
-    ↑
-单元测试 - 单个函数/类
-```
-
----
-
-## 运行测试
-
-### 本地运行
-
-```bash
-# 单元测试 + 集成测试
-pytest tests/ -v -m "not e2e"
-
-# 指定模块
-pytest tests/test_agent_definition.py -v
-
-# 生成覆盖率报告
-pytest --cov=agent --cov-report=html tests/ -m "not e2e"
-
-# E2E 测试(可选,需要 API Key)
-GEMINI_API_KEY=xxx pytest tests/e2e/ -v -m e2e
-```
-
-### CI 运行
-
-```bash
-# 单元测试 + 集成测试(PR 时运行)
-pytest tests/ -v -m "not e2e" --cov=agent
-
-# E2E 测试(仅 main 分支)
-pytest tests/e2e/ -v -m e2e
-```
-
----
-
-## 覆盖率要求
-
-| 模块 | 目标覆盖率 |
-|------|-----------|
-| agent/core/ | 90%+ |
-| agent/execution/ | 90%+ |
-| agent/memory/ | 80%+ |
-| agent/tools/ | 85%+ |
-| agent/llm/ | 70%+ |
-
----
-
-## 测试标记
-
-```python
-# 单元测试(默认)
-def test_something():
-    pass
-
-# E2E 测试(需要 API Key)
-@pytest.mark.e2e
-def test_real_llm():
-    pass
-```
-
----
-
-## CI 配置
-
-```yaml
-# .github/workflows/test.yml
-
-jobs:
-  test:
-    - name: Run tests
-      run: pytest tests/ -v -m "not e2e" --cov=agent
-
-  e2e:
-    if: github.ref == 'refs/heads/main'
-    - name: Run E2E tests
-      env:
-        GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
-      run: pytest tests/e2e/ -v -m e2e
-```
-
----
-
-## 测试文件结构
-
-```
-tests/
-├── conftest.py              # Fixtures
-├── test_agent_definition.py
-├── test_trace.py
-├── test_tools.py
-├── integration/
-│   ├── test_subagent.py
-│   └── test_trace_store.py
-└── e2e/
-    └── test_subagent_e2e.py
-```

+ 0 - 232
docs/tools-adapters.md

@@ -1,232 +0,0 @@
-# 工具适配器
-
-> 参考 opencode 实现的基础工具,移植到 Python 并集成到框架
-
----
-
-## ⚠️ 第三方工具接入规范
-
-**工具分类原则**:
-
-| 类型 | 实现方式 | 目录结构 | 示例 |
-|------|---------|---------|------|
-| **高频&简单工具** | Python 原生实现 | `agent/tools/builtin/` | read_file, edit_file, bash_command |
-| **复杂&低频工具** | 第三方仓库 + 适配器 | `vendor/` + `agent/tools/adapters/` + `agent/tools/advanced/` | webfetch, lsp_diagnostics |
-| **CLI 命令行工具** | pip 安装到虚拟环境 | `requirements.txt` + `bash_command` 调用 | browser-use |
-
-**接入流程**:
-
-1. **简单工具**:直接在 `builtin/` 实现
-   - 参考第三方设计(如 opencode)
-   - Python 完整实现
-   - 通过 `@tool` 装饰器注册
-
-2. **复杂工具**:通过适配器调用
-   - 第三方仓库 → `vendor/`(Git Submodule,只读)
-   - 适配器/桥接 → `agent/tools/adapters/`
-   - 工具注册 → `agent/tools/advanced/`
-
-3. **CLI 工具**:通过 bash_command 调用
-   - 添加到 `requirements.txt`
-   - pip 安装到项目虚拟环境
-   - 通过 `bash_command` 工具调用 CLI
-   - 在 `skills/` 中提供使用指导
-
-**设计目标**:
-- 参考 opencode 的成熟工具设计(Git Submodule: `vendor/opencode/`)
-- Python 实现:基础工具(builtin/)完整复刻,高级工具(advanced/)通过 Bun 调用
-- 保持解耦,便于独立维护和更新
-
----
-
-## 架构设计
-
-```
-vendor/opencode/                       # Git submodule(只读参考)
-  └── packages/opencode/src/tool/      # TypeScript 实现
-
-agent/tools/
-  ├── builtin/                         # 基础工具(Python 完整实现)
-  │   ├── read.py, edit.py, write.py  # 文件操作
-  │   ├── bash.py                      # 命令执行
-  │   └── glob.py, grep.py             # 文件搜索
-  │
-  ├── advanced/                        # 高级工具(Bun 适配器)
-  │   ├── webfetch.py                  # 网页抓取
-  │   └── lsp.py                       # LSP 诊断
-  │
-  └── adapters/
-      ├── base.py                      # 适配器基类
-      ├── opencode_bun_adapter.py      # Bun 调用适配器
-      └── opencode-wrapper.ts          # TypeScript 桥接
-```
-
-**实现策略**:
-- 基础工具(高频):Python 完整实现(~1-5ms,零依赖)
-- 高级工具(低频复杂):通过 Bun 调用 opencode(~50-100ms,功能完整)
-
----
-
-## 工具清单
-
-### 基础工具(Python 实现)
-
-| 工具 | 实现 | 功能 |
-|------|------|------|
-| `read_file` | `builtin/read.py` | 读取文件(文本/图片/PDF),分页支持 |
-| `edit_file` | `builtin/edit.py` | 文件编辑,9 种智能匹配策略 |
-| `write_file` | `builtin/write.py` | 文件写入,自动创建目录 |
-| `bash_command` | `builtin/bash.py` | 命令执行,超时控制 |
-| `glob_files` | `builtin/glob.py` | 文件模式匹配 |
-| `grep_content` | `builtin/grep.py` | 内容搜索(ripgrep 优先) |
-| `skill` | `builtin/skill.py` | 加载技能文档 |
-| `list_skills` | `builtin/skill.py` | 列出可用技能 |
-
-**参考源**:`vendor/opencode/packages/opencode/src/tool/*.ts`
-
-**关键实现**:
-- `edit_file` 的 9 种策略:包含 Levenshtein 算法的 BlockAnchorReplacer
-- `bash_command` 的环境变量支持
-- `grep_content` 的 ripgrep fallback
-
-### 高级工具(Bun 适配器)
-
-| 工具 | 实现 | 调用方式 |
-|------|------|----------|
-| `webfetch` | `advanced/webfetch.py` | Bun → opencode(HTML 转 MD)|
-| `lsp_diagnostics` | `advanced/lsp.py` | Bun → opencode(LSP 集成)|
-
-**依赖**:需要 [Bun](https://bun.sh/) 运行时
-
-**桥接**:`adapters/opencode-wrapper.ts` 通过子进程调用 opencode 工具
-
-### CLI 工具(pip 安装)
-
-| 工具 | 安装方式 | 调用方式 |
-|------|---------|----------|
-| `browser-use` | `pip install browser-use` | `bash_command` 调用 CLI |
-
-**依赖检查**:自动检测并提示缺失的依赖
-
-```python
-# 1. 加载 skill 时自动检查(会提示缺失的依赖)
-await skill(skill_name="browser-use")
-# 输出包含:⚠️ Setup Required - pip install browser-use / uvx browser-use install
-
-# 2. 手动检查依赖(使用 skill 自带的配置工具)
-from agent.skills.browser_use import check_browser_use
-await check_browser_use()
-# 返回详细的依赖状态
-
-# 3. 自动安装 Chromium(可选,需要下载 200-300MB)
-from agent.skills.browser_use import install_browser_use_chromium
-await install_browser_use_chromium()
-```
-
-**使用示例**:
-```python
-# Agent 加载使用指南
-await skill(skill_name="browser-use")
-
-# 通过 bash_command 调用 CLI
-await bash_command(command="browser-use open https://example.com")
-await bash_command(command="browser-use state")
-await bash_command(command="browser-use click 5")
-```
-
-**Skill 文档**:`agent/skills/browser_use/browser-use.md` 提供完整的 CLI 使用指南
-
----
-
-## 适配器接口
-
-**实现**:`agent/tools/adapters/base.py:ToolAdapter`
-
-核心方法:
-- `adapt_execute()` - 执行工具并转换结果为 `ToolResult`
-- `adapt_schema()` - 转换工具 Schema
-- `extract_memory()` - 提取长期记忆摘要
-
-**Bun 适配器**:`adapters/opencode_bun_adapter.py:OpenCodeBunAdapter`
-- 通过子进程调用 `bun run opencode-wrapper.ts <tool> <args>`
-- 30 秒超时
-- JSON 结果解析
-
----
-
-## 更新 opencode 参考
-
-### 检查更新
-
-```bash
-# 查看 submodule 状态
-git submodule status
-
-# 更新 submodule
-cd vendor/opencode
-git pull origin main
-cd ../..
-
-# 提交更新
-git add vendor/opencode
-git commit -m "chore: update opencode reference"
-```
-
-### 同步改进
-
-1. **查看 opencode 变更**:
-   ```bash
-   cd vendor/opencode
-   git log --oneline --since="1 month ago" -- packages/opencode/src/tool/
-   ```
-
-2. **对比实现差异**:
-   - 查看 opencode 的具体改动
-   - 评估是否需要同步到 Python 实现
-
-3. **更新 Python 实现**:
-   - 在 `agent/tools/builtin/` 中更新对应工具
-   - 更新代码注释中的参考位置
-
-4. **测试验证**:
-   ```bash
-   pytest tests/tools/builtin/ -v
-   ```
-
----
-
-## 使用示例
-
-```python
-from agent.tools.builtin import read_file, edit_file, bash_command
-from agent.tools.advanced import webfetch
-
-# 基础工具
-result = await read_file(file_path="config.py", limit=100)
-result = await edit_file(file_path="config.py", old_string="DEBUG = True", new_string="DEBUG = False")
-result = await bash_command(command="git status", timeout=30)
-
-# 高级工具(需要 Bun)
-result = await webfetch(url="https://docs.python.org/3/")
-```
-
-工具通过 `@tool` 装饰器自动注册到 `ToolRegistry`。
-
-**完整示例**:`examples/tools_complete_demo.py`
-
----
-
-## 关键设计
-
-**Git Submodule 原则**:
-- `vendor/opencode/` 只读,绝不修改
-- 作为参考源,可随时更新:`cd vendor/opencode && git pull`
-- 基础工具手动同步重要改进,高级工具自动获得更新
-
-**高内聚原则**:
-- `opencode-wrapper.ts` 与 `opencode_bun_adapter.py` 在同一目录(`adapters/`)
-- Wrapper 是适配器的一部分,不是独立脚本
-
-**性能权衡**:
-- 基础工具(高频):Python 实现避免子进程开销
-- 高级工具(低频):Bun 适配器避免重复实现复杂逻辑

+ 41 - 0
docs/tools.md

@@ -712,6 +712,47 @@ print(f"Success rate: {stats['success_rate']:.1%}")
 | `bash_command` | 执行 shell 命令 | opencode bash.ts |
 | `glob_files` | 文件模式匹配 | opencode glob.ts |
 | `grep_content` | 内容搜索(正则表达式) | opencode grep.ts |
+| `subagent` | 统一子 Agent 调用(evaluate/delegate/explore) | main 自研 |
+
+### Subagent 工具
+
+创建子 Agent 执行任务,支持三种模式:
+
+| 模式 | 用途 | 并行执行 | 工具权限 |
+|------|------|---------|---------|
+| **explore** | 并行探索多个方案 | ✅ | 只读(read_file, grep_content, glob_files) |
+| **delegate** | 委托单个任务 | ❌ | 完整(除 subagent 外) |
+| **evaluate** | 评估任务结果 | ❌ | 完整(除 subagent 外) |
+
+**Explore 模式**:
+- 适合对比多个方案(如技术选型、架构设计)
+- 使用 `asyncio.gather()` 并行执行,显著提升效率
+- 每个分支创建独立的 Sub-Trace,互不干扰
+- 只读权限,防止副作用
+
+**Delegate 模式**:
+- 适合委托专门任务(如代码分析、文档生成)
+- 完整工具权限,可执行复杂操作
+- 支持 `continue_from` 参数继续执行
+
+**Evaluate 模式**:
+- 适合评估任务完成质量
+- 可访问目标 Goal 的执行结果
+- 提供评估结论和改进建议
+
+**Sub-Trace 结构**:
+- 每个 subagent 调用创建独立的 Sub-Trace
+- Sub-Trace ID 格式:`{parent_id}@{mode}-{序号}-{timestamp}-001`
+- 通过 `parent_trace_id` 和 `parent_goal_id` 建立父子关系
+- Sub-Trace 信息存储在独立的 trace 目录中
+
+**Goal 集成**:
+- Subagent 调用会将 Goal 标记为 `type: "agent_call"`
+- `agent_call_mode` 记录使用的模式
+- `sub_trace_ids` 记录所有创建的 Sub-Trace
+- Goal 完成后,`summary` 包含格式化的汇总结果
+
+**实现位置**:`agent/tools/builtin/subagent.py`
 
 ### 快速使用
 

+ 1 - 1
examples/feature_extract/run.py

@@ -17,7 +17,7 @@ load_dotenv()
 
 from agent.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner
-from agent.execution import (
+from agent.trace import (
     FileSystemTraceStore,
     Trace,
     Message,

+ 76 - 27
examples/research/run.py

@@ -1,7 +1,7 @@
 """
-浏览器调研示例
+创意写作调研示例
 
-使用 Agent 模式 + 浏览器工具进行网络调研
+使用 Agent 模式 + explore 工具进行创意内容探索
 """
 
 import os
@@ -17,7 +17,7 @@ load_dotenv()
 
 from agent.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner
-from agent.execution import (
+from agent.trace import (
     FileSystemTraceStore,
     Trace,
     Message,
@@ -33,12 +33,8 @@ async def main():
     output_dir = base_dir / "output"
     output_dir.mkdir(exist_ok=True)
 
-    # Skills 目录(可选:用户自定义 skills)
-    # 注意:内置 skills(agent/skills/core.md)会自动加载
-    skills_dir = None  # 或者指定自定义 skills 目录,如: project_root / "skills"
-
     print("=" * 60)
-    print("浏览器调研任务 (Agent 模式)")
+    print("创意写作调研 (Agent 模式)")
     print("=" * 60)
     print()
 
@@ -59,17 +55,20 @@ async def main():
     print("2. 构建任务消息...")
     messages = prompt.build_messages()
 
-    # 3. 创建 Agent Runner(配置 skills 和浏览器工具)
+    # 3. 创建 Agent Runner
     print("3. 创建 Agent Runner...")
-    print(f"   - Skills 目录: {skills_dir}")
     print(f"   - 模型: {model_name} (via OpenRouter)")
 
-    # 使用 OpenRouter 的 Gemini 模型
+    # Trace 输出到测试目录
+    trace_dir = base_dir / ".trace"
+    trace_dir.mkdir(exist_ok=True)
+    print(f"   - Trace 目录: {trace_dir}")
+
     runner = AgentRunner(
-        trace_store=FileSystemTraceStore(base_path=".trace"),
+        trace_store=FileSystemTraceStore(base_path=str(trace_dir)),
         llm_call=create_openrouter_llm_call(model=f"google/{model_name}"),
-        skills_dir=skills_dir,
-        debug=True  # 启用 debug,输出到 .trace/
+        skills_dir=None,
+        debug=True
     )
 
     # 4. Agent 模式执行
@@ -78,6 +77,7 @@ async def main():
 
     final_response = ""
     current_trace_id = None
+    subagent_calls = []
 
     async for item in runner.run(
         task=user_task,
@@ -85,9 +85,9 @@ async def main():
         system_prompt=system_prompt,
         model=f"google/{model_name}",
         temperature=temperature,
-        max_iterations=20,  # 调研任务可能需要更多迭代
+        max_iterations=30,  # 增加迭代次数以支持多个 subagent 调用
     ):
-        # 处理 Trace 对象(整体状态变化)
+        # 处理 Trace 对象
         if isinstance(item, Trace):
             current_trace_id = item.trace_id
             if item.status == "running":
@@ -100,7 +100,7 @@ async def main():
             elif item.status == "failed":
                 print(f"[Trace] 失败: {item.error_message}")
 
-        # 处理 Message 对象(执行过程)
+        # 处理 Message 对象
         elif isinstance(item, Message):
             if item.role == "assistant":
                 content = item.content
@@ -109,7 +109,6 @@ async def main():
                     tool_calls = content.get("tool_calls")
 
                     if text and not tool_calls:
-                        # 纯文本回复(最终响应)
                         final_response = text
                         print(f"[Response] Agent 完成")
                     elif text:
@@ -120,6 +119,23 @@ async def main():
                             tool_name = tc.get("function", {}).get("name", "unknown")
                             print(f"[Tool Call] {tool_name}")
 
+                            # 记录 subagent 调用
+                            if tool_name == "subagent":
+                                import json
+                                args = tc.get("function", {}).get("arguments", {})
+                                # arguments 可能是字符串,需要解析
+                                if isinstance(args, str):
+                                    try:
+                                        args = json.loads(args)
+                                    except:
+                                        args = {}
+                                mode = args.get("mode", "unknown")
+                                subagent_calls.append({
+                                    "mode": mode,
+                                    "task": args.get("task", args.get("background", ""))[:50]
+                                })
+                                print(f"  → mode: {mode}")
+
             elif item.role == "tool":
                 content = item.content
                 if isinstance(content, dict):
@@ -138,25 +154,58 @@ async def main():
     print("=" * 60)
     print()
 
-    # 6. 保存结果
-    output_file = output_dir / "research_result.txt"
+    # 6. 统计 subagent 调用
+    print("=" * 60)
+    print("Subagent 调用统计:")
+    print("=" * 60)
+    delegate_count = sum(1 for call in subagent_calls if call["mode"] == "delegate")
+    explore_count = sum(1 for call in subagent_calls if call["mode"] == "explore")
+    evaluate_count = sum(1 for call in subagent_calls if call["mode"] == "evaluate")
+
+    print(f"  - delegate 模式: {delegate_count} 次")
+    print(f"  - explore 模式: {explore_count} 次")
+    print(f"  - evaluate 模式: {evaluate_count} 次")
+    print(f"  - 总计: {len(subagent_calls)} 次")
+    print()
+
+    for i, call in enumerate(subagent_calls, 1):
+        print(f"  {i}. [{call['mode']}] {call['task']}...")
+    print("=" * 60)
+    print()
+
+    # 7. 保存结果
+    output_file = output_dir / "subagent_test_result.txt"
     with open(output_file, 'w', encoding='utf-8') as f:
+        f.write("=" * 60 + "\n")
+        f.write("Agent 响应\n")
+        f.write("=" * 60 + "\n\n")
         f.write(final_response)
+        f.write("\n\n" + "=" * 60 + "\n")
+        f.write("Subagent 调用统计\n")
+        f.write("=" * 60 + "\n\n")
+        f.write(f"delegate 模式: {delegate_count} 次\n")
+        f.write(f"explore 模式: {explore_count} 次\n")
+        f.write(f"evaluate 模式: {evaluate_count} 次\n")
+        f.write(f"总计: {len(subagent_calls)} 次\n\n")
+        for i, call in enumerate(subagent_calls, 1):
+            f.write(f"{i}. [{call['mode']}] {call['task']}...\n")
 
     print(f"✓ 结果已保存到: {output_file}")
     print()
 
-    # 提示使用 API 可视化
+    # 8. 可视化提示
     print("=" * 60)
-    print("可视化 Step Tree:")
+    print("Trace 信息:")
     print("=" * 60)
-    print("1. 启动 API Server:")
-    print("   python3 api_server.py")
+    print(f"Trace ID: {current_trace_id}")
+    print(f"Trace 目录: {trace_dir}")
     print()
-    print("2. 浏览器访问:")
-    print("   http://localhost:8000/api/traces")
+    print("查看 trace 文件:")
+    print(f"   ls -la {trace_dir}")
     print()
-    print(f"3. Trace ID: {current_trace_id}")
+    print("或启动 API Server 可视化:")
+    print("   python3 api_server.py")
+    print("   访问: http://localhost:8000/api/traces")
     print("=" * 60)
 
 

+ 7 - 2
examples/research/test.prompt

@@ -7,5 +7,10 @@ $system$
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 
 $user$
-使用浏览器帮我做个调研:一张图片中的构图可以如何表示?我希望寻找一些构图特征的表示方法。
-注意使用explore工具,在合适的时候调用多个分支并行探索。
+请为"一个人在雨夜独自等车"这个场景,创作三种不同风格的短篇描写。
+使用 subagent 的 explore 模式,并行探索以下三个方向:
+1. 悬疑惊悚风格
+2. 温馨治愈风格
+3. 科幻未来风格
+
+每个方向写 100-150 字的场景描写即可。

+ 0 - 276
examples/test_plan.py

@@ -1,276 +0,0 @@
-"""
-测试新的 Plan 系统
-
-测试 GoalTree、Message、TraceStore 的基本功能
-"""
-
-import asyncio
-import sys
-from pathlib import Path
-
-# 添加项目根目录到 Python 路径
-sys.path.insert(0, str(Path(__file__).parent.parent.parent))
-
-from agent.goal.models import GoalTree, Goal, GoalStats
-from agent.execution.models import Trace, Message
-from agent.execution.fs_store import FileSystemTraceStore
-from agent.goal.tool import goal_tool
-
-
-async def test_basic_plan():
-    """测试基本的计划功能"""
-    print("=" * 60)
-    print("测试 1: 基本计划功能")
-    print("=" * 60)
-    print()
-
-    # 1. 创建 GoalTree
-    tree = GoalTree(mission="实现用户认证功能")
-    print("1. 创建 GoalTree")
-    print(f"   Mission: {tree.mission}")
-    print()
-
-    # 2. 添加顶层目标
-    print("2. 添加顶层目标")
-    result = goal_tool(tree, add="分析代码, 实现功能, 测试")
-    print(result)
-    print()
-
-    # 3. Focus 到目标 2
-    print("3. Focus 到目标 2")
-    result = goal_tool(tree, focus="2")
-    print(result)
-    print()
-
-    # 4. 添加子目标
-    print("4. 在目标 2 下添加子目标")
-    result = goal_tool(tree, add="设计接口, 实现代码, 单元测试")
-    print(result)
-    print()
-
-    # 5. Focus 到子目标并完成
-    print("5. Focus 到 2.1 并完成")
-    result = goal_tool(tree, focus="3")  # 内部 ID 是 3("2.1" 的内部 ID)
-    print(result)
-    print()
-
-    # 通过显示 ID 完成
-    print("6. 完成目标(使用内部 ID)")
-    result = goal_tool(tree, done="API 接口设计完成,定义了登录和注册端点")
-    print(result)
-    print()
-
-    # 7. 测试 abandon
-    print("7. Focus 到 4(2.2)并放弃")
-    result = goal_tool(tree, focus="4")
-    print(result)
-    print()
-
-    result = goal_tool(tree, abandon="发现需求变更,需要改用 OAuth")
-    print(result)
-    print()
-
-    # 8. 添加新方案
-    print("8. 添加新的实现方案")
-    result = goal_tool(tree, add="实现 OAuth 认证")
-    print(result)
-    print()
-
-    # 9. 查看最终状态
-    print("9. 查看完整计划(包含废弃目标)")
-    print(tree.to_prompt(include_abandoned=True))
-    print()
-
-    # 10. 查看过滤后的计划(默认不显示废弃目标)
-    print("10. 查看过滤后的计划(默认)")
-    print(tree.to_prompt())
-    print()
-
-
-async def test_trace_store():
-    """测试 TraceStore 的 GoalTree 和 Message 存储"""
-    print("=" * 60)
-    print("测试 2: TraceStore 存储功能")
-    print("=" * 60)
-    print()
-
-    # 创建 TraceStore
-    store = FileSystemTraceStore(base_path=".trace_test")
-
-    # 1. 创建 Trace
-    print("1. 创建 Trace")
-    trace = Trace.create(
-        mode="agent",
-        task="测试任务"
-    )
-    await store.create_trace(trace)
-    print(f"   Trace ID: {trace.trace_id[:8]}...")
-    print()
-
-    # 2. 创建并保存 GoalTree
-    print("2. 创建并保存 GoalTree")
-    tree = GoalTree(mission="测试任务")
-    tree.add_goals(["分析", "实现", "测试"])
-    await store.update_goal_tree(trace.trace_id, tree)
-    print(f"   添加了 {len(tree.goals)} 个目标")
-    print()
-
-    # 3. 添加 Messages
-    print("3. 添加 Messages")
-
-    # Focus 到第一个目标
-    tree.focus("1")
-    await store.update_goal_tree(trace.trace_id, tree)
-
-    # 添加 assistant message
-    msg1 = Message.create(
-        trace_id=trace.trace_id,
-        role="assistant",
-        sequence=1,
-        goal_id="1",
-        content={"text": "开始分析代码", "tool_calls": [
-            {
-                "id": "call_1",
-                "function": {
-                    "name": "read_file",
-                    "arguments": '{"path": "src/main.py"}'
-                }
-            }
-        ]},
-        tokens=100,
-        cost=0.002
-    )
-    await store.add_message(msg1)
-    print(f"   Message 1: {msg1.description}")
-
-    # 添加 tool message
-    msg2 = Message.create(
-        trace_id=trace.trace_id,
-        role="tool",
-        sequence=2,
-        goal_id="1",
-        tool_call_id="call_1",
-        content={"tool_name": "read_file", "result": "文件内容..."},
-        tokens=50,
-        cost=0.001
-    )
-    await store.add_message(msg2)
-    print(f"   Message 2: {msg2.description}")
-    print()
-
-    # 4. 查看更新后的 GoalTree(stats 应该自动更新)
-    print("4. 查看更新后的 GoalTree(含 stats)")
-    updated_tree = await store.get_goal_tree(trace.trace_id)
-    goal1 = updated_tree.find("1")
-    print(f"   Goal 1 stats:")
-    print(f"     - message_count: {goal1.self_stats.message_count}")
-    print(f"     - total_tokens: {goal1.self_stats.total_tokens}")
-    print(f"     - total_cost: ${goal1.self_stats.total_cost:.4f}")
-    print()
-
-    # 5. 添加子目标和 Message
-    print("5. 添加子目标和 Message")
-    updated_tree.add_goals(["读取配置", "解析代码"], parent_id="1")
-    updated_tree.focus("4")  # Focus 到第一个子目标
-    await store.update_goal_tree(trace.trace_id, updated_tree)
-
-    msg3 = Message.create(
-        trace_id=trace.trace_id,
-        role="assistant",
-        sequence=3,
-        goal_id="4",
-        content={"text": "读取配置文件"},
-        tokens=80,
-        cost=0.0015
-    )
-    await store.add_message(msg3)
-    print(f"   添加子目标 Message: {msg3.description}")
-    print()
-
-    # 6. 查看累计 stats(父节点应该包含子节点的统计)
-    print("6. 查看累计 stats")
-    updated_tree = await store.get_goal_tree(trace.trace_id)
-    goal1 = updated_tree.find("1")
-    print(f"   Goal 1 cumulative stats:")
-    print(f"     - message_count: {goal1.cumulative_stats.message_count}")
-    print(f"     - total_tokens: {goal1.cumulative_stats.total_tokens}")
-    print(f"     - total_cost: ${goal1.cumulative_stats.total_cost:.4f}")
-    print()
-
-    # 7. 查看 Messages
-    print("7. 查询 Messages")
-    all_messages = await store.get_trace_messages(trace.trace_id)
-    print(f"   总共 {len(all_messages)} 条 Messages")
-
-    goal1_messages = await store.get_messages_by_goal(trace.trace_id, "1")
-    print(f"   Goal 1 的 Messages: {len(goal1_messages)} 条")
-    print()
-
-    # 8. 显示完整 GoalTree
-    print("8. 完整 GoalTree")
-    print(updated_tree.to_prompt())
-    print()
-
-    # 9. 测试级联完成
-    print("9. 测试级联完成")
-    updated_tree.focus("4")
-    updated_tree.complete("4", "配置读取完成")
-    updated_tree.focus("5")
-    updated_tree.complete("5", "代码解析完成")
-
-    # 检查父节点是否自动完成
-    goal1 = updated_tree.find("1")
-    print(f"   Goal 1 status: {goal1.status}")
-    print(f"   Goal 1 summary: {goal1.summary}")
-    print()
-
-    print("✅ TraceStore 测试完成!")
-    print(f"   数据保存在: .trace_test/{trace.trace_id[:8]}...")
-    print()
-
-
-async def test_display_ids():
-    """测试显示 ID 的生成"""
-    print("=" * 60)
-    print("测试 3: 显示 ID 生成")
-    print("=" * 60)
-    print()
-
-    tree = GoalTree(mission="测试显示 ID")
-
-    # 添加多层嵌套目标
-    tree.add_goals(["A", "B", "C"])
-    tree.focus("2")
-    tree.add_goals(["B1", "B2"])
-    tree.focus("4")
-    tree.add_goals(["B1-1", "B1-2"])
-
-    print("完整结构:")
-    print(tree.to_prompt())
-    print()
-
-    # 测试 abandon 后的重新编号
-    print("放弃 B1-1 后:")
-    tree.focus("6")
-    tree.abandon("6", "测试废弃")
-    print(tree.to_prompt())
-    print()
-
-    print("包含废弃目标的完整视图:")
-    print(tree.to_prompt(include_abandoned=True))
-    print()
-
-
-async def main():
-    """运行所有测试"""
-    await test_basic_plan()
-    await test_trace_store()
-    await test_display_ids()
-
-    print("=" * 60)
-    print("所有测试完成!")
-    print("=" * 60)
-
-
-if __name__ == "__main__":
-    asyncio.run(main())

+ 61 - 0
examples/test_subagent_real/README.md

@@ -0,0 +1,61 @@
+# Subagent 工具真实测试
+
+本测试用例用于验证 subagent 工具在真实 LLM 环境下的表现。
+
+## 测试目标
+
+测试 subagent 工具的三种核心模式:
+
+1. **delegate 模式** - 委托子任务给专门的 agent 处理
+2. **explore 模式** - 并行探索多个可能的方案
+3. **evaluate 模式** - 评估任务完成情况
+
+## 测试场景
+
+分析 Agent-main 项目的架构,这个任务自然需要:
+- 委托不同模块的分析(delegate)
+- 并行探索改进方案(explore)
+- 评估分析完整性(evaluate)
+
+## 运行方式
+
+```bash
+cd /Users/elksmmx/Desktop/agent_2.9/Agent-main
+python examples/test_subagent_real/run.py
+```
+
+## 前置要求
+
+1. 配置 `.env` 文件,设置 OpenRouter API Key:
+   ```
+   OPENROUTER_API_KEY=your_key_here
+   ```
+
+2. 确保已安装依赖:
+   ```bash
+   pip install -r requirements.txt
+   ```
+
+## 预期结果
+
+Agent 应该:
+1. 使用 delegate 模式委托 2-4 个子任务分析不同模块
+2. 使用 explore 模式并行探索 2-3 个改进方案
+3. 使用 evaluate 模式评估分析的完整性
+4. 生成完整的架构分析报告
+
+## 输出
+
+- 控制台:实时显示 agent 执行过程和 subagent 调用
+- 文件:`output/subagent_test_result.txt` 包含最终结果和统计
+- Trace:`.trace/` 目录保存完整执行记录
+
+## 可视化
+
+启动 API Server 查看 trace tree:
+
+```bash
+python3 api_server.py
+```
+
+访问:http://localhost:8000/api/traces

+ 216 - 0
examples/test_subagent_real/run.py

@@ -0,0 +1,216 @@
+"""
+Subagent 工具真实测试
+
+使用真实 LLM 测试 subagent 工具的三种模式:
+1. delegate - 委托子任务
+2. explore - 并行探索方案
+3. evaluate - 评估结果
+"""
+
+import os
+import sys
+import asyncio
+from pathlib import Path
+
+# 添加项目根目录到 Python 路径
+sys.path.insert(0, str(Path(__file__).parent.parent.parent))
+
+from dotenv import load_dotenv
+load_dotenv()
+
+from agent.llm.prompts import SimplePrompt
+from agent.core.runner import AgentRunner
+from agent.trace import (
+    FileSystemTraceStore,
+    Trace,
+    Message,
+)
+from agent.llm import create_openrouter_llm_call
+
+
+async def main():
+    # 路径配置
+    base_dir = Path(__file__).parent
+    project_root = base_dir.parent.parent
+    prompt_path = base_dir / "test.prompt"
+    output_dir = base_dir / "output"
+    output_dir.mkdir(exist_ok=True)
+
+    print("=" * 60)
+    print("Subagent 工具测试 (真实 LLM)")
+    print("=" * 60)
+    print()
+
+    # 1. 加载 prompt
+    print("1. 加载 prompt...")
+    prompt = SimplePrompt(prompt_path)
+
+    # 提取配置
+    system_prompt = prompt._messages.get("system", "")
+    user_task = prompt._messages.get("user", "")
+    model_name = prompt.config.get('model', 'gemini-2.5-flash')
+    temperature = float(prompt.config.get('temperature', 0.3))
+
+    print(f"   - 任务: {user_task[:80]}...")
+    print(f"   - 模型: {model_name}")
+
+    # 2. 构建消息
+    print("2. 构建任务消息...")
+    messages = prompt.build_messages()
+
+    # 3. 创建 Agent Runner
+    print("3. 创建 Agent Runner...")
+    print(f"   - 模型: {model_name} (via OpenRouter)")
+
+    # Trace 输出到测试目录
+    trace_dir = base_dir / ".trace"
+    trace_dir.mkdir(exist_ok=True)
+    print(f"   - Trace 目录: {trace_dir}")
+
+    runner = AgentRunner(
+        trace_store=FileSystemTraceStore(base_path=str(trace_dir)),
+        llm_call=create_openrouter_llm_call(model=f"google/{model_name}"),
+        skills_dir=None,
+        debug=True
+    )
+
+    # 4. Agent 模式执行
+    print(f"4. 启动 Agent 模式...")
+    print()
+
+    final_response = ""
+    current_trace_id = None
+    subagent_calls = []
+
+    async for item in runner.run(
+        task=user_task,
+        messages=messages,
+        system_prompt=system_prompt,
+        model=f"google/{model_name}",
+        temperature=temperature,
+        max_iterations=30,  # 增加迭代次数以支持多个 subagent 调用
+    ):
+        # 处理 Trace 对象
+        if isinstance(item, Trace):
+            current_trace_id = item.trace_id
+            if item.status == "running":
+                print(f"[Trace] 开始: {item.trace_id[:8]}")
+            elif item.status == "completed":
+                print(f"[Trace] 完成")
+                print(f"  - Total messages: {item.total_messages}")
+                print(f"  - Total tokens: {item.total_tokens}")
+                print(f"  - Total cost: ${item.total_cost:.4f}")
+            elif item.status == "failed":
+                print(f"[Trace] 失败: {item.error_message}")
+
+        # 处理 Message 对象
+        elif isinstance(item, Message):
+            if item.role == "assistant":
+                content = item.content
+                if isinstance(content, dict):
+                    text = content.get("text", "")
+                    tool_calls = content.get("tool_calls")
+
+                    if text and not tool_calls:
+                        final_response = text
+                        print(f"[Response] Agent 完成")
+                    elif text:
+                        print(f"[Assistant] {text[:100]}...")
+
+                    if tool_calls:
+                        for tc in tool_calls:
+                            tool_name = tc.get("function", {}).get("name", "unknown")
+                            print(f"[Tool Call] {tool_name}")
+
+                            # 记录 subagent 调用
+                            if tool_name == "subagent":
+                                import json
+                                args = tc.get("function", {}).get("arguments", {})
+                                # arguments 可能是字符串,需要解析
+                                if isinstance(args, str):
+                                    try:
+                                        args = json.loads(args)
+                                    except:
+                                        args = {}
+                                mode = args.get("mode", "unknown")
+                                subagent_calls.append({
+                                    "mode": mode,
+                                    "task": args.get("task", args.get("background", ""))[:50]
+                                })
+                                print(f"  → mode: {mode}")
+
+            elif item.role == "tool":
+                content = item.content
+                if isinstance(content, dict):
+                    tool_name = content.get("tool_name", "unknown")
+                    print(f"[Tool Result] {tool_name}")
+                if item.description:
+                    desc = item.description[:80] if len(item.description) > 80 else item.description
+                    print(f"  {desc}...")
+
+    # 5. 输出结果
+    print()
+    print("=" * 60)
+    print("Agent 响应:")
+    print("=" * 60)
+    print(final_response)
+    print("=" * 60)
+    print()
+
+    # 6. 统计 subagent 调用
+    print("=" * 60)
+    print("Subagent 调用统计:")
+    print("=" * 60)
+    delegate_count = sum(1 for call in subagent_calls if call["mode"] == "delegate")
+    explore_count = sum(1 for call in subagent_calls if call["mode"] == "explore")
+    evaluate_count = sum(1 for call in subagent_calls if call["mode"] == "evaluate")
+
+    print(f"  - delegate 模式: {delegate_count} 次")
+    print(f"  - explore 模式: {explore_count} 次")
+    print(f"  - evaluate 模式: {evaluate_count} 次")
+    print(f"  - 总计: {len(subagent_calls)} 次")
+    print()
+
+    for i, call in enumerate(subagent_calls, 1):
+        print(f"  {i}. [{call['mode']}] {call['task']}...")
+    print("=" * 60)
+    print()
+
+    # 7. 保存结果
+    output_file = output_dir / "subagent_test_result.txt"
+    with open(output_file, 'w', encoding='utf-8') as f:
+        f.write("=" * 60 + "\n")
+        f.write("Agent 响应\n")
+        f.write("=" * 60 + "\n\n")
+        f.write(final_response)
+        f.write("\n\n" + "=" * 60 + "\n")
+        f.write("Subagent 调用统计\n")
+        f.write("=" * 60 + "\n\n")
+        f.write(f"delegate 模式: {delegate_count} 次\n")
+        f.write(f"explore 模式: {explore_count} 次\n")
+        f.write(f"evaluate 模式: {evaluate_count} 次\n")
+        f.write(f"总计: {len(subagent_calls)} 次\n\n")
+        for i, call in enumerate(subagent_calls, 1):
+            f.write(f"{i}. [{call['mode']}] {call['task']}...\n")
+
+    print(f"✓ 结果已保存到: {output_file}")
+    print()
+
+    # 8. 可视化提示
+    print("=" * 60)
+    print("Trace 信息:")
+    print("=" * 60)
+    print(f"Trace ID: {current_trace_id}")
+    print(f"Trace 目录: {trace_dir}")
+    print()
+    print("查看 trace 文件:")
+    print(f"   ls -la {trace_dir}")
+    print()
+    print("或启动 API Server 可视化:")
+    print("   python3 api_server.py")
+    print("   访问: http://localhost:8000/api/traces")
+    print("=" * 60)
+
+
+if __name__ == "__main__":
+    asyncio.run(main())

+ 28 - 0
examples/test_subagent_real/test.prompt

@@ -0,0 +1,28 @@
+---
+model: gemini-2.5-flash
+temperature: 0.3
+---
+
+$system$
+你是一个专业的代码分析助手,擅长使用 subagent 工具来分解复杂任务。
+
+你有以下工具可用:
+- subagent: 用于委托子任务、并行探索方案、评估结果
+- read_file, glob_files, grep_content: 用于代码分析
+- goal: 用于任务规划和进度追踪
+
+**重要规则**:
+- 在任务完成前,必须始终保持至少一个活跃的 goal
+- 当所有 goal 完成后,如果任务还未完全结束,必须立即创建新的 goal
+- 只有在确认任务完全完成后,才能让 goal 列表为空
+- goal 为空表示任务已完成,系统将结束执行
+
+$user$
+请分析 /Users/elksmmx/Desktop/agent_2.9/Agent-main 项目的架构,并提出改进建议。
+
+具体要求:
+1. 使用 subagent 的 delegate 模式,委托子 agent 分析不同模块(core、trace、tools、memory)
+2. 使用 subagent 的 explore 模式,并行探索 2-3 个可能的架构改进方案
+3. 使用 subagent 的 evaluate 模式,评估你的分析是否完整
+
+请充分利用 subagent 工具的各种模式来完成这个任务。

+ 28 - 0
examples/test_subagent_real/test_continue.prompt

@@ -0,0 +1,28 @@
+---
+model: gemini-2.5-flash
+temperature: 0.3
+---
+
+$system$
+你是一个专业的代码分析助手,擅长使用 subagent 工具来分解复杂任务。
+
+你有以下工具可用:
+- subagent: 用于委托子任务、并行探索方案、评估结果
+  - mode="delegate": 委托子任务
+  - mode="explore": 并行探索多个方案
+  - mode="evaluate": 评估结果
+  - continue_from: 继续已有的 trace(用于迭代改进)
+- read_file, glob_files, grep_content: 用于代码分析
+- goal: 用于任务规划和进度追踪
+
+$user$
+请分析 /Users/elksmmx/Desktop/agent_2.9/Agent-main 项目的 core 模块架构,并提出改进建议。
+
+具体要求:
+1. 使用 subagent 的 delegate 模式,委托子 agent 分析 core 模块的基本架构
+2. 使用 subagent 的 delegate 模式 + continue_from 参数,继续深入分析 core 模块的设计模式和最佳实践
+3. 使用 subagent 的 explore 模式,并行探索 2-3 个可能的改进方案
+4. 使用 subagent 的 evaluate 模式,评估你的分析是否完整
+5. 如果评估不通过,使用 continue_from 继续改进分析
+
+**重点测试 continue_from 参数的使用**,展示如何在同一个 trace 上迭代改进任务。

+ 187 - 0
examples/test_subagent_real/visualize_trace.py

@@ -0,0 +1,187 @@
+"""
+Trace 树可视化工具
+
+读取 trace 目录并生成树形结构的可视化输出
+"""
+
+import json
+import sys
+from pathlib import Path
+from datetime import datetime
+
+
+def load_trace_meta(trace_dir):
+    """加载 trace 的 meta.json"""
+    meta_file = trace_dir / "meta.json"
+    if not meta_file.exists():
+        return None
+    with open(meta_file, 'r', encoding='utf-8') as f:
+        return json.load(f)
+
+
+def format_duration(start_str, end_str):
+    """计算并格式化持续时间"""
+    if not start_str or not end_str:
+        return "N/A"
+    try:
+        start = datetime.fromisoformat(start_str)
+        end = datetime.fromisoformat(end_str)
+        duration = (end - start).total_seconds()
+        return f"{duration:.1f}s"
+    except:
+        return "N/A"
+
+
+def extract_mode_from_trace_id(trace_id):
+    """从 trace_id 中提取模式"""
+    if '@delegate-' in trace_id:
+        return 'delegate'
+    elif '@explore-' in trace_id:
+        return 'explore'
+    elif '@evaluate-' in trace_id:
+        return 'evaluate'
+    return 'main'
+
+
+def print_trace_tree(trace_base_path, output_file=None):
+    """打印 trace 树结构"""
+    trace_base = Path(trace_base_path)
+
+    if not trace_base.exists():
+        print(f"错误: Trace 目录不存在: {trace_base}")
+        return
+
+    # 查找所有 trace 目录
+    all_traces = {}
+    main_trace_id = None
+
+    for trace_dir in sorted(trace_base.iterdir()):
+        if not trace_dir.is_dir():
+            continue
+
+        meta = load_trace_meta(trace_dir)
+        if not meta:
+            continue
+
+        trace_id = meta['trace_id']
+        all_traces[trace_id] = {
+            'meta': meta,
+            'dir': trace_dir,
+            'children': []
+        }
+
+        # 找到主 trace
+        if meta.get('parent_trace_id') is None:
+            main_trace_id = trace_id
+
+    if not main_trace_id:
+        print("错误: 未找到主 trace")
+        return
+
+    # 构建树结构
+    for trace_id, trace_info in all_traces.items():
+        parent_id = trace_info['meta'].get('parent_trace_id')
+        if parent_id and parent_id in all_traces:
+            all_traces[parent_id]['children'].append(trace_id)
+
+    # 输出函数
+    def output(text):
+        print(text)
+        if output_file:
+            output_file.write(text + '\n')
+
+    # 打印树
+    output("=" * 80)
+    output("Trace 执行树")
+    output("=" * 80)
+    output("")
+
+    def print_node(trace_id, prefix="", is_last=True):
+        trace_info = all_traces[trace_id]
+        meta = trace_info['meta']
+
+        # 树形连接符
+        connector = "└── " if is_last else "├── "
+
+        # 提取信息
+        mode = extract_mode_from_trace_id(trace_id)
+        task = meta.get('task', 'N/A')
+        if len(task) > 60:
+            task = task[:60] + "..."
+        status = meta.get('status', 'unknown')
+        messages = meta.get('total_messages', 0)
+        tokens = meta.get('total_tokens', 0)
+        duration = format_duration(
+            meta.get('created_at'),
+            meta.get('completed_at')
+        )
+
+        # 状态符号
+        status_symbol = {
+            'completed': '✓',
+            'failed': '✗',
+            'running': '⟳',
+        }.get(status, '?')
+
+        # 打印节点
+        output(f"{prefix}{connector}[{mode}] {status_symbol} {trace_id[:8]}")
+        output(f"{prefix}{'    ' if is_last else '│   '}Task: {task}")
+        output(f"{prefix}{'    ' if is_last else '│   '}Stats: {messages} msgs, {tokens:,} tokens, {duration}")
+
+        # 打印子节点
+        children = trace_info['children']
+        for i, child_id in enumerate(children):
+            is_last_child = (i == len(children) - 1)
+            child_prefix = prefix + ("    " if is_last else "│   ")
+            print_node(child_id, child_prefix, is_last_child)
+
+    # 从主 trace 开始打印
+    print_node(main_trace_id)
+
+    output("")
+    output("=" * 80)
+    output("统计信息")
+    output("=" * 80)
+
+    # 统计各模式的数量
+    mode_counts = {}
+    total_messages = 0
+    total_tokens = 0
+
+    for trace_info in all_traces.values():
+        meta = trace_info['meta']
+        mode = extract_mode_from_trace_id(meta['trace_id'])
+        mode_counts[mode] = mode_counts.get(mode, 0) + 1
+        total_messages += meta.get('total_messages', 0)
+        total_tokens += meta.get('total_tokens', 0)
+
+    output(f"总 Trace 数: {len(all_traces)}")
+    output(f"  - main: {mode_counts.get('main', 0)}")
+    output(f"  - delegate: {mode_counts.get('delegate', 0)}")
+    output(f"  - explore: {mode_counts.get('explore', 0)}")
+    output(f"  - evaluate: {mode_counts.get('evaluate', 0)}")
+    output(f"")
+    output(f"总消息数: {total_messages}")
+    output(f"总 Token 数: {total_tokens:,}")
+    output("=" * 80)
+
+
+if __name__ == "__main__":
+    if len(sys.argv) < 2:
+        print("用法: python visualize_trace.py <trace_directory> [output_file]")
+        print("示例: python visualize_trace.py .trace")
+        sys.exit(1)
+
+    trace_dir = sys.argv[1]
+    output_path = sys.argv[2] if len(sys.argv) > 2 else None
+
+    output_file = None
+    if output_path:
+        output_file = open(output_path, 'w', encoding='utf-8')
+
+    try:
+        print_trace_tree(trace_dir, output_file)
+    finally:
+        if output_file:
+            output_file.close()
+            print(f"\n✓ 输出已保存到: {output_path}")

+ 1 - 0
vendor/browser-use

@@ -0,0 +1 @@
+Subproject commit c011d07e7e9e41c06b5c72036c6e3efa5b9f287d