# 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": "" } ``` 本地子孙继承该持久化策略;续跑、回溯或服务环境变更不会改变已有任务树的模式。没有模式字段的历史 Trace 按 `legacy` 续跑并补写模式。 Recursive revision 2 的新根 Trace 必须同时具备 `RootTaskAnchor`、其内容哈希和预算快照。缺少这些必需数据的早期实验 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 时继承。`read_context_ref` 同样只能逐层继承,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 继承的硬约束。 - 从根 Trace 读取的不可变 RootTaskAnchor。 - 当前 Trace 本地已授权的最多 8 个 ContextRef 快照;深层获取祖先结论必须逐边转授。 - `parent_trace_id` / `root_trace_id` / `agent_depth` 等 Trace 血缘。 - 冻结的工具能力快照和调度策略。 - 任务协议状态和预算所属根 Trace。 不自动继承父 Agent 的完整消息历史、未筛选 Tool Result、父 Runner 的全部 `config.context`、内部运行对象或祖先 GoalTree。`parent_findings`、普通 `context` 和 ContextRef 也不会自动累积;引用需逐层重新下发。完整规则见 [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` | 根门禁、预算包装和停止后协议行为 | | `tests/test_recursive_context_policy.py` | 根锚点、TaskBrief 版本、引用授权与限额 | | `tests/test_recursive_context_integration.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 继承、裁剪规则 |