Commit Graph

265 Commits

Author SHA1 Message Date
caoqianming c0169e7766 feat(ppt): 视觉系统升级为卡片式 —— 治"生成效果不太行"
学 hugohe3/ppt-master 后定位根因:被 python-pptx 原语(平矩形+左色条+
圆点 bullet)摁死视觉天花板。选路径 B(升级 python-pptx 设计系统,保留
单脚本批量架构、原生可编辑)。

- pptx_helpers: 加 add_card(圆角+柔和投影)/add_gradient_rect/add_kpi/
  add_icon_tile/add_pill/add_eyebrow/add_chevron/add_notes;set_palette
  派生明暗色阶 WASH/SOFT/DARK;apply_brand 封面/章节改渐变大色块;
  所有 helper 把 name= 写进形状 .name(原来只喂 assert_inside)
- layouts.md: 9 版式重写成卡片式 + 扩到 13 种(KPI 卡/卡片网格/流程/大数字)
- quality_check: 跟新设计语言对齐 —— 三色制按色相归桶、标签按 .name 豁免
  小字号/bullet、≥40pt 展示字跳过溢出估算、bullet≤5 改按列判
- SKILL.md: opt-in 真实配图(imagegen, ¥0.22/张)+ 每页演讲者备注
- design_principles/SKILL_LIST/PROGRESS 同步

验证:13 版式全覆盖 demo + 6 页样例 deck quality_check 全过;单列 6-bullet
回归仍触发。未动 SVG 路线/live preview/动画(更大工程)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 15:28:37 +08:00
caoqianming b95c247971 feat(prompt): 工作原则加「少来回」—— 独立步骤合并,广谱减轮
ppt 之外的长尾 task(改代码/跑数据/画图)无专属 skill 兜底,加一条通用原则:
互相独立、不依赖中间结果的操作合到一个脚本/一轮并发 tool call 里做,别一步
一 call(每轮重发整段上下文,轮数=token 体量线性乘数);但下一步要看上一步
结果时(探索检索/按报错改/需用户确认)就分步,别硬批 —— 精准措辞避免过度
批处理踩掉该有的 checkpoint。prompt 走前缀缓存近零成本。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 11:31:22 +08:00
caoqianming 1f6661b8df refactor(ppt): 工作流批量化减轮 —— 逐页生成改一脚本整建 deck
高成本 task 几乎全是 100+ 轮的逐步 tool 循环,轮数是 token 体量线性乘数。
ppt 作最低风险试点:

- 阶段一 spec 增「逐页大纲」表(页|版式|标题|要点|图标),作替代逐页确认的
  前置 checkpoint —— 改文字大纲比建完 slide 再推翻便宜
- 阶段二改成写一个 build_deck.py 一次建整 deck(同进程 new_presentation→
  按大纲循环 add_slide→一次 save,坐标天然一致;pptx_helpers 模块化已消解
  逐页防漂移理由),图标全 deck 批量预取
- 验收:quality_check 一次 → 改脚本重跑(不 edit 成品)
- 可选「风格探针」(先建封面+1页看观感)兜视觉返工险

N 页从 ~2N 轮降到 ~3-4 轮。改 SKILL.md / layouts.md(§通用起手换单脚本模板)/
SKILL_LIST.md。冒烟过单脚本建 2 页,API 与模板一致。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 10:22:09 +08:00
caoqianming 824f746571 fix(progress): 停压 task_progress 参数修进度还原 + 进度区移到对话区顶部
问题1(进度不对): 上下文压缩把旧 task_progress tool_call 参数换成
{"_compacted":true,"step_id":"sX"} 这种像合法调用的标记, 既毒化模型让它
照抄出残废 update_step(丢 step.status)入库, 又让前端 applyProgressAction
读不到 args.step → 步骤永停 pending。修复: task_progress 参数一律不压缩。

问题2(没像 codex 在顶部): 删掉每条消息卡内联进度块, 进度统一只在对话区
顶部单一 dock 实时显示(钉顶不滚); 全部完成时折叠成一行摘要。prompt/tool
描述改为跑完标 completed 而非 clear, 留住全绿收尾。

校验: unittest test_context_compaction/test_task_progress_tool 12 过;
node --test frontend_task_progress 2 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:52:44 +08:00
caoqianming 2136fdd306 fix(web): 修登录无反应(newtask.js 漏 import $)+ 补 favicon
newtask.js 用了 dom.js 的 $ 简写却漏 import,模块加载即抛
ReferenceError: $ is not defined,中断 newtask 绑定及 auth/chat
链路 → 点登录无反应。补 import 与其余模块对齐。

另在 dev.html head 加内联 SVG favicon,消掉根 /favicon.ico 404。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 09:22:59 +08:00
caoqianming c898ff863d Disable static asset caching 2026-06-08 09:16:31 +08:00
caoqianming 4ee09976ee Show task progress above composer 2026-06-08 09:04:43 +08:00
caoqianming 8616ba2b56 Add task progress tool 2026-06-08 08:44:16 +08:00
caoqianming 15ecf45c93 refactor(dev): 前端模块化 Step 2 收官 — 抽出 chat.js,main 缩成 75 行入口
最后也最缠的一块:任务列表(浏览/筛选/滚动)+ selectTask 切换 +
renderChatMeta/模型下拉 + renderMessages + live-run 助手 + sendMessage/cancel +
fetchSse/handleSseEvent + 润色/粘贴文件 + 完成/废弃/删除/导出/清空
(原 main 连续区 64–1132)→ chat.js(1086 行)。

