Просмотр исходного кода

补充递归模式和任务协议文档

SamLee 1 день назад
Родитель
Сommit
7a11b33d9f

+ 101 - 30
cyber_agent/README.md

@@ -15,7 +15,7 @@
 Agent Core 是一个完整的 Agent 执行框架,提供:
 - Trace、Message、Goal 管理
 - 工具系统(文件、命令、网络、浏览器)
-- LLM 集成(Gemini、OpenRouter、Yescode)
+- LLM 集成(Qwen、Gemini、Claude、OpenRouter、Yescode)
 - Skills(领域知识注入)
 - 子 Agent 机制
 
@@ -26,10 +26,12 @@ Agent Core 是一个完整的 Agent 执行框架,提供:
 ## 模块结构
 
 ```
-agent/
+cyber_agent/
 ├── core/                  # 核心引擎
 │   ├── runner.py          # AgentRunner + 运行时配置
-│   └── presets.py         # Agent 预设(explore、analyst 等)
+│   ├── agent_mode.py      # Legacy / Recursive 模式策略
+│   ├── task_protocol.py   # Recursive 任务报告与审核协议
+│   └── presets.py         # Agent 预设(delegate、explore、evaluate 等)
 ├── trace/                 # 执行追踪(含计划管理)
 │   ├── models.py          # Trace, Message
@@ -58,7 +60,9 @@ agent/
 │   └── skills/            # 内置 Skills
 └── llm/                   # LLM 集成
+    ├── qwen.py           # Qwen / 百炼 Provider
     ├── gemini.py          # Gemini Provider
+    ├── claude.py          # Anthropic 原生 Provider
     ├── openrouter.py      # OpenRouter Provider
     └── yescode.py         # Yescode Provider
 ```
@@ -89,29 +93,93 @@ agent/
 
 ## 快速开始
 
+### 安装与环境变量
+
+```bash
+python3.11 -m venv .venv
+source .venv/bin/activate
+pip install -e . openai
+cp .env.template .env
+```
+
+Qwen 最小配置:
+
+```env
+QWEN_API_KEY=...
+QWEN_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
+AGENT_MODE=legacy
+```
+
+代码读取的是 `QWEN_API_KEY`;如果已有百炼 `BAILIAN_API_KEY`,需将其值同步给 `QWEN_API_KEY`。`AGENT_MODE=recursive` 还要在 `RunConfig.root_completion_criteria` 中提供根任务完成标准。
+
 ### 基础使用
 
 ```python
+import asyncio
+from dotenv import load_dotenv
+
 from cyber_agent.core.runner import AgentRunner, RunConfig
 from cyber_agent.trace import FileSystemTraceStore, Trace, Message
 from cyber_agent.llm import create_qwen_llm_call
 
-runner = AgentRunner(
-    llm_call=create_qwen_llm_call(model="qwen3.5-plus"),
-    trace_store=FileSystemTraceStore(base_path=".trace"),
-    skills_dir="./skills",      # 项目 skills 目录(可选)
-)
+load_dotenv()
+
+async def main():
+    runner = AgentRunner(
+        llm_call=create_qwen_llm_call(model="qwen3.5-plus"),
+        trace_store=FileSystemTraceStore(base_path=".trace"),
+        skills_dir="./skills",      # 项目 skills 目录(可选)
+    )
+
+    async for item in runner.run(
+        messages=[{"role": "user", "content": "分析项目架构"}],
+        config=RunConfig(model="qwen3.5-plus"),
+    ):
+        if isinstance(item, Trace):
+            print(f"Trace: {item.trace_id}")
+        elif isinstance(item, Message) and item.role == "assistant":
+            print(item.content)
+
+asyncio.run(main())
+```
+
+### Recursive 最小示例
+
+先在环境中选择模式:
+
+```env
+AGENT_MODE=recursive
+```
+
+新建 Recursive 根 Trace 必须显式提供完成标准。在上例的 `main()`
+中将 `runner.run(...)` 替换为:
 
+```python
 async for item in runner.run(
-    messages=[{"role": "user", "content": "分析项目架构"}],
-    config=RunConfig(model="qwen3.5-plus"),
+    messages=[{"role": "user", "content": "分析项目架构并给出依赖关系"}],
+    config=RunConfig(
+        model="qwen3.5-plus",
+        root_completion_criteria=[
+            "列出主要模块及职责",
+            "说明核心调用链和模块依赖",
+        ],
+    ),
 ):
-    if isinstance(item, Trace):
-        print(f"Trace: {item.trace_id}")
-    elif isinstance(item, Message) and item.role == "assistant":
-        print(item.content)
+    ...
 ```
 
+Recursive 子任务使用结构化 TaskBrief,并经报告、独立验收和父级审核后继续规划。深度、工具权限、调度、停止和资源预算见 [Agent 运行模式](./docs/agent-modes.md),报告与审核见 [Recursive 任务协议](./docs/recursive-task-protocol.md)。
+
+其他 Provider 通过对应的 `create_*_llm_call()` 创建,再传入 `AgentRunner(llm_call=...)`:
+
+| Provider | 创建函数 | 环境变量 |
+|------|------|------|
+| Qwen | `create_qwen_llm_call()` | `QWEN_API_KEY`,可选 `QWEN_BASE_URL` |
+| Gemini | `create_gemini_llm_call()` | `GEMINI_API_KEY` |
+| Claude | `create_claude_llm_call()` | `CLAUDE_CODE_KEY` 或 `ANTHROPIC_API_KEY`,可选对应 Base URL |
+| OpenRouter | `create_openrouter_llm_call()` | `OPEN_ROUTER_API_KEY` |
+| Yescode | `create_yescode_llm_call()` | `YESCODE_BASE_URL` + `YESCODE_API_KEY` |
+
 ### RunConfig 关键参数
 
 ```python
@@ -170,12 +238,22 @@ async def my_tool(arg: str, ctx: ToolContext) -> ToolResult:
 examples/research/
 ├── run.py              # 入口(含交互控制、续跑、暂停)
 ├── config.py           # RunConfig + KnowledgeConfig 配置
-├── presets.json        # Agent 预设(工具权限、skills 过滤)
+├── presets.json        # Agent 预设(system prompt、skills 过滤)
 ├── requirement.prompt  # 任务 prompt($system$ + $user$ 段)
 ├── skills/             # 项目自定义 skills
 └── tools/              # 项目自定义工具
 ```
 
+### SDK 与 CLI 入口
+
+| 入口 | 用途 |
+|------|------|
+| `AgentRunner.run()` / `run_result()` | Python 底层 SDK,创建或续跑 Trace |
+| `cyber_agent.client.invoke_agent()` | 统一调用本地或 `remote_*` Agent |
+| `api_server.py` | Trace REST API 和 WebSocket 服务 |
+| `cyber_agent.cli.interactive.InteractiveController` | 示例运行器可接入的暂停、续跑、反思与压缩菜单 |
+| `python -m cyber_agent.cli.extraction_review --trace <ID>` | 知识提取的审核和提交 CLI |
+
 ---
 
 ## 文档
@@ -183,6 +261,8 @@ examples/research/
 | 文档 | 内容 |
 |------|------|
 | [架构设计](./docs/architecture.md) | 框架完整架构 |
+| [Agent 运行模式](./docs/agent-modes.md) | Legacy / Recursive 模式、权限、调度、停止与资源预算 |
+| [Recursive 任务协议](./docs/recursive-task-protocol.md) | TaskBrief、TaskReport、Validator 和父级审核 |
 | [Context 管理](./docs/context-management.md) | 注入机制、压缩策略、Skill 指定注入 |
 | [工具系统](./docs/tools.md) | 工具定义、注册、参数注入 |
 | [Skills 指南](./docs/skills.md) | Skill 分类、编写、加载 |
@@ -209,30 +289,21 @@ examples/research/
 
 ## Claude Code 集成
 
-本仓库自带项目级 Claude Code skill:[`.claude/skills/agent/`](../.claude/skills/agent/)
+本仓库自带 Claude Code / Codex skill 源码:[`skills4claude/agent/`](../skills4claude/agent/)
 
 - `SKILL.md` — skill 元数据(description 给 Claude Code 路由用)
 - `invoke.py` — 20 行薄脚本,`from cyber_agent import invoke_agent` 然后透传命令行参数
 
-### 在本仓库使用(自动激活)
+### 安装 skill
 
-Claude Code 在本仓库目录下启动时自动加载 `.claude/skills/` 下的 skill。前提是 `cyber-agent` 包可 import:
+`cyber-agent` 包可 import 后,使用仓库安装脚本链接到 Claude Code
 
 ```bash
 pip install -e .
+bash skills4claude/install.sh --claude --skills agent
 ```
 
-### 在其他项目使用
-
-把 skill symlink 或复制到用户级目录:
-
-```bash
-ln -s "$(pwd)/.claude/skills/agent" ~/.claude/skills/agent
-```
-
-之后任何 Claude Code session(无论 cwd 在哪)都能调用。
-
-### SDK 入口
+### invoke_agent 调用
 
 Claude Code skill 脚本最终调用 `cyber_agent/client.py::invoke_agent`,该函数也是公开 SDK 供任何 Python 代码复用:
 
