Claude-Code源码解读--自主运行模式ProActive篇 --持续更新中...
这是 Claude Code 的一种自主运行模式:没人发消息时,Claude 也会自己找事做。没人说话时 Claude 自己找活干

核心行为:
- 自己驱动对话 — 不等用户下指令,会主动探索、执行、推进任务
- 周期性唤醒 — 系统会发
<tick>提示,让模型检查有没有事可做 - 没活就 Sleep — 没事时会调用
SleepTool休眠,等下一次 tick - 用户可随时接管 — 按 Esc 会暂停主动模式;你下次输入时会恢复
激活方式:
- 启动参数:
--proactive - 环境变量:
CLAUDE_CODE_PROACTIVE - 斜杠命令:
/proactive
Prompts:
src/constants/prompts.ts getProactiveSection
prompt:
自主工作
你在自主运行。你会收到 <${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 自己发起工作循环 |
|
你主导 |
模型主导,你随时可插话 |

| 场景 | 是否需要你先输入 |
|---|---|
|
|
不需要 — 系统会自动发第一个 |
|
|
需要这一次 — CLI 把 prompt 当初始消息自动提交 |
|
Resume 已有会话 |
不需要 — 从历史继续,直接进自主循环 |
|
Headless |
通常需要 stdin 或 |
完全零输入也能启动:
Proactive 的设计就是:没人说话时,系统自己注入 <tick> 驱动循环。
Headless 路径(print.ts)在 turn 结束、队列为空时会调度 tick:
if (proactiveModule?.isProactiveActive() && !proactiveModule.isProactivePaused()) {
if (peek(isMainThread) === undefined && !inputClosed) {
scheduleProactiveTick!()
return
}
}
交互 REPL 则通过 useProactive hook 在空闲时提交 tick(ant 构建)。
所以:开 --proactive 后,不必先打字,循环会自动跑起来。
但第一次唤醒的行为是「问候 + 等方向」,不是立刻乱改代码:
系统提示对第一次 tick 有专门约束:
## First wake-up
On your very first tick in a new session, greet the user briefly and ask what they'd 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):

用户交互:暂停与恢复

- Esc:
pauseProactive()+ abort 当前 query,用户拿回控制权 - 下次输入:
resumeProactive(),tick 循环恢复 - 终端失焦:
userContext注入terminalFocus: 'unfocused',模型知道用户没在盯着 - API 错误:
contextBlocked = true,防止 tick → 错误 → tick 死循环 - Compaction 后:清除
contextBlocked,tick 恢复
与其他子系统的协作:
| 子系统 | 与 Proactive 的关系 |
|---|---|
|
Cron 定时任务 |
也通过队列 |
|
BriefTool |
|
|
Compaction |
压缩后专用提示:继续工作,不要重新问候 |
|
Bridge / Remote |
远程消息走队列 |
|
Messages UI |
主动模式关闭 terminal progress bar;tick 消息不渲染 |
总结:
Proactive 是在主查询循环外围加的一层「自主驱动器」:用模块级状态控制开关,用系统提示教模型如何自主工作,用 <tick> + 命令队列在无用户输入时注入唤醒信号,用 Sleep 工具在无活干时节流,用 paused / contextBlocked / 优先级队列保证用户随时可接管、错误不会死循环。它没有独立的事件循环,完全寄生在现有的 enqueue → drain → query → tool loop 主流程上。
更多推荐



所有评论(0)