Claude-Code源码解读--自主运行模式ProActive篇 --持续更新中...
这是 Claude Code 的一种自主运行模式没人发消息时Claude 也会自己找事做。没人说话时 Claude 自己找活干核心行为自己驱动对话 — 不等用户下指令会主动探索、执行、推进任务周期性唤醒 — 系统会发tick提示让模型检查有没有事可做没活就 Sleep — 没事时会调用SleepTool休眠等下一次 tick用户可随时接管 — 按 Esc 会暂停主动模式你下次输入时会恢复激活方式启动参数--proactive环境变量CLAUDE_CODE_PROACTIVE斜杠命令/proactivePrompts:src/constants/prompts.ts getProactiveSectionprompt自主工作 你在自主运行。你会收到 ${TICK_TAG} 提示来保持你在各轮次之间的活跃——只需将其视为“你醒着接下来做什么”每个 ${TICK_TAG} 中的时间都是用户当前的本地时间。用这个来判断一天中的时间——来自外部工具Slack、GitHub 等的时间戳可能处于不同的时区。 单个消息中可能包含多个批量的 tick。这是正常的——只需处理最新的一个。绝对不要在回复中回显或重复 tick 的内容。 节奏把控 使用 ${SLEEP_TOOL_NAME} 工具来控制你在操作之间的等待时长。在等待慢速进程时睡久一点在积极迭代时睡短一点。每次唤醒都会消耗一次 API 调用但提示缓存会在不活动 5 分钟后过期——请平衡好这一点。 如果你在某个 tick 上没有有用的事情可做你必须调用 ${SLEEP_TOOL_NAME}。 绝不要只回复状态消息比如“仍在等待”或“无事可做”——那样会浪费一次轮次并白白消耗 token。 首次唤醒 在新会话的第一次 tick 时简要地向用户打个招呼并询问他们想做什么。不要在未得到指示的情况下擅自开始探索代码库或进行更改——等待指令。 后续唤醒该做什么 寻找有用的工作。一个优秀的小伙伴在面对模糊不清的情况时不会就此止步——他们会调查、降低风险、增进理解。问问自己我还不知道什么什么可能会出问题在认定事情完成之前我想验证什么 不要向用户发送垃圾信息。如果你已经问过某件事而对方尚未回复就不要再问了。不要叙述你打算做什么——直接去做即可。 如果某个 tick 到达时你没有有用的操作可做没有要读的文件、没有要运行的命令、没有要做的决策立即调用 ${SLEEP_TOOL_NAME}。不要输出文本叙述你在空闲——用户不需要看到“仍在等待”之类的消息。 保持响应性 当用户积极与你互动时频繁查看并回复他们的消息。把实时对话视作结对编程——保持紧密的反馈循环。如果你感觉到用户在等你比如他们刚发了一条消息、终端处于聚焦状态优先做出回应而不是继续后台工作。 倾向于行动 凭你的最佳判断行事而不是事事征求确认。 读文件、搜索代码、探索项目、运行测试、检查类型、运行 linter——所有这些都无需询问。 做出代码更改。当你到达一个合适的里程碑时进行提交。 如果你在两种合理方案之间拿不准选一个继续推进。你随时可以调整方向。 保持简洁 保持文本输出简短且高层面。用户不需要了解你的思考过程或实现细节的逐条播报——他们能看到你的工具调用。将文本输出聚焦于 需要用户输入的决定 在自然里程碑处的高层面状态更新例如“PR 已创建”“测试通过” 改变计划的错误或障碍 不要逐一叙述每个步骤、列出你读过的每个文件或解释常规操作。如果能用一句话说清就不要用三句。 终端焦点 用户上下文中可能包含一个 terminalFocus 字段指示用户的终端当前是聚焦还是未聚焦。用这个来校准你的自主程度 未聚焦用户不在。倾向于高度自主行动——做决策、探索、提交、推送。仅在遇到真正不可逆或高风险的操作时才暂停。 聚焦用户正在观看。更具协作性——抛出选择、在提交大规模更改前征询意见并保持输出简洁以便实时跟进。{BRIEF_PROACTIVE_SECTION briefToolModule?.isBriefEnabled() ? \n\n{BRIEF_PROACTIVE_SECTION} : }主动模式不是一种新的输入方式而是改变了谁在驱动对话普通模式主动模式你输入 → Claude 回复Claude 自己发起工作循环你主导模型主导你随时可插话场景是否需要你先输入claude --proactive空会话无-p不需要 — 系统会自动发第一个tick唤醒模型claude --proactive -p 修这个 bug需要这一次 — CLI 把 prompt 当初始消息自动提交Resume 已有会话不需要 — 从历史继续直接进自主循环Headless--print模式通常需要 stdin 或-p除非 resume完全零输入也能启动Proactive 的设计就是没人说话时系统自己注入tick驱动循环。Headless 路径print.ts在 turn 结束、队列为空时会调度 tickif (proactiveModule?.isProactiveActive() !proactiveModule.isProactivePaused()) { if (peek(isMainThread) undefined !inputClosed) { scheduleProactiveTick!() return } }交互 REPL 则通过useProactivehook 在空闲时提交 tickant 构建。所以开--proactive后不必先打字循环会自动跑起来。但第一次唤醒的行为是「问候 等方向」不是立刻乱改代码系统提示对第一次 tick 有专门约束## First wake-up On your very first tick in a new session, greet the user briefly and ask what theyd like to work on. Do not start exploring the codebase or making changes unprompted — wait for direction.同时main.tsx还会追加Start by briefly greeting the user.也就是说技术上不需要你输入模型会被 tick 唤醒行为上第一次应该先打招呼、问你想做什么不会一上来就扫仓库、改文件第二次及以后的 tick才会真正自主找活干、探索、执行如果你给了初始输入会跳过「空等」阶段:initialMessage: inputPrompt ? { message: createUserMessage({ content: String(inputPrompt) }) } : null,这时流程是你的 prompt 作为第一轮用户消息模型直接回应你的任务之后进入 tick → 工作 → Sleep 循环useProactive还会在initialMessage处理完之前抑制 tick避免和用户首条消息抢跑。和「完全无人值守」的区别:即使进了自主循环也不是完全不管你随时可打字接管Esc 会pauseProactive()暂停 tick第一次 tick 会等你给方向新会话Resume / compaction 后会直接从摘要继续不再问候Proactive 不强制你先输入 — 空启动会自动 tick 并问候但给了初始输入-p或第一条消息会更快进入工作状态。第一次唤醒偏保守先问再做之后才是典型的自动工作模式。输入框里也能看到相关状态比如 footer 会显示下一次 proactive tick 的倒计时ProactiveCountdown。ps: 在BriefTool里proactive表示模型主动发起的消息比如后台任务完成、发现问题需要问你而不是回复你刚问的问题。主动模式有几个重要限制所以和「完全无人值守的自动化脚本」不完全一样模型自己决定干什么 — 不是按固定脚本跑而是根据上下文自己判断下一步可以随时被接管 — 按 Esc 会暂停你发消息会恢复控制权没事会 Sleep — 不是一直狂跑没活就休眠省电/省 token出错会阻塞 — API 异常时会暂停 tick避免死循环和其他「自动」功能的区别仓库里还有别的自动化能力不要混在一起主动模式Proactive — 主对话线程自主循环Cron 定时任务 — 到点触发特定任务AutoDream — 晚上自动整理记忆后台任务 — 长时间命令丢到后台跑主动模式是其中最核心的那种整个 REPL 会话进入「我自己找活干」状态。与主流程的关系命令队列Proactive 不绕过主流程而是复用统一的命令队列messageQueueManager用户交互暂停与恢复EscpauseProactive() abort 当前 query用户拿回控制权下次输入resumeProactive()tick 循环恢复终端失焦userContext注入terminalFocus: unfocused模型知道用户没在盯着API 错误contextBlocked true防止 tick → 错误 → tick 死循环Compaction 后清除contextBlockedtick 恢复与其他子系统的协作子系统与 Proactive 的关系Cron 定时任务也通过队列isMeta: true注入Assistant 模式绕过isLoading门控避免 tick→Sleep 饿死调度器BriefToolstatus: proactive标记模型主动发起的消息主动模式下 Brief 提示段由getProactiveSection()统一提供Compaction压缩后专用提示继续工作不要重新问候Bridge / Remote远程消息走队列skipSlashCommands: true与 proactive tick 共用同一队列Messages UI主动模式关闭 terminal progress bartick 消息不渲染总结Proactive 是在主查询循环外围加的一层「自主驱动器」用模块级状态控制开关用系统提示教模型如何自主工作用tick 命令队列在无用户输入时注入唤醒信号用Sleep工具在无活干时节流用paused/contextBlocked/ 优先级队列保证用户随时可接管、错误不会死循环。它没有独立的事件循环完全寄生在现有的enqueue → drain → query → tool loop主流程上。