决策:合一个 chat.js 而非强拆 tasks.js+stream.js —— 二者共享
state.liveRuns + chat-stream DOM + run 生命周期,live-run 助手被
selectTask 与 SSE 机器两边调用、骑墙;强拆会制造双向各 ~4-5 个 import
且边界不自然。

- 导出 loadTaskList / loadModels / selectTask;embed/files/newtask 对这
  三个的 import 从 ./main.js 改指 ./chat.js;formatUploadProgress 加 export。
- chat 不调 enterApp → 与 main 无环。
- main.js 仅留 enterApp(编排)+ loadStorage + Esc 关栈 + boot = 75 行入口,
  import 精简到 11 行(layout/markdown/media 不再被 main 直接引用,但经
  chat 仍在依赖图、副作用照常)。

校验升级:node 全检 + import/export 一致性 + 从 main BFS 的模块可达性
(14/14 可达,确保副作用模块不掉出图)。

dev.html 4087 行单文件 → 14 个零构建 ES module + 纯 HTML;main 2719→75。
路径 1(拆文件)完成。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-08 08:09:53 +08:00
caoqianming 36dbdb2dda refactor(dev): 前端模块化 Step 2 — 抽出 embed.js(iframe 模式)
父页面经 postMessage 推 token 进入应用 + 401 重签(原 main 1147–1209 +
顶层 _embedInitialTaskHandled 一次性标志)→ embed.js(75 行)。

- 导出 embedInit(boot 调)+ embedPostToParent / embedShowWaiting
  (auth 的 logout 在 embed 下通知父页面/显示等待态)—— 后两个从 main
  迁出后,auth.js 对它们的 import 从 ./main.js 改指 ./embed.js
  (auth 仍从 main import enterApp)。
- 反向 import main glue enterApp / loadTaskList / selectTask。
  main↔embed、auth↔embed 均运行时调用环,安全。

main.js 删至 1154 行(2719 起,已搬出约 58%)。node 全检过、
import/export 一致性过、静态测试 2 过。剩 main 内:enterApp glue +
tasks(列表/选择/渲染消息)+ stream(发送/SSE)+ boot + Esc 关栈。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:35:01 +08:00
caoqianming 40fefdffef refactor(dev): 前端模块化 Step 2 — 抽出 newtask.js(新建任务弹框)
任务名 / 工作目录(新建 sentinel 或复用已有 + 二级 input 联动)/ 描述 /
skill / 模型 select,提交 POST /v1/tasks(原 main 1146–1320)→
newtask.js(186 行)。

- 顶层自绑 hd-new 打开 / nt-go 提交 / 各 input 联动;唯一对外导出
  loadFolderSuggestions(供 main enterApp 初始化顶部 filter-wd、files
  复制/移动后刷目录)—— 它从 main 迁来后,files.js 对它的 import 从
  ./main.js 改指 ./newtask.js。
- 反向 import main glue loadModels(加 export)/ loadTaskList / selectTask
  + logout(auth)。

main.js 删至 1220 行。node 全检过、import/export 一致性校验过、
私有符号清零。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:24:29 +08:00
caoqianming e5940266ca refactor(dev): 前端模块化 Step 2 — 抽出 media.js + 收敛 downloadFile 反向依赖
对话内工具活动标签 + artifact(产物)抽取/渲染:toolActivityLabel /
extractArtifactRels / extractMediaBanner / renderArtifactBarHtml /
upgradeMediaArtifacts / downloadFile → media.js(237 行,原 main 1134–1359)。

收敛点:downloadFile 移入 media 后,preview.js / files.js 对它的 import
从 ./main.js 改指 ./media.js —— 把这条反向依赖从 main 挪开。media 导入极少
(escapeHtml / _categorize(preview)/ state / logout),与 preview 成
media↔preview 环(均运行时调用,安全)。

两次险漏靠校验抓回:
- 共享 const ARTIFACT_PRODUCING_TOOLS(main renderMessages/SSE 用 4 处,
  .has() 访问非函数调用,"被调标识符"法漏掉)
- 内部函数 _flushMediaArtifactCache(selectTask 切任务清缓存用)
两者经残留符号检查发现后补 export。

新增全模块 import/export 一致性校验脚本(每个 import{X} 必在目标 export),
11 模块全过。main.js 删至 1393 行。node --check 11 模块全过、静态测试 2 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-07 19:05:48 +08:00
caoqianming 71bac870ed refactor(dev): 前端模块化 Step 2 — 抽出 files.js(文件面板 + 选入 + 拖拽上传)
右栏文件列表浏览/导航/删除/重命名 + 刷新 + "选入"弹框(跨目录勾选
复制/移动)+ 拖拽 overlay + 上传(XHR 带进度)+ 上传状态条。

代码原分散在 main.js 两段非连续区(1133–1459 文件列表/选入/拖拽 +
1697–1786 上传 helper,中间夹着 media 段)→ 合并进 files.js(433 行)。

- 导出 loadFiles / scheduleFilesRefresh(SSE 文件事件刷新)/
  closeSrcPicker(main Esc 关栈)/ uploadFiles(聊天区粘贴/拖拽复用);
  其余入口模块顶层自绑。
- 反向 import openFilePreview(preview)、logout(auth)、main glue
  downloadFile / selectTask / loadTaskList / loadFolderSuggestions
  (后三个加 export,后续随 tasks/newtask 模块化再迁)。
- 依赖分析用"段内被调标识符 − 段内定义 − 叶子/全局"全量提取,补回固定
  清单漏掉的 loadFolderSuggestions / loadTaskList。

