From 8a7e0cd233591ea95d5354a9f339eebc7fc435b0 Mon Sep 17 00:00:00 2001 From: caoqianming Date: Sat, 6 Jun 2026 20:51:45 +0800 Subject: [PATCH] =?UTF-8?q?fix(loop):=20=E5=B7=A5=E5=85=B7=E8=B0=83?= =?UTF-8?q?=E7=94=A8=20arguments=20=E6=8D=9F=E5=9D=8F=E6=97=B6=E4=B8=A2?= =?UTF-8?q?=E5=BC=83=E9=87=8D=E8=AF=95=20+=20=E9=9D=9E=E6=B5=81=E5=BC=8F?= =?UTF-8?q?=E5=85=9C=E5=BA=95,=E6=96=AD=E6=8A=95=E6=AF=92=E7=BA=A7?= =?UTF-8?q?=E8=81=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit deepseek-v4-flash 大参数工具调用(大 write/run_python,≈7K+ 字符)偶发把内容 碎片错位粘进 arguments 开头致 JSON 解析失败,或退化成空 {} 缺参。隔离批量验证 (干净上下文)非流式 8/8、流式 8/8 全干净 → 上游间歇抖动,概率低;真正放大成 灾的是 loop 把损坏的 assistant 消息入库 + 每轮重发,诱导模型继续学坏(投毒级联)。 - core/loop.py:_stream_llm 拉一轮后用 _malformed_tool_calls 校验 tool_call arguments 能否 json.loads,不能则丢弃整轮(不 append/不记账)重 roll(≤3 次), 最后一次降级 _nonstream_once(provider 服务端拼,绕开流式错位)。流式收集逻辑 抽成 _collect_stream_once,正常路径不变。 - core/executor_host.py / core/sandbox/tool_runner.py:缺必填参数早返 「缺少必填参数 [...];请带齐 [...] 重新调用」,替掉暴露内部签名的 TypeError missing N required positional arguments(host + docker 两路覆盖)。 - 文档:PROGRESS.md 加 2026-06-06 条;RUN.md 故障兜底加一行。 Co-Authored-By: Claude Opus 4.8 (1M context) --- PROGRESS.md | 4 ++ RUN.md | 1 + core/executor_host.py | 11 +++++ core/loop.py | 82 +++++++++++++++++++++++++++++++++++-- core/sandbox/tool_runner.py | 12 ++++++ 5 files changed, 106 insertions(+), 4 deletions(-) diff --git a/PROGRESS.md b/PROGRESS.md index 5635101..e73ad94 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -21,6 +21,10 @@ ## 已完成关键能力 +### 2026-06-06 + +- **修 deepseek-v4-flash 大参数工具调用 arguments 损坏 → loop 畸形重试 + 非流式兜底**:用户报"测试docx"任务里 zcbot 回 `[Error] bad arguments to write: WriteTool.execute() missing 2 required positional arguments`。实证定位(dump 失败 task 全量 messages):**大参数(≈7–10K 字符)的 write/run_python 偶发把别处内容碎片错位粘进 `arguments` 开头**(如 `].cells[1].merge(...{"path":...}`),`json.loads` 直接失败;有时退化成空 `{}` → execute 缺参报 TypeError。**根因双层**:① 上游 deepseek-v4-flash 流式 delta 偶发错位(隔离复现 16/16 全干净,说明概率低);② 真正放大成灾的是 **loop 把损坏的 assistant 消息原样入库 + 每轮重发 → 模型学坏的投毒级联**(失败 task 里大半 write 连锁失败)。读 litellm `stream_chunk_builder` 源码排除"content 混进 args"(content 与 tool_args 两趟独立合并);批量验证非流式 8/8、流式 8/8 在干净上下文均不复现 → 确认是间歇上游抖动 + loop 零容错。**修法**(`core/loop.py`):`_stream_llm` 重构成「拉一轮 → `_malformed_tool_calls` 校验 tool_call arguments 能否 `json.loads` → 不能则**丢弃整轮(不 append/不记账)重 roll**」,最多 3 次;最后一次降级 `_nonstream_once`(provider 服务端拼 tool_calls,绕开流式错位,content 整段补 emit)。断投毒环 + 不依赖猜准上游成因 + 不动正常路径。**backstop**:`executor_host.py` / `sandbox/tool_runner.py` 缺必填参数(空 `{}`)早返 `缺少必填参数 [...];请带齐 [...] 重新调用`,替掉暴露内部签名的 `missing N required positional arguments`。重试消耗 token 不单独记账(罕见路径)。tests 全过(唯一失败 `test_static_vendor::formatContextStats` 是前端 ES module 化遗留,与本改无关)。 + ### 2026-06-05 - **前端模块化 Step 1:`dev.html` 单文件拆零构建 ES module(叶子优先)**:`web/static/dev.html` 原 4087 行(纯原生 JS、手写 `state` + 手动 DOM、零内联 `onclick` 全 `addEventListener`、唯一 `window.*` 是比较非赋值)。定方案「1 拆文件 → 2 后续引 Alpine/petite-vue 局部响应式 → 3 永不上 Vue+构建链」,本步只做 1。抽出 4 个无依赖叶子模块到 `web/static/js/`:`state.js`(`state` 单例 + `LS_*` + `EMBED*`)、`format.js`(escapeHtml/humanSize/fmtTokens/fmtCost/usage 系列等纯格式化)、`dom.js`(`$` + 浮层菜单 showMenu/hideMenu,import escapeHtml)、`api.js`(`api()` Bearer 封装,import state)、`markdown.js`(renderMd/highlightIn,依赖 vendor 全局)。剩余主体(login→boot,原 1387–4084)整体落 `main.js` 并 import 上述叶子;`dev.html` 内联大 `