"""诊断 run_python 失败聚集: python: can't open file '': No such file or directory 捞近 7 天 role=tool & name=run_python & content 含 "can't open file" 的消息, 关联 assistant tool_call 的 arguments,看 script_path 形态;并回看同 task 前文 有没有 write/run_python(code) 生成过该文件,判断是「没写就跑」还是「路径基准错」。 """ import json import os from pathlib import Path env = Path(__file__).resolve().parent.parent / ".env" for line in env.read_text(encoding="utf-8").splitlines(): if line.strip().startswith("ZCBOT_DB_URL="): os.environ["ZCBOT_DB_URL"] = line.split("=", 1)[1].strip() from sqlalchemy import create_engine, text # noqa: E402 engine = create_engine(os.environ["ZCBOT_DB_URL"]) with engine.connect() as conn: rows = conn.execute( text( "select m.task_id, t.user_id, m.idx, m.created_at, " " m.payload->>'tool_call_id' as tcid, " " m.payload->>'content' as content " "from messages m join tasks t on t.task_id = m.task_id " "where m.created_at >= now() - interval '7 days' " " and m.payload->>'role' = 'tool' " " and m.payload->>'name' = 'run_python' " " and m.payload->>'content' like '%can''t open file%' " "order by m.created_at" ) ).fetchall() print(f"[hits] {len(rows)} tool-result messages\n") for task_id, user_id, idx, created, tcid, content in rows: tid8 = str(task_id)[:8] # 找对应 assistant 调用的 arguments call_row = conn.execute( text( "select payload from messages " "where task_id=:t and payload->>'role'='assistant' and idx < :i " "order by idx desc limit 20" ), {"t": task_id, "i": idx}, ).fetchall() args_repr = "?" for (payload,) in call_row: for tc in payload.get("tool_calls") or []: if tc.get("id") == tcid: args_repr = (tc.get("function") or {}).get("arguments") or "?" break if args_repr != "?": break # 提取 script_path sp = None try: sp = json.loads(args_repr).get("script_path") except Exception: pass # 回看同 task 前文: 有没有 write 过这个文件 (basename 匹配) wrote = "?" if sp: base = sp.replace("\\", "/").split("/")[-1] w = conn.execute( text( "select count(*) from messages " "where task_id=:t and idx < :i " " and payload->>'role'='assistant' " " and payload::text like :pat" ), {"t": task_id, "i": idx, "pat": f"%{base}%"}, ).scalar() wrote = f"prior-mentions={w}" first_line = (content or "").strip().splitlines() err_line = next((ln for ln in first_line if "can't open file" in ln), "") print(f"[{tid8}] user={str(user_id)[:8]} idx={idx} {str(created)[:19]}") print(f" args: {args_repr[:220]}") print(f" err : {err_line[:220]}") print(f" ctx : {wrote}") print()