Files
workbuddy_skills/session-mechanism/references/forensics.md
T
admin 19101acd65 init: workbuddy_skills 重建,仅收录 session-mechanism
- 按用户指示清空原有 25 技能内容,只提交 session-mechanism(57 文件)
- 附 .gitignore(产物 + 本机凭据)
- 令牌明文已脱敏(历史 .neodata_token 与 pitfalls 引用均不入库)
- 本提交为孤儿提交(父提交为空),历史自此重新开始
2026-10-05 14:13:24 +08:00

5.9 KiB
Raw Blame History

会话复盘 · 取证手册("某个会话当时到底干了啥 / 为什么卡住")

取证脚本:scripts/forensics/proc-parent.py(纯 ctypes 查进程父链,⛔ 不依赖 psutil)。 深度内容(每条的前因后果、实测读数)⇒ references/pitfalls.md。

1 先定位会话

  • 会话表:sessions(title / custom_title / created_at / last_activity_at / cwd / status)。
  • 用户说「找最新的那个」时,两个指标都算、取更大者:createdAtMs(创建)与 lastActivityAtMs(最后活动)。 ⚠️ 两个指标打架 ⇒ 两条都列出来问用户,⛔ 别猜。
  • ⚠️ 同名多会话在这一层很常见(标题会被复用)⇒ 必须靠 id / 时间 / cwd 三者交叉确认。

2 读转录(原文在哪、怎么抽)

  • 转录是 JSON 行文件(.jsonl)。抽四类东西:用户原话 / AI 正文 / reasoning 思考 / 工具调用。
  • ⚠️ 转录会被清理(保留窗口有限)⇒ 过期后只能走 §6 的复原路径。
  • 🔴 别拿转录 mtime 当"有产出":agent 在跑时转录正文可能滞后甚至不落盘;反之活干完了也可能因推送失败而看起来没动。

3 🔴 四种"卡住"必须分开判(这是本节的核心)

型 症状 决定性判据
中断 那一轮从中间断了 转录尾部有宿主注入的错误(如 ## Model error retry: Tool Not Found)⇒ 中断点;其后长时间零 reasoning / function_call。瞬时工具不可用 ≠ 会话来死
工具循环不收敛 消息派发了、agent 真在跑,但永不回话 工作区日志状态机:finish_reason= 反复是 tool_calls,从不出现 stop;busy=true 恒不回 idle
干完了推不出去 界面显示"一直运行",但看不到任何执行 日志里 sendToClient: … SSE is closed 大量堆积 + 该轮 finish_reason="stop" ⇒ 活干完了,结果送不出去
投递悬挂 消息收到了、也入队了,但没有任何东西去排空队列 🔴 route=parkInQueue + hasWaiter=false(正常是 route=resolveWaiter + hasWaiter=true)

🔴 投递悬挂的定量指纹(把"症状"变"确诊"):hasWaiter=false 只会在出事那段出现、queueLen 只进不出(每再投一次就再压一条)、同一小时 No state found for connectionId 飙升。 ✅ 处置=让客户端重新挂上该会话(切走再切回 / 重开该会话窗口);⛔ 别反复发消息试探(只往队尾再堆一条)。

⚠️ 两条别混:同一次事故里常常同时出现"日志层异常"与"投递异常" ⇒ 分别归因、分别取证,⛔ 不要合成一个因。 ⚠️ 别拿会话状态字段当判据:客户端会把 working 基于错误假设主动写进库,而宿主那边根本 idle;last_activity_at 的刷新也可能只是"UI 打开了这个会话"。

4 日志撞上限(10 MiB)⇒ 整批丢写

三条同时成立即可确诊:

  1. 守护日志反复出现 diagnostic log write failed … EPERM,且 droppedLines 持续增长;
  2. 该会话的 <sid>.log 大小卡在 ≈10 MiB 且 mtime 冻结,而同期其他会话的同名日志正常在长(对照排除"全盘坏了");
  3. 同目录 <sid>.log.1 也是满的 ⇒ 轮转无空位。

修法(非破坏、可逆):把卡死的那个 <sid>.log 与 <sid>.log.1 改名挪开(⛔ 不要删)。 🔴 用 Python os.rename,⛔ 不要用 mv(MSYS 的 mv 在长路径/中文名下更易踩坑)。 验收必做:① 数十秒内被宿主自动重建 ② 它再次长大 ③ 告警不再新增。 ⚠️ 它必然复发(每个会话涨到上限都会重演)⇒ 需要持续兜底(本工作区有扫描器 + 钩子兜底)。 ⚠️ 这两个文件并没有被锁(可正常打开)⇒ ⛔ 别再往"查谁占用文件"上耗时间。

5 进程定性("它是不是某个会话的后台任务")

netstat -ano | grep ":<端口>"          # 只认 LISTENING 那行 ⇒ 拿 pid
python <本包>/scripts/forensics/proc-parent.py <pid>   # pid ⇒ 父链
  • 父链出现 … ← sandbox-cli.exe ← WorkBuddy.exe ⇒ ✅ 是会话/宿主托管的后台任务。
  • 父链是 services.exe / svchost.exe / explorer.exe ⇒ ⛔ 不是后台任务(独立进程)。
  • 更硬的证据在宿主日志里搜 executeInBackground / background task created / processExit。
  • ⚠️ 日志里的中文常是「UTF-8 字节被按 GBK 解」的乱码,还原一行:s.encode("gbk","replace").decode("utf-8","replace")。

6 转录已被清理时的复原路径(四条腿)

  1. 全工作区 memory 日志批量搜"行为的独有名词"(工具名 / 产物名 / 作品名)⇒ 一次命中就给出「工作区 + 日期」,比逐会话读 jsonl 快一个量级。
  2. 读命中的那天日志(它通常写了工具链、命令要点、产出绝对路径)。
  3. 按独有产物名跨盘搜文件系统(⚠️ 必须剪枝:跳过 node_modules/.git/Windows/Program Files/AppData,限制下钻深度;⛔ 别用全盘递归 glob,实测直接超时被杀)。
  4. 与规范化文档互证(文档里常写「基于 <日期> 实测沉淀」)。

⚠️ 交付时必须同时说明「原产出目录是否已失效」与「现存可核验的替代物」,别让用户去翻一个不存在的路径。

7 ⛔ 别做

  • ⛔ 别因为 status=working 就断定卡死(当前正在跑的会话也是它)。
  • ⛔ 别因为"抢不到锁"就去删锁 / 接管(红线,只能持有者释放)。
  • ⛔ 别"再发一条试试"救投递悬挂的会话(会重开一轮同样的循环 / 只往队尾堆一条)。
  • ⛔ 别在自动化会话里手动续聊当正式驱动。
  • ⛔ 别为"恢复它"随手重启应用(会打断其他线正在跑的棒)。