ThinkRail 智能体停住时,界面上往往既没有报错,也没有进度变化,此时能直接拿到的线索只有两处:终端输出的最后几行,以及任务面板里状态字段和最近更新时间。把这两处按时间顺序排一遍,基本能分出三种情况——它正在等你点一次确认,它还在执行只是没有新输出,它已经跑完或被截断而面板没有刷新。
建议按“终端末尾 → 面板状态与时间 → 一条只读命令”的顺序排查。终端最后一行是确认提示,就去点确认;最后一行仍在执行但没有新输出,先看面板的最近更新时间是否在跳;两者都不动,就发一条只读命令探测执行通道还通不通。若通道正常、任务却在处理大范围文件时反复回到同一步,多半是上下文吃紧,需要把任务拆小重跑。以上判断依赖你本地的日志和配置,字段名与路径以实际版本为准。
先看终端最后一行停在哪
终端末尾是成本最低的判断依据,因为它是原始输出,不像任务面板那样可能被前端状态缓存住。把最后 20~30 行完整看一遍,重点不在内容,而在“最后一行属于哪一类”。
第一类:等待确认的提示
典型形态是提问式的落尾,例如 Continue? [y/N]、Approve this command?、Press Enter to run、Allow write to src/... ?,或者一个停在原地的选择菜单。这类末尾有一个共同点:终端已经把控制权交回给你,光标在等待输入,进程没有退出。看到这种落尾,说明智能体没有卡住,而是在等你点按钮或敲回车——很多人以为的“跑到一半不动了”,实际就是确认环节没人应答。
第二类:命令仍在执行
落尾是某条具体命令本身,例如正在跑 npm test、pytest、grep -r,后面既没有返回提示符,也没有报错。此时智能体在等子进程返回,属于正常等待。可以在另一个终端确认子进程是否还活着:
ps -o pid,stat,etime,cmd -p <agent_pid>
ps aux | grep -E 'pytest|npm|node' | grep -v grep
STAT 是 R 或 S 且 ETIME 在增长,说明进程还在跑;STAT 里出现 Z 或进程已不存在,说明它早就结束了,只是界面没跟上。
第三类:长时间没有任何输出
落尾是一个空行、一个提示符前缀,或者干脆停在某段思考文本中间,之后几分钟不再变化。这类最需要区分:可能是模型侧还在生成,也可能是输出被缓冲、写进了日志文件而不是终端。可以先看会话日志的尾部有没有比终端更新的内容,常见做法是放到项目目录或用户目录下的会话日志文件里,具体路径以你本地配置为准:
tail -n 40 <session_log_path>
日志比终端新,说明终端显示滞后;日志和终端同时停住,才需要进入下一步。
对照任务面板的状态和时间
任务面板的价值不在于“显不显示在跑”,而在于两个字段的组合:状态值,以及最近更新时间或当前步骤耗时。单独看状态很容易误判,因为等待确认的任务在很多实现里同样显示为运行中。
- 状态字段的读法:
running / executing表示智能体自己认为还在推进;waiting / awaiting_input / needs_approval表示它在等人;paused表示被主动挂起;completed表示已结束但结果没渲染出来;failed / error才是真正的异常。你所在版本的字段名可能不同,看含义即可。 - 时间字段的读法:
最近更新或当前步骤耗时如果持续跳动,说明事件流还在走;如果它和终端一起停在同一秒,两边其实是一致的,问题不在显示层。 - 不一致时的观察点:面板显示
running而最近更新时间不动,先回到终端看是否有新行;如果面板的更新时间在跳、终端却毫无变化,重点观察日志文件是否在增长,这通常意味着输出被重定向了。
状态长时间不变时,不要反复点同一个按钮制造重复提交。先记录下卡住那一步的步骤编号和时间戳,再继续观察一到两分钟,确认它是真的静止还是低频输出。
发一条只读命令探测通道
判断“卡住”还是“不会做”,可以用一条不改变任何文件状态的命令来试探。在同一个会话里发一条只读指令,明确要求它不要修改工作区:
请在当前工作区执行只读命令并列回结果,不要修改任何文件:
ls -la | head -n 20
也可以用 pwd、git status `--short`、sed -n '1,20p' <某个文件> 这类命令替代,选一个输出短、必然有返回的即可。
返回什么表示还能继续:命令结果被原样贴回来,说明执行通道和会话都还活着,之前的停顿大概率是等待确认,或任务规划停在某一步而没有继续下发工具调用;此时可以把任务拆小后重新描述。若这条只读命令也立刻弹出确认提示,那答案就明确了——它一直在等人,只是提示被折叠在界面上方没看到。若只读命令同样长时间无回显且日志不动,说明会话或执行端已经失效,需要重启会话再跑。
内容装不下时把任务拆小重跑
如果探测命令能正常返回,智能体却在大范围文件的步骤上反复打转,或者开始出现“忘记前面约束、把已读内容又说一遍、工具输出被省略成一段截断标记”的表现,就要考虑上下文吃紧。这类症状通常不是报错,而是逐轮变差,所以不能只看某一轮。
拆分的关键是把“范围”和“动作”写死在任务描述里,让每一步只碰少量文件。下面是一个拆分前后的对照。
拆分前,一次覆盖整个目录:
检查 src/ 下所有文件的接口调用是否一致,把不一致的地方都改掉。
拆分后,限定目录、限定文件数量、限定动作:
第一步:只读取 src/api/ 目录下的前 3 个文件,列出各文件的函数签名,不要修改任何文件。
第二步:只对比 src/api/user.ts 与 src/service/user.ts,指出签名差异,给出修改建议,仍不要写文件。
第三步:只修改 src/service/user.ts 中确认不一致的 1 处调用,改完贴出 diff。
每步跑完再发下一步,比一次性塞进大范围文件更容易看出停在哪。需要继续压缩输入时,再补上限定写法,例如“只读每个文件的前 40 行”“只处理 examples/ 目录下按名称排序的第 1~3 个文件”“用 grep -rn 定位后只打开命中的那一处”。这些写法都可验证:跑完看它读了哪些文件、输出里有没有超出范围的内容,就能判断范围限定是否生效。