main.js 删至 1619 行。node --check 双过、main 残留 files 私有符号清零、
files 无未导入 glue、静态测试 2 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 22:08:55 +08:00
caoqianming 9394e065f1 refactor(dev): 前端模块化 Step 2 — 抽出 preview.js(文件预览 + mini 预览)
文件预览主弹框(图/视频/PDF/文本/markdown/docx/xlsx,大文件降级下载,
docx/xlsx 走 loadScript 懒加载 vendor)+ 同时再开的小窗预览
(原 main.js 1687–2048)→ preview.js(379 行)。

- 导出 openFilePreview / openPasteFilePreview / closeFilePreview /
  closeMiniPreview / _categorize(媒体段判图/视频用)。
- 反向 import downloadFile(main 媒体段,加 export)、logout(auth)。
- Esc 关弹窗栈处理器留 main(跨模块协调 chpw/选入/文件预览/小预览)。
- 一处去耦:deletePastedFile(留 main)原直接读 preview 私有
  _fpCurrentRel/_mpCurrentRel 判断要不要关预览 → 改为 preview 导出封装
  closePreviewIfShowing(rel),行为不变但不泄漏内部状态(唯一非纯剪切微调)。

main.js 删至 2034 行。node --check 双过、preview 私有符号在 main 清零、
无未导入 glue 引用、静态测试 2 过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:57:43 +08:00
caoqianming 16dc1719eb refactor(dev): 前端模块化 Step 2 — 抽出 auth.js(首个 main↔模块 ES 环)
登录(邮箱密码 / UUID+PLATFORM_KEY 两 tab)+ 管理员加用户 + 改密码
三节(原 main.js 21–227)→ auth.js(218 行)。

- 各入口在模块顶层自绑 onclick;只导出 logout(供全局 20 处 401 处理)
  / closeChpwModal(供 main 的 Esc 统一关弹窗栈)。
- 反向 import main 的 glue:enterApp / embedPostToParent / embedShowWaiting
  (main 给这三个加 export)——首次引入 main↔auth 循环依赖。三者皆 hoisted
  函数声明,模块实例化即就绪,且只在运行时(点击/401)调用,绝不在顶层
  求值时触发 → ES live binding 下安全。后续 features↔glue 环同理。
- main.js 删至 2397 行。node --check 双过、auth 私有符号在 main 清零、
  静态测试 2 过。逻辑零改动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:39:06 +08:00
caoqianming cbb16b896f refactor(dev): 前端模块化 Step 2(起)— 抽出 layout.js
三栏布局(pane 折叠 rail + 拖拽 splitter + 手机单列视图)是 main.js 里
唯一对其他功能节零出边的干净段,用它打样增量剥离。

- layout.js(121 行):import $ + 4 个 LS_*_COLLAPSED/WIDTH,只导出
  mqPhone / setMobileView(后者供 selectTask 在手机宽下选中任务自动切
  对话面板,是唯一跨模块调用)。折叠/splitter/mobile-tab 顶层事件绑定
  原样保留(ES module 默认 defer,import 时 DOM 已就绪)。
- main.js:删 114 行 → 2606 行,加 layout import 并清掉随之不再用的
  4 个 LS_* import。逻辑零改动,纯剪切 + 连线;node --check 过,
  main 残留 layout 私有符号清零。

