$ cat zh/blog/ai-coding-session-stalled-or-finished

AI 编程会话是停滞、运行中,还是已经结束?

安静的 transcript 含义并不唯一。最后一个有意义的回合事件及其时间,比文件时间或进程是否存在更可靠。

终端安静了,可能是模型在推理、工具在执行、网络等待、回合已完成,也可能是进程真的卡住。单看 CPU、进程或文件更新时间,无法把这些情况可靠地区分开。

先找最后一个有意义的事件

忽略纯元数据和无语义的 bookkeeping,寻找最后一次流式输出、工具调用、权限请求、结构化错误或终止事件。事件必须属于当前回合,并带有可比较的时间或顺序。

排除伪完成

限流、认证失败和供应商错误有时与普通结束使用相似的记录外壳。解析器应先读错误标记与内容语义,再判断是否完成。后台 worker、子线程和自动化产生的终止也不能直接当作“轮到用户”。

语义时间优先于文件时间

索引、归档或其他进程可能在事件发生很久后触碰文件。只要事件自带时间,就用事件时间;文件修改时间只作为缺少语义时间时的降级依据,并降低置信度。

新鲜度把事件变成状态

完成事件不是永久状态。后续用户输入、新回合或明确活动会覆盖它;长时间没有活动也只能在此前确实处于运行状态、且超过书面阈值时判断为停滞。没有前置运行证据的安静应归为未活动或未知。

证据变化时撤销提醒

提醒显示后,如果用户已经回到线程继续输入,或者新回合启动,旧提醒要自动撤销。正确的去重单位是会话加回合,而不是“完成”这两个字。

状态不承诺结果质量

“已完成”只表示当前回合有新鲜、未被覆盖的终止证据;它不保证代码正确、测试通过或需求已经实现。更完整的定义和测试可见会话状态分类清单

一个可执行的排查顺序

  1. 确认正在观察的是正确会话,而不是同项目下的旧线程。
  2. 读取最后一条语义事件,记录它的回合标识与事件时间。
  3. 检查事件之后是否出现新的用户输入、工具调用或错误。
  4. 只有此前明确运行且超过阈值,才标记停滞。
  5. 若证据冲突,保留原始时间并显示未知,不用文件时间强行裁决。

三个容易误判的例子

进程仍在但回合已经结束,应显示等待用户而不是运行中;文件刚被归档而修改时间很新,不能把旧完成当成今天的提醒;长时间没有输出但工具调用仍有心跳,仍属于运行中。状态判断的价值来自这些边界,而不是给所有安静会话贴同一个标签。