先规划、后动手:claude-code-from-scratch Plan Mode只读规划与审批工作流实现原理
【免费下载链接】claude-code-from-scratchBuild your own Claude Code from scratch. 🔍 Claude Code 开源了 50 万行代码,读不动?用 ~5000 行 TypeScript / Python 从零复现核心架构,11 章分步教程带你理解 coding agent 精髓项目地址: https://gitcode.com/gh_mirrors/cl/claude-code-from-scratch
cLAUDE-code-from-scratch 是一个用约 5000 行 TypeScript / Python 代码从零复现 Claude Code 核心架构的开源教学项目。这篇文章带你读懂它其中最有"工程安全感"的一个功能——Plan Mode(只读规划模式):Agent 先只读探索代码、把方案写进 plan 文件,再由你在四选项审批工作流中决定"照做、改改再做、手动执行还是继续规划"。不啃几十行代码,一篇讲透原理。
为什么需要 Plan Mode:先想清楚,再动手改
用过 Coding Agent 的人都有过这种经历:让它"顺便加个功能",结果它一上来就改了三个文件,方向还不对,回滚半天。🤯
Plan Mode 解决的就是这个问题:把"规划"和"执行"拆成两个阶段。
- 规划阶段:Agent 只能读文件、搜索代码、思考,写文件、跑 Shell 全被拦下
- 审批阶段:Agent 把完整方案写进 plan 文件提交给你,你拍板
- 执行阶段:批准后按你的选择切换权限模式,开始真正动手
关键点是:「只读」这条约束不是靠提示词求它别乱动,而是权限系统在代码层面强制的。提示词负责引导,权限闸负责兜底——双保险。
三个入口:启动时、会话中、Agent 自主决定
进入 Plan Mode 有三条路径,对应不同的工作流(入口实现在 src/cli.ts 中):
| 入口 | 方式 | 适用场景 |
|---|---|---|
--plan参数 | 启动命令时带上 | 整个会话从规划开始 |
/plan命令 | REPL 中随时切换 | 先聊后规划,中途切入 |
enter_plan_mode工具 | Agent 自主调用 | 它自己判断"这个任务该先规划" |
其中enter_plan_mode/exit_plan_mode两个工具定义在 src/tools.ts,标记为deferred(延迟加载)——因为大多数会话用不到 Plan Mode,延迟加载可以节省提示词空间。
一键体验:看它如何把写文件拦下来
想直接感受效果?无需 API key,跑一条命令即可(完整可运行代码在 steps/ 目录):
git clone https://gitcode.com/gh_mirrors/cl/claude-code-from-scratch cd claude-code-from-scratch npm install && npm run build node steps/run.mjs 10在--plan模式下,哪怕模型主动发起write_file想写report.txt,也会当场被拦(这条演示脚本对应场景文件 steps/scenarios/plan-blocks-write.json):
$ mini-claude --plan Create a file report.txt with the plan. → write_file({"file_path":"report.txt","content":"the plan"}) That was blocked because we're in plan (read-only) mode.什么都没写进去。🔒
只读是怎么强制的:权限闸 + plan 文件白名单
Plan Mode 的核心逻辑在 src/agent.ts 的权限检查环节(checkPermission),规则很简洁:
- 编辑类工具(
write_file、edit_file):默认全部拒绝 - 唯一的例外:目标路径完全等于plan 文件路径时放行
- Shell 工具(
run_shell):一律拒绝 enter_plan_mode/exit_plan_mode:始终允许(这是状态切换工具)
这里有个精巧设计:plan 文件路径是作为参数传进权限检查的,写文件前逐一比对路径。也就是说,系统提示词里那句"你只能写这个 plan 文件"不只是建议,而是代码层面的硬约束——模型就算"忘了规矩",写操作也会被闸拦截并返回错误。
Plan 文件按会话 ID 生成在~/.claude/plans/plan-{sessionId}.md,每个会话互不干扰。
四选项审批工作流:批准后到底发生什么
规划完成后,Agent 调用exit_plan_mode,终端会打印计划内容并弹出四个选项(审批 UI 在 src/ui.ts):
| 选项 | 行为 | 权限切换 | 适用场景 |
|---|---|---|---|
| 1️⃣ Clear + Execute | 清空对话历史再执行 | →acceptEdits(自动接受编辑) | 计划已完善,上下文很长,从零执行最高效 |
| 2️⃣ Execute | 保留历史直接执行 | →acceptEdits | Agent 已有足够上下文,直接开工 |
| 3️⃣ Manual | 恢复进入前的权限模式 | → 原模式 | 计划大致可以,但想逐个审批每次修改 |
| 4️⃣ Keep Planning | 留在规划模式,把你的反馈喂回给 Agent | 不变 | 计划要改,让它继续打磨 |
选项 4 是闭环的关键:你的反馈会作为工具结果返回给模型,它据此修改 plan 文件后再次调用exit_plan_mode,直到你满意为止——形成一个"规划 → 审批 → 反馈 → 再规划"的迭代环。
三个值得偷师的细节
- plan 文件写在磁盘,不是留在对话里。选"Clear + Execute"时对话历史会被清空以释放上下文,但计划文件安然躺在
~/.claude/plans/里,Agent 可以重新读取、跨会话查看。计划不随上下文"蒸发"。 - 审批是回调注入,不是 Agent 内置。Agent 类通过
setPlanApprovalFn挂接审批函数(见 src/cli.ts),CLI 用 readline、IDE 可以换成 GUI、测试时注入模拟函数——Agent 完全不知道也不关心 UI 长什么样。 - 没有审批函数时优雅降级。子 Agent(见 src/subagent.ts)没有交互式审批,此时
exit_plan_mode直接退出并恢复原模式,把计划内容原样返回给调用方,不需要任何特殊分支。
小结
Plan Mode 用极小的代码量实现了一个完整的人机协作范式:只读探索 → 落盘成文 → 人工审批 → 按决策执行。它证明了一件事:限制 Agent 的能力(只读)反而是放大它价值的手段——先想清楚再动手,比一上来就改文件靠谱得多。
想深入源码级细节,推荐按章节读官方教程:docs/10-plan-mode.md(本章全文)、docs/06-permissions.md(权限系统)和 docs/01-agent-loop.md(Agent 主循环),教学代码快照在 steps/canonical/ 目录,TypeScript 版与 Python 版一一对应。
【免费下载链接】claude-code-from-scratchBuild your own Claude Code from scratch. 🔍 Claude Code 开源了 50 万行代码,读不动?用 ~5000 行 TypeScript / Python 从零复现核心架构,11 章分步教程带你理解 coding agent 精髓项目地址: https://gitcode.com/gh_mirrors/cl/claude-code-from-scratch
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考