【AgentScope 2.0】06-计划模式(Plan Mode)详解

0 阅读18分钟

计划模式(Plan Mode)详解

版本基准:本文档基于 AgentScope 2.0 GA(v2.0.0)编写。具体版本号以 Release Notes 为准。

一句话概括

计划模式让 Agent 在动手干活之前先"写好施工图纸、交给监理审批",通过只读阶段强制思考、HITL 确认门控把关,有效降低"边想边改、改坏一片"的概率。

你能学到什么

  • 理解计划模式的核心思想:先规划再执行,两阶段分离
  • 掌握只读阶段的工具白名单机制(9 个白名单工具 + 只读工具)
  • 理解 HITL(Human-in-the-Loop)退出确认的原理
  • 学会使用 planFileDirectory 配置计划文件位置
  • 了解可选的 allowShellInPlanMode() —— 在 plan 阶段放开 shell 做只读调查
  • 掌握运行期切换 PermissionMode("危险开关"逃生口)
  • 了解计划状态的持久化与恢复
  • 掌握编程控制 API:enterPlanMode(ctx)、exitPlanMode(ctx)、isPlanModeActive(ctx)
  • 学会判断一次运行到底落到哪种终态(四种终态)
  • 理解计划模式与 todo_write 的配合方式
  • 了解子 Agent 在计划模式下的已知限制

前置知识