顺手修 Step 1 遗留测试失败:test_static_vendor 第二用例原只 grep
dev.html 找 formatContextStats / context_original_chars / cache_hit_tokens,
模块化后这些搬进 js/*.js → 改为扫 dev.html + js/*.js 合并源。2 测试全过。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-06 21:27:51 +08:00
caoqianming 8a7e0cd233 fix(loop): 工具调用 arguments 损坏时丢弃重试 + 非流式兜底,断投毒级联
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) <noreply@anthropic.com>
2026-06-06 20:51:45 +08:00
caoqianming 72ae41e122 refactor(dev): 前端模块化 Step 1 — dev.html 拆零构建 ES module(叶子优先)
dev.html 原 4087 行单文件原生 JS。本步只做"拆文件"(路径 1),
不引框架、不改逻辑——纯剪切 + import 连线。

抽出 4 个无依赖叶子到 web/static/js/:
- state.js   state 单例 + LS_* + EMBED*
- format.js  escapeHtml / humanSize / fmtTokens / usage 系列等纯格式化
- dom.js     $ + 浮层菜单 showMenu/hideMenu(import escapeHtml)
- api.js     api() Bearer 封装(import state)
- markdown.js renderMd / highlightIn(依赖 vendor 全局)

剩余主体(login→boot)落 main.js 并 import 叶子;dev.html 内联大
<script> 换成一行 <script type="module" src="js/main.js">,降到 1121 行。

app.py 加 mimetypes.add_type("text/javascript", ".js") 兜底,防 Windows
把 .js 判成 text/plain 致 module 被浏览器拒执行(本机实测本就 OK,纯防御)。

校验:6 模块 node --check 全过 + 无私有符号泄漏到 main。
后续 Step 2+ 再从 main.js 把 login/tasks/stream/files/preview 逐个剥成独立模块。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 16:55:20 +08:00
caoqianming 6f9ca26e3f fix(dev): 改密码弹框复用选入文件的头/体/脚分隔布局
#chpw-modal 原先无专属 CSS,.card 只继承公共骨架,缺 padding/width/
表单整形,卡片被撑近全宽且无内边距。改为复用 #src-picker-modal 的
头/体/脚分隔布局(标题/按钮区带分隔线、表单包进 .body),.card 收到
400px + flex column,input focus 高亮。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 16:01:48 +08:00
caoqianming 69fc2599e3 feat(run_python): 过程脚本约定落 task_dir/scripts/
模型显式写文件再 script_path 跑的过程脚本统一进 <task_dir>/scripts/
(可见/持久/可重跑),交付产物仍落 task_dir 根。inline code 匿名片段
维持临时用后即焚(host 系统 temp、docker .zcbot_tmp dotfile,均不动)。
改 agent_builder 系统提示工作目录段 + run_python tool description/参数说明。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:52:05 +08:00
caoqianming 02cd67f63a feat(skill): 新增 standard skill — 国标/行标/团标起草(GB/T 1.1-2020)
联网核实市面无可直接复用的"写标准文件本身"的 skill(搜到的均为相邻品类:
GB/T 8567 投标技术方案、GB/T 9704 党政公文),据 GB/T 1.1-2020 自建。

- 覆盖三层级:国标 GB·T / 行标 JC·T / 团标 T/(重点对接 CSTM → T/CSTM)
- 两体裁骨架:试验方法标准(GB/T 20001.4)+ 产品标准;配套编制说明(报批必交)
- 3 references:要素骨架(必备/可选·规范性/资料性·封面前言套话)、层级选型
  (CSTM 体系与立项程序)、起草铁律(能愿动词/不可考核词/指标量化闭环/术语/引用真实性)
- 渲染复用 proposal render_docx.py + render_diagrams.py(兄弟 skill,同 patent 范式)
- 不用 proposal quality_check.py(按申报书章节名误报"缺章节"),改 §8 人工自检清单
- 同步 SKILL_LIST.md(13→14)+ PROGRESS.md

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:46:06 +08:00
caoqianming f2b3675337 feat(dev): 顶栏自助改密码 + 选入按钮文字改图标(防换行)
改密码:web/auth.py::change_password(验旧密码 + bcrypt 重哈希,错误归一到
UserCreateError code);POST /v1/auth/change_password 挂 require_user,
user_id 取自 JWT 不信前端(旧密码错/无密码 403、弱密码 400)。前端顶栏
「退出登录」左侧加「改密码」按钮(并入 embed 隐藏规则)+ 复用 .modal 弹框
(旧/新/确认,前端先验长度与一致性,成功不登出,401 走 logout)。

选入:#btn-src-pick 文字「选入…」→ 单字符 ⊕(同 ⬆ ↻ › 风格,title 保留
语义),修窄面板偶发换行。

文档:PROGRESS / RUN(API 表 + 用户管理 + 两条兜底)/ DESIGN(auth API 清单)同步。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 15:07:21 +08:00
caoqianming f81dd2def6 fix(usage): 缓存命中率同源(修 822% 怪值)+ 列表花费改 hover + backfill 加 --assume-cache-hit-rate
- 命中率怪值修复:原以 tasks.tokens_prompt 作分母、usage_events.cache_hit 作分子,
  跨源(tokens_prompt 会被"清空对话"重置、usage_events 不重置)→ 算出 822%。
  _usage_aggregates 改为同时返回 chat tokens_in/out,_task_dict 的 token 总量优先
  取 usage_events 聚合(与 cache_hit 同源,命中率恒 ≤100%)
- 前端:任务列表行不再内联 ¥,花费/缓存命中率藏进 hover tooltip(taskUsageTooltip,
  多行:输入/输出拆分·命中+命中率·真实花费);顶栏保留内联简版 + 同款 tooltip
- backfill: 加 --assume-cache-hit-rate RATE,对无 cache_hit_tokens 字段的老事件按
  估算命中率折价(DeepSeek 当时缓存了只是没记,全价偏高);已记真实值的不受影响

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 08:41:51 +08:00
caoqianming af2ad3cef1 feat(usage): 记账给前缀缓存命中折价 + 前端体现缓存命中/真实成本
排查"rust→PPT"task(flash,34 轮)发现累计 tokens_in 69.9 万里 88.6% 是缓存
命中,但 _fallback_chat_cost_cny 把命中段也按 input 全价算,记账虚高 2-3x。

- capabilities: 加 cache_hit_cny_per_mtoken(deepseek flash 0.1 / pro 0.2;
  0=不区分按全价兜底,绝不少记)
- usage: 成本公式拆三段「命中×缓存价 + (input−命中)×input价 + output×output价」;
  loop 把 cache_hit_tokens + 缓存单价透传进 record_chat_usage
- web: 不加 DB 列。app.py 加 _usage_aggregates(单查询 GROUP BY usage_events,
  复用列表 msg_counts 批量范式,无 N+1)on-the-fly 算每 task 真实成本 + 缓存命中,
  _task_dict 带出;dev.html 列表行显 ¥、顶栏 formatTaskUsage 显「tok·缓存命中%·¥」
- scripts: backfill_chat_cost_cache_discount.py 按 units 已存 token 重算历史
  cost_cny(只改成本列,默认 dry-run,--apply 落库)

折价只对新 chat 事件即时生效;历史走 backfill 脚本(部署后跑)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 08:26:09 +08:00
caoqianming 5f8b157733 perf(context): 压缩旧 assistant tool_call arguments + keep_recent 20→12
旧 assistant `tool_calls[].function.arguments` 超 ~800 chars 时压成合法 JSON
标记(保留 path/script_path/name/original_chars),避免 `write(content=...)`
源码参数反复进 prompt;keep_recent 20→12 收窄原文窗口。role/tool_call_id/
name 等协议字段不变,tool_call 协议完整。stats 增 compacted_tool_call_arguments。
DESIGN §8.2 退出标准补一条:列表 N 条/N tok 是历史累计、不随发送前压缩下降。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-05 08:25:56 +08:00
caoqianming 1c30a9e54e Reduce chat context token usage 2026-06-04 16:41:14 +08:00
caoqianming 7f4712dcba feat(ppt): quality_check 加内容形状重叠检测
原有数值检查只覆盖越界 + 按字数估算的文本溢出,盲区是"两个都在画布内的
形状互相重叠"(文本框压图标 / 压另一文本框)。加纯数值两两包围盒重叠检测:

- 只检"内容形状"(有非空文字 / 是图片)。装饰元素(无文字纯色填充:品牌条/
  分隔线/圆点/色块标签/装饰星箭头)天然排除,"文字叠在色块上"也不误报
  (色块无文字 → 非内容)。要抓的是文字压文字 / 文字压图标 / 图标压图标。
- 交叠宽高均 >0.08in 且 交叠面积/较小形状面积 ≥25% 才报,滤掉边缘贴合/发丝线。
- 报告含重叠百分比 + 两形状名 + 文字头,便于定位。

零依赖、确定性、host+docker 都跑(不需 LibreOffice)。测试过:合规 deck
(L2 徽章+字 / L5 标签叠 chip / L4 圆点+bullet)零告警;两文本框故意叠触发。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:47:52 +08:00
caoqianming 07959eb738 refactor(ppt): 版式 helper 抽成可 import 模块 + 修中文字体没真生效 + quality_check 配色查纳入填充色
逐页生成是"每页一个 run_python",以前 ~150 行 helper(配色常量/add_textbox/
apply_brand 等)要在每页里默写一遍 —— 烧 token 且长 deck 里坐标会漂(第 7 页
apply_brand 跟第 2 页对不上)。

- 新增 scripts/pptx_helpers.py:每页 import pptx_helpers as P 调用;含 new_presentation
  / load(按文件实际尺寸回填画布常量,逐页进程间同步) / add_slide / set_palette(默认
  商务红,spec_path= 自动取 spec 前 3 个 hex 作主/辅/强调)入口。
- 字体修复:python-pptx font.name 只写 <a:latin>,中文字形走 <a:ea> 槽位没设 ——
  "指定微软雅黑却没真生效"的根因。set_text 改为同时写 latin=Arial + ea/cs=微软雅黑,
  中英混排各命中正确字体。
- quality_check.py 配色检查纳入形状填充色(品牌条/徽章/圆点/标签/底块以前全漏),
  并把粗阈值"≤5 色"改成贴合三色制的"非灰阶色 ≤3"(灰/黑/白按 R/G/B 极差 ≤12 排除),
  否则计入填充后合规商务红 deck 会狂报假阳;spec 比对也只比非灰阶色。
- 联动:layouts.md(helper 块换成 import 起手 + API 速查,9 个示例全改 P. 调用)、
  icons.md A5 示例、SKILL.md 资源/阶段二、PROGRESS.md。

冒烟测试过:ea 字体确写入、set_palette 覆盖生效、quality_check 正常解析;
合规红 deck 无配色 warning、塞 4+ 彩色触发、ACCENT 强调线填充被正确捕获。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:43:09 +08:00
caoqianming af97dd7c62 feat(web): 文件面板底部展示用户已用存储 + 配额
后端已有 user_disk_usage 表(后台 15min 扫描落库)但无对外查询口,
加 GET /v1/user/storage(require_user)返 {bytes_used, file_count,
limit_bytes, scanned_at};limit_bytes 由 parse_bytes(quotas.
disk_bytes_per_user) 得,≤0/None=不限。disk_quota.get_user_usage
扩为返 (bytes,count,scanned_at) 三元组(复用而非新开函数,顺手改唯一
调用方 check_disk_quota 解包)。

前端 dev.html 右侧文件面板底部钉一条进度条+文字:#pane-right 改 flex
列让 file-list 独占滚动、存储条钉底;loadStorage() 在 enterApp 拉一次;
不限额时只显已用、隐进度条;超额变红;hover 显文件数+统计时间。样式用
class 选择器压低特异性,让折叠/手机隐藏规则能盖住它。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 15:00:36 +08:00
caoqianming 42755e246e fix(sandbox): shell 也注入 PYTHONPATH + HOME=/tmp(修只读 rootfs 两副作用)
容器 --read-only rootfs 下两个副作用:
- PYTHONPATH 原先只 run_python 注入,shell `python -c "from skills..."` 撞
  ModuleNotFoundError
- /home/zcbot 不可写,matplotlib/fontconfig 往 ~/.config / ~/.cache 写缓存刷
  "Read-only file system" / "No writable cache" 噪音

抽 _CONTAINER_ENV = {PYTHONPATH, HOME=/tmp},shell/run_python/fs 三路共用
(-e 确定性覆盖)。HOME=/tmp 一刀让缓存落 tmpfs(matplotlib→/tmp/.config、
fontconfig→/tmp/.cache),免逐个 MPLCONFIGDIR/XDG_CACHE_HOME。纯代码改,
重启 web 生效,免重建镜像。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-04 14:12:53 +08:00
caoqianming 7b09c77f76 fix(sandbox): docker 下 system prompt / SKILL 文档统一用容器路径
docker backend 下 shell/run_python/fs 工具全在容器里跑(<workspace>/users/<uid>
bind 到 /workspace),但喂给 LLM 的路径多处仍是宿主绝对路径(容器内不存在),
agent 据此 find 全空、瞎转到 pwd 才发现真身在 /workspace/<wd>。

- core/agent_builder.py::_build_system_prompt:docker 下 task_dir + 「宪法」
  glob 范例换成容器路径 /workspace/<wd>,去掉容器里无意义的 cwd 行(cwd 恒等
  task_dir);host 不变。修法同 LoadSkillTool 的 container_skills_dir 改写。
- skills 文档同类宿主路径残留清扫:patent 跨 skill 调 proposal 脚本由
  <repo_root>/...(硬编码 D:/projects/zcbot)改兄弟相对 <skill_dir>/../proposal/...;
  research fetch_pdf 范例硬编码 D:/... 改 <task_dir> 占位;patent/proposal/ppt
  的 <task_dir> 举例的废弃旧布局 workspace/tasks/<task_id> 改 host/docker 双形态说明。
- ppt 图标缓存写进只读挂载修复(方案 A):种子图标库 <skill_dir>/assets/icons/
  降为只读(glob 读),fetch_icon.py 新拉图标一律 -o <task_dir>/assets/icons/,
  读路径改种子库 + 本 task 两处都 glob。docker 下 skills 是 :ro bind,旧写法必败;
  host 下还顺带污染仓库工作树。脚本已 out.parent.mkdir,无需改。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 16:33:59 +08:00
caoqianming 9a1e88d86f docs: 精简 PROGRESS 每条压回 1-2 句 + DESIGN 状态表去重
PROGRESS.md 按头部自带规约(每条 1-2 句:做了啥+关键判断)把
"已完成关键能力"压缩:砍根因长推理 / 否决方案 (a)(b)(c) / 测试细节 /
部署操作 / 结尾"DESIGN不动·RUN不动"样板尾巴;同质 UI 碎条合并
(05-20~05-25 dev SPA 堆)。字符 131K→35K(-73%),细节仍在 git log/diff。
四段不动:状态表 / 关键决策 / 文件清单 / 下一步候选。

DESIGN.md §7.6/§7.7 phase 表删纯状态列(/待,与 PROGRESS `## 状态`
重复=漂移源),保留设计意图(估时 / 撤销 / 前置依赖),状态指回 PROGRESS。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 13:26:10 +08:00
caoqianming ed136f8ed7 fix(deploy): npm 默认源改回腾讯 — npmmirror 访问不稳
registry.npmmirror.com 实测访问半天连不上。换回腾讯 npm
(https://mirrors.cloud.tencent.com/npm/)—— 它在历来 build 里一直 OK,
之前那次坏 wheel 只是腾讯 PyPI 的问题,npm 不受影响。pip+apt 仍清华不变。
备选记进文档:华为 repo.huaweicloud.com/repository/npm/ / USTC npmreg.mirrors.ustc.edu.cn。

update.sh:NPM_REGISTRY 默认 + 注释。RUN.md:最后更新 / §镜像构建说明 /
手动 build 示例 / 故障表 npm 行。PROGRESS.md:同条更新。DESIGN 不动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 11:00:28 +08:00
caoqianming fedf5a2cd5 fix(deploy): 默认镜像源改清华(pip+apt)/ npmmirror(npm)— 腾讯源给出损坏 litellm wheel
腾讯源返回的 litellm-1.87.0 wheel 文件损坏:镜像 index 声明的 sha256(fbbba7e…,
与 PyPI 官方一致)对,但实际吐出的文件字节算出 bbebefff…,pip 报
"THESE PACKAGES DO NOT MATCH THE HASHES"。本仓 requirements.txt 未钉 hash,
是镜像端 index 自声明 hash 与文件不符 = 镜像存的文件损坏/截断(非篡改、非 require-hashes)。

deploy/update.sh 三个默认值:
- PIP_INDEX_URL -> 清华 pypi.tuna.tsinghua.edu.cn(境内稳 + 同步及时;阿里 PyPI 曾滞后到没有 litellm>=1.83)
- APT_MIRROR    -> 清华 mirrors.tuna.tsinghua.edu.cn(Debian / security 路径同官方,sed 替 host 即可)
- NPM_REGISTRY  -> registry.npmmirror.com(清华无 npm registry,npmmirror 是境内事实标准)

换默认会让下次 build 从 pip 层全量重跑一次(~5-10min),之后命中 cache。
Dockerfile ARG 默认(官方源 fallback)不动。

RUN.md:头部「最后更新」、§镜像构建默认源说明、手动 build 示例、故障表
(新增 hash-不匹配诊断行 + 其余镜像行对齐新默认)。PROGRESS.md:2026-06-03 加一条。DESIGN 不动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:46:54 +08:00
caoqianming 9cd33cf4d6 revert(workspace): 撤 ZCBOT_WORKSPACE_DIR env 覆盖(架构 bug),数据盘改用 bind mount
2026-06-02 给 resolve_workspace 加的 env 覆盖与路径存储层冲突:core/paths.py
把 DB working_dir 锚定 ROOT(代码仓库目录)存相对串,to_db_path 对 ROOT 外路径
直接 relative_to raise。env 一旦指向 ROOT 外的 /data/...,三家分叉:
- 文件面板 /v1/files 走 resolve_workspace(吃到 env)看数据盘
- agent resume 走 from_db_path(锚 ROOT)看 ROOT/workspace
- 新建 task to_db_path 直接 500
现场症状:文件面板"目录尚未创建",但 agent 文件其实写在老 ROOT/workspace。

改法:resolve_workspace 回退成 显式 arg > cfg workspace_dir > 默 workspace
(均 ROOT/<值>)。import os 保留(别处仍用)。要把重写入区落独立数据盘改用
bind mount 把 /data/zcbot/workspace 接到 ROOT/workspace —— .resolve() 不展开
bind,内核路径保持 ROOT 内,relative_to(ROOT) 照常过,DB 不用改,dev 不受影响。

RUN.md:删 .env 的 ZCBOT_WORKSPACE_DIR 段;「workspace 落独立数据盘」段改
bind mount(+ RequiresMountsFor 开机顺序硬化);故障表替换旧 env 行 + 加
"目录尚未创建"诊断行。PROGRESS.md:2026-06-03 加回退记录。DESIGN 不动。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-03 10:24:14 +08:00
caoqianming 382a85e88e feat(workspace): resolve_workspace 加 ZCBOT_WORKSPACE_DIR env 覆盖(per-host 落盘)
prod 要把重写入的 workspace/users/ 落到独立数据盘(xfs prjquota),但
config/agent.yaml 的 workspace_dir 是 dev/prod 共用提交的,改绝对路径会带歪 dev。
resolve_workspace 优先级改为 env ZCBOT_WORKSPACE_DIR > yaml workspace_dir > 默 "workspace"
(对齐 sandbox.* 的 yaml+env 模式);env/cfg 值都按 ROOT/<值> 解析,绝对路径直接生效。
dev 不设 env 维持 ROOT/workspace,prod systemd 设数据盘绝对路径,两边不抢同一份 yaml。

PG 暂不迁(元数据库小,留默认 /var/lib/postgresql 少坑)。

RUN.md:env 段加 ZCBOT_WORKSPACE_DIR + 新增「workspace 落独立数据盘」段
(mkfs.xfs + fstab prjquota + rsync 迁移 + systemd env)+ 故障表一行。
PROGRESS.md:2026-06-02 加一条。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 14:42:00 +08:00
caoqianming 198e95cd84 docs(claude): 加 Shell 语法规约 — 别把 PowerShell here-string 喂给 Bash 工具
踩过:`@'...'@` 用 Bash 工具跑,首尾 `@` 被当字面量塞进 commit message,
只能 amend + force-push 收拾。立规矩:多行文本优先写临时文件 + `-F`;
必须内联时按工具选语法(Bash 用 `$'...'`/heredoc,PowerShell 才用 `@'...'@`)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 12:28:23 +08:00
caoqianming 33bea27d85 fix(web): embed 模式登录页一闪而过 — body 首行同步隐藏 #login
#login 默认 display:flex(带 login-in 动画),加 body.embed-mode 隐藏它的
embedInit() 在 body 末尾才执行;单文件 3800+ 行,浏览器常在解析到底部脚本前
就先把登录卡画出来,造成"一闪而过"。改法:在 <body> 第一行加一段同步内联脚本,
?embed=1 时立即 add embed-mode,赶在 #login 解析/绘制之前隐藏它。

只是绘制闸门,底部 embedInit(postMessage 握手 / embed-waiting / token 分支)
完全不动,embed-mode 幂等。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-02 09:23:32 +08:00
caoqianming 4c1a26cee8 fix(deploy): update.sh 的 docker build 回到默认 TTY 进度 UI(撤 --progress=plain)
分层折叠刷新的 TTY UI 更直观;step 2 pip 去 -q 已足够让装包进度可见。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:07:35 +08:00
caoqianming 74580e951e fix(deploy): update.sh 加自更新重跑守卫,修"改了源仍报旧错"
脚本 git pull 会改自己:变量默认值在 pull 前已求值、bash 又按字节偏移边读边跑,
首次拉到"改 update.sh"的提交那一轮跑的仍是旧脚本(默认源还是阿里 -> litellm 报缺版本)。
pull 后检出本脚本有变更即 exec 新版本从头重跑(ORIG_ARGS 原样回传,ZCBOT_UPDATE_REEXEC 防死循环)。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:04:25 +08:00
caoqianming 4eacfe1bd9 fix(deploy): update.sh 默认源改腾讯避开阿里滞后(卡 litellm>=1.83);build 跳过改 --skip-build;进度可见
阿里 PyPI 镜像同步滞后(只到 litellm 1.82.6),撞 requirements 的 >=1.83.0。
- 默认 APT/PIP/NPM 源阿里 -> 腾讯(已到 1.88);step 2 host pip 显式 --index-url(sudo -u 会洗掉 PIP_INDEX_URL env)
- ZCBOT_SKIP_SANDBOX_BUILD=1 env -> --skip-build CLI flag(开发期不留兼容)
- step 2 pip 去 -q + step 4 docker build --progress=plain,部署逐行流式可见

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 16:00:00 +08:00
caoqianming b6faeda83d fix(deploy): APP_USER 默认取部署目录属主,不写死 zcbot
与 APP_DIR 同思路:服务用户默认 = 部署目录属主(bootstrap chown 后即服务
用户),仍可 env 覆盖。加两道前置:推成 root 时 warn(服务/沙箱不该跑 root,
HOST_UID 要对齐 bind mount 属主),用户不存在直接 fail。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:28:16 +08:00
caoqianming e8aeef19de fix(deploy): update.sh 的 APP_DIR 从脚本位置推导,不写死 /opt/zcbot
部署目录 = 脚本所在 deploy/ 的上一级,用 readlink -f 解析(兼容相对路径 /
软链调用),仍可 APP_DIR env 覆盖。换部署路径不必再改脚本字面量。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:18:58 +08:00
caoqianming e62d4fce4a fix(tools): MP 工具改服务端限量查询,止血 IP 被封根因
mp_search_summary 之前不传分页参数,mp-api 默认 chunk_size=1000 且
list(docs) 自动翻完所有页,limit 只做客户端切片 —— 每次搜索都整库级下载,
被 MP 判 abusive traffic 封 host IP/ASN。改为 search(num_chunks=1,
chunk_size=limit, ...) 服务端单页限量。mp_get_entries 的 limit 同为"只裁剪
保存、不减流量"的假参数,但 get_entries_in_chemsys 天然全量(相图用途)无法
限量,只在 description 点明开销。测试加断言锁定 num_chunks/chunk_size 已传。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 15:11:16 +08:00
caoqianming f66d55cc3b feat(deploy): 加一键部署脚本 deploy/update.sh(pull/pip/migration/build/restart/healthz)
固化日常部署:git pull --ff-only → pip install → db upgrade head →
docker build sandbox → systemctl restart → curl /healthz 验活。

两处钉死:① migration 不能漏,env.py 直读 os.environ 不读 .env,脚本从
.env 抠 ZCBOT_DB_URL 显式喂进去;② build 必先于 restart —— 容器复用 +
tools/ 烤进镜像,restart 时 shutdown_all 清旧容器,下次 ensure() 才用
新镜像重建。sandbox 每次 build 无所谓(重活在 COPY tools/ 之上,cache
让改代码部署秒过)。镜像源默认阿里,可置空回落官方。前置守卫 + healthz
失败 dump journalctl 非零退出。bootstrap 不进脚本,留 RUN.md。

RUN.md §部署 SOP 重写为指向脚本 + 手动 fallback;PROGRESS 加一条。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 14:52:44 +08:00
caoqianming fb5e68d9e7 fix(sandbox): 装 CJK 字体修 matplotlib / mermaid 出图中文方块
sandbox 镜像基于 python:3.12-slim 一个中文字体都没装,matplotlib /
mermaid(chromium)/ render_icon 出的 PNG 中文全是豆腐块。

- Dockerfile: apt 装 fonts-noto-cjk + fonts-wqy-microhei + fontconfig,fc-cache 刷索引
- style.py: 候选首位加 "Noto Sans CJK SC",matplotlib 优先用 Noto
- PROGRESS / RUN 故障表同步

改了 Dockerfile 必须重 build 镜像 + 清旧容器才生效。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 13:47:04 +08:00
caoqianming 0343dd6dd2 docs(progress): MP host 工具联网实测闭环 —— 换 next-gen key 后 step D 复测通过
初次 403 定位为 .env legacy 旧版 key 在新版 mp-api 失效;换 next-gen materialsproject.org
长 key 后查 Ca3SiO5 返 3 条 mp-xxxx + energy_above_hull,MP host 工具端到端通路确认可用。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:38:05 +08:00
caoqianming 68ce996ad2 feat(tools): documents/pymatgen secret-bearing 能力改 host-side tools,key 不进 sandbox
新增 tools/documents.py(document_list_kb/search/download)和 tools/materials_project.py
(mp_search_summary/get_structure/get_entries),key 只在宿主读取,sandbox/run_python 拿不到。
agent_builder 仅在对应 env 存在时注册。删 skills/pymatgen/materials.py::mp_rester() 旧入口,
smoke 改走 host tool。同步 DESIGN §6.7 secret-bearing 规则 + RUN/SKILL_LIST/两个 SKILL.md。

实测:MP step D 真连 api.materialsproject.org 返 403(工具行为正确,干净透传 [Error]),
疑似 .env 里 legacy key 在新版 mp-api 失效,待换 next-gen key 再验。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-01 09:35:10 +08:00
caoqianming 4bd074079a design: 加 §8.1 seedream i2i + vision 后续步骤 + probe 实测 base64 通
用户场景"seedream 出图→基于该图二次修改"/"上传外部参考图让 agent 据此干活"
两条主模型 DeepSeek V4 纯文本覆盖不了。详评 3 方案选 E+C 组合(seedream
加 reference_images 走 5.0 i2i + 新 tools/look_at_image.py 走豆包 Seed 1.6
vision tool 调度),否决 A(换主模型降 code/tool calling 质量+改 loop/memory
工程面 5×)/ B(后台隐式 vision 路由失 agentic 控制+token 浪费)。

实测 scripts/probe_seedream_i2i.py:豆包 Seedream 5.0 /images/generations
接受 image_urls=[data:image/png;base64,...] 作 i2i 输入,200 返回新图 TOS URL
+ usage.generated_images=1。约束:输出 size≥3686400 像素(~1920²),单张参考
≤10MB,最多 14 张。**内网部署无需对象存储中介**,排除最大工程不确定性。

E+C 实施清单/风险/升级到 A 的信号已落 DESIGN §8.1,本版仅 probe+design,
tool 与 prompt 改造未启动。RUN.md / SKILL_LIST.md 不动(无 CLI/env/skill 变化)。

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-29 12:47:04 +08:00
caoqianming e6eebbc9ad feat(web): tool_call 标题行改显中文活动描述 + 修 args 字段 bug
dev.html 实时流读 ev.data.arguments 但后端 emit 的是 args,字段名对不上
导致参数永远为空、连带 artifact 路径提取失效。新增 toolActivityLabel 按
12 个工具的关键参数套中文动词(执行命令/运行 Python/读写编辑文件/查找搜索
/联网搜索/抓取网页/加载技能/生成图像视频),实时流与历史回放两处同步;完整
参数仍在折叠 <pre> 里。

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-05-29 12:24:54 +08:00