消失的问题:远程 Claude Code 与三小时陷阱
消失的问题:远程 Claude Code 与三小时陷阱
那个任务已经在我的远程主机上跑了一阵子——那种我启动之后就转身离开的、长时间多步骤的活。我人在别处,手机忽然震了一下。是我自己写的一个小提醒工具 claude-code-notify 发来的 Telegram 消息,它会在 Claude Code 的一轮任务需要我输入、跑完,或者挂掉的时候通知我——这样我就不用一直守着。这次是第一种:Claude 走到了一个它自己做不了主的决定,停下来问我。
我没能及时看到那条提醒。等我终于看到时,伸手去按计划好的方式回答:这个会话开着 Remote Control,照理我应该能直接在 Claude 手机 App 里回复。可那边的会话已经是 disconnected 状态——没有任何可以输入的地方。于是我回到笔记本上的 VS Code 插件面板,重新连上,结果那个问题也不在那儿了。会话重新跑起来之后再也没有问过一次;它悄悄跳过了本该由我填上的那个空档,径直往下走了。不是被回答了——是彻底消失了,历史记录里也没留下它到底决定了什么。唯一能证明那一刻真实存在过的,只有手机上那条还躺在那儿的通知——Claude needs you——指向一个已经无处可答的问题。
让我耿耿于怀的正是这种对不上号。那条提醒告诉我确实发生过一次交流,而我错过了自己该说的那一半——可我哪儿都找不到这次交流,看不到自己到底错过了什么。我花了小半天时间才弄清楚断层到底出在哪儿。找到之后,修法就只是一行配置。
我为什么把 agent 放在远程主机上跑
先倒回去讲,因为这套设置本身就是整个故事的关键。
我写了十几年软件,这种经历留下一种很难关掉的直觉:任何系统都会以你没预料到的方式出问题,所以你不会把没经过审视的软件放开手,让它不受限制地碰你在意的东西。AI agent 不会是例外——恰恰相反,它们比谁都更该守这条规矩,因为它们动作快、下手果断,而且能碰到你整个文件系统。所以从我用上 agent 的第一天起,就把它放在一个就算搞砸也碰不到我笔记本的地方跑:一台个人云端 VM,隔离、随时可以扔掉重来。
这种直觉后来得到了回报。Claude Code 有一次直接在聊天窗口里把一个 git token 用明文打了出来。它自己发现了这个失误,告诉我要把这个 token 当成已泄露处理并轮换掉——我照做了。这一次,它自己接住了。隔离真正的意义在于它没接住的那一次。如果那不是 git token 而是一把钱包私钥,"哦,轮换掉"这句话可救不了你。
我第一次尝试远程,用的是 Claude Code 的 Telegram 通道——完全靠聊天消息驱动 agent。它的效果跟纯 vibe-coding 一样:看不到文件,看不到 diff,等于蒙着眼睛开车,结果也确实体现出来了。后来我找到了真正合适的组合:VS Code 的 Remote-SSH 直接在远程主机上管理项目,Claude Code 插件跑在这个基础上。既安全又看得清楚。算力都在服务器上,不占我笔记本的资源——而我最喜欢的一点是,就算笔记本睡眠或者关机,任务照样接着跑。我可以启动一个很重的任务,合上盖子,它会自己继续。
要补上最后一道缺口——在不用守在电脑前的情况下,真正回答 agent 的提问——我靠的是两样东西。这篇文章开头提到的那个提醒工具是其中一半:它告诉我 Claude 什么时候需要我。另一半是 Remote Control(/remote-control),让我能在手机上的 Claude App 里批准提示、回答问题——这是怎么回答的那一半。收到提醒说它需要我,掏出手机回答。这就是当时的工作流程。(记住"什么时候"和"怎么"这个拆分,问题恰恰就出在这道分界上。)
为什么会失效——刚好够用的心智模型
为了搞清楚到底发生了什么,我花了些时间钻研 Claude Code 的心智模型,看它底层到底是怎么跑的。
一个 Claude Code 会话本身并不是一个正在运行的程序。它是磁盘上的数据——记录、消息历史——由某个进程加载并接续下去。进程空闲的时候,其实没有任何计算在发生,它只是在等输入。把进程杀掉,会话不会因此死掉,只是冷掉了:记录还在磁盘上,你之后可以恢复它。
那真正在跑的是什么?是一个进程。而在我这套设置里——VS Code 插件面板驱动一台远程主机——这个进程有一个我从没想过的父进程。直到我最后去看服务器上的进程树才发现:claude 进程是 VS Code Server 的 extension host 的子进程,而 extension host 正是那个活在远程机器上、支撑你编辑器窗口的东西。
现在把我的笔记本合盖睡眠。从远程服务器的角度看,我的笔记本是一个客户端,而它刚刚断开了连接。VS Code Server 不会立刻把东西拆掉——它会把 extension host 保留一段宽限期,等客户端回来。但一旦超过这个窗口,它就会放弃,把 extension host 销毁掉。而 host 一旦没了,所有子进程都会跟着一起没——包括 claude 进程,也包括它内存里那个还悬着、等我回答的问题。
这就是整个故障的全貌。我笔记本睡眠的时间超过了宽限期。远程服务器回收了 extension host。进程在问题问到一半的时候就死掉了。等我后来重新连接、恢复会话时,拿回来的是记录——但记录停在被打断的那一刻,一个问题,没有答案,而 resume 恢复的是一段对话,不是一个知道怎么按下播放键、暂停中的执行过程。任务没有重新问一遍,也没有捡起那个被卡住的步骤。(至少我看到的是这样;resume 在面对一个悬空提示时的具体行为,我不敢打包票每个版本都一样。)那次交流就这么彻底没了。
最扎心的是这一点,考虑到我当初为什么选这套设置:我用远程,就是为了让笔记本睡眠不会打断工作。而它确实没有打断算力——服务器从来没睡过。它打断的是这个进程"活下去"的权利,靠的是一个我根本不知道存在的计时器。
我在 Claude App 里碰到的那个 disconnected 不是偶然,也值得说清楚为什么"用手机回答"这个方案救不了我。Remote Control 给你的是一条能联系上会话的通道——文档写得很清楚,它走的是 Anthropic API 出站,手机中转到跑 claude 的那台机器,全程不经过我的笔记本。但一旦要回答的那个东西已经死了,能回答的通道也就毫无意义。到第三个小时,进程已经没了,会话的注册信息也跟着没了;另一头已经没有任何东西可以接收我的"是"。
修法——一行配置
宽限期就是整件事的关键,而它其实是一个设置项:remote.SSH.reconnectionGraceTime。默认值是三小时。这就是我掉进去的那个陷阱——我笔记本睡眠的时间超过了三小时。
你要在本地 VS Code 的用户设置里配置它。这是一个客户端侧的 Remote-SSH 设置项,尽管它控制的是远程服务器要等你多久:
"remote.SSH.reconnectionGraceTime": 86400
单位是秒,这一点我是踩了坑才确认的——86400 是 24 小时。现在我的笔记本可以整晚睡眠,远程进程还是原地待着,问题也还在。
有两件事,不知道的话会白白搭进去一下午:
光重新加载窗口是不够的。 这个值是在远程服务器启动的时候才生效的。如果已经有一个服务器在用旧值跑着,重新连接只会重新挂上去——旧值原封不动。你必须强制起一个全新的服务器。在远程终端里:
pkill -f vscode-server
然后重新连接,VS Code 会带着新的值起一个新的服务器。
验证它,别只是信它。 重新连接之后,在远程主机上:
ps aux | grep -- '--reconnection-grace-time'
Remote-SSH 会把你的设置以这个 flag 的形式传给服务器。如果你看到自己设的数字,说明它生效了。如果什么都没看到,说明你又挂回了旧的服务器——pkill 再重连一次。
有一个细节值得认真对待:这个宽限期针对的是意外断连——睡眠、网络掉线。主动退出 VS Code 是另一回事,可以立刻销毁会话,没有任何宽限。(Remote Control 自己的文档写得很直白:关掉终端或者退出 VS Code,会话就结束了。)"睡眠"和"退出"在你眼里都是编辑器消失了,但对服务器来说完全是两码事。
另外别把这个跟 Remote Control 的另一个计时器搞混——那个大约十分钟的计时器。它会在跑 claude 的机器本身是醒着的、但连不上网络十来分钟的时候触发;针对的是服务器到 Anthropic 的连接,跟你笔记本在不在没有关系。这是两个完全不同的时钟,我笔记本睡眠碰到的,从头到尾都只是宽限期这一个。
一段弯路
值得坦白一下我实际上是怎么找到这个答案的,因为这本身就是一个教训:我是和 Claude Code 一起调试的,而 Claude Code 在这个过程中不止一次说错了。它一开始告诉我 /remote-control 不是一个真实存在的命令——其实是的,只是我碰到了它知识截止日期之外的东西。后来在追查宽限期的时候,它引用了一个过时的数字——“几秒到几分钟”——来自一个过期的 GitHub issue,这一下把我带进了"这压根没法修"的死胡同,直到我们去查了当前的文档,才发现真正的默认值是三小时,而且真的有一个开关可以调。
这里的教训不是"AI 不可靠"。而是跟当初把 agent 放到远程主机上跑一模一样的那种直觉:不要轻信关于一个工具行为方式的第一个答案——拿 ps 和当前的文档去对一遍。这次模型两次都自己纠正了,这是好的情况。安全网是留给它没纠正过来的那一次的。
我到底改了什么
我原本以为,一个这么让人摸不着头脑的问题——任务悄无声息地跳过一个决定,一次连位置都找不到的交流——背后的原因应该也同样复杂。结果并不是。它就是一个三小时的计时器,加一行就能覆盖掉的配置。
如果你也在跑这种"启动就走开"的工作流——起个长任务,靠手机回答——真正的薄弱点不是大家第一反应会去查的通知通道。而是你回来的时候,那个进程是不是还活着。远程隔离是我能放手让 agent 自己跑的前提;但要安全地放手,需要同时抓住两件事——进程要活得足够久,等得到我的回答(宽限期),出错的影响范围要足够小,扛得住它犯的错(隔离)。两件事都抓住,合上笔记本、留一个 agent 在后台跑,才不再是一场赌博。
如果你也在远程主机上跑 Claude Code,下次敢放心合上盖子之前,先去检查一下你的 reconnectionGraceTime——另外,如果你的 resume 在碰到一个没有答案的问题时表现得跟我遇到的不一样,我是真心想听听是什么情况。
更多推荐



所有评论(0)