@@ -256,7 +327,7 @@ result = await invoke_agent(
 
 路由规则:`agent_type.startswith("remote_")` → HTTP 调用 KnowHub `/api/agent`;否则在当前进程起 `AgentRunner`。
 
-**实现**:`cyber_agent/client.py:invoke_agent` / `.claude/skills/agent/invoke.py`
+**实现**:`cyber_agent/client.py:invoke_agent` / `skills4claude/agent/invoke.py`
 
 ---
 

+ 216 - 0
cyber_agent/docs/agent-modes.md

@@ -0,0 +1,216 @@
+# Agent 运行模式
+
+## 文档维护规范
+
+0. **先改文档,再动代码** - 新功能或重大修改需先完成文档更新、并完成审阅后,再进行代码实现;除非改动较小、不被文档涵盖
+1. **文档分层,链接代码** - 重要或复杂设计可以另有详细文档;关键实现需标注代码文件路径;格式:`module/file.py:function_name`
+2. **简洁快照,日志分离** - 只记录最重要的、与代码准确对应的或者明确的已完成的设计信息,避免推测、建议或大量代码;决策依据或修改日志若有必要,可在 `docs/decisions.md` 另行记录
+
+---
+
+## 概述
+
+`AGENT_MODE` 决定新建根 Trace 使用哪种本地 Sub-Agent 语义:
+
+| 模式 | 定位 |
+|------|------|
+| `legacy` | 保留旧的一层 Sub-Agent 行为,默认模式 |
+| `recursive` | 启用受深度、孩子数、工具权限、审核和资源预算约束的递归任务树 |
+
+`AGENT_RECURSION_ENABLED` 已删除;环境中仍存在该变量时会报错,不会静默兼容。
+
+**实现**:`cyber_agent/core/agent_mode.py:AgentPolicy`,`cyber_agent/core/agent_mode.py:policy_from_environment`
+
+---
+
+## 模式选择与固化
+
+新建根 Trace 会在任何 LLM 调用前读取 `AGENT_MODE`;未设置时默认为 `legacy`,非法值直接拒绝创建。选定后,根 Trace 将以下字段写入 `context`:
+
+```json
+{
+  "agent_mode": "recursive",
+  "agent_mode_revision": 2,
+  "agent_depth": 0,
+  "root_trace_id": "<root-trace-id>"
+}
+```
+
+本地子孙继承该持久化策略;续跑、回溯或服务环境变更不会改变已有任务树的模式。没有模式字段的历史 Trace 按 `legacy` 续跑并补写模式。
+
+Recursive revision 2 的新根 Trace 必须同时具备根完成标准和预算快照。缺少这些必需数据的早期实验 Trace 不会自动升级,续跑时会要求重新创建任务。
+
+**实现**:`cyber_agent/core/runner.py:AgentRunner._prepare_new_trace`,`cyber_agent/core/runner.py:AgentRunner._prepare_existing_trace`
+
+---
+
+## 能力边界
+
+| 能力 | Legacy | Recursive revision 2 |
+|------|------|------|
+| 本地 Agent 深度 | 根为 depth 0,只允许 depth 1 | 根为 depth 0,允许 depth 1~5 |
+| 每个父 Trace 的本地直接孩子 | 无数量配额 | 累计最多 6 个 |
+| 子 Agent 再调用 `agent` | 禁止 | depth 1~4 且父级拥有 `agent` 时允许 |
+| 任务参数 | `task` / `messages` | 本地委派必须使用 `task_brief` |
+| 任务结束 | 自然语言 summary | 报告、独立验收和父级审核门禁 |
+| Goal 推进 | 保留自动切换 sibling 和父级级联完成 | 孩子返回后由父级重新审核与规划 |
+| 批量孩子 | 整批并行 | 默认串行,可显式开启最多 2 并行 |
+| 停止 | 当前 Trace | 当前进程内的当前 Trace 及其本地后代 |
+| 树级资源预算 | 不启用 | 可配置,默认启用 |
+
+Recursive 的 5 层和 6 孩子只统计 `agent` 工具创建的本地业务 Trace。已创建后失败或停止的孩子仍占直接孩子名额;`continue_from` 续跑原孩子不新增名额。批量请求会先检查整批数量,超限时整批拒绝。
+
+`remote_*` 调用保留原来的 `task/messages` 接口,不计入本地深度、每父 6 孩子或当前本地树预算。
+
+**实现**:`cyber_agent/tools/builtin/subagent.py:_run_agents`
+
+---
+
+## Recursive 运行流程
+
+```text
+父 Agent 规划任务
+  → 使用 TaskBrief 创建孩子
+  → 孩子执行,必要时继续向下委派
+  → 孩子提交 TaskReport
+  → 独立 Validator 验收持久化轨迹
+  → 父 Agent 审核并决定修订、重新规划、继续拆分或完成
+  → 根 Agent 的候选答案通过根 Validator 后才能完成
+```
+
+本文档只定义运行模式和边界。TaskBrief、TaskReport、TaskReview、Validator、Goal 状态机和逐级回退见 [Recursive 任务协议](./recursive-task-protocol.md)。
+
+---
+
+## 工具权限
+
+Recursive 子级的基础能力是下列集合的交集:
+
+```text
+父级本次运行实际有效工具
+∩ 当前 ToolRegistry 已注册工具
+∩ Recursive 子级允许工具
+∩ 当前深度允许工具
+```
+
+`evaluate` 和 `bash_command` 不会授予 Recursive 子级;`agent` 只能在父级实际拥有且子级未到 depth 5 时继承。协议工具还会按 `pending_review`、已批准下一任务、是否根 Trace 等当前状态动态收紧。
+
+权限同时作用于模型可见 Schema 和 `ToolRegistry.execute()` 的运行时 dispatch;即使伪造未暴露的 Tool Call,也会在参数注入和函数调用前被拒绝。内部权限快照由 Runner 注入,自定义 context 不能覆盖。
+
+**实现**:`cyber_agent/core/runner.py:AgentRunner._get_runtime_tool_names`,`cyber_agent/tools/builtin/subagent.py:_get_allowed_tools`,`cyber_agent/tools/registry.py:ToolRegistry.execute`
+
+---
+
+## 批量调度
+
+Recursive 默认使用:
+
+```python
+RunConfig(
+    child_execution_mode="sequential",
+    max_parallel_children=2,
+)
+```
+
+`child_execution_mode="parallel"` 时,单次 `agent(task_brief=[...])` 通过 semaphore 限制同时运行数,`max_parallel_children` 必须为 1~2。该上限只约束单批本地孩子,不是整棵树的全局并发器。无论完成顺序如何,汇合结果保持输入顺序。
+
+Legacy 忽略这两个配置字段,保留原有批量并行。
+
+**实现**:`cyber_agent/core/agent_mode.py:validate_recursive_child_execution`,`cyber_agent/tools/builtin/subagent.py:_run_agents`
+
+---
+
+## 停止传播
+
+Recursive 在当前 Python 进程内登记活跃和排队的父子 Trace。停止一个 Trace 时:
+
+- 停止信号传递给它当前已登记的所有后代。
+- 停止子 Trace 不影响父级和兄弟。
+- 排队但尚未调用模型的孩子不会启动模型调用。
+- 不强制中断已在进行的 HTTP/LLM 请求;请求返回后不再执行其 Tool Call 或发起下一轮。
+
+运行登记仅存于当前进程,不是持久化、跨 Worker 的取消机制。
+
+**实现**:`cyber_agent/core/runner.py:AgentRunner.stop`,`cyber_agent/core/runner.py:AgentRunner.request_stop`,`cyber_agent/core/runner.py:AgentRunner.register_recursive_child`
+
+---
+
+## Context 继承边界
+
+Recursive 孩子只获得当前任务必需的明确输入:
+
+- 规范化 TaskBrief 及从父 TaskBrief 继承的硬约束。
+- `parent_trace_id` / `root_trace_id` / `agent_depth` 等 Trace 血缘。
+- 冻结的工具能力快照和调度策略。
+- 任务协议状态和预算所属根 Trace。
+
+不自动继承父 Agent 的完整消息历史、未筛选 Tool Result、父 Runner 的全部 `config.context`、内部运行对象或祖先 GoalTree。TaskBrief 的规范化、大小限制和 Validator 输入裁剪见 [Context 管理](./context-management.md#recursive-委托的-context-边界)。
+
+**实现**:`cyber_agent/core/context_policy.py:normalize_task_brief`,`cyber_agent/core/context_policy.py:task_briefs_match`,`cyber_agent/core/runner.py:AgentRunner._build_tool_context`
+
+---
+
+## 树级资源预算
+
+Recursive 新根 Trace 会从环境变量读取一次预算,并把不可变快照保存到根 Trace context。Legacy 不解析这些预算变量。
+
+| 环境变量 | 默认值 | 含义 |
+|------|------:|------|
+| `AGENT_RESOURCE_BUDGET_ENABLED` | `true` | `false` 时不执行限额,仍保留用量记录 |
+| `AGENT_MAX_TOTAL_AGENTS` | `50` | 根 Agent 和本地业务子孙总数 |
+| `AGENT_MAX_LLM_CALLS` | `150` | 本地 Agent 与 Validator 的模型调用总数 |
+| `AGENT_MAX_TOTAL_TOKENS` | `1500000` | 输入和输出 Token 总数 |
+| `AGENT_MAX_TOTAL_COST_USD` | `15` | 累计模型成本(美元) |
+| `AGENT_MAX_DURATION_SECONDS` | `3600` | 从根用量初始化开始的最长时间 |
+| `AGENT_RESERVED_FINAL_CALLS` | `1` | 仅保留给根 Validator 的末尾调用数 |
+
+所有数值必须为正数,且 `AGENT_RESERVED_FINAL_CALLS < AGENT_MAX_LLM_CALLS`。预算动态用量保存在根 Trace 目录的 `resource_usage.json`,记录 Agent、LLM、Token、成本、起始时间和最后拒绝原因。
+
+关键计数语义:
+
+- Validator 不计入 Agent 数,但计入 LLM、Token、成本和时间。
+- `continue_from`、停止、失败、续跑和回溯不退还已产生用量。只有批量预留后实际未创建 Trace 的部分会撤销 Agent 预留。
+- 工具内部的模型调用只有在返回 `tool_usage` 时才能追加记录;`remote_*` 当前不纳入本地树预算。
+- LLM 次数在调用前预留,Token 和成本在响应后登记。在途响应导致超限时会保留实际消耗,但不再执行该响应的 Tool Call。
+
+用量文件缺失或损坏时 Recursive 会 fail closed。当前原子更新仅由单 Python 进程内每个 `root_trace_id` 的异步锁保护。
+
+**实现**:`cyber_agent/core/resource_budget.py:ResourceBudget`,`cyber_agent/core/resource_budget.py:ResourceBudgetController`,`cyber_agent/trace/store.py:FileSystemTraceStore.replace_resource_usage`
+
+---
+
+## 兼容性与当前限制
+
+- `legacy` 默认启用,并保留一层子 Agent、无每父 6 孩子配额、旧 Goal 自动推进、旧 `evaluate` 和自然语言 summary。
+- 两种模式共用 Runner、TraceStore 和 ToolRegistry,差异由 `AgentPolicy` 和 Recursive 协议状态控制,没有复制两套执行引擎。
+- Recursive 目前是单 Python 进程实验版:6 孩子配额、资源用量锁和取消登记都不是跨 Worker 原子机制。
+- 并行模式最多有两个在途模型响应,Token、成本或时间可能在响应返回后出现小幅超限;实际用量仍会记录。
+- 当前没有跨进程 Lease/heartbeat、持久化 cancel request、数据库 TraceStore 原子配额或全树 Scheduler。
+
+---
+
+## 关键测试
+
+| 测试文件 | 覆盖范围 |
+|------|------|
+| `tests/test_agent_mode.py` | 模式默认值、配置错误、固化与 Legacy Goal 行为 |
+| `tests/test_subagent_recursion.py` | 深度、每父 6 孩子、批量原子拒绝和续跑血缘 |
+| `tests/test_recursive_tool_capabilities.py` | 权限交集、内部 context 防伪造和 dispatch 门禁 |
+| `tests/test_recursive_child_scheduling.py` | 默认串行、最多 2 并行和 Legacy 隔离 |
+| `tests/test_recursive_subtree_cancellation.py` | 子树停止、排队取消和 API Runner 路由 |
+| `tests/test_recursive_resource_budget.py` | 预算解析、原子计数、超限和用量持久化 |
+| `tests/test_recursive_replan_context.py` | 上下文继承、任务匹配和逐级回退 |
+| `tests/test_recursive_validation_core.py` | Validator 输入、独立性和结果校验 |
+| `tests/test_recursive_runtime_extensions.py` | 根门禁、预算包装和停止后协议行为 |
+
+---
+
+## 相关文档
+
+| 文档 | 内容 |
+|------|------|
+| [Recursive 任务协议](./recursive-task-protocol.md) | TaskBrief、TaskReport、ValidationResult、TaskReview 和审核状态机 |
+| [架构设计](./architecture.md) | AgentRunner、Trace、Goal 和工具系统全景 |
+| [工具系统](./tools.md) | `agent`、协议工具与 ToolRegistry 接口 |
+| [Trace API](./trace-api.md) | Trace 存储、续跑、回溯与停止 API |
+| [Context 管理](./context-management.md) | 消息 Context 和 Recursive 继承、裁剪规则 |

+ 89 - 139
cyber_agent/docs/architecture.md

@@ -18,7 +18,7 @@
 | -------- | ----------------------- | ------------------------------------------- | -------- |
 | 主 Agent | 直接调用 `runner.run()` | 无 parent                                   | 正常执行 |
 | 子 Agent | 通过 `agent` 工具       | `parent_trace_id` / `parent_goal_id` 指向父 | 正常执行 |
-| 人类协助 | 通过 `ask_human` 工具   | `parent_trace_id` 指向父                    | 阻塞等待 |
+| 人类协助 | 通过 `ask_human` 工具(规划中) | `parent_trace_id` 指向父                    | 尚未实现 |
 
 ---
 
@@ -27,10 +27,15 @@
 ### 模块结构
 
 ```
-agent/
+cyber_agent/
 ├── core/                  # 核心引擎
 │   ├── runner.py          # AgentRunner + 运行时配置
-│   └── presets.py         # Agent 预设(explore、analyst 等)
+│   ├── agent_mode.py      # Legacy / Recursive 模式策略
+│   ├── task_protocol.py   # Recursive 任务协议
+│   ├── context_policy.py  # Recursive TaskBrief 与验收上下文策略
+│   ├── validation.py      # Recursive 独立 LLM Validator
+│   ├── resource_budget.py # Recursive 树级资源预算
+│   └── presets.py         # Agent 预设(delegate、explore、evaluate 等)
 ├── trace/                 # 执行追踪(含计划管理)
 │   ├── models.py          # Trace, Message
@@ -55,7 +60,8 @@ agent/
 │       ├── search.py      # 网络搜索
 │       ├── webfetch.py    # 网页抓取
 │       ├── skill.py       # 技能加载
-│       └── subagent.py    # agent / evaluate 工具(子 Agent 创建与评估)
+│       ├── subagent.py    # agent / evaluate 工具(子 Agent 创建与评估)
+│       └── task_protocol.py # Recursive 报告与审核工具
 ├── skill/                 # 技能系统
 │   ├── models.py          # Skill
@@ -66,7 +72,9 @@ agent/
 │       └── browser.md     # 浏览器自动化
 ├── llm/                   # LLM 集成
+│   ├── qwen.py           # Qwen / 百炼 Provider(OpenAI SDK)
 │   ├── gemini.py          # Gemini Provider
+│   ├── claude.py          # Anthropic 原生 Provider
 │   ├── openrouter.py      # OpenRouter Provider(OpenAI 兼容格式)
 │   ├── yescode.py         # Yescode Provider(Anthropic 原生 Messages API)
 │   └── prompts/           # Prompt 工具
@@ -118,6 +126,20 @@ agent/
 | 入(LLM 响应 → 框架) | 提取 content、tool_calls、usage,转换为统一 Dict |
 | 出(框架 → LLM 请求) | OpenAI 格式消息列表 → 各 API 原生格式            |
 
+#### 创建入口与配置
+
+Provider 工厂由 `cyber_agent.llm` 导出,返回的异步函数传入 `AgentRunner(llm_call=...)`。
+
+| Provider | 创建函数 | 代码实际读取的环境变量 |
+| ---------- | ---------- | ---------- |
+| Qwen / 百炼 | `create_qwen_llm_call()` | `QWEN_API_KEY`,可选 `QWEN_BASE_URL` |
+| Gemini | `create_gemini_llm_call()` | `GEMINI_API_KEY`;`base_url` 也可作为工厂参数传入 |
+| Claude 原生 | `create_claude_llm_call()` | `CLAUDE_CODE_KEY` 或 `ANTHROPIC_API_KEY`;可选 `CLAUDE_CODE_URL` 或 `ANTHROPIC_BASE_URL` |
+| OpenRouter | `create_openrouter_llm_call()` | `OPEN_ROUTER_API_KEY` |
+| Yescode | `create_yescode_llm_call()` | `YESCODE_BASE_URL` + `YESCODE_API_KEY` |
+
+**实现**:`cyber_agent/llm/__init__.py`,`cyber_agent/llm/qwen.py`,`cyber_agent/llm/gemini.py`,`cyber_agent/llm/claude.py`,`cyber_agent/llm/openrouter.py`,`cyber_agent/llm/yescode.py`
+
 #### 工具消息分组
 
 存储层每个 tool result 独立一条 Message(OpenAI 格式最大公约数)。各 Provider 在出方向按 API 要求自行分组:
@@ -446,7 +468,7 @@ class Goal:
     summary: Optional[str] = None            # 完成/放弃时的总结
 
     # agent_call 特有(启动 Sub-Trace)
-    sub_trace_ids: Optional[List[str]] = None
+    sub_trace_ids: Optional[List[Union[str, Dict[str, str]]]] = None
     agent_call_mode: Optional[str] = None    # explore | delegate | evaluate
     sub_trace_metadata: Optional[Dict] = None
 
@@ -547,7 +569,7 @@ Message 提供格式转换方法:
 
 ## Agent 预设
 
-不同类型 Agent 的配置模板,控制工具权限和参数
+不同类型 Agent 的配置模板。当前 Runner 使用预设中的 `system_prompt` 和 `skills`;运行轮数、温度和工具范围以 `RunConfig` 为准,`AgentPreset.allowed_tools/denied_tools/max_iterations/temperature` 尚未接入 Runner
 
 ```python
 @dataclass
@@ -560,30 +582,31 @@ class AgentPreset:
     skills: Optional[List[str]] = None         # 注入 system prompt 的 skill 名称列表;None = 加载全部
     description: Optional[str] = None
 
-
-_DEFAULT_SKILLS = ["planning", "research", "browser"]
-
 AGENT_PRESETS = {
     "default": AgentPreset(
         allowed_tools=None,
         max_iterations=30,
-        skills=_DEFAULT_SKILLS,
+        skills=[],
         description="默认 Agent,拥有全部工具权限",
     ),
+    "delegate": AgentPreset(
+        allowed_tools=None,
+        max_iterations=30,
+        skills=[],
+        description="委托子 Agent,拥有全部工具权限(由 agent 工具创建)",
+    ),
     "explore": AgentPreset(
         allowed_tools=["read", "glob", "grep", "list_files"],
         denied_tools=["write", "edit", "bash", "task"],
         max_iterations=15,
-        skills=["planning"],
+        skills=[],
         description="探索型 Agent,只读权限,用于代码分析",
     ),
-    "analyst": AgentPreset(
-        allowed_tools=["read", "glob", "grep", "web_search", "webfetch"],
-        denied_tools=["write", "edit", "bash", "task"],
-        temperature=0.3,
-        max_iterations=25,
-        skills=["planning", "research"],
-        description="分析型 Agent,用于深度分析和研究",
+    "evaluate": AgentPreset(
+        allowed_tools=["read_file", "grep_content", "glob_files", "goal"],
+        max_iterations=10,
+        skills=[],
+        description="评估型 Agent,只读权限,用于结果评估",
     ),
 }
 ```
@@ -630,75 +653,51 @@ agent(task="调研视频生成工具", agent_type="tool_research")
 
 ## 子 Trace 机制
 
-通过 `agent` 工具创建子 Agent 执行任务。`task` 参数为字符串时为单任务(delegate),为列表时并行执行多任务(explore)。支持通过 `messages` 参数预置消息,通过 `continue_from` 参数续跑已有 Sub-Trace。
-
-`agent` 工具负责创建 Sub-Trace 和初始化 GoalTree(因为需要设置自定义 context 元数据和命名规则),创建完成后将 `trace_id` 传给 `RunConfig`,由 Runner 接管后续执行。工具同时维护父 Trace 的 `context["collaborators"]` 列表。
-
-### 跨设备 Agent 通信
-
-支持跨设备的 Agent 间持续对话,通过远程 Trace ID 实现:
+通过公开的 `agent` 工具创建子 Agent。`delegate` 和 `explore` 是单任务/多任务的内部结果模式,不是独立工具。根 Trace 创建时从 `AGENT_MODE=legacy|recursive` 选择模式,模式固化在 Trace context 并由本地子孙继承。
 
-**Trace ID 格式**
+Legacy 保留一层子 Agent 和旧 Goal 自动推进。Recursive 在同一套 Runner、TraceStore 和 ToolRegistry 上增加递归策略、任务协议、独立验收和树级预算:
 
-- 本地 Trace:`abc-123`
-- 远程 Trace:`agent://terminal-agent-456/abc-123`(协议 + Agent 地址 + 本地 ID)
-
-**使用方式**:
-
-```python
-# 调用远程 Agent
-result = agent(task="分析本地项目", agent_url="https://terminal-agent.local")
-# 返回: {"sub_trace_id": "agent://terminal-agent.local/abc-123"}
-
-# 续跑远程 Trace(持续对话)
-result2 = agent(
-    task="重点分析core模块",
-    continue_from="agent://terminal-agent.local/abc-123",
-    agent_url="https://terminal-agent.local"
-)
+```text
+根 Agent
+  → 用 TaskBrief 委派子 Agent(必要时继续递归)
+  → 子 Agent 提交 TaskReport
+  → 独立 Validator 验收实际 Trace
+  → 父 Agent 审核并重新规划
+  → 根候选结果通过根 Validator 后完成
 ```
 
-**实现**:`HybridTraceStore` 自动路由到本地或远程存储,远程访问通过 HTTP API 实现
+模式固化、深度与每父 6 孩子、工具权限、批量调度、子树停止和资源预算见 [Agent 运行模式](./agent-modes.md)。结构化报告、Validator、父级审核、Goal 状态和逐级回退见 [Recursive 任务协议](./recursive-task-protocol.md)。
 
-**实现位置**:`cyber_agent/trace/hybrid_store.py`(规划中)
+`agent` 工具负责创建 Sub-Trace 和初始化 GoalTree(因为需要设置自定义 context 元数据和命名规则),创建完成后将 `trace_id` 传给 `RunConfig`,由 Runner 接管后续执行。工具同时维护父 Trace 的 `context["collaborators"]` 列表。
 
 ### agent 工具
 
 ```python
 @tool(description="创建 Agent 执行任务")
 async def agent(
-    task: Union[str, List[str]],
+    task: Optional[Union[str, List[str]]] = None,
+    task_brief: Optional[Union[TaskBrief, List[TaskBrief]]] = None,
     messages: Optional[Union[Messages, List[Messages]]] = None,
     continue_from: Optional[str] = None,
     agent_type: Optional[str] = None,
     skills: Optional[List[str]] = None,
-    agent_url: Optional[str] = None,  # 远程 Agent 地址(跨设备)
     context: Optional[dict] = None,
 ) -> Dict[str, Any]:
 ```
 
 **参数**:
 
-- `agent_type`: 子 Agent 类型,决定工具权限和默认 skills(对应 `AgentPreset` 名称)
+- `agent_type`: 子 Agent 类型;`remote_` 前缀路由到 KnowHub `/api/agent`,其他值作为本地 preset 名称
 - `skills`: 覆盖 preset 默认值,显式指定注入 system prompt 的 skill 列表
-- `agent_url`: 远程 Agent 地址,用于跨设备调用(返回远程 Trace ID)
-- `continue_from`: 支持本地或远程 Trace ID
+- `continue_from`: 续跑单个已有 Trace;Recursive 只允许续跑当前父级审核后批准修订的直接孩子
 
-**单任务(delegate)**:`task: str`
+**Legacy**:
 
-- 创建单个 Sub-Trace
-- 完整工具权限(除 agent/evaluate 外,防止递归)
-- 支持 `continue_from` 续跑已有 Sub-Trace(本地或远程)
-- 支持 `messages` 预置上下文消息
+- 使用 `task/messages`、自由文本总结和原 Goal 自动推进。
 
-**多任务(explore)**:`task: List[str]`
+**Recursive revision 2**:
 
-- 使用 `asyncio.gather()` 并行执行所有任务
-- 每个任务创建独立的 Sub-Trace
-- 只读工具权限(read_file, grep_content, glob_files, goal)
-- `messages` 支持 1D(共享)或 2D(per-agent)
-- 不支持 `continue_from`
-- 汇总所有分支结果返回
+- 使用 `TaskBrief → TaskReport → ValidationResult → TaskReview` 的逐层任务协议,根结果也必须通过独立 Validator。
 
 ### evaluate 工具
 
@@ -717,6 +716,13 @@ async def evaluate(
 - `target_goal_id` 默认为当前 goal_id
 - 只读工具权限
 - 返回评估结论和改进建议
+- 仅 Legacy 模式保留;Recursive 使用框架管理的独立 Validator,Schema 和运行时都不暴露 `evaluate`。
+
+### 远程 Agent 与跨设备 Trace
+
+当前已实现的远程入口是 `agent_type="remote_*"`,由 `cyber_agent/tools/builtin/subagent.py:_run_remote_agent` 调用 KnowHub `/api/agent`,保留 `task/messages/continue_from/skills` 接口。
+
+`agent_url`、`agent://...` Trace ID 和 `HybridTraceStore` 自动路由属于规划中能力,当前公开 `agent()` 签名不接受 `agent_url`。
 
 ### 消息类型别名
 
@@ -730,7 +736,7 @@ MessageContent = Union[str, List[Dict[str, str]]]     # content 字段(文本
 
 **实现位置**:`cyber_agent/tools/builtin/subagent.py`
 
-**详细文档**:[工具系统 - Agent/Evaluate 工具](../cyber_agent/docs/tools.md#agent-工具)
+**详细文档**:[工具系统 - Agent/Evaluate 工具](./tools.md#agent-工具)
 
 ### ask_human 工具
 
@@ -779,10 +785,10 @@ MessageContent = Union[str, List[Dict[str, str]]]     # content 字段(文本
 
 ### 维护
 
-工具负责更新 collaborators 列表(通过 `context["store"]` 写入 trace.context):
+产生协作关系的工具负责更新 collaborators 列表(通过 `context["store"]` 写入 trace.context):
 
-- `agent` 工具:创建/续跑子 Agent 时更新
-- `feishu` 工具:发送消息/收到回复时更新
+- `agent` 工具:当前已实现,创建/续跑子 Agent 时更新
+- Human/Feishu 协作者自动维护:规划中
 - Runner 只负责读取和注入
 
 **持久联系人/Agent**:通过工具按需查询(如 `feishu_get_contact_list`),不随任务注入。
@@ -791,11 +797,11 @@ MessageContent = Union[str, List[Dict[str, str]]]     # content 字段(文本
 
 ---
 
-## Context Injection Hooks(上下文注入钩子)
+## Context Injection Hooks(上下文注入钩子,设计中
 
 ### 概述
 
-Context Injection Hooks 是一个可扩展机制,允许外部模块(如 A2A IM、监控系统)向 Agent 的周期性上下文注入中添加自定义内容
+Context Injection Hooks 是规划中的可扩展机制。当前 `_build_context_injection()` 已注入 GoalTree、Active Collaborators 和内置 IM 通知,但 `AgentRunner` 还没有 `context_hooks` 构造参数与注册机制。以下为未实现设计
 
 ### 设计理念
 
@@ -1060,58 +1066,7 @@ def create_timer_hook(timer):
     return timer_hook
 ```
 
-**实现位置**:各模块自行实现 hook 函数
-
----
-
-## Active Collaborators(活跃协作者)
-
-任务执行中与模型密切协作的实体(子 Agent 或人类),按 **与当前任务的关系** 分类,而非按 human/agent 分类:
-
-|       | 持久存在(外部可查)     | 任务内活跃(需要注入) |
-| ----- | ------------------------ | ---------------------- |
-| Agent | 专用 Agent(代码审查等) | 当前任务创建的子 Agent |
-| Human | 飞书通讯录               | 当前任务中正在对接的人 |
-
-### 数据模型
-
-活跃协作者存储在 `trace.context["collaborators"]`:
-
-```python
-{
-    "name": "researcher",            # 名称(模型可见)
-    "type": "agent",                 # agent | human
-    "trace_id": "abc-@delegate-001", # trace_id(agent 场景)
-    "status": "completed",           # running | waiting | completed | failed
-    "summary": "方案A最优",          # 最近状态摘要
-}
-```
-
-### 注入方式
-
-与 GoalTree 一同周期性注入(每 10 轮),渲染为 Markdown:
-
-```markdown
-## Active Collaborators
-
-- researcher [agent, completed]: 方案A最优
-- 谭景玉 [human, waiting]: 已发送方案确认,等待回复
-- coder [agent, running]: 正在实现特征提取模块
-```
-
-列表为空时不注入。
-
-### 维护
-
-各工具负责更新 collaborators 列表(通过 `context["store"]` 写入 trace.context):
-
-- `agent` 工具:创建/续跑子 Agent 时更新
-- `feishu` 工具:发送消息/收到回复时更新
-- Runner 只负责读取和注入
-
-**持久联系人/Agent**:通过工具按需查询(如 `feishu_get_contact_list`),不随任务注入。
-
-**实现**:`cyber_agent/core/runner.py:AgentRunner._build_context_injection`, `cyber_agent/tools/builtin/subagent.py`
+**实现位置**:各模块自行实现 hook 函数(设计中)
 
 ---
 
@@ -1157,7 +1112,7 @@ ToolResult(
 )
 ```
 
-**详细文档**:[工具系统](../cyber_agent/docs/tools.md)
+**详细文档**:[工具系统](./tools.md)
 
 ---
 
@@ -1198,7 +1153,7 @@ agent(task="...", agent_type="deconstruct", skills=["planning", "deconstruct"])
 
 **实现**:`cyber_agent/skill/skill_loader.py`
 
-**详细文档**:[Skills 使用指南](../cyber_agent/docs/skills.md)
+**详细文档**:[Skills 使用指南](./skills.md)
 
 ---
 
@@ -1404,7 +1359,7 @@ class TraceStore(Protocol):
 
 3. **tools/ 专注外部交互** - 文件、命令、网络、浏览器等与外部世界的交互
 
-4. **Agent 预设替代 Sub-Agent 配置** - 通过 `core/presets.py` 定义不同类型 Agent 的工具权限和参数
+4. **Agent 预设管理 Prompt 与 Skills** - 通过 `core/presets.py` 定义不同类型 Agent 的 system prompt 和 skills;运行参数和工具范围由 `RunConfig` 与模式策略控制
 
 ---
 
@@ -1412,22 +1367,17 @@ class TraceStore(Protocol):
 
 | 文档                                                            | 内容                            |
 | --------------------------------------------------------------- | ------------------------------- |
+| [Agent 运行模式](./agent-modes.md)                            | Legacy / Recursive 边界、调度、停止和预算 |
+| [Recursive 任务协议](./recursive-task-protocol.md)             | TaskBrief、Validator 与父级审核 |
 | [Context 管理](./context-management.md)                         | 注入机制、压缩策略、Skill 指定注入 |
-| [工具系统](../cyber_agent/docs/tools.md)                              | 工具定义、注册、双层记忆        |
-| [Skills 指南](../cyber_agent/docs/skills.md)                          | Skill 分类、编写、加载          |
-| [多模态支持](../cyber_agent/docs/multimodal.md)                       | 图片、PDF 处理                  |
-| [知识管理](./knowledge.md)                                      | 知识结构、检索、提取机制        |
+| [工具系统](./tools.md)                                         | 工具定义、注册、双层记忆        |
+| [Skills 指南](./skills.md)                                      | Skill 分类、编写、加载          |
+| [多模态支持](./multimodal.md)                                  | 图片、PDF 处理                  |
 | [Scope 设计](./scope-design.md)                                 | 知识可见性和权限控制            |
-| [Agent 设计决策](../cyber_agent/docs/decisions.md)                    | Agent Core 架构决策记录         |
-| [Gateway 设计决策](../gateway/docs/decisions.md)                | Gateway 架构决策记录            |
-| [组织级概览](../gateway/docs/enterprise/overview.md)            | 组织级 Agent 系统架构和规划     |
-| [Enterprise 实现](../gateway/docs/enterprise/implementation.md) | 认证、审计、多租户技术实现      |
-| [测试指南](./testing.md)                                        | 测试策略和命令                  |
-| [A2A 协议调研](./research/a2a-protocols.md)                     | 行业 A2A 通信协议和框架对比     |
-| [A2A 跨设备通信](./research/a2a-cross-device.md)                | 跨设备 Agent 通信方案(内部)   |
-| [A2A Trace 存储](./research/a2a-trace-storage.md)               | 跨设备 Trace 存储方案详细设计   |
-| [MAMP 协议](./research/a2a-mamp-protocol.md)                    | 与外部 Agent 系统的通用交互协议 |
-| [A2A IM 系统](./a2a-im.md)                                      | Agent 即时通讯系统架构和实现    |
-| [Gateway 架构](../gateway/docs/architecture.md)                 | Gateway 三层架构和设计决策      |
-| [Gateway 部署](../gateway/docs/deployment.md)                   | Gateway 部署模式和配置          |
-| [Gateway API](../gateway/docs/api.md)                           | Gateway API 完整参考            |
+| [Agent 设计决策](./decisions.md)                               | Agent Core 架构决策记录         |
+| [Gateway 设计决策](../../gateway/docs/decisions.md)             | Gateway 架构决策记录            |
+| [组织级概览](../../gateway/docs/enterprise/overview.md)         | 组织级 Agent 系统架构和规划     |
+| [Enterprise 实现](../../gateway/docs/enterprise/implementation.md) | 认证、审计、多租户技术实现      |
+| [Gateway 架构](../../gateway/docs/architecture.md)              | Gateway 三层架构和设计决策      |
+| [Gateway 部署](../../gateway/docs/deployment.md)                | Gateway 部署模式和配置          |
+| [Gateway API](../../gateway/docs/api.md)                        | Gateway API 完整参考            |

+ 41 - 1
cyber_agent/docs/context-management.md

@@ -73,7 +73,7 @@ Trace 创建时构建一次,后续续跑不重复发送。
 
 **实现**:`cyber_agent/core/runner.py`(`CONTEXT_INJECTION_INTERVAL`, 工具执行后的自动注入逻辑)
 
-**详细文档**:[架构设计 § Context Injection Hooks](./architecture.md#context-injection-hooks上下文注入钩子)
+**详细文档**:[架构设计 § Context Injection Hooks](./architecture.md#context-injection-hooks上下文注入钩子设计中)
 
 ---
 
@@ -166,6 +166,45 @@ knowhub/agents/skills/         # KnowHub Librarian 的 skills
 
 ---
 
+## Recursive 委托的 Context 边界
+
+Recursive revision 2 不把父 Agent 的完整 Context 复制给子 Agent。父级通过
+`TaskBrief` 显式下发:
+
+```text
+objective / reason
+completion_criteria / expected_outputs
+parent_findings / context / constraints
+```
+
+`cyber_agent/core/context_policy.py:normalize_task_brief` 负责边界校验:
+
+- 校验必填字段和 JSON 可序列化性,拒绝 `NaN/Infinity`。
+- 对列表字段稳定去重。
+- 只自动继承父 TaskBrief 的 `constraints`,子级只能追加,不能删除。
+- 规范化后的单份 TaskBrief 最多 16,000 字符。
+- 审核批准的下一份 TaskBrief 和实际 `agent()` 调用使用同一规范化逻辑,之后精确匹配。
+
+子 Trace 还会获得必要的 Trace 血缘、固化工具能力快照、树级预算根 ID
+和协议状态。不会自动继承父级完整消息历史、`RunConfig.context`、内部
+运行对象、未筛选 Tool Result 或祖先 GoalTree。Recursive 本地 `agent()` 因此不接受
+`messages`;需要继承的业务信息必须写入 TaskBrief。
+
+### Validator 输入裁剪
+
+Validator 的输入不由被验收 Agent 自行组装。
+`cyber_agent/core/validation.py:build_validation_packet` 固定保留 TaskBrief、
+TaskReport、完成标准、预期产出和根候选答案,再按最新消息优先纳入
+持久化主路径和真实 Tool Result。
+
+- 整个输入包最多 50,000 字符。
+- 固定协议部分本身已超限时直接失败,不会删减完成标准换取轨迹。
+- 侧分支消息不进入验收包,持久化的 `reasoning_content` 会被移除。
+
+完整字段和验收流程见 [Recursive 任务协议](./recursive-task-protocol.md)。
+
+---
+
 ## 压缩机制
 
 压缩分两级,通过 `RunConfig.goal_compression` 控制 Level 1 行为:
@@ -272,6 +311,7 @@ Skill 重注入的触发点:每次 runner 准备构建 LLM 调用前,检查
 | 文档 | 内容 |
 |------|------|
 | [架构设计](./architecture.md) | Agent 框架完整架构 |
+| [Recursive 任务协议](./recursive-task-protocol.md) | TaskBrief 边界、Validator 输入和审核流程 |
 | [Skills 指南](./skills.md) | Skill 文件格式、分类、加载机制 |
 | [Prompt 规范](./prompt-guidelines.md) | 信息分层、条件注入原则 |
 | [Trace API](./trace-api.md) | 压缩和反思的 REST API |

+ 243 - 0
cyber_agent/docs/recursive-task-protocol.md

@@ -0,0 +1,243 @@
+# Recursive 任务协议
+
+## 文档维护规范
+
+0. **代码是权威实现** - 本文档只记录 `AGENT_MODE=recursive` revision 2 已实现的协议;字段和门禁变更时需同步更新
+1. **协议与投影分层** - `Trace.context.task_protocol` 是权威状态,Goal 状态只用于流程展示
+2. **不扩展到 Legacy** - Legacy 仍使用自由文本 `task/summary` 和原 Goal 自动推进逻辑
+
+---
+
+## 概述
+
+Recursive 任务协议把本地父子 Agent 协作约束为:
+
+```
+父 Agent 下发 TaskBrief
+  → 子 Agent 执行并提交 TaskReport
+  → 独立 Validator 生成 ValidationResult
+  → 父 Agent 生成 TaskReview
+  → 修订原孩子 / 创建新孩子 / 重新规划当前层 / 完成或失败
+```
+
+这套协议只用于本地 Recursive Agent。`remote_*` 和 Legacy 不进入本协议。模式、深度、孩子配额、调度和资源预算见 [Agent 运行模式](./agent-modes.md)。
+
+---
+
+## 协议数据
+
+### TaskBrief:父级下发的任务说明
+
+| 字段 | 类型 | 规则 |
+|------|------|------|
+| `objective` | `string` | 必填,子任务目标 |
+| `reason` | `string` | 必填,说明为什么拆出该任务 |
+| `completion_criteria` | `string[]` | 必填且至少一项 |
+| `expected_outputs` | `string[]` | 必填且至少一项 |
+| `parent_findings` | `string[]` | 父级已确认的发现,默认为空 |
+| `context` | `object` | 与本任务直接相关的显式上下文 |
+| `constraints` | `string[]` | 硬约束;自动继承父 TaskBrief 的约束,子级只能增加 |
+
+TaskBrief 会去重、检查 JSON 可序列化性,规范化后最多 16,000 字符。Recursive 本地 `agent()` 使用 `task_brief`,不接受 `messages`。
+
+### TaskReport:子级提交的执行报告
+
+| 字段 | 类型 | 规则 |
+|------|------|------|
+| `child_trace_id` | `string` | 框架注入,必须等于当前子 Trace |
+| `summary` | `string` | 必填的执行总结 |
+| `outcome` | `satisfied \| partial \| failed \| protocol_error` | 任务结果 |
+| `validation` | `Validation` | 执行者自检,不是框架权威验收 |
+| `next_step_suggestion` | `object` | 子级建议,不直接创建父级的下一个孩子 |
+| `outputs` / `evidence` | `object[]` | 产出和证据 |
+| `remaining_issues` | `string[]` | 未解决问题 |
+
+`Validation` 只包含 `hard_passed` 和 `open_issues`。`outcome=satisfied` 时必须 `hard_passed=true`,且 `open_issues` 和 `remaining_issues` 都为空。
+
+`next_step_suggestion.direction` 为 `DESCEND / REFINE / ASCEND / NONE`。`DESCEND/REFINE` 必须附带 `suggested_next_task`;该字段仅是建议,父 Agent 必须审核后才能执行。
+
+### ValidationResult:框架权威验收
+
+| 字段 | 类型 | 规则 |
+|------|------|------|
+| `validator_trace_id` | `string` | 框架注入 |
+| `evaluated_trace_id` | `string` | 框架注入,指向被验收 Trace |
+| `outcome` | `passed \| failed \| error` | 独立验收结果 |
+| `scope` | `evidence \| hypothesis \| output \| task \| root` | 验收范围 |
+| `reason` | `string` | 必填结论 |
+| `issues` | `string[]` | 具体问题 |
+| `retry_from` | `evidence \| hypothesis \| output \| task_definition \| null` | 验收失败时的明确回退位置 |
+
+- `passed`:`issues=[]`、`retry_from=null`。
+- `failed`:至少一个 issue,必须有 `retry_from`。
+- `error`:至少一个 issue,`retry_from=null`,表示 Validator 自身异常或输出非法。
+
+### TaskReview:父级的审核决定
+
+| 字段 | 类型 | 规则 |
+|------|------|------|
+| `parent_trace_id` | `string` | 框架注入 |
+| `child_trace_id` | `string` | 必须是当前 Trace 的直接孩子,且 UID 一致 |
+| `decision` | `ReviewDecision` | 见下方决策矩阵 |
+| `reason` | `string` | 必填 |
+| `approved_next_task` | `TaskBrief \| null` | 批准的新建或修订任务 |
+| `retry_from` | `RetryFrom \| null` | 仅由框架从 `ValidationResult` 注入,不是公开工具参数 |
+
+---
+
+## 状态机与工具门禁
+
+Goal 可视状态为:
+
+```
+pending → in_progress → waiting_children → pending_review
+                                 ↓
+                     in_progress / completed / failed
+```
+
+1. 父 Agent 调用 `agent(task_brief=...)` 后,原 Goal 进入 `waiting_children`。
+2. 子 Agent 必须先审核完自己的孩子、执行完已批准 action,再调用 `submit_task_report`。
+3. 子报告经 Validator 处理后,父 Goal 进入 `pending_review`。
+4. `pending_review` 期间运行时只暴露 `review_task_result`,Goal 工具也拒绝新增、完成、放弃或切换。
+5. 审核产生 next action 后,运行时只暴露 `agent`,且实际 TaskBrief 必须与批准内容规范化后完全一致。
+
+Recursive 禁止 GoalTree 自动切换下一个 pending sibling,也禁止所有孩子完成时级联完成父 Goal。父 Agent 必须在审核后自己决定。
+
+---
+
+## 审核决策
+
+### 决策语义
+
+| 决策 | 效果 |
+|------|------|
+| `ACCEPT_REFINE` | 接受建议,把 `approved_next_task` 加入父级 next action,由父级创建新的直接孩子 |
+| `REJECT_REFINE` | 不采纳子级建议;其他待审报告处理完后,父级恢复 `in_progress` |
+| `REVISE_CHILD` | 进入修订 action;下一次只能用 `continue_from` 续跑原直接孩子,不消耗新孩子名额 |
+| `DESCEND_AGAIN` | 批准一个新 TaskBrief,由当前父级再创建一个直接孩子 |
+| `REPLAN_CURRENT` | 待当前批次审核完后,恢复创建该孩子的原 Goal 为 `in_progress` |
+| `ASCEND` | 完成父级当前 Goal,不级联完成它的父 Goal,不自动进入兄弟 Goal |
+| `FAIL` | 将父级当前 Goal 标为 `failed`,不级联到更高层 |
+
+### ValidationResult 决策矩阵
+
+| Validator 结果 | 允许的决策 |
+|------------------|------------|
+| `passed` | `ACCEPT_REFINE` / `REJECT_REFINE` / `REVISE_CHILD` / `DESCEND_AGAIN` / `ASCEND` / `FAIL` |
+| `failed` | `REVISE_CHILD` / `REPLAN_CURRENT` / `FAIL` |
+| `error` | `REVISE_CHILD` / `FAIL` |
+
+还有两层硬约束:
+
+- `TaskReport.outcome=failed/protocol_error` 只能选 `REVISE_CHILD/FAIL`。
+- `ASCEND/FAIL` 只能审核本批最后一份待审报告,且不得留有未执行的 next action。
+
+### REPLAN_CURRENT 只回退直接父级
+
+`REPLAN_CURRENT` 没有 `target_trace_id` 或 `ancestor_trace_id` 参数。回退目标由子 Trace 持久化的 `parent_trace_id` 和 `parent_goal_id` 确定:
+
+```
+孙 Trace 验收失败
+  → 子 Trace 审核并 REPLAN_CURRENT
+  → 只恢复子 Trace 中创建孙节点的 Goal
+  → 如果需要更高层回退,子 Agent 先提交自己的 TaskReport
+  → 父 Agent 再独立审核
+```
+
+`retry_from` 来自当前报告的 `ValidationResult`,父 Agent 不能修改。
+
+### 批量报告
+
+批量孩子各自产生 TaskReport、ValidationResult 和 TaskReview。父级必须逐份审核:
+
+- 只审核一部分时继续保持 `pending_review`。
+- `REPLAN_CURRENT` 先记入 `pending_replans`,本批全部审核完才恢复目标。
+- 本批后续选择 `FAIL` 会清除已记录的 replan。
+- 已批准的新孩子还会预占每父六孩子配额,避免审核通过后才发现无名额。
+
+---
+
+## 独立 Validator
+
+### 子任务验收
+
+`satisfied/partial` 报告会启动一次真实 LLM Validator;`failed/protocol_error` 报告以及框架为已停止孩子生成的 `failed` 报告,会得到确定性的不通过结果,不额外调用模型。每个报告版本只保存一份 Validator 结果;`REVISE_CHILD` 提交新报告后才会重新验收。
+
+Validator Trace 的约束:
+
+- `created_by_tool="validator"`,`agent_type="validator"`。
+- `parent_trace_id` 指向被验收的业务 Trace,`root_trace_id` 和 UID 保持一致。
+- 不增加业务 `agent_depth`,不占每父六孩子和业务 Agent 预算。
+- 无工具、无 Agent Loop、单次 LLM 调用,`temperature=0`。
+- 调用计入整树 LLM、Token、成本和时间预算,并参与当前进程的子树停止。
+- 不可 `continue_from`,不再生成 Validator。
+
+Validator 输入由框架从 TaskBrief、TaskReport、完成标准、预期产出、持久化主路径消息和真实 Tool Result 组装。固定协议字段优先保留,轨迹按最新消息优先裁剪,整体最多 50,000 字符;不包含侧分支和持久化的 `reasoning_content`。
+
+Validator 默认继承被验收 Agent 的模型,可用 `AGENT_VALIDATOR_MODEL` 覆盖。模型输出非法、返回错误 scope、尝试调用工具或调用异常时,直接生成 `outcome=error`,不再发起格式修正调用。
+
+### 根任务完成门禁
+
+新建 Recursive 根 Trace 必须显式提供 `root_completion_criteria`。根 Agent 生成无 Tool Call 的候选答案时,框架使用根标准、候选答案和持久化主路径运行 `scope=root` 的 Validator:
+
+- 通过:根 Trace 才可写为 `completed`。
+- 第一次未通过或 Validator 错误:将框架生成的 ValidationResult 追加到根消息,允许修正一次。
+- 第二次仍未通过:根 Trace 写为 `failed`。
+- Validator 异常、停止或预算不足都不会绕过完成门禁。
+
+---
+
+## 失败、停止、续跑与回溯
+
+- 非根 Agent 没有合法 TaskReport 时,框架最多注入两轮纠正提示;仍未提交时生成 `protocol_error` 报告并失败结束。
+- 子 Agent 异常或无有效报告时,父级依然会收到框架生成的报告和 ValidationResult,不会跳过审核。
+- 停止的子 Agent 生成 `outcome=failed` 的 TaskReport;父级只能选择 `REVISE_CHILD` 或 `FAIL`。
+- `REVISE_CHILD` 把旧报告移入 `report_history`,清空当前报告和 Validator 缓存;续跑后必须再提交新报告。
+- 服务重启后,`pending_reviews` 和 `next_actions` 从 Trace context 恢复。
+- 回溯会按 sequence 删除截断点之后的报告、审核和 action,恢复被回溯的待审报告,重建 `pending_replans` 和 Goal 状态投影。
+- 回溯、停止和续跑都不退还已消耗的树级资源。
+
+---
+
+## 持久化状态
+
+`Trace.context.task_protocol` 保存:
+
+```text
+task_brief
+task_report
+task_report_submitted_at_sequence
+task_report_validation
+report_history
+pending_reviews
+reviews
+next_actions
+pending_replans
+root_validation_attempts
+root_validation_history
+root_validation_passed
+protocol_correction_attempts
+```
+
+`pending_reviews` 的每一项同时保存 TaskReport、ValidationResult、原 Goal ID 和接收 sequence。GoalTree 不是这些协议事实的权威存储。
+
+---
+
+## 实现与测试
+
+| 内容 | 位置 |
+|------|------|
+| 数据模型和协议状态 | `cyber_agent/core/task_protocol.py` |
+| TaskBrief 规范化与约束继承 | `cyber_agent/core/context_policy.py` |
+| `submit_task_report` / `review_task_result` | `cyber_agent/tools/builtin/task_protocol.py` |
+| Validator、ValidationResult 和输入裁剪 | `cyber_agent/core/validation.py` |
+| 运行时门禁、根 Validator、回溯恢复 | `cyber_agent/core/runner.py` |
+| 子报告汇合和 Validator 触发 | `cyber_agent/tools/builtin/subagent.py` |
+| Goal 状态与变更门禁 | `cyber_agent/trace/goal_models.py` / `goal_tool.py` |
+
+主要测试:
+
+- `tests/test_task_protocol.py`
+- `tests/test_recursive_replan_context.py`
+- `tests/test_recursive_validation_core.py`
+- `tests/test_recursive_runtime_extensions.py`

+ 45 - 70
cyber_agent/docs/tools.md

@@ -846,8 +846,6 @@ RunConfig(tools=["knowledge_search", "read_file"]) # 精确指定(优先于 to
 - 适配器层:`cyber_agent/tools/adapters/`
 - OpenCode 参考:`vendor/opencode/` (git submodule)
 
-**详细文档**:参考 [`docs/tools-adapters.md`](./tools-adapters.md)
-
 ### 可用工具
 
 | 工具 | 功能 | 参考 |
@@ -860,7 +858,7 @@ RunConfig(tools=["knowledge_search", "read_file"]) # 精确指定(优先于 to
 | `glob_files` | 文件模式匹配 | opencode glob.ts |
 | `grep_content` | 内容搜索(正则表达式) | opencode grep.ts |
 | `agent` | 创建子 Agent 执行任务(本地执行或路由到远端服务器,由 `agent_type` 决定) | 自研 |
-| `evaluate` | 评估目标执行结果是否满足要求 | 自研 |
+| `evaluate` | Legacy 评估目标执行结果;Recursive revision 2 改用框架 Validator | 自研 |
 | `toolhub_health` | 检查 ToolHub 远程工具库服务状态 | 自研 |
 | `toolhub_search` | 搜索/发现 ToolHub 远程工具 | 自研 |
 | `toolhub_call` | 调用 ToolHub 远程工具(图片参数支持本地文件路径) | 自研 |
@@ -920,7 +918,7 @@ async def agent(
 
 | `agent_type` | 执行位置 | 通信通道 | 典型用途 |
 |--------------|---------|---------|---------|
-| 无前缀(`delegate` / `explore` / `deconstruct` 等) | **本地** 进程内 | 共享文件系统 + message | 项目内委托任务,可通过文件路径传递大数据 |
+| 未传 `agent_type` 或不带 `remote_` 前缀 | **本地** 进程内 | 共享文件系统 + message | 项目内委托任务,可通过文件路径传递大数据 |
 | `remote_` 前缀(`remote_librarian` / `remote_research` 等) | **远端** KnowHub 服务器 | **仅 message 通道** | 跨项目复用的能力(知识查询、深度调研) |
 
 **为什么要前缀**:远端 Agent 无法访问调用方的本地文件。前缀告诉模型"不要在 task 里引用本地路径,所有输入必须 inline;所有输出在 response message 里拿"。要传大文件时,先通过 `upload_knowledge` / `toolhub` 等基础设施上传到共享存储,再把 ID 传给远端 Agent。
@@ -934,16 +932,16 @@ async def agent(
 - remote_research: 深度调研,全网搜集+总结
 ```
 
-**Skill 白名单**:远端 `agent_type` 由服务器定义一个允许调用方注入的 skill 列表(如 `remote_librarian` 的 `["ask_strategy", "upload_strategy"]`)。调用方传的 skill 经白名单过滤后生效——这比拆出多个 agent_type 更简洁:**一个 Agent 多种模式,模式由 skill 触发**
+**Skills 传递**:当前客户端会把调用方传入的 `skills` 原样发给远端 `/api/agent`。是否过滤、允许哪些 skill 由远端服务实现决定,本仓库的客户端不执行白名单校验
 
 #### 本地模式:单任务 vs 多任务
 
-本地调用(`agent_type` 无 `remote_` 前缀)根据 `task` 类型分两种模式
+本地调用(`agent_type` 无 `remote_` 前缀)根据任务数量在 `agent()` 内部区分两种执行模式;`delegate` / `explore` 不是独立公开工具,也不需要调用方传入 `mode`
 
-| task 类型 | 模式 | 并行执行 | 工具权限 |
+| 任务输入 | 内部模式 | 并行执行 | 工具权限 |
 |-----------|------|---------|---------|
-| 单任务 | delegate | ❌ | 按 Trace 持久化的 `AGENT_MODE` 和深度授权 |
-| 多任务 | explore | Legacy 并行;Recursive 默认串行 | 与单任务使用同一模式策略 |
+| 单个 `task` / `task_brief` | delegate | ❌ | 按 Trace 持久化的 `AGENT_MODE` 和深度授权 |
+| `task` / `task_brief` 列表 | explore | Legacy 并行;Recursive 默认串行 | 与单任务使用同一模式策略 |
 
 **messages 参数**(Legacy 和 `remote_*` 保留):
 - `None`:无预置消息
@@ -952,7 +950,7 @@ async def agent(
 
 运行时判断:`messages[0]` 是 dict → 1D 共享;是 list → 2D per-agent。
 
-**单任务(delegate)**:适合委托专门任务,完整工具权限;支持 `continue_from` 续跑已有 Sub-Trace。
+**单任务(delegate)**:适合委托专门任务;Legacy 从 Registry 取工具后移除固定禁用项,Recursive 还会与父级实际权限和深度规则取交集。支持 `continue_from` 续跑已有 Sub-Trace。
 **多任务(explore)**:适合对比多个方案,不支持 `continue_from`。
 Legacy 保留整批并行;Recursive 由 `RunConfig.child_execution_mode`
 选择 `sequential/parallel`,默认串行,显式并行时
@@ -984,58 +982,33 @@ AGENT_MODE=legacy      # 或 recursive
 旧配置 `AGENT_RECURSION_ENABLED` 已删除。环境中仍存在时创建或续跑
 Trace 会直接报错,必须迁移到 `AGENT_MODE`。
 
-##### Recursive revision 2 任务协议
-
-新建的 `recursive` Trace 使用结构化的父子任务协议:
-
-- 本地 `agent()` 用 `task_brief` 下发必填的 `objective`、
-  `reason`、`completion_criteria`、`expected_outputs`,以及可选的
-  `parent_findings/context/constraints`;父级硬约束会自动继承。本地 Recursive
-  不接受 `messages`,`remote_*` 仍使用原有 `task/messages`。
-- 非根 Sub-Agent 结束前必须调用 `submit_task_report`,提交带
-  `Validation`、产出、证据和下一步建议的 `TaskReport`。
-- 子报告返回后,框架先创建无工具的独立 Validator Trace,读取
-  持久化主路径和真实 Tool Result,生成权威 `ValidationResult`。然后父
-  Goal 才从 `waiting_children` 进入 `pending_review`;
-  父 Agent 必须用 `review_task_result` 逐份生成 `TaskReview`。
-- 审核决策为 `ACCEPT_REFINE / REJECT_REFINE / REVISE_CHILD /
-  DESCEND_AGAIN / REPLAN_CURRENT / ASCEND / FAIL`。`REPLAN_CURRENT`
-  只能恢复正在审核孩子的直接父 Trace 和原 Goal,不能指定祖父或兄弟。
-  新孩子只能使用审核批准的
-  `TaskBrief`;`REVISE_CHILD` 只能 `continue_from` 原直接孩子。
-- 已批准的 next action 执行完前不能提交父级报告或
-  `ASCEND/FAIL`;批准新孩子前会同时校验现有孩子和已排队名额。
-- 待审核期间不允许新建孩子或修改 Goal;子 Agent 缺少合法报告时
-  最多纠正两轮,仍失败则由框架生成 `protocol_error` 报告交父级处理。
-- 该协议权威状态保存在 `Trace.context.task_protocol`,可在续跑和服务
-  重启后恢复;回溯到审核之前时会恢复原报告和 `pending_review`。
-  Legacy 和已有 Recursive revision 1 Trace 不进入该协议。
-- 新建 Recursive 根任务必须在 `RunConfig.root_completion_criteria`
-  (REST 为 `root_completion_criteria`)显式给出完成标准。根 Agent 候选答案只有
-  通过根 Validator 才能 `completed`;首次未通过允许修正一次,第二次仍未通过则
-  根 Trace 为 `failed`。
-- 新 Recursive Trace 还会固化一份树级资源预算;旧实验 Trace 若缺少根
-  标准或预算快照,续跑时会明确要求重新创建,不做隐式升级。
-
-##### Recursive 树级资源预算
-
-```env
-AGENT_VALIDATOR_MODEL=                 # 留空则继承被验收 Agent 模型
-AGENT_RESOURCE_BUDGET_ENABLED=true
-AGENT_MAX_TOTAL_AGENTS=50
-AGENT_MAX_LLM_CALLS=150
-AGENT_MAX_TOTAL_TOKENS=1500000
-AGENT_MAX_TOTAL_COST_USD=15
-AGENT_MAX_DURATION_SECONDS=3600
-AGENT_RESERVED_FINAL_CALLS=1
+模式、调度、子树停止和资源预算详见 [Agent 运行模式](./agent-modes.md)。
+
+##### Recursive revision 2 协议工具
+
+本地 Recursive `agent()` 使用 `task_brief`,其中
+`objective/reason/completion_criteria/expected_outputs` 必填;不接受
+`messages`。非根子 Agent 结束前必须提交报告:
+
+```python
+submit_task_report(task_report: TaskReportSubmission)
+```
+
+子报告经独立 Validator 生成 `ValidationResult` 后,直接父 Agent
+逐份调用:
+
+```python
+review_task_result(
+    child_trace_id: str,
+    decision: ReviewDecision,
+    reason: str,
+    approved_next_task: TaskBrief | None = None,
+)
 ```
 
-预算只在新建 Recursive 根 Trace 时读取并固化;Legacy 不解析这些变量。
-`total_agents` 计根及本地业务子孙,Validator 不计 Agent 数但计模型调用、
-Token、成本和耗时。`remote_*` 第一版不计入本地树预算;工具内部模型只在
-返回 `tool_usage` 时才能事后计入。单个子 Agent 批次显式并行时最多同时运行
-两个孩子。Token/成本是响应后统计,因此已在途的调用可能带来小幅超限;
-本轮不提供跨 worker 的全局硬并发上限。
+待审核期间不能创建孩子或修改 Goal;审核批准的新建或修订任务
+必须用同一份 TaskBrief 执行。字段、决策矩阵、Validator、根完成门禁
+与回溯恢复详见 [Recursive 任务协议](./recursive-task-protocol.md)。
 
 #### 远端模式
 
@@ -1044,7 +1017,8 @@ Token、成本和耗时。`remote_*` 第一版不计入本地树预算;工具
 | 字段 | 客户端传 | 服务器说了算 |
 |------|---------|-------------|
 | `agent_type` / `task` / `messages` / `continue_from` | ✓ | — |
-| `skills` / `tool_groups` / `model` / prompt | 传了也会被服务器忽略 | ✓(由服务器 preset 决定) |
+| `skills` | ✓(原样转发) | 是否过滤由远端实现决定 |
+| `tool_groups` / `model` / prompt | 非 `agent()` 公开参数 | ✓(由服务器 preset 决定) |
 
 **理由**:固定服务器端的能力包络避免越权(如客户端请求 `tool_groups=["knowledge_internal"]`),同时让 Agent 升级变成服务器单方面部署。
 
@@ -1056,11 +1030,12 @@ Token、成本和耗时。`remote_*` 第一版不计入本地树预算;工具
 
 **返回值**:Legacy、Recursive revision 1 和远端调用仍返回
 `{status, sub_trace_id, summary, stats}` 的自然语言结果;Recursive revision 2
-的本地调用还返回已校验的 `task_report`(批量时为 `task_reports`)。
+的本地调用还返回 `task_report` 和 `validation_result`
+(批量时为 `task_reports` 和 `validation_results`)。
 
 #### SDK / CLI 调用
 
-公开 SDK 入口:`cyber_agent.invoke_agent()`(定义在 `cyber_agent/client.py`),和 `agent` 工具签名一致,路由规则相同。任何 Python 进程只要装了 `cyber-agent` 包就能调用:
+公开 SDK 入口:`cyber_agent.invoke_agent()`(定义在 `cyber_agent/client.py`),使用相同的 `remote_` 路由规则,公开参数为 `agent_type/task/skills/continue_from/messages/project_root`。任何 Python 进程只要装了 `cyber-agent` 包就能调用:
 
 ```python
 import asyncio
@@ -1081,13 +1056,13 @@ result = asyncio.run(invoke_agent(
 
 #### `agent_type` 与 Presets
 
-- 本地 `agent_type`:在项目 `presets.json` 中定义(工具权限、system prompt、skills 等),支持从 `.prompt` 文件加载 system prompt
+- 本地 `agent_type`:在项目 `presets.json` 中定义 system prompt 和 skills,支持从 `.prompt` 文件加载;当前工具范围以 `RunConfig` 和 Recursive 权限继承为准
 - 远端 `agent_type`:在**服务器** `knowhub/agents/` 下定义(如 `knowhub/agents/research.py`),客户端 presets 不需要配置
 - 详见 `cyber_agent/docs/architecture.md` 的 "Agent 预设" 章节
 
-### Evaluate 工具
+### Evaluate 工具(Legacy)
 
-评估指定 Goal 的执行结果,提供质量评估和改进建议。
+评估指定 Goal 的执行结果,提供质量评估和改进建议。Recursive revision 2 不暴露该工具,由框架在子 Agent 提交 `TaskReport` 后自动运行独立 Validator。
 
 ```python
 @tool(description="评估目标执行结果是否满足要求")
@@ -1107,15 +1082,15 @@ async def evaluate(
 
 **Sub-Trace 结构**:
 - 每个 `agent`/`evaluate` 调用创建独立的 Sub-Trace
-- Sub-Trace ID 格式:`{parent_id}@{mode}-{序号}-{timestamp}-001`
+- Sub-Trace ID 格式:`{parent_id}@{mode}-{timestamp}-{seq}`
 - 通过 `parent_trace_id` 和 `parent_goal_id` 建立父子关系
 - Sub-Trace 信息存储在独立的 trace 目录中
 
 **Goal 集成**:
-- `agent`/`evaluate` 调用会将 Goal 标记为 `type: "agent_call"`
+- `agent`(以及 Legacy `evaluate`)调用会将 Goal 标记为 `type: "agent_call"`
 - `agent_call_mode` 记录使用的模式
-- `sub_trace_ids` 记录所有创建的 Sub-Trace
-- Goal 完成后,`summary` 包含格式化的汇总结果
+- `sub_trace_ids` 在 Legacy 记录当前批次,在 Recursive 累计当前 Goal 的直接孩子
+- Legacy 执行后直接更新 Goal 结果;Recursive 先进入 `waiting_children/pending_review`,由父级审核决定终态
 
 **实现位置**:`cyber_agent/tools/builtin/subagent.py`
 

+ 80 - 53
cyber_agent/docs/trace-api.md

@@ -40,7 +40,7 @@ cyber_agent/trace/
 # 主 Trace
 main_trace = Trace.create(mode="agent", task="探索代码库")
 
-# Sub-Trace(由 delegate 或 explore 工具创建)
+# Sub-Trace(由统一 agent() 工具创建)
 sub_trace = Trace(
     trace_id="2f8d3a1c...@explore-20260204220012-001",
     mode="agent",
@@ -64,8 +64,22 @@ trace.total_tokens    # Token 总数
 trace.total_cost      # 总成本
 trace.current_goal_id # 当前焦点 goal
 trace.head_sequence   # 当前主路径头节点 sequence(用于 build_llm_messages)
+trace.context         # 扩展上下文;Recursive 会固化模式、血缘、协议和预算快照
 ```
 
+Recursive 的关键 `context` 字段:
+
+| 字段 | 说明 |
+|------|------|
+| `agent_mode` / `agent_mode_revision` | 创建时固化的 `recursive` 模式及协议版本 |
+| `root_trace_id` / `agent_depth` | 根 Trace ID 和业务 Agent 深度(根为 0) |
+| `created_by_tool` | `agent` 表示业务子 Agent,`validator` 表示独立验收 Trace |
+| `root_completion_criteria` | Recursive 根任务的完成标准 |
+| `task_protocol` | TaskBrief、报告、审核、待审核项和下一步动作等权威协议状态 |
+| `resource_budget` | 根 Trace 固化的树级资源预算快照;动态用量单独保存 |
+
+旧 Trace 缺少 `agent_mode` 时按 Legacy 处理。详细语义见 [Agent 运行模式](./agent-modes.md) 和 [Recursive 任务协议](./recursive-task-protocol.md)。
+
 **Trace ID 格式**:
 - **主 Trace**:标准 UUID,例如 `2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d`
 - **Sub-Trace**:`{parent_uuid}@{mode}-{timestamp}-{seq}`,例如 `2f8d3a1c...@explore-20260204220012-001`
@@ -117,7 +131,13 @@ class TraceStore(Protocol):
     async def create_trace(self, trace: Trace) -> str: ...
     async def get_trace(self, trace_id: str) -> Optional[Trace]: ...
     async def update_trace(self, trace_id: str, **updates) -> None: ...
-    async def list_traces(self, ...) -> List[Trace]: ...
+    async def list_traces(
+        self, ..., parent_trace_id=None, created_by_tool=None
+    ) -> List[Trace]: ...
+
+    # Recursive 根 Trace 的树级动态用量
+    async def get_resource_usage(self, root_trace_id: str) -> Optional[Dict]: ...
+    async def replace_resource_usage(self, root_trace_id: str, usage: Dict) -> None: ...
 
     # GoalTree 操作(每个 Trace 有独立的 GoalTree)
     async def get_goal_tree(self, trace_id: str) -> Optional[GoalTree]: ...
@@ -140,6 +160,8 @@ class TraceStore(Protocol):
 
 **实现**:`cyber_agent/trace/protocols.py`
 
+`list_traces()` 先应用 `parent_trace_id` 和 `created_by_tool` 过滤,再应用 `limit`。因此可以准确查询某 Trace 由 `agent` 创建的直接业务孩子,Validator Trace 不会混入六孩子计数。
+
 ### FileSystemTraceStore
 
 ```python
@@ -154,6 +176,7 @@ store = FileSystemTraceStore(base_path=".trace")
 ├── 2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d/           # 主 Trace
 │   ├── meta.json                                   # Trace 元数据
 │   ├── goal.json                                   # GoalTree(扁平 JSON)
+│   ├── resource_usage.json                         # Recursive 根 Trace 的树级资源用量
 │   ├── messages/                                   # Messages
 │   │   ├── {message_id}.json
 │   │   └── ...
@@ -197,9 +220,11 @@ GET /api/traces/{trace_id}
 ```
 
 返回:
-- Trace 元数据
+- `trace`:Trace 元数据(包含 `context`)
 - GoalTree(该 Trace 的完整 Goal 树)
-- Sub-Traces 元数据(查询所有 `parent_trace_id == trace_id` 的 Traces)
+- `sub_traces`:所有 `parent_trace_id == trace_id` 的直接子 Trace 元数据,包括业务子 Agent 和 Validator Trace
+
+Validator Trace 使用 `agent_type="validator"` 且 `context.created_by_tool="validator"`;它不增加业务 `agent_depth`,也不计入每父六个业务孩子。如果只需业务子 Agent,存储层查询应同时使用 `parent_trace_id` 和 `created_by_tool="agent"`。
 
 #### 3. 获取 Messages
 
@@ -234,10 +259,14 @@ Content-Type: application/json
   "max_iterations": 200,
   "tools": null,
   "name": "任务名称",
-  "uid": "user_id"
+  "uid": "user_id",
+  "project_name": null,
+  "root_completion_criteria": ["根任务的完成标准"]
 }
 ```
 
+`root_completion_criteria` 在 Recursive 模式下必填且至少一项,Legacy 模式忽略该字段。
+
 #### 5. 运行(统一续跑 + 回溯)
 
 ```http
@@ -263,6 +292,9 @@ POST /api/traces/{trace_id}/stop
 ```
 
 设置取消信号,agent loop 在下一个检查点退出,Trace 状态置为 `stopped`。
+Legacy 只停止请求的 Trace;Recursive 会在当前进程内向活跃的直接孩子和后代传播停止信号。
+
+Recursive 为协作式停止:不强制中断已发出的 LLM 请求,但请求返回后不再执行 Tool Call 或开始下一轮。尚未启动的孩子直接写为 `stopped`;已停止子任务以 `outcome=failed` 的 TaskReport 返回父级。当前不支持跨 Worker 取消。
 
 #### 7. 列出正在运行的 Trace
 
@@ -281,9 +313,8 @@ Content-Type: application/json
 }
 ```
 
-在 trace 末尾追加一条包含反思 prompt 的 user message,作为侧枝运行。
-使用 `max_iterations=1, tools=[]` 进行单轮无工具 LLM 调用,生成经验总结,
-结果自动追加到 `./.cache/experiences.md`。head_sequence 通过 try/finally 保证恢复。
+以 `force_side_branch=["reflection"]` 启动反思侧分支,使用当前 Agent 循环进行多轮思考,必要时可调用知识工具。
+默认提取结果以 `extraction_pending` 写入 `cognition_log`,再通过提取审核 CLI/API 审核和提交;反思侧分支不改变主路径的 `head_sequence`。
 
 ### 经验端点
 
@@ -311,14 +342,15 @@ ws://43.106.118.91:8000/api/traces/{trace_id}/watch?since_event_id=0
 
 | 事件 | 触发时机 | payload |
 |------|---------|---------|
-| `connected` | WebSocket 连接成功 | trace_id, current_event_id, goal_tree, sub_traces |
+| `connected` | WebSocket 连接成功 | trace_id, current_event_id, trace_status, is_running, goal_tree, sub_traces |
 | `goal_added` | 新增 Goal | goal 完整数据(含 stats, parent_id, type) |
-| `goal_updated` | Goal 状态变化(含级联完成) | goal_id, updates, affected_goals(含级联完成的父节点) |
+| `goal_updated` | Goal 状态变化 | goal_id, patch(历史补发为 updates), affected_goals(Legacy 级联完成时可包含父节点) |
 | `message_added` | 新 Message | message 数据(含 goal_id),affected_goals |
 | `sub_trace_started` | Sub-Trace 开始执行 | trace_id, parent_goal_id, agent_type, task |
 | `sub_trace_completed` | Sub-Trace 完成 | trace_id, status, summary, stats |
 | `rewind` | 回溯执行 | after_sequence, head_sequence, goal_tree_snapshot |
-| `trace_completed` | 执行完成 | 统计信息 |
+| `trace_completed` | 执行完成 | trace_id, total_messages |
+| `trace_status_changed` | 停止或续跑导致 Trace 状态变化 | trace_id, status |
 
 ### Stats 更新逻辑
 
@@ -329,55 +361,42 @@ ws://43.106.118.91:8000/api/traces/{trace_id}/watch?since_event_id=0
 
 ### 级联完成(Cascade Completion)
 
-当所有子 Goals 都完成时,自动完成父 Goal:
+该自动行为只用于 Legacy:当所有子 Goals 都完成时,自动完成父 Goal:
 1. 检测子 Goals 全部 `status == "completed"`
 2. 自动设置父 Goal 的 `status = "completed"`
 3. 在 `goal_updated` 事件的 `affected_goals` 中包含级联完成的父节点
 
-**实现**:`cyber_agent/trace/websocket.py`
+Recursive 会显式关闭级联完成和自动切换下一个 sibling:子 Agent 返回后,父 Goal 从 `waiting_children` 进入 `pending_review`,由父 Agent 审核后再决定重新规划、修订、完成或失败。
+
+**实现**:`cyber_agent/trace/goal_models.py`、`cyber_agent/trace/goal_tool.py`、`cyber_agent/trace/store.py`
 
 ---
 
 ## Sub-Trace 工具
 
-### explore 工具
+### agent() 工具
 
-并行探索多个方向
+本地 Sub-Agent 统一由 `agent()` 工具创建。`delegate` 和 `explore` 只是内部运行模式,不是可单独导入或调用的工具
 
 ```python
-from cyber_agent.goal.explore import explore_tool
-
-result = await explore_tool(
-    current_trace_id="main_trace_id",
-    current_goal_id="3",
-    branches=["JWT 方案", "Session 方案"],
-    store=store,
-    run_agent=run_agent_func
-)
+# Legacy:单任务进入 delegate 模式
+agent(task="实现用户登录功能")
+
+# Legacy:多任务进入 explore 模式
+agent(task=["JWT 方案", "Session 方案"])
+
+# Recursive:使用结构化 TaskBrief,单个对象或对象列表
+agent(task_brief={
+    "objective": "实现用户登录功能",
+    "reason": "将身份认证从父任务中独立验证",
+    "completion_criteria": ["登录成功和失败路径均通过验证"],
+    "expected_outputs": ["实现代码", "验证结果"]
+})
 ```
 
-- 为每个 branch 创建独立的 Sub-Trace
-- 并行执行所有 Sub-Traces
-- 汇总结果返回
-
-### delegate 工具
-
-将大任务委托给独立 Sub-Agent:
-
-```python
-from cyber_agent.goal.delegate import delegate_tool
-
-result = await delegate_tool(
-    current_trace_id="main_trace_id",
-    current_goal_id="3",
-    task="实现用户登录功能",
-    store=store,
-    run_agent=run_agent_func
-)
-```
-
-- 创建单个 Sub-Trace,拥有完整权限
-- 执行任务并返回结果
+- 单任务使用内部 `delegate` 模式,多任务使用内部 `explore` 模式并汇总结果。
+- Legacy 的多任务整批并行;Recursive 默认串行,显式配置并行时最多同时执行 2 个孩子。
+- Trace/Goal 上下文由框架注入;Recursive 子 Agent 的工具权限不会超过父 Agent 的实际权限。
 
 ---
 
@@ -392,23 +411,31 @@ from cyber_agent.trace import FileSystemTraceStore
 store = FileSystemTraceStore(base_path=".trace")
 runner = AgentRunner(trace_store=store, llm_call=my_llm_fn)
 
-async for event in runner.run(task="探索代码库"):
+async for event in runner.run([
+    {"role": "user", "content": "探索代码库"},
+]):
     print(event)  # Trace 或 Message
 ```
 
 ### 查询 Sub-Traces
 
 ```python
-# 获取主 Trace 的所有 Sub-Traces
-all_traces = await store.list_traces(limit=1000)
-sub_traces = [t for t in all_traces if t.parent_trace_id == main_trace_id]
+# 获取主 Trace 的直接 Sub-Traces
+sub_traces = await store.list_traces(
+    parent_trace_id=main_trace_id,
+    created_by_tool="agent",
+    limit=1000,
+)
 ```
 
+Recursive 根 Trace 的 `resource_usage.json` 记录 Agent 数、LLM 调用、Token、成本、起始时间及超限原因。它由 TraceStore 供框架内部读写,不是 Trace 详情响应的独立顶层字段。停止、回溯和续跑不退还历史用量。
+
 ---
 
 ## 相关文档
 
-- [frontend/API.md](../frontend/API.md) - 前端对接 API 文档
+- [frontend/API.md](../../frontend/API.md) - 前端对接 API 文档
+- [Agent 运行模式](./agent-modes.md) - Legacy/Recursive 运行边界、调度和预算
+- [Recursive 任务协议](./recursive-task-protocol.md) - TaskBrief、TaskReport、Validator 和审核状态机
 - [context-management.md](./context-management.md) - Context 管理完整设计
-- [agent/goal/models.py](../agent/goal/models.py) - GoalTree 模型定义
-- [docs/REFACTOR_SUMMARY.md](./REFACTOR_SUMMARY.md) - 重构总结
+- [trace/goal_models.py](../trace/goal_models.py) - GoalTree 模型定义

+ 159 - 121
frontend/API.md

@@ -1,7 +1,7 @@
 # Agent Execution API - 前端对接文档
 
 > 版本:v4.0
-> 更新日期:2026-02-04
+> 更新日期:2026-07-17
 
 ---
 
@@ -16,7 +16,7 @@
 - **GoalTree** - 每个 Trace 的目标树
 - **Goal** - 一个目标节点,包含 self_stats(自身统计)和 cumulative_stats(含后代统计)
 - **Message** - 执行消息,对应 LLM API 的 assistant/tool 消息
-- **Sub-Trace** - 子 Agent(通过 explore/delegate 工具启动
+- **Sub-Trace** - 子 Agent(通过统一 `agent()` 工具启动;`explore/delegate` 是内部模式
 
 **统一的 Trace 模型**:
 ```
@@ -40,13 +40,15 @@
 - Goal.stats 从关联的 Messages 聚合计算
 - Sub-Trace 通过 parent_trace_id 关联
 - Goal 通过 sub_trace_ids 关联启动的 Sub-Traces
+- 前端从 `trace.context.agent_mode` 识别 `legacy` / `recursive`;缺失时按 Legacy 显示
 ```
 
 **DAG 可视化**(前端负责):
 - 从 GoalTree 生成 DAG 视图
 - 节点 = Goal 完成后的里程碑
 - 边 = 相邻节点之间的执行过程
-- Sub-Trace 可以折叠(显示为单个节点)或展开(显示内部 Goals)
+- Recursive 的 Agent Goal 最多显示 6 个 `A1~A6` 入口,点击后切换到对应子 Trace,子 Trace 内可继续进入孙 Trace
+- Legacy 不显示 `A1~A6`;当前不在同一张图内展开完整五层树
 - 边的统计数据从 target Goal 的 stats 获取
 
 ---
@@ -60,6 +62,38 @@
 
 ---
 
+### 0. 新建 Trace 并执行
+
+```http
+POST /api/traces
+Content-Type: application/json
+
+{
+  "messages": [{"role": "user", "content": "完成任务"}],
+  "model": "qwen-plus",
+  "temperature": 0.3,
+  "max_iterations": 200,
+  "tools": null,
+  "name": "任务名称",
+  "uid": "user_id",
+  "project_name": null,
+  "root_completion_criteria": ["产出物通过验收"]
+}
+```
+
+`root_completion_criteria` 在 Recursive 模式下必填且至少一项;Legacy 忽略该字段。该端点立即返回 `trace_id` 和 `status="started"`,执行过程通过 WebSocket 监听。
+
+### 0.1 续跑、回溯与停止
+
+```http
+POST /api/traces/{trace_id}/run
+POST /api/traces/{trace_id}/stop
+```
+
+`run` 请求使用 `messages` 和可选 `after_message_id`。`stop` 是协作式停止:Legacy 只停请求的 Trace;Recursive 在当前 Python 进程内向活跃子孙传播,但不强制中断已发出的 LLM 请求。
+
+---
+
 ### 1. 列出 Traces
 
 ```http
@@ -69,9 +103,9 @@ GET /api/traces?status=running&limit=20
 **查询参数**:
 | 参数 | 类型 | 必填 | 说明 |
 |------|------|------|------|
-| `status` | string | 否 | 过滤状态:`running` / `completed` / `failed` |
+| `status` | string | 否 | 过滤状态:`running` / `completed` / `failed` / `stopped` |
 | `mode` | string | 否 | 过滤模式:`call` / `agent` |
-| `limit` | int | 否 | 返回数量(默认 50,最大 100)|
+| `limit` | int | 否 | 返回数量(默认 20,最大 100)|
 
 **响应示例**:
 ```json
@@ -88,8 +122,7 @@ GET /api/traces?status=running&limit=20
       "current_goal_id": "2.1",
       "created_at": "2026-02-04T15:30:00"
     }
-  ],
-  "total": 1
+  ]
 }
 ```
 
@@ -101,21 +134,36 @@ GET /api/traces?status=running&limit=20
 GET /api/traces/{trace_id}
 ```
 
+真实响应外层固定为:
+
+```json
+{
+  "trace": {"trace_id": "abc123", "context": {"agent_mode": "recursive"}},
+  "goal_tree": {"mission": "...", "current_id": "2", "goals": []},
+  "sub_traces": {}
+}
+```
+
+`sub_traces` 是当前 Trace 的直接子 Trace,可同时包含业务子 Agent 和 `agent_type="validator"` 的验收 Trace。前端判断业务模式和树级血缘时使用 `trace.context.agent_mode`、`agent_mode_revision`、`root_trace_id` 和 `agent_depth`。
+
 **响应示例 1**(主 Trace,explore 进行中):
 ```json
 {
-  "trace_id": "abc123",
-  "mode": "agent",
-  "task": "实现用户认证功能",
-  "status": "running",
-  "parent_trace_id": null,
-  "parent_goal_id": null,
-  "agent_type": "main",
-  "total_messages": 15,
-  "total_tokens": 5000,
-  "total_cost": 0.05,
-  "created_at": "2026-02-04T15:30:00",
-  "completed_at": null,
+  "trace": {
+    "trace_id": "abc123",
+    "mode": "agent",
+    "task": "实现用户认证功能",
+    "status": "running",
+    "parent_trace_id": null,
+    "parent_goal_id": null,
+    "agent_type": "main",
+    "total_messages": 15,
+    "total_tokens": 5000,
+    "total_cost": 0.05,
+    "created_at": "2026-02-04T15:30:00",
+    "completed_at": null,
+    "context": {"agent_mode": "recursive", "agent_mode_revision": 2, "root_trace_id": "abc123", "agent_depth": 0}
+  },
   "goal_tree": {
     "mission": "实现用户认证功能",
     "current_id": "2",
@@ -137,7 +185,7 @@ GET /api/traces/{trace_id}
         "type": "agent_call",
         "description": "并行探索认证方案",
         "reason": "评估不同技术选型",
-        "status": "in_progress",
+        "status": "waiting_children",
         "agent_call_mode": "explore",
         "sub_trace_ids": [
           {"trace_id": "abc123.A", "mission": "JWT 方案"},
@@ -189,10 +237,13 @@ GET /api/traces/{trace_id}
 **响应示例 2**(主 Trace,explore 已完成并合并):
 ```json
 {
-  "trace_id": "abc123",
-  "mode": "agent",
-  "task": "实现用户认证功能",
-  "status": "running",
+  "trace": {
+    "trace_id": "abc123",
+    "mode": "agent",
+    "task": "实现用户认证功能",
+    "status": "running",
+    "context": {"agent_mode": "legacy"}
+  },
   "goal_tree": {
     "mission": "实现用户认证功能",
     "current_id": "3",
@@ -289,17 +340,19 @@ GET /api/traces/abc123.A
 **响应示例**:
 ```json
 {
-  "trace_id": "abc123.A",
-  "parent_trace_id": "abc123",
-  "parent_goal_id": "2",
-  "agent_type": "explore",
-  "task": "JWT 方案",
-  "status": "completed",
-  "total_messages": 8,
-  "total_tokens": 4000,
-  "total_cost": 0.05,
-  "created_at": "2026-02-04T15:31:00",
-  "completed_at": "2026-02-04T15:35:00",
+  "trace": {
+    "trace_id": "abc123.A",
+    "parent_trace_id": "abc123",
+    "parent_goal_id": "2",
+    "agent_type": "explore",
+    "task": "JWT 方案",
+    "status": "completed",
+    "total_messages": 8,
+    "total_tokens": 4000,
+    "total_cost": 0.05,
+    "created_at": "2026-02-04T15:31:00",
+    "completed_at": "2026-02-04T15:35:00"
+  },
   "goal_tree": {
     "mission": "JWT 方案",
     "current_id": null,
@@ -347,7 +400,6 @@ GET /api/traces/abc123.A/messages?goal_id=2
 **响应示例**:
 ```json
 {
-  "trace_id": "abc123",
   "messages": [
     {
       "message_id": "msg-001",
@@ -383,8 +435,7 @@ GET /api/traces/abc123.A/messages?goal_id=2
       "cost": null,
       "created_at": "2026-02-04T15:31:01"
     }
-  ],
-  "total": 2
+  ]
 }
 ```
 
@@ -418,16 +469,14 @@ const ws = new WebSocket(
   "event": "connected",
   "trace_id": "abc123",
   "current_event_id": 15,
-  "trace": {
-    "trace_id": "abc123",
-    "status": "running",
-    "goal_tree": {
-      "mission": "实现用户认证功能",
-      "current_id": "2",
-      "goals": [...]
-    },
-    "sub_traces": {}
-  }
+  "trace_status": "running",
+  "is_running": true,
+  "goal_tree": {
+    "mission": "实现用户认证功能",
+    "current_id": "2",
+    "goals": [...]
+  },
+  "sub_traces": {}
 }
 ```
 
@@ -457,15 +506,14 @@ if (data.event === 'connected') {
     "summary": null,
     "self_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null },
     "cumulative_stats": { "message_count": 0, "total_tokens": 0, "total_cost": 0.0, "preview": null }
-  },
-  "parent_id": "2"
+  }
 }
 ```
 
 **前端处理**:
 ```javascript
 if (data.event === 'goal_added') {
-  insertGoal(data.goal, data.parent_id)
+  insertGoal(data.goal, data.goal.parent_id)
   regenerateDAG()
 }
 ```
@@ -474,7 +522,8 @@ if (data.event === 'goal_added') {
 
 #### 3. goal_updated(Goal 状态变化)
 
-包含级联完成场景:当所有子 Goal 完成时,父 Goal 自动 completed。
+Legacy 中可包含级联完成;Recursive 不自动完成父 Goal,子任务返回后会经过 `waiting_children` / `pending_review` 等待父 Agent 审核。
+实时广播的变更字段名为 `patch`,从 `events.jsonl` 补发的历史事件为 `updates`;前端同时兼容两者。
 
 ```json
 {
@@ -503,7 +552,7 @@ if (data.event === 'goal_added') {
 **前端处理**:
 ```javascript
 if (data.event === 'goal_updated') {
-  updateGoal(data.goal_id, data.updates)
+  updateGoal(data.goal_id, data.patch ?? data.updates)
   for (const g of data.affected_goals) {
     updateGoalStats(g.goal_id, g)
   }
@@ -567,9 +616,7 @@ if (data.event === 'message_added') {
   "event": "trace_completed",
   "event_id": 50,
   "trace_id": "abc123",
-  "total_messages": 50,
-  "total_tokens": 25000,
-  "total_cost": 0.25
+  "total_messages": 50
 }
 ```
 
@@ -581,39 +628,34 @@ if (data.event === 'trace_completed') {
 }
 ```
 
+`trace_status_changed` 用于停止和续跑时的状态同步:
+
+```json
+{
+  "event": "trace_status_changed",
+  "trace_id": "abc123",
+  "status": "stopped"
+}
+```
+
 ---
 
 #### 6. sub_trace_started(Sub-Trace 开始)
 
-explore 或 delegate 工具启动 Sub-Trace 时触发。
+`agent()` 工具启动 Sub-Trace 时触发;`explore` / `delegate` 只是内部运行模式
 
 ```json
 {
   "event": "sub_trace_started",
   "event_id": 20,
-  "parent_trace_id": "abc123",
+  "trace_id": "abc123@explore-20260204220012-001",
   "parent_goal_id": "2",
-  "sub_trace": {
-    "trace_id": "abc123.A",
-    "parent_trace_id": "abc123",
-    "parent_goal_id": "2",
-    "agent_type": "explore",
-    "task": "JWT 方案",
-    "status": "running",
-    "total_messages": 0,
-    "total_tokens": 0,
-    "total_cost": 0.0
-  }
+  "agent_type": "explore",
+  "task": "JWT 方案"
 }
 ```
 
-**前端处理**:
-```javascript
-if (data.event === 'sub_trace_started') {
-  insertSubTrace(data.parent_trace_id, data.sub_trace)
-  regenerateDAG()
-}
-```
+Goal 上的 `sub_trace_ids` 以 `goal_updated` 事件为准;该事件可用于补充子 Trace 运行提示。
 
 ---
 
@@ -625,13 +667,14 @@ Sub-Trace 执行完成后触发。
 {
   "event": "sub_trace_completed",
   "event_id": 35,
-  "trace_id": "abc123.A",
-  "parent_trace_id": "abc123",
-  "parent_goal_id": "2",
+  "trace_id": "abc123@explore-20260204220012-001",
+  "status": "completed",
   "summary": "JWT 方案实现完成,无状态但 token 较大",
-  "total_messages": 8,
-  "total_tokens": 4000,
-  "total_cost": 0.05
+  "stats": {
+    "total_messages": 8,
+    "total_tokens": 4000,
+    "total_cost": 0.05
+  }
 }
 ```
 
@@ -639,13 +682,10 @@ Sub-Trace 执行完成后触发。
 ```javascript
 if (data.event === 'sub_trace_completed') {
   updateSubTrace(data.trace_id, {
-    status: 'completed',
+    status: data.status,
     summary: data.summary,
-    total_messages: data.total_messages,
-    total_tokens: data.total_tokens,
-    total_cost: data.total_cost
+    ...data.stats
   })
-  regenerateDAG()
 }
 ```
 
@@ -657,19 +697,29 @@ if (data.event === 'sub_trace_completed') {
 
 | 字段 | 类型 | 说明 |
 |------|------|------|
-| `trace_id` | string | 层级化 ID(如 "abc123", "abc123.A", "abc123.A.1")|
+| `trace_id` | string | 根 Trace 为 UUID;子 Trace 为 `{parent}@{mode}-{timestamp}-{seq}` |
 | `mode` | string | `call` - 单次调用 / `agent` - Agent 模式 |
 | `task` | string | 任务描述 |
 | `parent_trace_id` | string \| null | 父 Trace ID(Sub-Trace 才有)|
 | `parent_goal_id` | string \| null | 哪个 Goal 启动的(Sub-Trace 才有)|
-| `agent_type` | string \| null | "main" / "explore" / "delegate" / "compaction" |
-| `status` | string | `running` / `completed` / `failed` |
+| `agent_type` | string \| null | `main` / `explore` / `delegate` / `compaction` / `validator` |
+| `status` | string | `running` / `completed` / `failed` / `stopped` |
+| `context` | object | 模式、血缘及 Recursive 协议状态;前端至少使用 `agent_mode` |
 | `total_messages` | int | Message 总数 |
 | `total_tokens` | int | Token 总消耗 |
 | `total_cost` | float | 成本总和 |
 | `created_at` | string | 创建时间(ISO 8601)|
 | `completed_at` | string \| null | 完成时间 |
 
+Recursive 前端相关的 `context` 字段:
+
+| 字段 | 说明 |
+|------|------|
+| `agent_mode` / `agent_mode_revision` | 当前 Trace 固化的模式和协议版本 |
+| `root_trace_id` / `agent_depth` | 任务树根 ID 和业务 Agent 深度 |
+| `created_by_tool` | `agent` 为业务子 Trace,`validator` 为验收 Trace |
+| `task_protocol` | TaskBrief、待审核报告、审核记录和下一步动作 |
+
 **Trace ID 规则**:
 - 主 Trace:标准 UUID(如 "2f8d3a1c-4b6e-4f9a-8c2d-1e5b7a9f3c4d")
 - Sub-Trace:`{parent_uuid}@{mode}-{timestamp}-{seq}`
@@ -699,9 +749,9 @@ if (data.event === 'sub_trace_completed') {
 | `type` | string | `normal` / `agent_call` |
 | `description` | string | 目标描述(做什么)|
 | `reason` | string | 创建理由(为什么做)|
-| `status` | string | `pending` / `in_progress` / `completed` / `abandoned` |
+| `status` | string | 后端:`pending` / `in_progress` / `waiting_children` / `pending_review` / `completed` / `failed` / `abandoned`;前端类型额外保留 `running` 兼容旧数据 |
 | `summary` | string \| null | 完成/放弃时的总结 |
-| `sub_trace_ids` | Array<{trace_id: string, mission: string}> \| null | 启动的 Sub-Trace 信息(仅 agent_call)|
+| `sub_trace_ids` | Array<string \| {trace_id: string, mission?: string}> \| null | 启动的 Sub-Trace 信息(仅 agent_call)|
 | `agent_call_mode` | string \| null | "explore" / "delegate" / "sequential"(仅 agent_call)|
 | `sub_trace_metadata` | object \| null | Sub-Trace 元数据(仅 agent_call,包含最后消息等)|
 | `self_stats` | GoalStats | 自身统计 |
@@ -717,14 +767,15 @@ if (data.event === 'sub_trace_completed') {
 - 表示启动了 Sub-Agent 的特殊 Goal
 - 不直接关联 Messages,而是启动一个或多个独立的 Sub-Trace
 - 通过 `sub_trace_ids` 关联启动的所有 Sub-Traces
+- Legacy 同一 Goal 的 `sub_trace_ids` 保持当前批次覆盖语义;Recursive 累计合并、去重,每个父 Trace 最多 6 个直接业务孩子
 - 通过 `agent_call_mode` 标记执行模式:
-  - `"explore"` - 并行探索:启动多个 Sub-Trace 并行执行,汇总结果后合并
+  - `"explore"` - 多任务汇合:Legacy 整批并行;Recursive 默认串行,显式并行时最多 2 个
   - `"delegate"` - 单线委托:将大任务委托给单个 Sub-Trace 执行
   - `"sequential"` - 顺序执行:按顺序启动多个 Sub-Trace
 
 **分支探索与合并(`explore` 模式)**:
-1. **分支开始**:`agent_call` Goal 同时启动多个 Sub-Trace(如 2-5 个)
-2. **并行执行**:各 Sub-Trace 独立运行,有各自的 GoalTree 和 Messages
+1. **分支开始**:`agent_call` Goal 创建多个 Sub-Trace(Recursive 每父累计最多 6 个)
+2. **调度执行**:各 Sub-Trace 独立运行;Legacy 批量并行,Recursive 按 Runner 配置串行或最多两路并行
 3. **收集元数据**:执行完成后,收集每个 Sub-Trace 的:
    - 最后一条 assistant 消息(`last_message`)
    - 执行总结(`summary`)
@@ -785,7 +836,7 @@ if (data.event === 'sub_trace_completed') {
 
 **字段说明**:
 - `task` - Sub-Trace 的任务描述
-- `status` - Sub-Trace 的最终状态(completed/failed)
+- `status` - Sub-Trace 的最终状态(completed/failed/stopped
 - `summary` - Sub-Trace 的执行总结
   - 优先使用 `run_agent()` 返回的 `summary` 字段(如果有)
   - 否则使用最后一条 assistant 消息的内容(截断至 200 字符)
@@ -1078,7 +1129,7 @@ function generateEdges(visibleGoals) {
 }
 ```
 
-### 示例:Sub-Trace 展开
+### 示例:Recursive Sub-Trace 逐层钻取
 
 ```javascript
 // 主 Trace 的 GoalTree
@@ -1100,36 +1151,21 @@ const mainTrace = {
   }
 }
 
-// 折叠视图:[1] → [2:并行探索] → [3]
-
-// 展开 Sub-Traces 后的视图:
-//           ┌→ [abc123.A] ────┐
-// [1] ──────┼                 ├──→ [3]
-//           └→ [abc123.B] ────┘
+// 仅 Recursive 把 sub_trace_ids 规范化为最多 6 个 A1~A6 入口。
+// 点击入口后切换 selectedTraceId,而不是在当前图内聚合展开。
 
-// 继续展开 Sub-Trace abc123.A 内部
-async function loadSubTrace(traceId) {
-  const resp = await fetch(`/api/traces/${traceId}`)
-  return await resp.json()  // 返回完整 Trace,含 goal_tree
+async function openSubTrace(entry) {
+  setSelectedTraceId(entry.trace_id)
+  const resp = await fetch(`/api/traces/${entry.trace_id}`)
+  return await resp.json()
 }
 
-const subTraceA = await loadSubTrace("abc123.A")
-// {
-//   trace_id: "abc123.A",
-//   goal_tree: {
-//     goals: [
-//       { id: "1", description: "JWT 设计" },
-//       { id: "2", description: "JWT 实现" }
-//     ]
-//   }
-// }
-
-// 展开后显示 Sub-Trace 内部 Goals:
+// 以下“同图展开”仅是旧设计示意,当前未实现:
 //           ┌→ [A.1:JWT设计] → [A.2:JWT实现] ──┐
 // [1] ──────┼                                  ├──→ [3]
 //           └→ [abc123.B] ─────────────────────┘
 
-// 注意:前端显示为 "A.1",实际查询是 GET /api/traces/abc123.A/messages?goal_id=1
+// 当前实际行为是切换到 abc123.A 的独立流程图。
 ```
 
 ### 视觉区分
@@ -1195,4 +1231,6 @@ function connect(traceId) {
 ## 相关文档
 
 - [Context 管理设计](../cyber_agent/docs/context-management.md) - Goal 机制完整设计
-- [Trace 模块说明](../docs/trace-api.md) - 后端实现细节
+- [Trace 模块说明](../cyber_agent/docs/trace-api.md) - 后端实现细节
+- [Agent 运行模式](../cyber_agent/docs/agent-modes.md) - Legacy/Recursive 边界和调度
+- [Recursive 任务协议](../cyber_agent/docs/recursive-task-protocol.md) - 报告、Validator 和审核状态机