详见 README.md 前置知识部分。本篇额外需要:

  • Middleware 机制:了解 2.0 的洋葱模型和挂载点(见 01-overview
  • AgentState:了解运行时状态管理(见 03-context
  • 工作区目录结构:知道 workspace 下有哪些文件和文件夹(见 01-overview

核心概念

先规划再执行 —— 就像建筑工地的图纸审核

想象你是一个建筑工地的项目经理。现在要盖一栋大楼,你不会拿到需求就抡起锤子开干——你会先让设计师画好图纸,然后拿着图纸去找监理审批。监理觉得没问题、签了字,施工队才开始动手。

如果设计师边画图边施工,可能出现"地基打了一半发现图纸错了"这种灾难。所以正确流程是:图纸阶段只能画图和看现场,不能动一砖一瓦

计划模式的核心思想也是如此:把 Agent 的工作分成两个阶段——

阶段建筑工地类比Agent 行为
只读阶段(Plan Phase)设计师画图纸、勘察现场只能读文件、搜索代码、写计划文件,不能执行任何修改操作
执行阶段(Build Phase)施工队按图纸施工所有工具解禁,可以写文件、执行命令等

这两个阶段之间有一个硬性门控:Agent 不能自己从只读阶段跳到执行阶段,必须经过人类确认(HITL),就像监理必须签字才能开工。


只读阶段的工具白名单 —— 就像阅览室的"只许看不许动"规则

生活类比:想象你在图书馆的古籍阅览室。规则很明确——你可以看、可以记笔记、可以翻目录,但绝对不能在书上写字、不能把书带走、不能撕页。如果你违反规则,管理员会立刻阻止你。

技术解释:当 Agent 进入计划模式后,它被限制在一个"阅览室"里。PlanModeMiddlewareonActing 阶段拦截工具调用,判定为允许的工具才放行,其余的一律生成一个 DENIED 的工具结果(不真正执行)。被允许的工具有三类,共 9 个白名单工具 + 所有只读工具:

类别工具说明
计划控制工具plan_enterplan_writeplan_exit计划模式的进入 / 写计划文件 / 退出
协作工具todo_writeagent_spawnagent_sendagent_listtask_outputtask_list待办清单与子 agent 编排(非破坏性)
所有只读工具read_filegrep_fileslist_directoryToolBase.isReadOnly() 判定,可以查看代码和文件

如果 Agent 在只读阶段尝试调用非白名单工具(比如 write_file 或默认情况下的 execute),会立刻被拒绝,并收到这样的提示:

Blocked: you are in PLAN mode (read-only). You may investigate and run read-only
tools, record your plan with plan_write, and call plan_exit when ready to execute.
Do not modify files or run mutating commands until the plan is approved.

Agent 看到这个拒绝信息后,会自然地回到"先写计划"的正确路径上。

实现要点(GA 源码)PlanModeMiddleware 的判定写在 isPermitted(toolName) 里——只要工具名命中 ALWAYS_ALLOWED 集合、或被额外放行(如 allowShellInPlanMode 注册的 execute)、或通过 readOnlyResolver 判定为只读,就放行。注意它没有复用 PermissionMode.EXPLORE:那个 mode 是权限引擎构建时快照、无法运行时切换,而 plan mode 必须能动态开关。

为什么要用 plan_write 而不是直接开放 write_file

因为 write_file 可以写任意文件,如果把 write_file 加入白名单,Agent 在只读阶段就能偷偷修改代码文件,那"只读"就形同虚设了。plan_write 只能写计划文件,安全可控。


HITL 退出确认 —— 就像监理签字放行

生活类比:建筑师画完图纸后,不能自己宣布"开搞!"——他必须把图纸交给监理工程师审核。监理看完觉得方案可行,在图纸上签了字,施工队才能进场。如果监理觉得有问题,打回去重画。

技术解释:Agent 在只读阶段完成了计划编写,调用 plan_exit 退出时,不会直接进入执行阶段。PlanExitTool.checkPermissions(...) 恒定返回一个 ASK 决策,复用权限系统的 HITL 流程弹出人工确认请求:

Agent 调用 plan_exit(参数:summary,可选)
        │
        ▼
┌─────────────────────────────┐
│  HITL 确认请求               │
│                              │
│  "Agent 已完成计划编写,       │
│   是否允许进入执行阶段?"      │
│                              │
│   [确认]  [拒绝]              │
└──────────┬──────────┬────────┘
           │          │
        确认         拒绝
           │          │
           ▼          ▼
     进入执行阶段   留在只读阶段

这个 HITL 门控是计划模式最关键的安全设计——它防止了模型"自作主张"直接进入执行阶段,确保人类始终在重大操作前有最终决定权。即使没有配置任何 allow/deny 规则,轻量级权限路径也能 honor 这个 ASK 自检,所以开箱即用。


计划文件与 planFileDirectory —— 就像图纸专用档案柜

生活类比:建筑公司的图纸不会随便丢在办公桌上,而是存放在专门的图纸档案柜里。柜子的位置可以指定(比如"3 号档案室 B 柜"),但默认放在"2 号档案室"。

技术解释:计划文件默认存放在工作区的 plans/PLAN.mdPlanModeManager.DEFAULT_PLAN_DIR = "plans"),你可以通过 planFileDirectory() 自定义这个位置:

workspace/
├── plans/                  ← 默认的计划文件目录
│   └── PLAN.md            ← 计划内容写在这里
├── AGENTS.md
├── MEMORY.md
└── ...

配置方式:

Builder 方法默认值说明
enablePlanMode() / enablePlanMode(boolean)false是否开启计划模式
planFileDirectory(String)"plans"计划文件根目录(相对于 workspace)
allowShellInPlanMode() / allowShellInPlanMode(boolean)false按需放开 plan 阶段的 shell(execute)——见下节

计划文件跟着你选择的文件系统模式走——本机、沙箱、远端 KV 都行,天然分布式可用。


在 plan 阶段放开 shell(可选)—— 就像"借工具但保证只读"

生活类比:古籍阅览室原则上不让带笔(怕你在书上写)。但有些调查任务确实需要用放大镜、紫外灯看细节。馆长说:"行,工具借你,但你要保证只用来看,不能改。"这就是 allowShellInPlanMode()

为什么默认禁 shell? shell 是双用途工具:同一次调用既可能是读(cat / ls / grep / git log),也可能是写(rm / > / git commit / npm install)。计划模式完全靠工具名判定是否放行,无法区分这一次是读还是写。默认禁掉 shell,是为了保住"只读"这条保证。

为什么有时需要放开? 通过 shell 读取各种内容,往往是调查代码库、产出切实可行计划的最灵活方式。

放开方式

HarnessAgent agent = HarnessAgent.builder()
    .name("planner")
    .model(model)
    .workspace(workspace)
    .enablePlanMode()
    .allowShellInPlanMode()    // 让模型在 plan 阶段以只读方式跑 shell
    .build();

开启后(PlanModeMiddlewareadditionalAllowed 集合会包含 execute):

  • execute 被加入 plan 阶段放行名单,模型可以用 shell 做调查;
  • plan banner 会追加一条提示,要求模型把 shell 用法限制在只读cat / ls / grep / git log/diff/show/status),在计划获批前不要跑 mutating 命令;
  • 专用的文件编辑工具(write_file / edit_file仍然被拒——它们是主要的改动入口,所以对文件写入的只读意图依然被强制保证。

取舍提示:这条保证比默认情况更弱(模型仍可能通过 shell 改东西),建议配合沙箱文件系统一起开,把爆炸半径关进沙箱。


运行期切换权限模式 —— "危险开关"逃生口

计划模式只是一个具体的阶段开关。在它之下,每个 session 都带着一个 PermissionMode,由权限引擎在评估时使用。你可以在运行期翻转这个 mode——比如提供一个由用户显式触发的"跳过所有权限确认"开关(类似其它编码工具里的 YOLO / dangerous-skip 开关):

RuntimeContext ctx = RuntimeContext.builder().sessionId("my-session").build();

agent.setPermissionMode(ctx, PermissionMode.BYPASS);    // 全部放开、不再弹确认
// ... 跑需要完整权限的操作 ...
agent.setPermissionMode(ctx, PermissionMode.DEFAULT);   // 恢复正常管控

PermissionMode current = agent.getPermissionMode(userId, sessionId);

四种 PermissionMode

Mode含义
DEFAULT默认,所有操作需要显式 allow 规则
EXPLORE只读模式,修改工具被拒
BYPASS全部放开、不再做规则评估
DONT_ASKASK 决策降级为 DENY(适合无人值守,但仍保留管控)

setPermissionMode(...) 只改 mode,会保留该 session 已配置的 allow/deny/ask 规则与工作目录,并重建该 session 缓存的权限引擎,使切换在下一次 call 生效;正在进行中的 call 仍沿用启动时的引擎。

BYPASS 会关闭所有规则评估,因此应当作为显式的、按 session 的主动操作,并建议配合沙箱使用。如果想要无人值守且不弹确认、但仍保留管控,请改用 PermissionMode.DONT_ASK(ASK 决策变成 DENY,而不是被自动放行)。


状态持久化 —— 就像建筑师下班前保存图纸进度

生活类比:建筑师下班时,会把画了一半的图纸存进保险柜。第二天上班,打开保险柜取出图纸,接着昨天的进度继续画。即使公司搬家了(进程重启),只要保险柜还在,工作就不会断。

技术解释:计划模式是运行时状态,会随 AgentState 自动持久化。具体来说,AgentState.getPlanModeContext() 保存了当前的计划模式上下文信息:

┌────────────────────────────────────────────────────┐
│  AgentState                                         │
│  ├─ 对话上下文                                      │
│  ├─ 摘要                                            │
│  ├─ 权限上下文                                      │
│  ├─ PlanModeContext  ← 计划模式状态在这里            │
│  │   ├─ 是否处于只读阶段(planActive)              │
│  │   ├─ 当前计划文件路径                            │
│  │   └─ 其他计划相关元数据                          │
│  ├─ 任务上下文                                      │
│  └─ 工具上下文                                      │
└────────────────────────────────────────────────────┘
        │
        │ call() 结束,AgentState 自动持久化
        ▼
┌────────────────────────────────────────────────────┐
│  AgentStateStore                                    │
│  (默认 JsonFileAgentStateStore,~/.agentscope/      │
│   state/<userId>/<sessionId>/agent_state.json)       │
│  └─ 包含完整的 AgentState(含 PlanModeContext)      │
└────────────────────────────────────────────────────┘

这意味着:进程重启、节点切换、跨副本恢复后,计划阶段会一起恢复。Agent 不会因为重启就从执行阶段退回只读阶段,也不会丢失已经写好的计划文件。


如何判断运行结果 —— 别只看 isPlanModeActive()

是否进入计划模式由模型自主决定,所以一次运行可能落到四种终态。只看 isPlanModeActive() == false 是有歧义的——别在没确认"是否真的规划过"之前就当成成功:

终态含义判断依据
从未进入 plan mode模型选择直接在 build 模式工作——合法决定,常因任务与 workspace 不匹配没调用过 plan_enter
进入 → plan_exit成功:规划完、获批,已进入 build 模式调用过 plan_enter,当前 planActive == false
仍在 plan mode + 有 PLAN.md起草了计划但没退出;在同一 session 发后续消息批准继续当前 planActive == trueplans/PLAN.md 存在
仍在 plan mode + 无 PLAN.md"只说不做":最终文本看着像计划但没真正写出当前 planActive == trueplans/PLAN.md 不存在

要在代码里区分这几种,可在监听最终 isPlanModeActive() 和计划文件是否存在的同时,记录是否调用过 plan_enter / plan_write(例如从 ToolCallStartEvent 捕获)。


与 todo_write 的配合 —— 就像图纸 + 施工任务单

生活类比:图纸(PLAN.md)描述的是"要建什么、为什么这么建";施工任务单(todo list)描述的是"每天具体干什么"。先有图纸,再根据图纸拆出任务单,施工队按任务单逐条推进。

技术解释:计划模式和 todo_write 是两个独立但常常一起用的概念:

概念职责类比
计划模式(Plan Mode)阶段开关 + 计划文件 + HITL 退出确认图纸 + 监理审批
todo_write维护"当前要做什么"的结构化清单(全量替换,恰好一个 in_progress施工任务单

典型工作流:

1. Plan 阶段:
   Agent 思考 → 读文件 → 写 PLAN.md → plan_exit → HITL 确认

2. 执行阶段:
   Agent 根据 PLAN.md → 用 todo_write 拆成 5-8 条任务 → 逐条推进
                                                ↑
                                          每轮推理前自动展示
                                          帮助 Agent 保持聚焦

关键区别:不要把 todo_write 和子 Agent 的后台任务(task_output / task_list)混淆——那是完全不同的机制,详见 09-subagent


关键代码解读

1. 开启计划模式

// 构建一个开启了计划模式的 Agent
HarnessAgent agent = HarnessAgent.builder()
    .name("planner")                              // Agent 名字
    .model("my-model-id")                         // 模型 ID
    .workspace(Paths.get(".agentscope/workspace")) // 工作区路径
    .enablePlanMode()                             // 开启计划模式三件套
    .planFileDirectory("plans")                   // 可选;计划文件目录,默认 "plans"
    .build();

enablePlanMode() 做了两件事:

  1. 注册 plan_enterplan_writeplan_exit 三个工具(plan_exit 内部 checkPermissions 恒返回 ASK,从而复用权限系统的 HITL 流程要求人类确认)
  2. 安装 PlanModeMiddleware,在只读阶段拦截并拒绝非白名单工具

你也可以用 enablePlanMode(true)enablePlanMode(false) 显式控制开关。


2. 三个计划工具的参数

// plan_enter:进入计划模式(无参数)
// Agent 调用后进入只读阶段,工具白名单生效

// plan_write:把计划写到 plans/PLAN.md
// 参数:content(String 类型,计划文件的完整内容)
// 这是专门为计划模式设计的写入入口——安全、可控

// plan_exit:退出计划模式(触发 HITL 确认)
// 参数:summary(可选,给人类确认用的计划摘要)
// 人类确认后才真正进入执行阶段

3. 编程控制 API

如果你想在业务代码里主动控制计划模式(比如管理台有个"开始规划"按钮),可以直接调用 Agent 的方法:

// 进入计划模式——等价于 LLM 调用 plan_enter
// 需要带上 RuntimeContext(或 userId/sessionId),按会话寻址
agent.enterPlanMode(runtimeContext);

// 退出计划模式——等价于 plan_exit
// 注意:通过程序接口退出不会触发 HITL 确认
agent.exitPlanMode(runtimeContext);

// 查询当前是否处于计划模式
boolean planning = agent.isPlanModeActive(runtimeContext);

注意:通过编程 API 调用 exitPlanMode() 不会触发 HITL 确认——因为人类已经在管理台点了按钮,相当于已经"签字"了。只有 LLM 自己调用 plan_exit 工具时才会触发 HITL 确认。

如果用了 agentscope-admin-spring-boot-starter,还可以通过 HTTP 接口操作:

# 进入计划模式
POST /v1/admin/sessions/{id}:enter-plan-mode

# 退出计划模式
POST /v1/admin/sessions/{id}:exit-plan-mode

# 查看当前计划
GET /v1/admin/sessions/{id}/plan

4. 与 todo_write 配合的完整示例

HarnessAgent agent = HarnessAgent.builder()
    .name("planner")
    .model("my-model-id")
    .workspace(Paths.get(".agentscope/workspace"))
    .enablePlanMode()          // 开启计划模式
    .enableTaskList()          // 开启 todo 提示(可选但推荐)
    .build();

// 用户发起一个复杂的重构任务
String reply = agent.call("帮我重构 X 模块,把所有硬编码的配置项提取到配置文件里");

// Agent 的工作流程:
// 1. 自动调用 plan_enter 进入只读阶段
// 2. 读代码、搜索相关文件(只读操作)
// 3. 调用 plan_write 把重构计划写到 plans/PLAN.md
// 4. 调用 plan_exit → 弹出 HITL 确认
// 5. 人类确认后进入执行阶段
// 6. 用 todo_write 把计划拆成多个任务(恰好一个 in_progress)
// 7. 逐条执行任务

整体流程图

┌──────────────────────────────────────────────────────────────────┐
│                     计划模式完整流程                               │
├──────────────────────────────────────────────────────────────────┤
│                                                                  │
│   用户:"帮我重构 X 模块"                                         │
│         │                                                        │
│         ▼                                                        │
│   ┌────────────────────┐                                        │
│   │  Agent 推理循环     │                                        │
│   │  调用 plan_enter    │ ← 进入只读阶段                          │
│   └────────┬───────────┘                                        │
│            │                                                     │
│            ▼                                                     │
│   ╔══════════════════════════════════════════════════╗           │
│   ║  只读阶段(Plan Phase)                          ║           │
│   ║                                                  ║           │
│   ║  ┌──────────┐  ┌──────────┐  ┌──────────┐      ║           │
│   ║  │读文件    │  │搜索代码  │  │读计划    │      ║           │
│   ║  │read_file │  │grep_files│  │plan_write│      ║           │
│   ║  └──────────┘  └──────────┘  └──────────┘      ║           │
│   ║                                                  ║           │
│   ║  ✅ 允许:9 个白名单工具(plan_* + todo_write     ║           │
│   ║     + agent_* + task_*)+ 所有只读工具            ║           │
│   ║     (allowShellInPlanMode 开启后 + execute)     ║           │
│   ║  ❌ 拒绝:write_file, edit_file, 默认 execute    ║           │
│   ║                                                  ║           │
│   ║  工具被拒时返回:                                 ║           │
│   ║  "Blocked: you are in PLAN mode (read-only)..."  ║           │
│   ╚════════════╤═════════════════════════════════════╝           │
│                │                                                  │
│                │ 调用 plan_exit                                   │
│                ▼                                                  │
│   ┌────────────────────────────────────────┐                    │
│   │          HITL 确认门控                  │                    │
│   │                                        │                    │
│   │   "Agent 已完成计划,是否允许执行?"      │                    │
│   │          [确认]    [拒绝]               │                    │
│   └────┬───────────────────────┬───────────┘                    │
│        │                       │                                 │
│     确认✅                  拒绝❌                                │
│        │                       │                                 │
│        ▼                       ▼                                 │
│   ╔══════════════════╗   留在只读阶段                             │
│   ║  执行阶段         ║   继续修改计划                             │
│   ║  (Build Phase)   ║                                            │
│   ║  所有工具解禁 ✅  ║                                            │
│   ║                  ║                                            │
│   ║  ┌────────────┐  ║                                            │
│   ║  │ todo_write │  ║ ← 把计划拆成任务清单                         │
│   ║  │ 拆分任务    │  ║                                            │
│   ║  └─────┬──────┘  ║                                            │
│   ║        │         ║                                            │
│   ║        ▼         ║                                            │
│   ║  逐条执行任务     ║                                            │
│   ╚══════════════════╝                                            │
│                                                                  │
└──────────────────────────────────────────────────────────────────┘

模块关系与学习顺序

模块与本篇的关联
上下文AgentState.getPlanModeContext() 持久化、(userId, sessionId) 寻址
文件系统plans/ 目录跟着文件系统模式走(本机、沙箱或远端 KV)
权限系统plan_exit 复用 ASK HITL 流程;运行期可通过 setPermissionMode 切换权限模式
子 Agent子 Agent 的后台任务(task_*)不等于 todo_write,不要混淆

学习要点

必须记住

  1. 计划模式 = 两阶段分离:只读阶段(只能看和写计划)→ HITL 确认 → 执行阶段(全部解禁),不跳步
  2. 白名单不止计划三件套:9 个白名单工具(plan_enterplan_writeplan_exittodo_writeagent_spawnagent_sendagent_listtask_outputtask_list)+ 所有只读工具,其余一律被拒
  3. plan_write 不是 write_file:专门的安全写入入口,只能写计划文件,不会破坏其他文件
  4. HITL 是硬门控plan_exit 恒返回 ASK,LLM 不能自己跳过确认,只有人类同意才能进入执行阶段
  5. 状态会持久化:计划模式状态存在 AgentState.getPlanModeContext() 里,重启不丢失
  6. allowShellInPlanMode() 是可选的弱保证:放开 shell 便于调查,但保证比默认弱,建议配合沙箱
  7. setPermissionMode 是逃生口BYPASS 全放开(慎用)、DONT_ASK 把 ASK 降级 DENY(适合无人值守)

容易混淆

  1. plan_write vs write_file

    • plan_write:只写 plans/PLAN.md,计划模式专用,安全可控
    • write_file:可以写任意文件,计划模式下被禁用
  2. 计划模式 vs todo_write

    • 计划模式:阶段开关 + 计划文件 + HITL 门控("图纸 + 审批")
    • todo_write:执行阶段维护结构化任务清单("施工任务单",恰好一个 in_progress
    • 两者独立但经常配合使用
  3. todo_write vs 子 Agent 后台任务

    • todo_write:Agent 自己的任务清单,全量替换模式
    • 子 Agent 任务(task_output / task_list):委派给子 Agent 的异步任务
    • 完全不同的概念,不要混淆
  4. 编程退出 vs 工具退出

    • agent.exitPlanMode()(编程调用):不触发 HITL 确认
    • plan_exit(LLM 调用工具):触发 HITL 确认
  5. Plan Mode vs PermissionMode

    • Plan Mode:具体的阶段开关(只读 → 执行),由 PlanModeMiddleware 拦截
    • PermissionMode:session 级别的权限评估模式(DEFAULT/EXPLORE/BYPASS/DONT_ASK),可在运行期切换
    • Plan Mode 没有复用 EXPLORE,因为后者构建时快照、无法动态切换
  6. 四种终态isPlanModeActive() == false 不等于"成功"——也可能是从未进入,或仍在起草但 session 未结束。需要结合是否调用过 plan_enter / 是否有 PLAN.md 一起判断

实践建议

  1. 复杂任务必开计划模式:涉及多文件修改、架构调整的任务,开启计划模式能有效降低出错概率
  2. 配合 todo_write 使用:在计划阶段写好 PLAN.md,执行阶段用 todo_write 拆成 5-8 条任务逐条推进
  3. 合理设置 planFileDirectory:如果多个 Agent 共用工作区,给每个 Agent 设置不同的计划目录避免冲突
  4. 调查型任务可考虑 allowShellInPlanMode():配合沙箱文件系统一起开,既保留调查灵活性又控制爆炸半径
  5. 无人值守用 DONT_ASK 而非 BYPASS:前者仍保留规则评估,后者全放开风险大
  6. 注意子 Agent 的限制:当前子 Agent 不会自动继承只读限制,需要手动过滤工具或单独开启计划模式

常见问题

Q:如果 Agent 在只读阶段被拒绝了很多次怎么办?

A:这是正常的。每次拒绝都会返回提示信息,Agent 看到后通常会调整策略,转而使用只读工具继续思考或完善计划。如果反复拒绝,说明 Agent 的工具选择策略需要优化。

Q:HITL 确认被拒绝后,Agent 会怎样?

A:Agent 会留在只读阶段,可以继续修改计划文件,再次调用 plan_exit 请求确认。流程可以循环多次,直到人类满意为止。

Q:计划模式状态下进程重启了,会怎样?

A:AgentState 会自动恢复,计划模式的状态(包括是否在只读阶段)也会一起恢复。Agent 会继续留在之前的阶段,不会丢失进度。

Q:子 Agent 在计划模式下有什么限制?

A:当前已知限制——通过 agent_spawn 启动的子 Agent 不会自动继承只读限制。如果需要子 Agent 也只读,需要在子 Agent 声明里过滤工具到只读集合,或者在子 Agent 的 builder 里也开启 enablePlanMode()。未来版本会自动传播这个限制。

Q:可以不开计划模式,只用 todo_write 吗?

A:可以。todo_write 是 core 提供的独立工具,不依赖计划模式。但建议对复杂任务两者配合使用——计划模式提供安全门控,todo_write 提供执行聚焦。

Q:我想让 Agent 在 plan 阶段用 git log / grep 调查代码,但默认 execute 被拒,怎么办?

A:两个办法。一是用专门的只读工具(read_file / grep_files / list_directory),它们本来就在白名单里;二是 .enablePlanMode().allowShellInPlanMode(),把 execute 加入放行名单(plan banner 会提示模型只用来做只读调查)。后者保证更弱,建议配合沙箱使用。

Q:一次 call 结束后 isPlanModeActive() 返回 false,能说明任务成功了吗?

A:不能。false 有两种合法情况——从未进入 plan mode,或进入并 plan_exit 成功。需要结合是否调用过 plan_enter / plan_write,以及 plans/PLAN.md 是否存在来综合判断(见"如何判断运行结果"四态表)。