feat(sandbox): 补 olefile/xlrd/extract-msg 老格式解析依赖(bump 0.58.23)

toolfail 巡检:run_python/exit ModuleNotFoundError olefile 3 用户/3 任务,
模型在 run_python 里手撸解析老 Office/Outlook 二进制件(OLE 复合文档)。
markitdown 只吃现代 xlsx/docx/pptx,盖不到 OLE;建材院高频收老 .doc/.xls/.msg。
按 Dockerfile「装齐免 ModuleNotFoundError 摩擦」策略补 olefile+xlrd+extract-msg。
纯 Python 无重依赖;要生效需重建 sandbox 镜像。additive 内部依赖,不碰对外契约。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
caoqianming 2026-07-15 08:16:18 +08:00
parent 732420a3b7
commit 72418a5321
3 changed files with 10 additions and 2 deletions

View File

@ -2,7 +2,7 @@
> 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9` > 配合 `DESIGN.md`。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 `git log` / `git diff` / `DESIGN §7.9`
最后更新:2026-07-11(mermaid 渲染必崩定案:chromium 坏点版本;refresh 旋钮 + build canary 根治,bump 0.58.8) 最后更新:2026-07-15(sandbox 补 olefile/xlrd/extract-msg 老格式解析依赖,bump 0.58.23)
--- ---
@ -23,6 +23,7 @@
### 2026-07 ### 2026-07
- **07-15 / 0.58.23**:**sandbox 补齐老格式(OLE 复合文档)解析依赖**(toolfail 巡检:`run_python/exit` `ModuleNotFoundError: No module named 'olefile'` 3 用户/3 任务,模型在 run_python 里 `import olefile, struct` 手撸解析老 Office/Outlook 二进制件)。`markitdown[...]` 只吃现代 `xlsx/docx/pptx`,盖不到 OLE;建材院高频收到老 `.doc/.xls/.msg` 转发件。按 Dockerfile:46「装齐免 ModuleNotFoundError 摩擦」策略(同 `file` 命令准入线),`requirements.txt` 补 `olefile`(OLE 底层读)+ `xlrd`(老 .xls BIFF,openpyxl 读不了)+ `extract-msg`(Outlook .msg,省手撸)。纯 Python、无重依赖,镜像膨胀忽略;要生效需重建 sandbox 镜像(pip 大层重跑)。additive 内部依赖,不碰对外契约。
- **07-13 / 0.58.22**:**run_python script_path 宿主预检 + 路径容错 + code 落盘回退**(失败聚集面板 `run_python/exit`「can't open file」15 次/7 天/13 task,逐条 DB 核对出三种形态):① 形态 1(13/15,没写就跑):模型调 `script_path` 但前文从未 write 过该文件(如 90c8bba9 #29),Docker 执行器原本不预检直接 `docker exec python <path>`,模型拿裸 stderr 自愈慢——现宿主侧 `is_file()` 预检(bind mount 免进容器),返带纠偏提示的 `[Error] script_path not found`(与 host 执行器行为对齐)。② 形态 2(双重拼接,0d7dd9d9 #74):根因是**展示/解析不对称**——fs 工具输出渲染 user_root 相对路径(`tools/base.py::_display` → `吴中伟科技奖/scripts/x.py`),模型原样回传,而 `_container_script_path` 按 workdir 相对拼 → `/workspace/<dir>/<dir>/…`;现 workdir 相对不存在时按 user_root 相对再试一次。③ 形态 3(a395ee0c #134,code+script_path 同给):原本 script_path 优先、文件不存在报错,整段完好 code 被扔掉;现文件不存在且 code 非空 → code 落盘到 script_path 再执行(正合工具描述「先写后跑」引导,schema 描述同步注明)。`DockerExecutor._container_script_path` 重写为 `_prepare_script`(前台+background 两路共用,code 回写在宿主侧故加 user_root 越界防护);`tools/run_python.py` host 路径同语义(`_prepare_script` helper,execute/_execute_background 共用)。`tests/test_executor_docker.py` +4 用例(not found 不起 docker / 前缀容错 / code 落盘 / 越界拒),`tests/test_run_python_script_path.py` +4(host 侧同形态 + 已存在文件不被 code 覆盖)。诊断脚本 `scripts/diag_run_python_noent{,2}.py` 留档。 - **07-13 / 0.58.22**:**run_python script_path 宿主预检 + 路径容错 + code 落盘回退**(失败聚集面板 `run_python/exit`「can't open file」15 次/7 天/13 task,逐条 DB 核对出三种形态):① 形态 1(13/15,没写就跑):模型调 `script_path` 但前文从未 write 过该文件(如 90c8bba9 #29),Docker 执行器原本不预检直接 `docker exec python <path>`,模型拿裸 stderr 自愈慢——现宿主侧 `is_file()` 预检(bind mount 免进容器),返带纠偏提示的 `[Error] script_path not found`(与 host 执行器行为对齐)。② 形态 2(双重拼接,0d7dd9d9 #74):根因是**展示/解析不对称**——fs 工具输出渲染 user_root 相对路径(`tools/base.py::_display` → `吴中伟科技奖/scripts/x.py`),模型原样回传,而 `_container_script_path` 按 workdir 相对拼 → `/workspace/<dir>/<dir>/…`;现 workdir 相对不存在时按 user_root 相对再试一次。③ 形态 3(a395ee0c #134,code+script_path 同给):原本 script_path 优先、文件不存在报错,整段完好 code 被扔掉;现文件不存在且 code 非空 → code 落盘到 script_path 再执行(正合工具描述「先写后跑」引导,schema 描述同步注明)。`DockerExecutor._container_script_path` 重写为 `_prepare_script`(前台+background 两路共用,code 回写在宿主侧故加 user_root 越界防护);`tools/run_python.py` host 路径同语义(`_prepare_script` helper,execute/_execute_background 共用)。`tests/test_executor_docker.py` +4 用例(not found 不起 docker / 前缀容错 / code 落盘 / 越界拒),`tests/test_run_python_script_path.py` +4(host 侧同形态 + 已存在文件不被 code 覆盖)。诊断脚本 `scripts/diag_run_python_noent{,2}.py` 留档。
- **07-13 / 0.58.21**:**run 级错误前端持久提示 + 进失败聚集面板 + provider 级致命错误即时告警**(用户报 task 2a1bc25d "ppt3":Zai 余额不足连挂 3 次续跑,前端零提示、admin 零感知):错误其实有 emit(SSE error 事件→前端错误卡),但 fetchSse 收尾 `loadMessages()``renderMessages``innerHTML=""` 整屏重建,错误不是持久化消息 → 卡瞬间被冲掉;历史打开更没有(run_error 唯一出口是任务列表红点 hover title)。admin 侧 toolfail 巡检只扫 tool 消息 + tool_malformed,LLM 请求层直接抛的 run 级错误(整轮无 tool 消息)全盲。三改:① chat.js 新增 `renderPersistedRunError()`(挂在 renderMessages 两分支末尾,所有重渲路径统一走):meta.run_status=error 且无 live run → 消息流末尾补「上次运行出错」持久卡(含重试引导),刷新/切任务都在,下次 run 起跑后端清 run_error 自然消失;selectTask 本就先赋 taskMeta 再 renderMessages,时序天然成立。② run 错误留痕:`_run_agent_bg` except 里加 `record_run_error`(usage_events kind=run_error,units={err},cost 0——run_error 列只留最后一次,此行才是完整数据源);`core/toolfail.py` 加第三段扫描聚成 kind=run / tool=(run) 的 cluster,admin 面板/巡检邮件零改动自动带上。③ provider 级致命错误(余额不足/配额/认证,正则关键词)即时邮件 `alert_provider_critical`:这类错误该 provider 上所有 run 全挂,等日巡检 5 次/2 task 阈值太慢;同归一化签名 6h 进程内冷却,SMTP 未配只打日志,全路径静默失败不反噬错误路径。`tests/test_toolfail_malformed.py` +3 用例(run_error 聚集 / 关键词命中+冷却 / 普通异常不告警),FakeSession 分流补第三段。 - **07-13 / 0.58.21**:**run 级错误前端持久提示 + 进失败聚集面板 + provider 级致命错误即时告警**(用户报 task 2a1bc25d "ppt3":Zai 余额不足连挂 3 次续跑,前端零提示、admin 零感知):错误其实有 emit(SSE error 事件→前端错误卡),但 fetchSse 收尾 `loadMessages()``renderMessages``innerHTML=""` 整屏重建,错误不是持久化消息 → 卡瞬间被冲掉;历史打开更没有(run_error 唯一出口是任务列表红点 hover title)。admin 侧 toolfail 巡检只扫 tool 消息 + tool_malformed,LLM 请求层直接抛的 run 级错误(整轮无 tool 消息)全盲。三改:① chat.js 新增 `renderPersistedRunError()`(挂在 renderMessages 两分支末尾,所有重渲路径统一走):meta.run_status=error 且无 live run → 消息流末尾补「上次运行出错」持久卡(含重试引导),刷新/切任务都在,下次 run 起跑后端清 run_error 自然消失;selectTask 本就先赋 taskMeta 再 renderMessages,时序天然成立。② run 错误留痕:`_run_agent_bg` except 里加 `record_run_error`(usage_events kind=run_error,units={err},cost 0——run_error 列只留最后一次,此行才是完整数据源);`core/toolfail.py` 加第三段扫描聚成 kind=run / tool=(run) 的 cluster,admin 面板/巡检邮件零改动自动带上。③ provider 级致命错误(余额不足/配额/认证,正则关键词)即时邮件 `alert_provider_critical`:这类错误该 provider 上所有 run 全挂,等日巡检 5 次/2 task 阈值太慢;同归一化签名 6h 进程内冷却,SMTP 未配只打日志,全路径静默失败不反噬错误路径。`tests/test_toolfail_malformed.py` +3 用例(run_error 聚集 / 关键词命中+冷却 / 普通异常不告警),FakeSession 分流补第三段。
- **07-13 / 0.58.20**:**非流式重试期间「思考中」指示恢复 + 停止即时生效**(用户报 716ed3be 重试期间页面无 loading、与卡死无法区分;代码已随 da289b2/0.58.19 一并入库,本条补文档):非流式一次生成可达分钟级且完成前零事件,而 `warn` 事件会让前端把当前文字段定稿关闭(chat.js `closeTextSeg`),「思考中 · Ns」占位段没人重建 → 页面静止到重试完成。① `llm_start` emit 挪进 attempt 循环体每次重发(stats 同一份):前端既有逻辑 `ensureTextSeg + setRunPhase("llm")` 自动重建占位段恢复跳秒,前端零改动。② `_nonstream_once` 阻塞调用挪后台 daemon 线程,主线程 0.5s 一拍 poll cancel,命中返 `None``_stream_llm``(None, True)` 走既有 cancelled 路径——此前点「停止」要同步等非流式调用整个返回(分钟级);弃养线程自行跑完、响应丢弃不入库不记账,与流式 cancel 丢弃已收 chunk 语义一致(provider 侧照跑照计费,litellm 同步调用不暴露连接中断,省不掉)。`tests/test_loop_malformed_retry.py` +4 用例:每 attempt 重发 llm_start 计数 / 非流式期 cancel 短路 / 真 `_nonstream_once` 在 llm.chat 阻塞 30s 时 poll 拍内返回 / 不取消行为与原同步版等价。 - **07-13 / 0.58.20**:**非流式重试期间「思考中」指示恢复 + 停止即时生效**(用户报 716ed3be 重试期间页面无 loading、与卡死无法区分;代码已随 da289b2/0.58.19 一并入库,本条补文档):非流式一次生成可达分钟级且完成前零事件,而 `warn` 事件会让前端把当前文字段定稿关闭(chat.js `closeTextSeg`),「思考中 · Ns」占位段没人重建 → 页面静止到重试完成。① `llm_start` emit 挪进 attempt 循环体每次重发(stats 同一份):前端既有逻辑 `ensureTextSeg + setRunPhase("llm")` 自动重建占位段恢复跳秒,前端零改动。② `_nonstream_once` 阻塞调用挪后台 daemon 线程,主线程 0.5s 一拍 poll cancel,命中返 `None``_stream_llm``(None, True)` 走既有 cancelled 路径——此前点「停止」要同步等非流式调用整个返回(分钟级);弃养线程自行跑完、响应丢弃不入库不记账,与流式 cancel 丢弃已收 chunk 语义一致(provider 侧照跑照计费,litellm 同步调用不暴露连接中断,省不掉)。`tests/test_loop_malformed_retry.py` +4 用例:每 attempt 重发 llm_start 计数 / 非流式期 cancel 短路 / 真 `_nonstream_once` 在 llm.chat 阻塞 30s 时 poll 拍内返回 / 不取消行为与原同步版等价。

View File

@ -1,3 +1,3 @@
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。 # zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
# 改版本只动这一行。 # 改版本只动这一行。
__version__ = "0.58.22" __version__ = "0.58.23"

View File

@ -16,6 +16,13 @@ markdown>=3.5 # skills/_shared/render_pdf.py: md→HTML→chromium 出 PDF(
# 素材摄取: PDF/DOCX/PPTX/XLSX/HTML/URL → Markdown (ppt 阶段零 + proposal 阶段零) # 素材摄取: PDF/DOCX/PPTX/XLSX/HTML/URL → Markdown (ppt 阶段零 + proposal 阶段零)
markitdown[pdf,docx,pptx,xlsx]>=0.0.1 markitdown[pdf,docx,pptx,xlsx]>=0.0.1
# 老格式(OLE 复合文档)解析 —— markitdown 只吃现代 xlsx/docx/pptx,盖不到二进制老件;
# 建材院高频收到老 .doc/.xls/.msg 转发件,run_python 里模型手撸解析撞 ModuleNotFoundError
# (toolfail 巡检:olefile 3 用户/3 任务),按"装齐免摩擦"策略补齐这一族:
olefile>=0.46 # OLE 复合文档底层读取(老 .doc/.ppt/.xls/.msg 手工解析)
xlrd>=2.0 # 老 .xls(BIFF)读取,openpyxl 读不了
extract-msg>=0.48 # Outlook .msg 直接解析,省手撸 olefile
# 联网搜索 / web fetch # 联网搜索 / web fetch
httpx>=0.27.0 httpx>=0.27.0
html2text>=2024.0 html2text>=2024.0