Browse Source

feat:合并代码

max_liu 1 month ago
parent
commit
f14463c935
61 changed files with 2301 additions and 5280 deletions
  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(pip install:*)",
       "Bash(timeout 60 python:*)",
       "Bash(timeout 60 python:*)",
       "Bash(timeout 240 python:*)",
       "Bash(timeout 240 python:*)",
-      "Bash(curl:*)"
+      "Bash(curl:*)",
+      "Bash(tree:*)"
     ],
     ],
     "deny": [],
     "deny": [],
     "ask": []
     "ask": []

+ 17 - 7
agent/__init__.py

@@ -4,19 +4,21 @@ Reson Agent - 模块化、可扩展的 Agent 框架
 核心导出:
 核心导出:
 - AgentRunner: Agent 执行引擎
 - AgentRunner: Agent 执行引擎
 - AgentConfig: Agent 配置
 - AgentConfig: Agent 配置
-- Trace, Step: 执行追踪
+- Trace, Message, Goal: 执行追踪
 - Experience, Skill: 记忆模型
 - Experience, Skill: 记忆模型
 - tool: 工具装饰器
 - tool: 工具装饰器
 - TraceStore, MemoryStore: 存储接口
 - 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
 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 import tool, ToolRegistry, get_tool_registry
 from agent.tools.models import ToolResult, ToolContext
 from agent.tools.models import ToolResult, ToolContext
 
 
-__version__ = "0.2.0"
+__version__ = "0.3.0"
 
 
 __all__ = [
 __all__ = [
     # Core
     # Core
     "AgentRunner",
     "AgentRunner",
     "AgentConfig",
     "AgentConfig",
     "CallResult",
     "CallResult",
-    # Execution
+    "AgentPreset",
+    "AGENT_PRESETS",
+    "get_preset",
+    # Trace
     "Trace",
     "Trace",
+    "Message",
     "Step",
     "Step",
     "StepType",
     "StepType",
     "StepStatus",
     "StepStatus",
+    "Goal",
+    "GoalTree",
+    "GoalStatus",
     "TraceStore",
     "TraceStore",
+    "FileSystemTraceStore",
     # Memory
     # Memory
     "Experience",
     "Experience",
     "Skill",
     "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())
 1. Agent 主循环逻辑(call() 和 run())
 2. 配置数据类(AgentConfig, CallResult)
 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__ = [
 __all__ = [
     "AgentRunner",
     "AgentRunner",
     "BUILTIN_TOOLS",
     "BUILTIN_TOOLS",
     "AgentConfig",
     "AgentConfig",
     "CallResult",
     "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
 import logging
+from dataclasses import dataclass
 from datetime import datetime
 from datetime import datetime
 from typing import AsyncIterator, Optional, Dict, Any, List, Callable, Literal, Union
 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.models import Experience, Skill
 from agent.memory.protocols import MemoryStore, StateStore
 from agent.memory.protocols import MemoryStore, StateStore
 from agent.memory.skill_loader import load_skills_from_dir
 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__)
 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 = [
 BUILTIN_TOOLS = [
     # 文件操作工具
     # 文件操作工具
@@ -42,6 +61,7 @@ BUILTIN_TOOLS = [
     "skill",
     "skill",
     "list_skills",
     "list_skills",
     "goal",
     "goal",
+    "subagent",
 
 
     # 搜索工具
     # 搜索工具
     "search_posts",
     "search_posts",
@@ -166,7 +186,6 @@ class AgentRunner:
             for tool in tools:
             for tool in tools:
                 if tool not in tool_names:
                 if tool not in tool_names:
                     tool_names.append(tool)
                     tool_names.append(tool)
-
         tool_schemas = self.tools.get_schemas(tool_names)
         tool_schemas = self.tools.get_schemas(tool_names)
 
 
         # 创建 Trace
         # 创建 Trace
@@ -224,6 +243,78 @@ class AgentRunner:
 
 
     # ===== Agent 模式 =====
     # ===== 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(
     async def run(
         self,
         self,
         task: str,
         task: str,
@@ -236,6 +327,7 @@ class AgentRunner:
         max_iterations: Optional[int] = None,
         max_iterations: Optional[int] = None,
         enable_memory: Optional[bool] = None,
         enable_memory: Optional[bool] = None,
         auto_execute_tools: Optional[bool] = None,
         auto_execute_tools: Optional[bool] = None,
+        trace_id: Optional[str] = None,
         **kwargs
         **kwargs
     ) -> AsyncIterator[Union[Trace, Message]]:
     ) -> AsyncIterator[Union[Trace, Message]]:
         """
         """
@@ -252,6 +344,7 @@ class AgentRunner:
             max_iterations: 最大迭代次数
             max_iterations: 最大迭代次数
             enable_memory: 是否启用记忆
             enable_memory: 是否启用记忆
             auto_execute_tools: 是否自动执行工具
             auto_execute_tools: 是否自动执行工具
+            trace_id: Trace ID(可选,传入时复用已有 Trace)
             **kwargs: 其他参数
             **kwargs: 其他参数
 
 
         Yields:
         Yields:
@@ -274,26 +367,44 @@ class AgentRunner:
                     tool_names.append(tool)
                     tool_names.append(tool)
         tool_schemas = self.tools.get_schemas(tool_names)
         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(表示开始)
         # 返回 Trace(表示开始)
         yield trace_obj
         yield trace_obj
@@ -319,13 +430,34 @@ class AgentRunner:
                     logger.info(f"加载 {len(skills)} 个内置 skills")
                     logger.info(f"加载 {len(skills)} 个内置 skills")
 
 
             # 构建初始消息
             # 构建初始消息
+            sequence = 1
             if messages is None:
             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
             # 记录初始 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
                 # 注入记忆和 skills 到 system prompt
                 full_system = system_prompt
                 full_system = system_prompt
                 if skills_text:
                 if skills_text:
@@ -348,21 +480,22 @@ class AgentRunner:
                     yield system_msg
                     yield system_msg
                     sequence += 1
                     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
             # 获取 GoalTree
             goal_tree = None
             goal_tree = None
@@ -370,7 +503,7 @@ class AgentRunner:
                 goal_tree = await self.trace_store.get_goal_tree(trace_id)
                 goal_tree = await self.trace_store.get_goal_tree(trace_id)
 
 
                 # 设置 goal_tree 到 goal 工具(供 LLM 调用)
                 # 设置 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)
                 set_goal_tree(goal_tree)
 
 
             # 执行循环
             # 执行循环
@@ -451,7 +584,9 @@ class AgentRunner:
                             uid=uid or "",
                             uid=uid or "",
                             context={
                             context={
                                 "store": self.trace_store,
                                 "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.grep import grep_content
 from agent.tools.builtin.bash import bash_command
 from agent.tools.builtin.bash import bash_command
 from agent.tools.builtin.skill import skill, list_skills
 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.search import search_posts, get_search_suggestions
 from agent.tools.builtin.sandbox import (sandbox_create_environment, sandbox_run_shell,
 from agent.tools.builtin.sandbox import (sandbox_create_environment, sandbox_run_shell,
                                          sandbox_rebuild_with_ports,sandbox_destroy_environment)
                                          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
 import agent.tools.builtin.browser  # noqa: F401
 
 
 __all__ = [
 __all__ = [
+    # 文件操作
     "read_file",
     "read_file",
     "edit_file",
     "edit_file",
     "write_file",
     "write_file",
     "glob_files",
     "glob_files",
     "grep_content",
     "grep_content",
+    # 系统工具
     "bash_command",
     "bash_command",
     "skill",
     "skill",
     "list_skills",
     "list_skills",
-    "goal",
+    "subagent",
     "search_posts",
     "search_posts",
     "get_search_suggestions",
     "get_search_suggestions",
     "sandbox_create_environment",
     "sandbox_create_environment",

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

@@ -1250,8 +1250,24 @@ async def browser_get_selector_map() -> ToolResult:
     try:
     try:
         browser, tools = await get_browser_session()
         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 = []
         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)
 # 默认 skills 目录(优先级:项目 skills > 框架 skills)
 DEFAULT_SKILLS_DIRS = [
 DEFAULT_SKILLS_DIRS = [
     os.getenv("SKILLS_DIR", "./skills"),      # 项目特定 skills(优先)
     os.getenv("SKILLS_DIR", "./skills"),      # 项目特定 skills(优先)
-    "./agent/skills"                           # 框架内置 skills
+    "./agent/memory/skills"                    # 框架内置 skills
 ]
 ]
 
 
 # 默认单一目录(用于 list_skills)
 # 默认单一目录(用于 list_skills)
@@ -36,7 +36,7 @@ def _check_skill_setup(skill_name: str) -> Optional[str]:
     if skill_name in ["browser-use", "browser_use"]:
     if skill_name in ["browser-use", "browser_use"]:
         try:
         try:
             # 动态导入 browser-use skill 的 setup 模块
             # 动态导入 browser-use skill 的 setup 模块
-            from agent.skills.browser_use.setup import (
+            from agent.memory.skills.browser_use.setup import (
                 _check_browser_use_cli,
                 _check_browser_use_cli,
                 _check_chromium_installed
                 _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 fastapi import APIRouter, HTTPException, Query
 from pydantic import BaseModel
 from pydantic import BaseModel
 
 
-from agent.execution.protocols import TraceStore
+from .protocols import TraceStore
 
 
 
 
 router = APIRouter(prefix="/api/traces", tags=["traces"])
 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 typing import List, Dict, Any, Optional
-from agent.goal.models import GoalTree, Goal
+from .goal_models import GoalTree, Goal
 
 
 
 
 def compress_messages_for_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 typing import Optional, List, TYPE_CHECKING
 
 
+from agent.tools import tool
+
 if TYPE_CHECKING:
 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(
 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 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
 @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 typing import Dict, List, Optional, Any
 from datetime import datetime
 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:
 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
        code .trace/tree.txt
 
 
     3. 代码中使用:
     3. 代码中使用:
-       from agent.execution import dump_tree
+       from agent.trace import dump_tree
        dump_tree(trace, steps)
        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 datetime import datetime
 from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
 from fastapi import APIRouter, WebSocket, WebSocketDisconnect, Query
 
 
-from agent.execution.protocols import TraceStore
+from .protocols import TraceStore
 
 
 
 
 router = APIRouter(prefix="/api/traces", tags=["websocket"])
 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
 from fastapi.middleware.cors import CORSMiddleware
 import uvicorn
 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 功能需求与架构设计文档
 # 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(技能库)                                     │
 │ Layer 3: Skills(技能库)                                     │
-│ - Markdown 文件,存储详细的能力描述                            │
-│ - 通过 skill 工具按需加载                                     │
+│ - Markdown 文件,存储领域知识和能力描述
+│ - 通过 skill 工具按需加载到对话历史
 └─────────────────────────────────────────────────────────────┘
 └─────────────────────────────────────────────────────────────┘
                               │ 归纳
                               │ 归纳
@@ -53,43 +97,39 @@
                               │ 提取
                               │ 提取
 ┌─────────────────────────────────────────────────────────────┐
 ┌─────────────────────────────────────────────────────────────┐
-│ Layer 1: Task State(任务状态)
+│ Layer 1: Trace(任务状态)     
 │ - 当前任务的工作记忆                                          │
 │ - 当前任务的工作记忆                                          │
 │ - Trace + Messages 记录执行过程                               │
 │ - Trace + Messages 记录执行过程                               │
-│ - GoalTree 管理执行计划
+│ - Goals 管理执行计划   
 └─────────────────────────────────────────────────────────────┘
 └─────────────────────────────────────────────────────────────┘
 ```
 ```
 
 
-**注入方式**:
-- **Skills**:通过 `skill` 工具动态加载到对话历史
-- **Experiences**:检索后注入到 system prompt
-
 ---
 ---
 
 
 ## 核心流程:Agent Loop
 ## 核心流程:Agent Loop
 
 
 ```python
 ```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
     # 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 = 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}]
     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()
         plan_text = goal_tree.to_prompt()
 
 
         # 调用 LLM
         # 调用 LLM
@@ -100,67 +140,42 @@ async def run(task: str, max_steps: int = 50) -> AsyncIterator[Union[Trace, Mess
         )
         )
 
 
         # 记录 assistant Message
         # 记录 assistant Message
-        assistant_msg = Message.create(
+        await store.add_message(Message.create(
             trace_id=trace.trace_id,
             trace_id=trace.trace_id,
             role="assistant",
             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
         yield assistant_msg
 
 
         # 没有工具调用,完成
         # 没有工具调用,完成
         if not response.tool_calls:
         if not response.tool_calls:
             break
             break
 
 
-        # 执行工具
+        # 执行工具,记录 tool Message
         for tool_call in response.tool_calls:
         for tool_call in response.tool_calls:
             result = await execute_tool(tool_call)
             result = await execute_tool(tool_call)
-
-            # 记录 tool Message
-            tool_msg = Message.create(
+            await store.add_message(Message.create(
                 trace_id=trace.trace_id,
                 trace_id=trace.trace_id,
                 role="tool",
                 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
             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"
     trace.status = "completed"
-    await trace_store.update_trace(trace.trace_id, status="completed")
     yield trace
     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(任务执行)
 ### Trace(任务执行)
 
 
+一次完整的 Agent 执行。所有 Agent(主、子、人类协助)都是 Trace。
+
 ```python
 ```python
 @dataclass
 @dataclass
 class Trace:
 class Trace:
     trace_id: str
     trace_id: str
-    mode: Literal["call", "agent"]
+    mode: Literal["call", "agent"]           # 单次调用 or Agent 模式
+
+    # Prompt 标识
+    prompt_name: Optional[str] = None
+
+    # Agent 模式特有
     task: Optional[str] = None
     task: Optional[str] = None
     agent_type: 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_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_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
     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(执行消息)
 ### Message(执行消息)
 
 
-对应 LLM API 的消息,加上元数据。不再有 parent_id 树结构。
+对应 LLM API 的消息,每条 Message 关联一个 Goal
 
 
 ```python
 ```python
 @dataclass
 @dataclass
 class Message:
 class Message:
-    message_id: str
+    message_id: str                          # 格式:{trace_id}-{sequence:04d}
     trace_id: str
     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
     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
 ```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` 工具时 |
 | **普通 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
 ```python
 # 1. 检索相关 Experiences
 # 1. 检索相关 Experiences
 experiences = await db.query(
 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
 # 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
 ```python
 class TraceStore(Protocol):
 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 add_message(self, message: Message) -> None: ...
     async def get_messages(self, trace_id: str) -> List[Message]: ...
     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]: ...
     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
    - 不需要拆分成 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 |
 | `bash_command` | 执行 shell 命令 | opencode bash.ts |
 | `glob_files` | 文件模式匹配 | opencode glob.ts |
 | `glob_files` | 文件模式匹配 | opencode glob.ts |
 | `grep_content` | 内容搜索(正则表达式) | opencode grep.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.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner
 from agent.core.runner import AgentRunner
-from agent.execution import (
+from agent.trace import (
     FileSystemTraceStore,
     FileSystemTraceStore,
     Trace,
     Trace,
     Message,
     Message,

+ 76 - 27
examples/research/run.py

@@ -1,7 +1,7 @@
 """
 """
-浏览器调研示例
+创意写作调研示例
 
 
-使用 Agent 模式 + 浏览器工具进行网络调研
+使用 Agent 模式 + explore 工具进行创意内容探索
 """
 """
 
 
 import os
 import os
@@ -17,7 +17,7 @@ load_dotenv()
 
 
 from agent.llm.prompts import SimplePrompt
 from agent.llm.prompts import SimplePrompt
 from agent.core.runner import AgentRunner
 from agent.core.runner import AgentRunner
-from agent.execution import (
+from agent.trace import (
     FileSystemTraceStore,
     FileSystemTraceStore,
     Trace,
     Trace,
     Message,
     Message,
@@ -33,12 +33,8 @@ async def main():
     output_dir = base_dir / "output"
     output_dir = base_dir / "output"
     output_dir.mkdir(exist_ok=True)
     output_dir.mkdir(exist_ok=True)
 
 
-    # Skills 目录(可选:用户自定义 skills)
-    # 注意:内置 skills(agent/skills/core.md)会自动加载
-    skills_dir = None  # 或者指定自定义 skills 目录,如: project_root / "skills"
-
     print("=" * 60)
     print("=" * 60)
-    print("浏览器调研任务 (Agent 模式)")
+    print("创意写作调研 (Agent 模式)")
     print("=" * 60)
     print("=" * 60)
     print()
     print()
 
 
@@ -59,17 +55,20 @@ async def main():
     print("2. 构建任务消息...")
     print("2. 构建任务消息...")
     messages = prompt.build_messages()
     messages = prompt.build_messages()
 
 
-    # 3. 创建 Agent Runner(配置 skills 和浏览器工具)
+    # 3. 创建 Agent Runner
     print("3. 创建 Agent Runner...")
     print("3. 创建 Agent Runner...")
-    print(f"   - Skills 目录: {skills_dir}")
     print(f"   - 模型: {model_name} (via OpenRouter)")
     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(
     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}"),
         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 模式执行
     # 4. Agent 模式执行
@@ -78,6 +77,7 @@ async def main():
 
 
     final_response = ""
     final_response = ""
     current_trace_id = None
     current_trace_id = None
+    subagent_calls = []
 
 
     async for item in runner.run(
     async for item in runner.run(
         task=user_task,
         task=user_task,
@@ -85,9 +85,9 @@ async def main():
         system_prompt=system_prompt,
         system_prompt=system_prompt,
         model=f"google/{model_name}",
         model=f"google/{model_name}",
         temperature=temperature,
         temperature=temperature,
-        max_iterations=20,  # 调研任务可能需要更多迭代
+        max_iterations=30,  # 增加迭代次数以支持多个 subagent 调用
     ):
     ):
-        # 处理 Trace 对象(整体状态变化)
+        # 处理 Trace 对象
         if isinstance(item, Trace):
         if isinstance(item, Trace):
             current_trace_id = item.trace_id
             current_trace_id = item.trace_id
             if item.status == "running":
             if item.status == "running":
@@ -100,7 +100,7 @@ async def main():
             elif item.status == "failed":
             elif item.status == "failed":
                 print(f"[Trace] 失败: {item.error_message}")
                 print(f"[Trace] 失败: {item.error_message}")
 
 
-        # 处理 Message 对象(执行过程)
+        # 处理 Message 对象
         elif isinstance(item, Message):
         elif isinstance(item, Message):
             if item.role == "assistant":
             if item.role == "assistant":
                 content = item.content
                 content = item.content
@@ -109,7 +109,6 @@ async def main():
                     tool_calls = content.get("tool_calls")
                     tool_calls = content.get("tool_calls")
 
 
                     if text and not tool_calls:
                     if text and not tool_calls:
-                        # 纯文本回复(最终响应)
                         final_response = text
                         final_response = text
                         print(f"[Response] Agent 完成")
                         print(f"[Response] Agent 完成")
                     elif text:
                     elif text:
@@ -120,6 +119,23 @@ async def main():
                             tool_name = tc.get("function", {}).get("name", "unknown")
                             tool_name = tc.get("function", {}).get("name", "unknown")
                             print(f"[Tool Call] {tool_name}")
                             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":
             elif item.role == "tool":
                 content = item.content
                 content = item.content
                 if isinstance(content, dict):
                 if isinstance(content, dict):
@@ -138,25 +154,58 @@ async def main():
     print("=" * 60)
     print("=" * 60)
     print()
     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:
     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(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(f"✓ 结果已保存到: {output_file}")
     print()
     print()
 
 
-    # 提示使用 API 可视化
+    # 8. 可视化提示
     print("=" * 60)
     print("=" * 60)
-    print("可视化 Step Tree:")
+    print("Trace 信息:")
     print("=" * 60)
     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()
-    print("2. 浏览器访问:")
-    print("   http://localhost:8000/api/traces")
+    print("查看 trace 文件:")
+    print(f"   ls -la {trace_dir}")
     print()
     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)
     print("=" * 60)
 
 
 
 

+ 7 - 2
examples/research/test.prompt

@@ -7,5 +7,10 @@ $system$
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 你是最顶尖的AI助手,可以拆分并调用工具逐步解决复杂问题。
 
 
 $user$
 $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