92 KiB
实施进度
配合
DESIGN.md。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查git log/git diff/DESIGN §7.9。
最后更新:2026-06-24(微信绑定表重构为统一 channel_bindings 判别列+JSONB,合并 ClawBot/企微两表 + bump 0.24.3)
状态
| Phase | 标题 | 状态 | 备注 |
|---|---|---|---|
| 1-3 | 骨架 + Skill + run_python | ✅ | 多 skill(coding/proposal/ppt/research/documents/imagegen/videogen/review/patent);CoreCoder 唯一匹配 edit;敏感 env 过滤 |
| 4 | 演化性能力 | 🟡 | Model Profile + Probing ✅;版本化 prompt 未做 |
| 5 | Eval Suite | ⏸ 不做 | dogfooding 替代,probe 覆盖健康检查 |
| 6 | 长任务工程化 | 🟡 | task + 恢复 ✅;双层记忆 ✅;context 压缩 ✅(加压力门槛) |
| 7 | 打磨 | ❌ | Docker 沙盒 / 更多 skill |
| §7 SaaS | DESIGN §7 路线 | 🟡 | A 事件流化 ✅;B 完工 ✅;D /v1 JSON API ✅;D' 过渡 auth + dev SPA ✅;单活 run 锁 + cancel ✅;0004 schema 瘦身 ✅;入口归位 ✅;真 OIDC 待;C Step 1-3 + 3d ✅(Executor + Docker 池 + DockerExecutor + fs 工具进容器)+ Step 5 部署前置对账 ✅ + 容器资源 yaml + 应用层磁盘配额 ✅ + dogfood 网络放开 + 容器内 pip/npm 源持久化 ✅;Step 4 完整 egress proxy + Step 3b PGID kill 协议延后到外部用户开放前(还需 egress proxy + xfs project quota OS 层硬化,§7.5 落地清单 #2 #4)。 |
已完成关键能力
2026-06-24 / 微信绑定表重构:两表合一 channel_bindings(判别列+JSONB,bump 0.24.3)
- 起因:ClawBot(0012
wechat_bot_bindings,8 列)+ 企微(0014wecom_bindings,1 列)各一表。从架构角度复盘:渠道绑定本质="用户在某渠道的一份配置",各渠道字段形态不同 → 最优是判别列 + JSONB 多态(与本库usage_eventskind+units /scheduled_jobs.notify同范式),加渠道(飞书/TG…)零 migration。分表不扛增长、与库内范式不一致;单宽表(NULL 列并列)最差。 - 重构:
ChannelBinding(user_id, channel, status, config JSONB)PK=(user_id,channel);clawbot config 装{bot_token*, user_im_id, base_url, latest_context_token*, context_token_at, chat_task_id}(*crypto 加密入 JSONB),wecom 装{wecom_userid}。migration0015建表 + 把旧两表数据搬进 config(token 本就是密文串、原样搬)+ drop 旧表;DDL+DML 同事务,失败回滚不丢。 - 关键:只动 models + service 内部 + migration,
service公共 API 与BindingSnapshot形状不变 → inbound/web/tool/scheduler 零改动(纯内部数据层重构,对外行为不变)。趁绑定数据极少时合表最省。 - 文件:
core/storage/models.py(ChannelBinding替WeChatBotBinding/WeComBinding)、core/wechat/service.py(存取改读写 config)、migration0015_channel_bindings(含 down 拆回)。import/编译 +_snap反序列化单测过;DB 往返 + migration 待部署联调。
2026-06-24 / 修复微信绑定弹框标题样式错乱(bump 0.24.2)
- 根因:
#wechat-modal h3只设了 flex 布局,漏了其他弹框(crons/memory)都有的margin:0; padding:12px 16px; font-size:16px; border-bottom→ 标题吃浏览器默认 h3 样式(大字号 + ~21px 上下默认 margin + 无分隔线),看着比别的弹框又大又飘。 - 修复:
web/static/dev.html给#wechat-modal h3补齐标题样式,并加h3 svg{opacity:.85}与.sk-x关闭按钮样式,与 crons/memory 弹框对齐。
2026-06-24 / 修复 host-side 文件工具发不出附件(docker 容器路径未翻译,bump 0.24.1)
- 根因:生产 docker 模式下,fs 工具在容器里跑(文件落容器卷=宿主
users/<uid>/<wd>/),但send_email/wechat_push是宿主进程工具;它们base_dir=Path.cwd()(部署根)且不识别容器↔宿主路径映射 → agent 给的相对路径拼到 cwd、容器绝对路径/workspace/...宿主上瞎解析,relative_to(user_root)必越界 → 附件永远发不出(微信 DB 实锤#7相对 +#15容器绝对两条都「文件路径越界」)。probe 脚本能发是因直接调send_file绕过解析。 - 修复:
tools/base.py加共享_resolve_user_file(/workspace前缀翻回user_root+ 相对拼base_dir+ 越界校验,抽FileOutOfBounds);agent_builder给两个 host 工具传base_dir=working_dir_path(宿主 task 目录)而非 cwd;send_email/wechat_bot改用 helper。host 模式同样受益(相对路径之前也错)。 - 测试:
tests/test_secret_host_tools.py加 3 例(helper 翻译+越界、send_email 容器路径附件、wechat_push 相对路径);诊断脚本scripts/diag_wechat_push.py。
2026-06-24 / 企业微信渠道 B:纯推送 + OAuth 扫码绑定(bump 0.24.0)
- 决策:企业微信只做推送、不做对话(用户拍板"和邮箱似的")——省掉入站回调 + AES + 5s ACK + agent 回推一整套;要对话走 ClawBot。企业微信的无条件主动推(不挑活跃度、无 24h 窗口)正补 ClawBot 短板,定时简报必达首选。
- 定位 touser:OAuth 网页授权扫码拿企业成员
userid(用户拍板,优于手填 opaque id)。前提:管理员建自建应用给WECOM_CORPID/AGENTID/SECRET+ 配「网页授权可信域名」。 - 文件(后端 import/编译 + 前端 node --check 自测过):
core/wechat/wecom.py(access_token 2h 缓存+线程安全+失效重取、OAuth getuserinfo、message/send text/file、media/upload、state HMAC 签名);WeComBinding模型 + migration0014_wecom_bindings(0013 被 task_channel 占);service.py加 wecom CRUD +push_wecom+send_to_user接 wecom 一路;web/app.py5 端点(/v1/wecom/oauth/url、/v1/wecom/oauth/callback公开-身份从 state 验、/v1/wecom/bindGET/DELETE、/v1/wecom/test);前端 rail「微信」modal 加企业微信段(wechat.js+ dev.html)。 - env:
WECOM_CORPID/AGENTID/SECRET+ 可选ZCBOT_PUBLIC_BASE_URL(OAuth redirect 主机,须在可信域名内)。待办:管理员就绪后端到端验(扫码绑 → test → 简报推);回调端点须公开(已不挂 require_user)且 redirect 主机匹配可信域名。
2026-06-24 / 配置 QQ/foxmail SMTP 发信 + 发件人显示名品牌化(bump 0.23.2)
.env填入 foxmail SMTP(smtp.qq.com:25 / STARTTLS / 授权码),send_emailtool 与定时任务 notify 兜底投递就此生效;自检发信链路通过。tools/send_email.py发件人显示名从硬编码zcbot改为读SMTP_FROM_NAME,默认「总院科研辅助智能体」—— 对外不暴露内部代号。RUN.md env 段补SMTP_FROM_NAME。
2026-06-24 / 微信任务徽章改品牌绿 + 微信 logo + 整行绿边(bump 0.23.1)
- 上一版徽章复用
.badge.active(蓝灰),与旁边「进行中」状态徽章撞色、不显眼。 - 新增
.badge.wx(微信品牌绿#07C160+ 白字 + 内嵌微信 logo SVG)与.task-row.wx(绿色左边框 + 极淡绿底 + hover 加深),让置顶的微信任务从普通任务里跳出来。文件:web/static/dev.html(CSS)、web/static/js/chat.js(WECHAT_ICON常量 + badge/row class)。
2026-06-24 / 微信对话 task 渠道标记 + 置顶(bump 0.23.0)
- 痛点:微信常驻 task 与网页常规 task 结构相同,只能靠 description 魔法值反推;且
created_at固定后随用户开新 task 越沉越深,这个「渠道收件箱」反而最难找。 tasks加channel列(web/wechat,migration 0013,server_default='web'回填存量、并把 description=(微信 ClawBot 对话)的存量 task backfill 成wechat)。ensure_local_task_row加channel参数,微信建 task 处传wechat;channel仅 INSERT 写定,后续 upsert/save 不传 → 不覆盖。_task_dict透出channel;列表查询排序前置case((channel=='wechat',0),else_=1)pin 表达式 → 微信 task 后端强制置顶(跨分页稳定),用户选的排序对其余 task 照常生效。- 前端
chat.js任务名前打绿色「微信」徽章(channel==='wechat')。文件:core/storage/models.py、core/storage/utils.py、web/app.py、web/static/js/chat.js、db/migrations/versions/...0013_task_channel.py。
2026-06-24 / 微信绑定 UI 并入主 SPA(bump 0.22.2)
- 上一版绑定页是独立
/static/wechat_bind.html,主界面没入口、用户找不到。 - 集成:左栏 rail 加「微信」按钮(
hd-wechat)→ 扫码绑定 modal(wechat-modal),复用api()调已有 5 端点(起码/轮询/查/解绑/自检),仿crons.jsmodal 范式;过期自动换码、绑定成功提示去微信开口。文件:web/static/js/wechat.js(新)、web/static/dev.html(rail 按钮 + modal + CSS)、web/static/js/main.js(import 触发绑定 + Esc 关闭)。 - 独立页
web/static/wechat_bind.html保留作嵌入/兜底入口(同套端点)。
2026-06-24 / 修复顶栏 token 计量栏回复后不刷新(bump 0.22.1)
- 现象:提问→助手答完后,对话顶栏的「总 token · 缓存命中 · 花费」计量栏停在发问前旧值,要切到别的 task 再切回才更新。
- 根因:计量栏由
renderChatMeta()读state.taskMeta渲染,而state.taskMeta只在selectTask里GET /v1/tasks/{id}时刷新。SSE 流结束后fetchSse的 finally 只loadTaskList()(左栏列表)+loadMessages(),从未重拉 meta 也没调renderChatMeta——SSE 期间用量只累计进 hint,没落 taskMeta。 - 修:
fetchSsefinally 块里,当收尾的是当前可见 task 时补一次GET /v1/tasks/{id}→ 重置state.taskMeta→renderChatMeta();失败 try/catch 吞掉不打断收尾。web/static/js/chat.js。
2026-06-24 / 微信接入第一期:ClawBot 个人微信(后端完成,bump 0.22.0)
- 需求:把 zcbot 送进用户个人微信——能对话、能推简报/结果。调研三条路:wechaty/hook(违规高封号,排除)、企业微信自建应用(官方但要管理员+仅企业成员)、微信 ClawBot(腾讯 2026-03 官方个人号 Bot API,iLink 协议,零封号,后端接谁都行)。选 ClawBot 先行。详 DESIGN §8.7。
- 协议全程真机实测(
scripts/probe_clawbot*.py,本人微信号在灰度内):① 扫码绑定拿bot_token;②getupdates长轮询收消息;③sendmessage每条client_id必唯一(漏则同 token 后续被丢——前几轮误判"纯被动"的真因),多条/长文中间块state=1末块state=2;④context_token24h 可复用 → 主动推送成立(需用户先开口一次);⑤ 文件:getuploadurl→AES-128-ECB(PKCS7)→CDN(URL 带filekey,漏则 400 mismatch)→file_item,docx/pdf 原生直推。 - 关键设计决策:入站对话→每用户一条 persistent「微信」task(连续性,token 靠 §8.2 压缩);凭据(bot_token/context_token)加密列(env
ZCBOT_WECHAT_SECRET_KEY),绝不进沙箱/日志;入站出站一体——主动推送依赖入站给的 context_token,故 getupdates 长轮询常驻(既收对话又刷新 24h 窗口)。 - 文件(后端全部 import/编译自测过):
core/wechat/{ilink.py 协议客户端, crypto.py 凭据加密, service.py 绑定CRUD+推送+send_to_user 渠道抽象, inbound.py 长轮询管理器+回复提取};core/storage/models.py加WeChatBotBinding+ migration0012_wechat_bot_bindings;tools/wechat_bot.pyWechatPushTool+core/agent_builder.py注册(有开关才挂);core/scheduler.pydeliver_notify加wechat通道(未送达退邮件兜底);web/app.pylifespan 起入站管理器 +_run_wechat_message回调 + 5 端点(/v1/wechat/bind/qrcode|status、/v1/wechat/bindGET/DELETE、/v1/wechat/test);web/static/wechat_bind.html自包含绑定页;requirements.txt加 segno+cryptography。 - env:
ZCBOT_WECHAT_BOT_ENABLED=1(渠道开关)+ZCBOT_WECHAT_SECRET_KEY=<串>(凭据加密,缺则退明文标记)+ 可选ZCBOT_WECHAT_BASE_URL。 - 待办(部署后联调):migration
0012上库;起 web 进程端到端验(扫码绑定→对话→主动推→定时简报推);渠道 B 企业微信(无条件推送,补 ClawBot 24h 窗口短板)按 §8.7「渠道 B」实现。SPA 集成已落(见下条)。
2026-06-23 / 平台渲染层 rendering/:三 skill docx 统一 + chromium md→pdf(bump 0.21.0)
- 背景:线上
简报task 用户要"输出为pdf",模型因 brief 无 PDF 路径而临场即兴——试apt install libreoffice(只读 fs 失败)→pip install weasyprint markdown手搓 md→HTML→weasyprint;容器空闲回收后包不持久,二次导出又重装一遍。深挖发现两个问题:① skill 缺 PDF 路径、weasyprint 不在镜像;②_CHEM_RE化学式白名单在 brief/paper/proposal 三份 render_docx.py 逐字重复(改一处易漏改),patent/standard 还复用 proposal 那份。 - 架构判断:渲染不是 skill 内容,是平台能力(像 chromium/document_search)。Skills 走 Anthropic 自包含/可 fork bundle 标准,把共享渲染库塞
skills/_shared让各 skillimport会破坏 fork。故新建顶层rendering/平台包,bind-mount 进/sandbox/rendering(pool.py,与 skills 同款 ro),各 skill 调render.py不再自带 render 脚本。 rendering/:common.py(叶子原语单一事实源:字体/CHEM_RE/块级正则/表格行/图片路径)+docx_manuscript.py(paper/proposal 配置化双 profile)+docx_brief.py(brief 富渲染,复用 common)+pdf.py(md→HTML→chromium--print-to-pdf,复用common.CHEM_RE)+render.py(统一 CLI--profile {brief,paper,proposal} --format {docx,pdf},sys.path bootstrap 让python /sandbox/rendering/render.py直调可解析)。- 零回归证明:重构前后对三 profile 各渲 docx、解包 diff
word/document.xml,brief/paper/proposal 全部字节一致(12962/10755/11401 bytes)。纯搬移+共享原语,输出不变。 - chromium md→pdf:不用 weasyprint(要 pango/cairo、不在仓库 Dockerfile);chromium 镜像已装(给 mermaid)+ fonts-noto-cjk 已装,完整内核 CSS 保真度更高。固定
--no-sandbox --disable-dev-shm-usage --user-data-dir=/tmp/* --no-pdf-header-footer。冒烟deploy/sandbox/probe_chromium_pdf.sh(照 probe_mermaid.sh):最小 chromium 镜像在--read-only --cap-drop=ALL+ 64MB/dev/shm下实测出图,中文/下标/DOI 超链/表格/callout 全绿、页眉已关。 - 删:
skills/{brief,paper,proposal}/scripts/render_docx.py(3 份)+ 短命的skills/_shared/render_pdf.py。改 5 个 SKILL.md(brief/paper/proposal 直接调,patent/standard 复用 proposal profile)调用到 render.py + 补反模式"渲染一律调 render.py、禁止手搓"。requirements.txt加markdown。 - 部署要点:
/sandbox/rendering挂载靠 pool.py(restart 重建容器才生效)+markdown进镜像靠 requirements 变更触发的整体重建 —— 需一次 deploy(update.sh)原子激活,旧 render_docx 路径已删,deploy 前别只推 SKILL 改动。引文[n]上标回链 pdf 仍按字面渲(docx 有,pdf 后补)。 - 文件:
rendering/{__init__,common,docx_manuscript,docx_brief,pdf,render}.py(新)、core/sandbox/pool.py(+rendering 挂载)、deploy/sandbox/probe_chromium_pdf.sh(新)、requirements.txt、5×SKILL.md、skills/brief/SKILL.md(另删 research 索引滞后描述)、core/__init__.py0.20.4→0.21.0。
2026-06-23 / 消息目录定位错位修复(bump 0.20.4)
- 现象:点右侧圆点轨道第一个圆点,活跃高亮常落到第二个。根因是两套锚点不一致——
jumpToMessage用block:"center"居中,但第一轮上方无内容无法居中、被钉到顶端;而updateActiveOutlineDot按「顶线 80px 容差」判活跃轮,第一轮短时下一轮卡片顶也落进 80px 带内 → 越界高亮第二个圆点(滚动监听又覆盖了 jumpToMessage 的显式 setActiveOutlineIdx)。 - 修复:跳转改
block:"start"(顶部对齐,与活跃判定同锚点)+.msg加scroll-margin-top:16px留呼吸;活跃容差 80→24 与之对齐,贴顶短轮判到自己不越界。 - 文件:
web/static/js/chat.js(jumpToMessage/updateActiveOutlineDot)、web/static/dev.html(.msgCSS);core/__init__.py0.20.3→0.20.4。
2026-06-22 / 前端两处 bug 修复(bump 0.20.3)
- 定时弹窗"被遮挡":
#crons-modal漏了 z-index,退回基础.modal(无 z-index)被 z-index:5 的侧栏/面板盖住;补z-index: 112与兄弟只读 modal(#skills-modal/#memory-modal)对齐。排查用 node 加 DOM mock 跑通整条前端模块图,确认hd-crons绑定确实执行(排除了"按钮没绑事件"),定位到纯 CSS 层叠问题。 - 登录页 focus 引用错 id:
web/static/js/main.js:106$("li-token").focus()中li-token不存在(登录输入框实际是li-email),未登录 boot 末尾会抛 TypeError;改为li-email。 - 文件:
web/static/dev.html、web/static/js/main.js;core/__init__.py0.20.2→0.20.3。
2026-06-21 / 发送期修复悬空 tool_calls(bump 0.20.2)
- 根因(监控页 error 任务排查,task 5c5d6d25 DB 实测):run 在写入
assistant.tool_calls之后、tool 结果写库之前被中断(上游流式断连 / 用户取消 / 崩溃),历史里留下一条assistant.tool_calls后面没有对应 tool 结果的消息;用户随后继续发言,下一轮把历史原样发给 DeepSeek/OpenAI 即被拒An assistant message with 'tool_calls' must be followed by tool messages responding to each 'tool_call_id'→ 任务进run_status=error卡死。区别于 06-06/06-12 的 arguments 损坏/投毒修复(那治"参数被压成 marker"),这是结构性悬空,旧修复不覆盖。 - 修复(方案 A,发送期兜底):
core/context.py新增_repair_dangling_tool_calls,在prepare_messages_with_stats入口(早返回分支之前)对每条assistant.tool_calls扫描紧随其后的连续 tool 结果,为缺失的tool_call_id补一条占位 tool 消息([interrupted: ...],带原 function name)。纯发送期、不改库 → 覆盖所有中断路径 + 已存在的坏数据自愈(下次发消息即修复),stats.repaired_tool_calls计数。选 A 而非写入期防御(方案 B):B 要覆盖所有中断路径易漏且救不了存量。 - 验证:真实坏 task 5c5d6d25 修复前 idx 19 悬空 1 条 → 修复后 0 悬空、协议合法(压缩开/跳过两分支均覆盖);新增 4 个单测,context 套件 14 项全过。
- 文件:
core/context.py、tests/test_context_compaction.py;core/__init__.py0.20.1→0.20.2。
2026-06-18 / brief 简报重定位「重要文献速览」+ 精简三文件(bump 0.20.0)
- 需求漂移收敛:brief 从"热点聚类趋势判断型简报"重定位为**「重要论文列表 + 内容总结」速览型** —— ①只描述不给建议(去掉启示/判断/空白争议);②开头一份重要期刊论文列表(各大相关刊、Elsevier 数据库优先),每篇带一段简介/摘要概述;③对这批论文做客观总结即可。
- 数据源:research + documents 都是取文献主力(research 逐刊精确取最新 Elsevier 论文 + DOI;documents 取内部材料库全文),web search 取动向单列不混进论文总结。
- 精简到三文件(原 8 文件):
SKILL.md(自包含:spec 字段/骨架/检索法/核验铁律/渲染说明)+references/journals.md(各建材子领域主流期刊清单,Elsevier 标注 + 精确 publication_name + 0 命中降级)+scripts/render_docx.py。删templates/spec.md、templates/brief_outline.md、references/search_strategy.md、references/citation_verify.md、scripts/quality_check.py。 render_docx.py两处小改并已 smoke test 验证:①「重要论文列表」段(标题含"论文列表/文献列表/参考文献")H3 期刊子标题下的[n]条目仍作锚点(只在 H1/H2 重判段类型);②条目内 DOI 子串(末尾 "DOI: 10.xxx")也做 https://doi.org 超链接。验证:ref 锚点/内部回链/外部 DOI 链/化学式下标全在。- 文件:
skills/brief/{SKILL.md,references/journals.md,scripts/render_docx.py};core/__init__.py0.19.0→0.20.0;SKILL_LIST.md(brief 条目重写,总数仍 17)同步。
2026-06-18 / 定时任务 v1(scheduled_jobs,DESIGN §8.5)
- 需求:对话方式建"每天 X 点干 Y"的定时任务(跑 skill 出简报 / 发邮件 / 打招呼皆可)。调研 OpenClaw/Autobot/Claude Code/geta 四源收敛,定方案见 DESIGN §8.5。
- 核心解耦:job 本体 =
cron+tz + 一句 prompt + 会话模式;"发邮件"不是字段,是 agent 据 prompt 调send_email的动作 → 加任何能力不改 schema。 - 不引调度框架:croniter(唯一新依赖)只当 next_run 计算器(正确处理 dom/dow OR 语义 + 时区);"每 30s 醒来扫到点 job"是 plain-asyncio 守护循环,仿 §8.4
_disk_scanner,复用_run_agent_bg,不上 APScheduler/Celery。 - 文件:
core/storage/models.py加ScheduledJob+ migration0011_scheduled_jobs(独立加表,公测兼容)/core/scheduler.py(cron 数学 + claim+advance 防重复触发 + record_result 失败阈值自停 + notify 兜底投递 + CRUD 服务层list/create/update/set_enabled/cancel_job,工具与 REST 共用)/tools/schedule.py(create/list/update/cancel 四件套,薄包装服务层,user_id ctor 注入,定时 run 内不挂防自我繁殖)/tools/send_email.py(host-side,SMTP_* 齐才挂)/web/app.pylifespan_scheduler_loop+_execute_scheduled_job(认领→抢 run 锁→to_thread 跑→超时协作 cancel→notify→记账)+/v1/schedulesGET/PATCH/DELETE 三端点。 - 对话端 = 完整 CRUD(建/改/删/查都说着办);前端 = 只读展示 + 停用/删除两个便捷按钮(左栏 rail「定时」按钮 →
crons.js只读 master-detail modal,复用 skills modal 范式;建/改无 REST、故意只走对话,§8.5)。两条路径共用core.scheduler服务层不漂移。 - 会话模式:isolated(默认,每次新建临时 task
scheduled-<id8>目录,省 token)/ persistent(绑定 bound_task_id 续上下文)。env:SMTP_*/ZCBOT_DISABLE_SCHEDULER/ZCBOT_SCHEDULER_TICK_SECONDS/ZCBOT_SCHEDULER_CONCURRENCY(见 RUN)。已验:migration 上库 0011、CRUD 服务层端到端、3 REST 路由 + 4 工具注册、crons.js 语法。bump 0.18.0 → 0.19.0。 - v2 待做:对话工具教写好 job.prompt 的薄 skill;退避重试(transient/permanent 区分)目前简化为"到下一 cron 点 + 连失败 5 次自停";真机邮件 smoke + 守护循环定时触发的端到端验证(需起 web 进程跑一轮)。
2026-06-18 / brief skill:科研方向简报
- 需求:用户要"水泥/建材方向的科研简报"。联网调研简报类做法——Anthropic 官方 digest skill(办公活动聚合)+ Paper Digest(论文影响力周报)+ 文献计量趋势报告(热点聚类/新兴方法/地理格局)。结论:现有 skill 缺"某方向近期文献 → 有判断的趋势简报"这一环(research/documents 只取文献不组织、paper-review 出可投稿综述、analyze 拆问题不查文献)。
- 方案:新建自包含
skills/brief/,定位"文献计量趋势型简报",数据底座三路并用:documents(内部胶凝材料库取全文)/ research(补 DOI + year_gte 卡时间窗)/ web(政策·标准·产业动向,单列不混学术引文计数)。六阶段:定题对齐 spec(方向+边界/时间窗/受众/深度/源开关/语言/关注点)→ 三路检索取数(中→英术语转译 + 跨源去重,证据表 evidence.md)→ 趋势分析(3-7 热点簇,BLOCKING-lite 对齐)→ 逐段起草 → 引文核验(复用 paper 三层协议,CITATIONS.md)→ 渲染验收。 - 深度三档 flash/standard/deep 配字数/簇数/引文数预算;骨架:TL;DR→概览→热点聚类→新兴方法→标志性进展→研究空白→产业政策动向(web)→参考文献。渲染早期复用 proposal,后改为自带 render_docx。
- 文件:
SKILL.md+templates/{spec,brief_outline}.md+references/{search_strategy,citation_verify}.md+scripts/quality_check.py(结构/簇数预算/过度宣称/无源句式/引文交叉核对)+scripts/render_docx.py(简报专属:商务红主题 + 引文 [n]/[Wn] 上标并锚到文末 + DOI/URL 可点击超链接 + TL;DR/判断 callout 底纹)。 - 顺带修 zcbot 全局「角标」问题:水泥化学式在 docx 里平排数字(CO2/C3S/SO3...)是 paper/proposal 渲染器的老毛病。抽一份化学式下标白名单(长在前 +
\b防误伤 LC3/C595/Ca2+/2026,实测命中精确零误伤)统一补进paper、proposal、brief三个render_docx.py的add_inlineplain 分支(按"自包含 skill 脚本不跨 skill 引"的既有约定各自复制同一份,不建共享模块)。core/export_docx.py是对话原文转录、非排版文档,不动。bump 0.17.0 → 0.18.0。
2026-06-17 / 任务软删除(留对话轨迹做语料 + 可恢复)
- 背景:公测后目标转为沉淀用户对话/文件做训练研究语料;原"hard cascade"硬删任务会连带 messages/usage_events 永久丢失,推翻该决策(DESIGN §取舍同步标注)。
- 改动:
tasks加deleted_at列(0010 migration,additive 可空);DELETE /v1/tasks/{id}从DELETE改为置deleted_at=now(),不再触发 CASCADE、不动工作目录文件(原 rmdir 清理一并去掉);list_tasks/list_folders计数加WHERE deleted_at IS NULL过滤;新增POST /v1/tasks/{id}/restore恢复;delete_file顶层目录 409 引用检查排除软删 task。 - 文件留存(归档)方案已在 DESIGN 记录(restic 备份地基 + DB 事件日志 + 起步同盘),实现待办,优先级靠后。bump 0.16.2 → 0.17.0。
2026-06-17 / 用户操作说明书(详 + 精简两版)+ 文献库库容 21W→100W 全量更新
- 新增
docs/操作说明书.md(详版)+docs/操作说明书-精简版.md:面向科研用户、不出现产品代号、从登录后正式操作讲起。覆盖三栏布局、个人文件夹 → 工作目录 → 任务三层概念(任务≠文件夹、多任务可共享一个工作目录)、新建任务、对话、技能矩阵(含 paper)、文件管理、进阶(方案确认卡/消息目录/记忆)、任务管理、图像视频、账户存储、FAQ;截图留占位标注。突出对外优势(内部文献库、科研计算、可直接产出文件)。 - 文献库库容口径 21W+ → 100W+ 全量改一遍:
SKILL_LIST.md、skills/documents/SKILL.md(含 description,模型运行时据此向用户描述库容)、skills/patent/SKILL.md、PROGRESS.md、scripts/optimize_arch_ppt.py、两份说明书,共 10 处。bump 0.16.1 → 0.16.2。
2026-06-17 / paper skill:学术期刊论文写作
- 需求:现有 skill(proposal 写本子 / review 改稿 / research 查文献 / plot_pub 出图)缺"从零起草期刊投稿稿"这一环。联网调研开源论文 skill(ARS 32.1k★ / paper-writer-skill / claude-scientific-writer 1.9k★)——结论:不直接装(ARS 是 CC-BY-NC 非商用、全偏英文/医学/CS、引文默认 APA、依赖外部 API),但流程值得移植。
- 方案:新建自包含
skills/paper/,流程骨架取 paper-writer 的"先定图表 + stage-gate"与 ARS 的"三角引文核验 + 反谄媚审稿",底座全换成 zcbot 自有(documents/research 查文献与核验、plot_pub 出图、复用 proposal 的渲染心智)。中英双语 × 三类型(original/review/letter)用子 md 分流(cite_gbt7714/cite_elsevier + redlines_zh/redlines_en,一篇只挂一套)。 - 六阶段:摄取 → 八条对齐 spec → 文献矩阵 → 先定图表 → 逐章一段一卡(Methods→Results→Intro→Discussion→Abstract→Title 顺序) → 引文三角核验(存在性/三角/支撑度,台账 CITATIONS.md) → 验收渲染 + 投稿件。终审复用 review skill。
- 脚本(自带,不跨 skill 引):
render_diagrams.py(照搬)/render_docx.py(去 fund-type,加--lang {zh,en}图题切换 +--toc默认关)/word_count.py(类型×语言双口径预算)/quality_check.py(论文版核心=引文交叉核对 orphan/uncited/编号连续 + 结构/占位符/过度宣称/插图)。 - 验证:微型 fixture 端到端 smoke——word_count 正确标欠预算、quality_check happy path 全 OK 且 orphan/uncited/缺号负例正确触发、render_docx 出 37KB docx。文件:1 SKILL.md + 6 references + 3 templates + 4 scripts。SKILL_LIST 同步(15→16)。bump 0.16.0 → 0.16.1。
2026-06-16 / look_at_image 图像理解(DESIGN §8.1 C 路落地)
- 需求:DeepSeek V4 主模型纯文本无视觉,挂
look_at_image工具按需"借眼睛"读图(OCR / 描述 / 读图表),模型自决何时调。 - 模型选型:设计时的 Seed 1.6 vision 已过时(联网核实),改用 Doubao Seed 2.0 Lite(
doubao-seed-2-0-lite-260428,全模态 SOTA 细粒度感知)。token 计费输入 ¥0.6 / 输出 ¥3.6 / Mtok,一次读图 < ¥0.01。否决「换主模型走 A 路」——DeepSeek 的 code/tool-calling 仍是核心,vision 当工具更稳。 - 后端:新建
tools/look_at_image.py(/chat/completionsOpenAI 兼容,base64 单图 + question → 文本解读,默认 question 覆盖描述+OCR+图表读数);config/media/doubao.yaml加vision:段;core/storage/usage.py加record_vision_usage(kind="vision",按 token,单价 snapshot 进 units);agent_builder.py注册(yaml 有 vision 段才挂)+ media prompt 段教「何时调 / 何时别调」。usage_events.kind自由文本,vision 无需 migration。 - 重构:图片路径解析 + base64 抽到
tools/image_ref.py,seedream(i2i)与 look_at_image 共用(三形态路径 + user_root 边界 + 扩展名/大小校验)。 - 验证:真机 smoke
scripts/smoke_look_at_image.py合成含已知文字图 → OCR 准确读出 + usage_events 落 kind=vision(实测 ¥0.0011)。bump 0.15.0 → 0.16.0。
2026-06-16 / seedream i2i 改图(DESIGN §8.1 E 路落地)+ 前端 paste 路径注入
- 需求:覆盖「基于已生成 / 上传的图做修改」(像素级),核心循环=文生图 → 用户"改成 X" → i2i 改那张(不重画)。base64 通路 probe 2026-05-29 已验,本次落 tool。
- 后端
tools/seedream.py:加reference_images数组参数(v1 单图,传 >1 直接报错不静默截断)。路径解析走共享tools/image_ref.py(与 look_at_image 同一套)——依次试working_dir/rel→user_root/rel→ 绝对,强制结果落在 user_root 子树内(防越界读任意文件),吃三种路径形态(figures/x.png/ saved 形态<taskname>/figures/x.png/ 绝对);校验存在 + 图片扩展名(png/jpg/jpeg/webp/gif)+ ≤10MB;读 base64 → data URL → ARK bodyimage_urls。不传 reference_images = 文生图,行为 100% 不变(向后兼容)。banner 加· mode=i2i+reference=行(前端正则兼容),meta.json 记mode/reference_images(派生链可追溯)。 - 前端
web/static/js/chat.js:sendMessage发送时takePastedRels()收集chat-hint的 paste-chip 路径,作[用户上传的参考图] <rel>行注入正文 + 清 chip ——修了既有缺口(之前粘贴的图路径根本到不了模型)。这样"上传外部图 → 改图 / 看图"才能定位到文件。 - 引导:
skills/imagegen/SKILL.md删旧「不接图像输入」结论 + 加「改图(i2i)」专段(最易踩错=该 i2i 却重新 t2i 丢构图);agent_builder.py媒体 block 提 i2i + paste 注入约定;SKILL_LIST.md同步。bump 0.14.0 → 0.15.0(看图 look_at_image 同日落地见上一条)。
2026-06-16 / ask_user:回复里渲染可点击「方案确认」选项卡(Claude 式)
- 需求:agent 在分叉点能像 Claude 那样抛出可点选项,用户点一个继续、或不点直接用文字讨论。设计取舍见下。
- 收窄定位:不是通用提问器,只做「方案/分支确认」——存在 2-4 个互斥方向且选择会实质改变后续动作时才用。防 agent「变爱问」(高轮数烧 token 已知痛点)是成败关键,故系统提示严格约束使用条件。
- 与轮次模型同构、无阻塞:复用「LLM 出无 tool_call 消息即结束本轮」语义——
ask_user是虚拟工具(同task_progress范式),core/loop.py检测到本步调用它就 emit done 提前结束本轮、不回灌 LLM;点选项 = 把该选项 label 当新用户消息发出(复用POST /messages),零额外 LLM 往返。 - 后端:新增
tools/ask_user.py(AskUserTool,question + 2-4 个{label, description}选项,结果仅占位);core/agent_builder.py注册;core/loop.py加提前终止分支;prompts/system/general_v1.md加「方案确认约定」段 + 工具清单一行。 - 前端
web/static/js/chat.js:buildAskUserCard渲染选项卡;handleSseEvent的tool_call/tool_result特判 ask_user(选项卡 / 抑制占位结果);renderMessages历史重渲特判(改 index 遍历,向后看有无 user 回复判「已答」,命中项标「✓ 已选」);sendMessage(overrideText)支持点击直发不清输入框;chat-stream点击委托接.ask-option。dev.html加.ask-user/.ask-option等样式。持久化天然免费(选项在tool_calls.arguments里,刷新页面按钮还在)。bump 0.13.0 → 0.14.0。
2026-06-16 / 消息目录:右侧悬浮圆点轨道导航(ChatGPT 式)+ 双向分页
- 需求:长对话里快速定位历史某轮提问。参考 ChatGPT 扩展(Scrollbar / Outline)的交互——每点=一轮"我"的提问,hover 出标题气泡,点击滚动定位。
- 后端
web/app.py:①list_messages加after_idx参数 + 响应加has_more_after,支持向下翻页(从目录跳到旧消息后下方还有未加载的新消息);② 新增GET /v1/tasks/{id}/outline,只取全部 role=user 的idx + 首行片段(payload->>'content',不回传整 payload,轻量),_outline_snippet取首个非空行截 48 字。走(task_id,idx)索引按 task 收窄。 - 前端:
state.js加outline / msgHasMoreNewer / msgLoadingNewer;chat.js加refreshOutline / renderOutlineRail / jumpToMessage / loadMessagesAround / loadNewerMessages、消息卡补data-idx锚点、底部 sentinel(下滑加载更新)、滚动高亮当前轮;selectTask把 outline 并入 meta/messages 并发拉,run 收尾后刷新。跳未加载轮次用before_idx=idx+11拉居中窗口再scrollIntoView。 dev.html:#pane-mid加position:relative,新增#msg-outline-rail悬浮轨道(容器pointer-events:none不挡滚动条、仅圆点可点,hover 整列展开标题),手机端隐藏。embed 页无该元素,绑定与渲染均 null-safe。bump 0.12.16 → 0.13.0。
2026-06-16 / 切 task 提速:meta+messages 并发拉 + 默认窗口降到 30
- 体感诊断:切 task 慢不是索引问题——
messages的UniqueConstraint(task_id, idx)在 PG 自带(task_id, idx)复合索引,主查询WHERE task_id=? ORDER BY idx(app.py:1442)既走索引过滤又免排序;也不是"全量加载",前端早已尾部窗口分页。真正的低垂果实是selectTask里 meta 与 messages 串行 await,以及首屏窗口偏大。 web/static/js/chat.js:selectTask把GET /v1/tasks/{id}(meta)与loadMessages(messages)改Promise.all并发(两者无依赖、落不同 DOM 区),省一个 RTT;MSG_PAGE60→30,降首屏传输 + markdown/highlight 同步渲染量。bump 0.12.15 → 0.12.16。
2026-06-15 / plot_pub 吸收 nature-figure 投稿级复合图设计纪律
- 联网调研
nature-figureskill(MIT,github.com/Yuan1z0825/nature-skills):双层 manifest 路由 + Python/R 双后端 + 生物医学 gallery。判断不整包移植 —— 与已有 plot_pub 高度重叠、R/单细胞/在体内容跟建材院领域不沾边、多文件结构破坏 zcbot 单 SKILL.md 约定。 - 只迁移可复用的设计 IP,折进
skills/plot_pub:style.py补svg.fonttype='none'(可编辑矢量,原本只设了 PDF Type 42 漏了 SVG)+SEMANTIC_COLORS语义色表 +clean_spines()spine 纪律 +ablation_alphas()同色变 alpha;SKILL.md新增「投稿级多 panel 复合图」段(五点 figure contract / 语义配色 / 信息架构 / 导出纪律),示例全改建材领域。纯 Python、零新依赖、保留中文字体。bump 0.12.14 → 0.12.15。
2026-06-15 / 消息分页:尾部窗口 + 向上滚动加载更早(切 task 提速)
- 痛点:切 task 卡顿 ——
/v1/tasks/{id}/messages无分页一次拉全量,前端renderMessages又对每条跑 markdown+highlight+media 全量渲 DOM,消息多时两段成本都线性涨。 - 后端
web/app.pylist_messages:加可选 querylimit、before_idx。不传 → 旧行为(升序全量,仅多返has_more:false,向后兼容);传limit→ 取尾部最近 N 条(idx desc + limit再 reverse);传before_idx→ 取该 idx 之前更早一批。响应恒含has_more。 - 前端
chat.js:①selectTask进来立即把 chat-stream 换「加载中…」(治感知,切换瞬时跟手);②loadMessages默认limit=60,结果存state.loadedMessages/msgHasMore;③ 新增loadEarlierMessages+_msgScrollObserver(复用 task list 的 sentinel 范式),顶部 sentinel 进视口自动 prepend 更早一批后整窗重渲(renderMessages 仍是对 loadedMessages 的纯函数,时序累积逻辑不动),重渲后锚回滚动位不跳视口。 state.js加loadedMessages/msgHasMore/msgLoadingEarlier;dev.html加.msg-top-sentinel样式。取舍:只载尾部时进度 dock 仅反映窗口内 task_progress,补满更早后一致。bump 0.12.13 → 0.12.14。
2026-06-15 / 图片预览:左键拖动平移 + 光标语义改正
- 光标:100% 时改回普通箭头(原
zoom-in放大镜误导 —— 左键不缩放,缩放是 Ctrl+滚轮);放大后改grab、拖动中grabbing,贴合"可拖"语义。 - 左键拖动平移:放大态下 mousedown 记起点 + body 滚动位,mousemove 改
bodyEl.scrollLeft/Top平移看局部(替代拖滚动条);img.draggable=false关原生 ghost 拖拽。document 上的 move/up 监听存z._onMove/_onUp,_clearZoom时移除避免泄漏。bump 0.12.12 → 0.12.13。
2026-06-15 / 文件预览缩放加固 + 双击复位提示
- 图片 load 完即量基准尺寸(
_captureBase,免首次缩放时还没渲染量到 0px 导致塌成 0);基准未量到时本次缩放跳过不破坏;双击复位时徽标显式提示「已复位 · 100%」(停留 1.4s)。bump 0.12.11 → 0.12.12。 - 排查提示:左栏底部版本号 =
core/__init__.py __version__,用户报"缩放完全没动静"且本地 8765 无服务 → 多半是远端实例未 pull/重启,版本号对不上即旧代码。
2026-06-15 / 文件预览缩放改显式 px:修 CSS zoom 放不大
- 接上一条:CSS
zoom对带max-width/height:100%的 flex item 不生效 —— zoom 放大后被百分比 max 约束重新夹回,视觉无变化(用户实测"还是不能放大")。 - 改法(
web/static/js/preview.js_applyZoom):以 scale=1 的贴合显示尺寸(clientWidth/Height)为基准缓存到z.baseW/baseH,缩放时max-width/height:none+ 显式width/height = base × scalepx;复位时清空还原 CSS 自适应。显式 px 真正撑大布局,body 才出滚动条。bump 0.12.10 → 0.12.11。
2026-06-15 / 文件预览:修滚动穿透 + 图片 Ctrl+滚轮缩放
- 现象:web 端文件预览弹框内滚滚轮,事件冒泡到背景把对话列表也滚了(scroll chaining);且图片预览无缩放手段。
- 修法(纯前端,
web/static/js/preview.js+web/static/dev.html):- 滚动不穿透:主/小预览
.body加overscroll-behavior: contain,再挂一次性非 passivewheel监听 ── 容器不可滚(如图片正好铺满)或已到顶/底时preventDefault()断掉冒泡。 - 图片缩放:仅图片(文本/md/docx/pdf 各有原生流/阅读器)。Ctrl+滚轮按 ×1.1 步进缩放(夹 0.1–8×),用 CSS
zoom而非 transform(zoom 改布局盒尺寸,放大后 body 才出滚动条能看溢出);右下角浮xx%比例徽标(挂.card上,滚动不跟走,1s 后淡出);双击复位 100%。.body.center改safe center防 flex 居中把溢出顶/左裁掉够不到。 - wheel 监听只在 init 挂一次到复用的 body 元素,缩放目标走
_zoomStateWeakMap,避免每次预览重复 addEventListener 泄漏。
- 滚动不穿透:主/小预览
- bump 0.12.9 → 0.12.10。
2026-06-15 / sandbox 装 emoji 字体:修 mermaid 图满图豆腐块
- 现象:模型生成的 mermaid 架构图里几乎每个节点标签前缀的 emoji 图标(🌐🔥🛡 等)全渲染成空心方框 □。根因不在 mermaid 语法 / 布局 ──
deploy/sandbox/Dockerfile只装了fonts-noto-cjk+fonts-wqy-microhei(中文不豆腐),缺 emoji 字体,chromium 渲染时找不到 emoji glyph 就用 tofu 占位。 - 修法:Dockerfile 字体安装行加
fonts-noto-color-emoji(+~10MB),与 CJK / WQY 同fc-cache -f刷索引。chromium 支持 COLR/CBDT 彩色 emoji,fontconfig fallback 即正常出图标。纯增量容器改动,不碰对外契约。需重建 sandbox 镜像 + 重启 per-user 容器生效。bump 0.12.8 → 0.12.9。
2026-06-15 / 左栏任务筛选区默认折叠
- 接 2026-06-13「筛选区可折叠」一条:把默认态从展开改为折叠(进页面只见「筛选 ▸」一行,点开才展开)。偏好仍持久化 —— 用户显式展开过(
zcbot.task-filters-collapsed存"0")才默认展开,否则一律折叠。改动:web/static/js/chat.js(默认判定!== "0",onclick 改存"1"/"0")、web/static/js/state.js注释。bump 0.12.7 → 0.12.8。
2026-06-15 / system prompt 按 backend 注入「运行环境」段:纠正平台误报 + 写明禁外网
- 接上两条(--shm-size + mmdc wrapper 修执行层)。再查发现引导层的根问题在 system prompt:
general_v1.md的「平台」段写死 "Windows + cmd.exe",但线上是 docker = Ubuntu 容器 + bash ── 模型被误导在 Linux 里打 cmd 构文(where mmdc 2>nul),且没引导"渲图走本地",模型以为 mermaid.ink 等在线服务能用、反复去试(其实境外被墙,容器有外网但渲图不该依赖出站)白烧 token。 - 修法(引导层,环境事实归 system 而非 skill):
general_v1.md:删写死的 Windows 平台段,改为中立一句"平台以系统消息「运行环境」段为准"。agent_builder.py:_build_system_prompt按 backend 注入环境段 ── docker =_CONTAINER_ENV_BLOCK(Linux/Ubuntu·bash·渲图走本地 mmdc 别调境外在线服务·mmdc/chromium/中文字体已装·mmdc -i x -o y直接渲图·/tmp 可写);host =_HOST_ENV_BLOCK(一行 Windows/cmd 提示,免 general_v1 指向落空)。- 撤回上一条加到 imagegen skill 的渲图引导(环境事实收归 system,不重复)。
- 原则沉淀:全局不变的环境事实(在哪/能否联网/装了啥)→ system(高杠杆,一句省一类试错);具体可选方法/流程 → skill。这是"换"不是"加" ── 删掉的是每轮都发且 docker 下错误的 Windows 段,token 量级相当、信息变对。改动文件:
prompts/system/general_v1.md、core/agent_builder.py、skills/imagegen/SKILL.md、RUN.md。bump 0.12.6 → 0.12.7。
2026-06-14 / mmdc wrapper:容器内裸调 mmdc 自动带 puppeteer config,渲图开箱即用
- 接上条
--shm-size修复。--shm-size只填了"模型自己摸对 config 后那一下能成";模型初始裸调mmdc仍因 chromium 缺--no-sandbox(容器--cap-drop=ALL)直接跪,然后反复试mermaid.ink等在线服务 ── 但那是境外、被墙/不稳(容器虽有外网,渲图也不该依赖出站),实测又一条对话这么烧掉上百 k token。 - 修法(执行层 + 引导层,均不破坏对外契约):
- 执行层 wrapper:Dockerfile 给
/usr/local/bin/mmdc套 wrapper,没显式-p时自动注入-p /sandbox/puppeteer-config.json(含--no-sandbox --disable-setuid-sandbox --disable-dev-shm-usage)。裸调mmdc -i x.md -o x.png一次成;render_diagrams.py等走which mmdc的脚本透明受益。删掉没人读的MERMAID_PUPPETEER_CONFIGenv(mmdc 本就不认它,只认-p)。 - 引导层:imagegen skill「mermaid vs seedream」段加硬引导 ── 渲图直接
mmdc -i x -o y、⛔ 容器禁外网别试 mermaid.ink 等在线 API。
- 执行层 wrapper:Dockerfile 给
- 取舍:没开 first-class
render_mermaidtool ── mermaid 是纯本地计算,zcbot 专用 tool 只留给带 key/计费的能力(seedream/seedance);wrapper(执行兜底)+ skill 一句(affordance 引导)已覆盖,不扩工具面。需 rebuild 镜像才带 wrapper(旧容器没有)。改动文件:deploy/sandbox/Dockerfile、skills/imagegen/SKILL.md、RUN.md。bump 0.12.5 → 0.12.6。
2026-06-14 / sandbox 容器加 --shm-size:修 mmdc 渲 mermaid 挂超时
- 实测一个"生图测试"任务(
caoqianming@foxmail.com)对话:模型裸调mmdc渲 mermaid,自造的 puppeteer config 漏了--disable-dev-shm-usage,chromium 用 64MB 的/dev/shm起不来 → 连试 6 次全超时,烧约 120k token 才绕道 mermaid.ink 出了个 SVG。根因:pool.py的docker run没传--shm-size,容器/dev/shm= docker 默认 64MB(镜像备的/sandbox/puppeteer-config.json虽有--disable-dev-shm-usage,但模型不一定用那份;且mmdc不读MERMAID_PUPPETEER_CONFIGenv)。 - 修法(只做最小 infra,不动模型侧):
docker run加--shm-size(DEFAULT_SHM_SIZE=512m,envZCBOT_SANDBOX_SHM_SIZE/ yamlsandbox.shm_size可配,优先级同 memory/cpus)。从根上让任何 chromium 路径都不再挂,连模型自造的漏 flag config 也能跑。已 running 旧容器需重启 web + idle 回收后新起才带。 - 实测脚本
deploy/sandbox/probe_mermaid.sh(区分 chromium 缺包 vs 纯 shm 超时);诊断脚本scripts/diag_dump_task.py(按 email+任务名 dump 对话)。改动文件:core/sandbox/pool.py、config/agent.yaml、RUN.md。bump 0.12.4 → 0.12.5。
2026-06-13 / 模型选择瘦身:对话模型常驻 + 生图/生视频收进 ⚙ 弹层
#chat-meta右侧原三个带标签下拉(模型/生图/生视频)占满整行。改为高频的对话模型下拉常驻(一眼可见当前模型、直接切),低频的生图/生视频收进一个「⚙ 媒体」弹层(fixed 定位逃出 pane overflow,点开才渲染 select)。meta 行从"3 下拉"降到"1 下拉 + 1 齿轮"。- 行为不变:生图/生视频选中值仍只进
state.imageModel/videoModel、随下条消息 POST 的image_model/video_model发(send 逻辑读 state 不读 DOM,迁移安全);onChangeImageModel/onChangeVideoModel复用。imageModels/videoModels 皆空时连 ⚙ 都不画。 - 改动文件:
dev.html(弹层元素 + CSS)、chat.js(renderMediaModelTrigger / openMediaModelPop + 点外/resize/scroll 关闭)。bump 0.12.3 → 0.12.4。
2026-06-13 / 左栏筛选区可折叠(默认展开)
- 左栏顶部原 4 行固定头把任务列表压矮。把搜索/状态/目录/排序四个筛选控件归到两行
.task-filter-row,标题行加「筛选 ▾」toggle:默认展开,点击折叠只藏 UI(已选条件仍生效),偏好存localStorage(zcbot.task-filters-collapsed),与 pane 折叠同套范式。折叠后左栏顶部从 4 行降到 2 行(标题 + 新建),列表可视区更高。 - 顺手把状态下拉从标题行并入筛选区(原
width:auto→ flex),搜索框给flex:2更宽;目录/排序合一行,去掉独立"排序"文字标签改title提示。 - 改动文件:
dev.html(markup + CSS)、chat.js(toggle 接线 + 复用 LS 范式)、state.js(新增 LS key)。bump 0.12.2 → 0.12.3。
2026-06-13 / 前端 UI 优化:中栏操作收菜单 + 阅读限宽 + 色彩收敛
- 中栏顶栏 5 按钮 → 「完成」+「⋯」菜单:原导出/清空/完成/废弃/删除 平铺,与任务行的
⋯浮层菜单两套范式打架,且破坏性操作(废弃/删除)平铺易误点、移动端挤。改为只留高频「完成」+ 一个⋯,菜单复用taskMenuItems(过滤掉 complete);单一事实源,两处共用。顺带把「清空」在菜单里按run_status也禁用(taskMeta 带该字段,修了之前菜单清空运行中会 409-after-confirm 的小坑)。 - 消息阅读限宽:
.msg由max-width:92%收到min(92%,48rem)(assistant ~60-80 字/行),user 气泡min(92%,36rem);宽屏长文不再满屏铺开难回扫,窄屏 92% 仍生效。 - 色彩负载收敛:语义色由"每个操作一色"改为"颜色=后果"——正向(完成/下载)绿、破坏性(废弃橙/删除红),中性(导出/清空)不着色;移除紫色"清空"与蓝色"导出"。删掉已不存在的顶栏按钮 hover 规则(保留 file-picker 的 sp-copy/sp-move)。
- 改动文件:
dev.html(中栏 markup + 三处 CSS)、chat.js(菜单接线 + renderChatMeta/deleteTask 收口)。未动左栏 4 行筛选头折叠(点 2,行为变化较大,留作下一步)。 - bump 0.12.1 → 0.12.2(patch:UI 重构 + 样式)。
2026-06-13 / 前端小修:导出按钮简写 + 任务菜单加清空 + 移动端 task 可滚 + admin 自适应
- 顶栏「导出对话记录」→「导出对话」:与「清空对话」对齐(
dev.html按钮 +chat.js任务菜单 export 项同步)。 - 任务菜单加「清空对话」项:
chat.js::taskMenuItems新增一条,复用已有clearMessages;disabled 条件!hasMsg与 export 项一致;dropdown 新增.dd-item.act-clear紫色(与顶栏清空按钮 hover 同色)。 - 修移动端 task 列表无法滚动:手机断点把
#pane-left设成display:block,但#task-scroll靠flex:1撑高才能滚 —— 父级非 flex 时 flex:1 失效,列表被overflow:hidden截断不能滚。改body.mv-left #pane-left { display:flex }(flex-direction:column由默认规则给),恢复滚动。 - admin 移动端自适应增强:
admin.html的@media(max-width:640px)补 header 紧凑化(缩 padding/字号、gen-at 时间戳截断)+.card-head/.ctrl允许换行(标题长 + 下拉不再撑出横向溢出)。 - bump 0.12.0 → 0.12.1(patch:bugfix + 样式)。
2026-06-12 / 双层记忆升级为 agent 自管(写入路径)
-
背景:
.memory/(core.md + extended/)存储原语已在,但纯手工维护 —— 系统不往里写,用户也不会主动整理 → 记忆形同虚设。这轮补「写入」与「召回」两条路,不碰存储/DB,不破坏存量.memory/数据。 -
写入 = agent 自管(选型:不引专用工具、不做后台蒸馏):
memory_block把.memory/可写绝对路径锚点 + 一段「记忆维护契约」注进 prompt,契约+锚点常驻(即使记忆为空,解新用户冷启动不知道能记)。agent 学到跨 task 稳定事实就用已有write/edit/grep维护,写前查重、extended 一事一文件 + frontmatterdescription。复用 fs 工具改动最小,人仍可审核手编。 -
召回升级:extended 索引从「读首行当标题」升成优先解析 frontmatter
description(召回依据更准),无 frontmatter 的存量文件退回首行标题(公测期平滑兼容)。 -
docker 路径转译:发现旧 extended 索引注的是宿主绝对路径,docker 下 agent 看到的是
/workspace/...→ 指不到。mem_dir_display按 backend 给 host 绝对路径 //workspace/.memory,与 working_dir 同套转译。 -
改动文件:
core/memory.py(frontmatter 解析 + 契约 + 路径锚点)、core/agent_builder.py(算mem_dir_display传入)、DESIGN.md§3.7 同步心智+语义。单测覆盖 frontmatter 解析 / legacy 兜底 / 空记忆常驻契约 / host·docker 路径。明确不做:向量/RAG、全文搜索端点(正交,要做单开)。 -
前端只读记忆面板(GUI 当眼睛、模型当手):左栏「记忆」按钮(技能旁)开只读 modal 看全貌。取舍:查完业界(Claude 文件式给全套 view+edit;ChatGPT/Gemini 黑箱只给看/删)后定为 GUI 只读 + "改"全走对话(agent 自管已建好)—— "看全貌"是读不是 operation,走 LLM 又贵又只拿转述;"改"走对话 = 单一写入口 + 自然语言 + 不会写坏 frontmatter。后端只加 2 个只读端点
GET /v1/memory、GET /v1/memory/extended/{filename}(路径穿越校验收口在core/memory.py::read_extended_file),零写/删 API。前端新增web/static/js/memory.js+ modal/CSS,复用 skills-modal 同构。契约里补明「用户说记住/改/忘掉是直接指令」。单测覆盖只读视图 / 单篇读 / 文件名安全 / 越界拦截。bump 0.11.1 → 0.12.0(本批含 agent 自管 + 记忆面板,同一 minor)。
2026-06-12 / 进入公测期:对外兼容策略
- 项目进入公测(对外真实用户在用)。
CLAUDE.md「开发阶段心智」从"开发期可随意 break、不写兼容层"翻新为对外契约(用户数据 / DB schema / 对外 API / CLI·env·文件布局)必须向后兼容,仅纯内部实现仍以最优为准放手重构;拿不准 → 当对外契约处理。版本号段同步:公测保持0.x,1.0 留给"对外冻结行为 / 正式 GA"。同条记忆feedback_dev_phase_no_compat一并翻新。bump 0.11.0 → 0.11.1。
2026-06-12(傍晚)修上下文压缩投毒 → run_python 空转报错
- 根因(DB 实测,60 个 task 命中 83 次
[Error] bad arguments to run_python: code or script_path must be provided):core/context.py把旧 assistanttool_call.arguments(>800 字符)压成{"_compacted":true,"original_chars":N,"note":...}marker 发给 LLM。模型在长 doc/ppt 任务里看到几十次"过去的 run_python 长这样",就照葫芦画瓢把 marker 当真实参数原样吐出来 → executor 拿不到 code/script_path → 报错空转。83 次里 61 次是模型仿写 marker(铁证:抓到{"_compacted":true,"original_chars":85}——85<800 压缩器根本不会出手、且缺note字段,压缩器必带 → 只能是模型伪造),22 次是真·空{}。这正是代码里早已为task_progress单独豁免、注释明写"会毒化模型"的同一个坑,只是 run_python 没豁免。 - 修复(方案 A,把 task_progress 特例升级成通用规则):删掉
_compact_assistant_tool_calls/_compact_tool_call_arguments,prepare_messages_with_stats不再压任何 assistant tool_call 参数(去掉old_tool_arg_chars形参与compacted_tool_call_arguments统计)。只压 tool 结果 + skill(省 token 的大头),参数原样留 = 模型看到的范本永远是真实可执行调用,投毒向量连根拔。代价仅个别一次性大参数(如 12KB pptx 脚本)留在历史 1 条消息,不随轮数翻倍。 - 诊断脚本落盘可复用:
scripts/diag_run_python_empty.py(扫最近 task 的报错形态分桶)、scripts/diag_run_python_trace.py(回溯每条报错配对的 assistant 参数)。 - 验证:
tests/test_context_compaction.py改 2 条旧"压参数"断言为"原样保留"+ 去除已删统计键;全量 120 tests OK。bump 0.10.0 → 0.10.1。
2026-06-12(下午)admin 后台增强:目录 + 筛选排序 + 分页 + 导出 PDF
- 目录(TOC)+ 平滑滚动:admin.html 左侧加 sticky 目录(运行态/任务/用户与用量/按模型/各用户用量/存储),点击
scrollIntoView平滑滚到对应区(.anchor { scroll-margin-top }避开 sticky 顶栏);IntersectionObserver 高亮当前区;窄屏目录变顶部横向 chip 条。 - 按模型 / 各用户用量:时间筛选 + 排序:两表从 overview bundle 拆成独立端点
GET /v1/admin/usage/models?range=&sort=、GET /v1/admin/usage/users?range=&sort=&page=&page_size=。range = all/7d/30d(_range_cutoff);sort = cost(按成本)/ tokens(按用量=输入+输出)。各用户用量含零用量用户故时间条件放 JOIN ON(非 WHERE),否则带 cutoff 会把零用量用户挤掉。前端每表一组 range/sort 下拉,改筛选即重拉(用户表回第 0 页);热力色按当前排序维度上色。 - 存储分页:
GET /v1/admin/storage/users?page=&page_size=(bytes desc + user_id 兜底),前端独立翻页;overview 不再含 storage/by_model(只留 runtime/tasks/users/usage 总用量+近7d趋势,固定形态供轮询)。三个独立表各自 fetch、自管 range/sort/page,overview tick 顺手刷新但不丢状态。 - 导出 PDF(客户端打印):顶栏「导出 PDF」→ 现取 overview + models(all/cost)+ users(all/cost top10)+ storage(top10)+ /healthz 版本,填充隐藏的
#print-report后window.print();@media print只显报告、@page边距、表格描边版式。零依赖(不引 jsPDF / 不走服务端 soffice)、中文走浏览器字体、版式完全可控;列表只取前 10(符合需求)。报告版式:抬头(标题/生成时间/版本)→ 运行态 → 任务 → 用户 → 用量总览 → 近7天 → 按模型 Top10 → 各用户用量 Top10 → 存储 Top10。 - 验证:TestClient 跑通 models(range all=6/7d=4/30d=6、sort cost/tokens)、users(range+sort+分页)、storage(分页 42 行);overview 已不含 by_model/storage;admin.js
node --check通过。bump 0.10.1 → 0.11.0。
2026-06-12(上午)
- admin 管理后台(角色鉴权 + 独立监控页,可扩展为管理动作总入口):此前只有共享口令
ZCBOT_ADMIN_TOKEN(仅用于发用户),无"管理员角色"概念,运维指标只打 stdout([stats])无界面。本次落地按角色的 admin 区:① schema:users加role列(user/admin,server_default='user',migration 0009 只加列不动现有数据);② 鉴权:make_require_admin(cfg)先验 JWT(同require_user)再查users.role=='admin',否则 403——role 走 DB 查不进 JWT,改完下次请求即时生效、老 token 不重签;③ 端点:web/admin.py的register_admin_routes挂GET /v1/admin/overview(整组Depends(require_admin)),一次返回 runtime(active_runs/max_workers/sse_subs/rss_peak,读 app.state,与_stats_logger同源)/ tasks(按 status+run_status 计数)/ users(总数+近7d活跃)/ usage(全局总用量+近7d按天+按模型)/ storage(各用户 bytes/file_count+配额)五段,全 GROUP BY 无 N+1;另挂GET /v1/admin/usage/users?page=&page_size=分页返各用户 token 用量(全表 LEFT JOIN usage_events 含零用量用户,cost desc,稳定排序兜底 user_id;cost 全 kind、token/缓存命中仅 chat,与总用量同源)——前端独立翻页、不随 overview 轮询丢页码;④ 前端:独立单页web/static/admin.html+js/admin.js(复用 localStoragezcbot.token与 format 工具,不挂主应用模块图),纯数字卡片+表格不画图、阈值/热力色差(active_runs 逼近 max_workers 变橙/红、磁盘按配额占比变色、cost 列相对热力底色)、响应式(窄屏竖排)、默 10s 轮询(切后台暂停);401/403 给明确提示+回控制台链接;⑤ 入口:/v1/me返{user_id, role},dev SPAenterApp拉一次,admin 才显顶栏"管理"链接(/static/admin.html);⑥ 建用户带 role:POST /v1/auth/admin/create_user+ 登录页弹框加角色下拉,main.py user add --role/ 新增main.py user role --email X --role admin改角色。命名取舍:先按 inspect/dashboard 摇摆,最终定 admin——这页会长出建用户/改角色/配置(磁盘配额等)管理动作,admin 既盖"看"又盖"管"、且与require_admin/role='admin'//v1/auth/admin/*一脉相承;监控总览只是其第一个 tab,后续在web/admin.py续挂/v1/admin/users、/v1/admin/config。已用 TestClient 验:admin→200、非 admin→403、无 token→401;五段聚合对真实数据跑通。
2026-06-11
- 版本号机制(单一事实源 + 前端展示):此前只有
web/app.py写死version="0.8"(仅进 OpenAPI 文档,前端拿不到)。改为core/__init__.py的__version__(当前0.8.0)作唯一来源 → FastAPIversion、/healthz返回{"status":"ok","version":..}、前端左栏底部展示全引它,改版本只动这一行。前端main.jsboot 时无条件 fetch/healthz(auth 豁免,embed/未登录都拿得到)填进#app-version,钉在右侧文件面板底部存储条(.storage-foot)最左、带细分隔线、垂直居中(纯展示不可点;随存储条一起显隐)。不放顶栏:embed 模式桌面端整层 header 被 CSS 隐藏,顶栏点不到;也不放左栏:左栏底部留给后续按钮。CLAUDE.md「文档维护」段已加规矩:每次 commit/push bump__version__(patch=修复/重构/调参/skill、minor=成批新功能/对外行为变化、major=1.0 发版)。 - 并发/线程池轻量监控 + 接管默认 executor(§8.4 落地第 1 步):已上生产后线程池排队此前无观测手段。lifespan 显式建
ThreadPoolExecutor(尺寸复刻 Python 默认min(32, cpu+4),envZCBOT_RUN_MAX_WORKERS可调大)+set_default_executor接管——run 走asyncio.to_thread即用它,这样既能读max_workers判断排队、也成了日后调并发的旋钮(行为不变,只从匿名默认池换成显式同尺寸池;run 与 disk scan/pptx/reaper 仍共享此池,同原默认)。加_stats_logger后台 task 每 60s 采样:active_runs(=len(inflight),含排队中)逼近max_workers即排队、新 run 的 SSE 会卡着不吐 token;刷新峰值时打[stats] new peak active_runs=N max_workers=M(≥max_workers 带[WARN 已在排队]),有负载时打[stats] active_runs=.. max_workers=.. sse_subs=.. rss_peak=..MB,空闲静默不刷屏。RSS 用 stdlibresource(Unix 峰值/high-water;Windows dev 降级跳过),零新依赖;新broker.total_subscribers()给全局 SSE 订阅数。查看:journalctl -u zcbot | grep '\[stats\]'。不做监控界面(运维健康是少数标量、日志够诊断;业务分析数据已落 DB 走 SQL)——界面阶梯见 DESIGN §8.4。 - dev SPA「技能」查看 modal(左侧 rail 底部入口):因
.skills在文件面板隐藏,加左侧 rail 底部「我的资源」分组(#rail-resources,留位给后续「记忆」)+「技能」按钮 → 弹 modal 分「平台 skill / 我的 skill」两组列表,点任一项展开完整 SKILL.md(GET /v1/skills/{name}+ 现有 markdown 渲染),「我的」每项带删除(二次确认 →DELETE /v1/skills/{name},只删 user 源 + 防穿越);覆盖标已覆盖平台同名,load_errors提示未加载的。创建/改/fork 仍走对话。新web/static/js/skills.js(零构建 ES module,main.js import + Esc 栈接入);/v1/skills已带 source/overrides/load_errors。纯查看 + 删除,不在 UI 做创建/编辑(编辑天然对话式)。 - 用户私有 skill(每用户
.skills/,可从零写或 fork 内置再改):SkillRegistry从单目录改多来源(SkillSource列表:内置ROOT/skills+ 用户user_root/.skills),后扫同名覆盖先扫 → user wins;覆盖关系记进user_overrides,discovery 显式标[你的·已覆盖内置](不静默)。Skill加source字段;from_dir区分"无 SKILL.md(静默跳过)"与"有但格式错(抛SkillLoadError)",_scan捕获用户来源的错收进load_errors、注入 system prompt 提示用户修(一个坏 skill 不再崩整次扫描)。容器路径改写从 LoadSkillTool 下沉到 registry(container_dir按source给/sandbox/skills或/workspace/.skills),LoadSkillTool 去掉container_skills_dir参数。关键判断:写 skill 用 host-side typed tool(save_skill/fork_skill,tools/skill_authoring.py)而非 fs/shell —— 因 fs 的 base_dir 锚 cwd(host)/ 容器 wd(docker),都够不到user_root/.skills,跨 backend 不可靠;host-side 工具知道 user_root 一个落点两模式通吃(与 seedream/DocumentDownload 一致范式)。save_skill写时校验 frontmatter(名合法 / YAML 合法 / 有 description / name 一致),fork_skillcopytree 整目录(带脚本)+ 自动把 frontmatter name 对齐新名(否则 fork ppt 仍叫 ppt 会反覆盖内置)。.skills是 dotfile(文件面板隐藏,与.memory一致;validate_task_name已禁.起头 working_dir,天然不撞)。/v1/skills带上用户 skill +source/overrides_builtin/load_errors。新增skill-creator引导 skill。+test_user_skills.py(20 例)+ 改写test_load_skill.py。性能:多扫一目录,没.skills的用户一次exists()跳过;有 skill 仅每 run +1-3ms,不在热路径。
2026-06-10
- system prompt 精简(瘦身 ~40 行 + 媒体段按需注入):
general_v1.md+_build_system_prompt去冗余:① 「宪法」文件命名约定从 ~25 行压到 ~6 行(只留格式定义 + 注入值 + 一行 current/重定调,操作细节本就由 proposal/ppt skill 各自讲,引用仍成立);② run_python「先 write script 再 script_path」指引去重(原模板 + agent_builder 两处 → 合并进模板 1 处,顺带把scripts/子目录约定收进去);③ 媒体工具段(seedream/seedance 红线)从常驻模板抽成_MEDIA_TOOLS_BLOCK,仅ArkConfig.load() is not None(有 ARK_API_KEY)时由 agent_builder 追加——无 key 用户不再背 7 行永远报错工具的说明,且 ark_cfg 提前 load 一次复用给下方 tool 注册;④ 「路径 echo 全形式」段 8 行压到 4 行。通用任务每轮 system prompt 净瘦 ~40-50 行,领域 task 加载 skill 后信息不丢。test_system_prompt_paths仍过。 - 上下文压缩加压力门槛:压缩只在总 chars 超阈值(
caps.reliable_context×0.5×2.5 char/token,flash ≈33 万)时才做,未超则原样发——护 DeepSeek 前缀缓存(短任务字节逐轮一致、全程命中)+ 不白丢旧细节。prepare_messages_with_stats(compact_threshold_chars=),compaction_skipped进事件;默认 0=向后兼容永远压。实测高轮 task 缓存命中已 92-94%,故只补门槛不改滑动边界。+2 测试。 - 单轮停机判据从「步数」解耦为「是否在推进」:
max_iterations从「轮预算」降级为纯安全 backstop(flash 50→120 / pro 100→150),真正掐空转靠两道进展信号——_RepeatGuard逐指纹「无产出重复」累计(SOFT2 注提示 / HARD4 拦截)+ run 级全局_stall(整步所有 tool 无净产出 +1、任一净产出清零,连续 8 步主动停)。撞 backstop / 熔断都 emit「回复『继续』可续跑」提示,不静默停。(诊断:taskb27466a0所谓「中途断」实为撞旧 50 步上限干净停下。) systemctl restart优雅 drain in-flight run:restart 不再硬杀 BG run 致 reaper 误标 error。纯进程内零 DB 改动:lifespan 加draining+inflight登记,先拒新 run(503+Retry-After)再asyncio.wait(drain_timeout)收尾,超时转协作式 cancel。部署强耦合:unitTimeoutStopSec提到 90(必须 > drain+grace),前端发送包退避重试。
2026-06-09
- PPTX 前端在线预览(LibreOffice→PDF,DESIGN §8.3 Stage 1):文件区点
.pptx改在线预览。关键洞察=前端已有 PDF iframe 路径,所以后端把 pptx 转 PDF 即可前端几乎不动。新web/pptx_render.py(soffice 转 PDF,独立临时 profile 绕单 profile 锁 + 缓存.preview/<hash>.pdf+ 超时 kill)+GET /v1/files/preview_pdf(复用鉴权防穿越 + per-path lock + run_in_executor)。转换在 web host 进程不进沙盒;部署装 libreoffice-impress + noto-cjk。 - 药3 复核:
/home/ubuntu/zcbot幽灵路径不复现 + 回归测试钉死:该路径(docker 下 system prompt 焊死宿主路径,容器内找不到致 51 次重试风暴)已于 06-03 修复,复核当前代码 docker 分支只注入容器路径不泄漏宿主路径/uid。加test_system_prompt_paths.py(2 例)防回归。高轮数三味药全部收口。 - ppt skill 补「信息设计纪律」+ 混合背景 + pptx 预览器:深读 pptmaster 后定位 ppt 观感差真因是信息设计纪律(~70%)非 SVG 渲染(~30%)——且这些全是 editable python-pptx 能做的。加
add_takeaway/add_kpi(baseline+delta)/add_source/add_toc+ 组合件add_card_grid/add_timeline/add_cycle+render_bg.py(Chrome 渲 mesh 渐变背景)+pptx_preview.py(渲 PNG 肉眼验观感,当场抓到 set_text 多行只给首段上色的 bug);投影改克制(add_card默认不投影)。未动:SVG→原生转换器(论证零增益)。
2026-06-08
- loop 加病理性重复调用守卫(药1):
_RepeatGuard按(工具名, canonical 参数)指纹跟踪「无产出重复」——结果每次不同(改脚本重跑)算有产出、清零永不误伤;结果是[Error]或一字不差才累计;SOFT2 注软提示、HARD4 拦截。顺带堵_malformed_tool_calls退化成空{}的风暴。+test_loop_repeat_guard.py(7 例)。 - 检索/抓取类 host 工具批量化:DB 实测高轮数烧 token 三股根因(空
{}风暴 / 报错重试 / 检索不收敛)。把web_fetch/document_search/document_download从单数改列表入参、一轮并发处理一批(批内去重 + 单条失败隔离 + 超量截断明示),直接换签名不留单数别名。 - ppt skill 视觉系统升级为卡片式:学 ppt-master 后岔路三选,选 B(升级 python-pptx 设计系统,非自建 SVG 转换器——保留单脚本批量架构、原生可编辑)。
pptx_helpers加 add_card/gradient/kpi 等质感件 + 派生明暗色阶,layouts 扩到 13 版式,quality_check 按色相归桶对齐三色制。 - system prompt 加「少来回」全局原则:互相独立的操作合到一个脚本 / 一轮并发 call(轮数=token 线性乘数),但需看上一步结果的就老实分步。便宜补充(走缓存),不指望动 100+ 轮大头。
- ppt skill 工作流批量化:阶段二从逐页(每页一 run_python,~2N 轮)改成写一个
build_deck.py一次建整 deck + 图标全 deck 批量预取,逐页大纲表替代逐页确认。N 页降到 ~3-4 轮。
2026-06-06
- 前端模块化 Step 2 收官:把 main.js 剩余主体按干净度逐个剥成独立 ES module——layout / auth / preview / files / media / newtask / embed / chat(对话视图,合一个 chat.js 而非强拆 tasks+stream,因二者共享 state.liveRuns + run 生命周期)。main.js 2719→75 行入口;靠 ES live binding 解 main↔模块循环依赖;新增 import/export 一致性 + 从 main BFS 可达性校验。逻辑零改动纯剪切+连线。
- 修 deepseek-v4-flash 大参数工具调用 arguments 损坏 → loop 畸形重试:大参数(7-10K)write/run_python 偶发把碎片错位粘进
arguments致 json 解析失败;真正放大成灾的是 loop 把损坏消息入库 + 每轮重发的投毒级联。_stream_llm改「校验 tool_call arguments 能否 json.loads,不能则丢弃整轮(不入库)重 roll,最多 3 次,最后降级非流式」+ executor 缺参早返友好提示替掉暴露签名的 TypeError。
2026-06-05
- 前端模块化 Step 1:
web/static/dev.html4087 行单文件起步拆零构建 ES module(定方案「1 拆文件 → 2 局部引 petite-vue → 3 永不上 Vue+构建链」)。本步抽 5 个无依赖叶子(state/format/dom/api/markdown),主体落 main.js,app.py加mimetypes.add_type兜底。逻辑零改动。 - 改密码弹框样式修复:
#chpw-modal复用「选入文件」弹框头/体/脚布局,纯 CSS。 - run_python 过程脚本约定
<task_dir>/scripts/:显式写文件再script_path跑的过程脚本落scripts/(可见/可重跑),inlinecode匿名片段维持临时用后即焚。改系统提示 + tool 描述。 - 新增
standardskill(国标/行标/团标起草):核实市面无可复用 skill,据 GB/T 1.1—2020 自建。覆盖三层级(重点对接 CSTM 团标)× 两体裁;渲染复用 proposalrender_docx/render_diagrams;quality_check 对标准误报无跳过开关 → 改 drafting_rules §8 人工 12 条清单。
2026-06-04
- ppt 版式 helper 收进可 import 模块 + 修中文字体没真生效:抽出
pptx_helpers.py(每页import P免默写 150 行 + 治长 deck 坐标漂移);字体修复=set_text同时写 latin=Arial + ea/cs=微软雅黑(python-pptxfont.name只写 latin 是中文不生效根因)。 - ppt
quality_check.py加内容形状重叠检测:纯数值两两包围盒,只检有文字/图片的内容形状(装饰元素天然排除),交叠 ≥25% 才报。 - ppt
quality_check.py配色纳入形状填充色 + 改三色制判定:加_shape_fill_hex,粗阈值「≤5 色」改「非灰阶色 ≤3」(_is_neutral排中性色),否则合规红 deck 狂报假阳。 - 前端顶栏展示用户已用存储:
GET /v1/user/storage(复用user_disk_usage表),右侧文件面板底部钉进度条;不限额只显已用。 - sandbox 容器 env 收编到一处 + shell 也注入:
executor_docker抽_CONTAINER_ENV={PYTHONPATH=/sandbox:/workspace, HOME=/tmp},shell/run_python/fs 三路共用(修 shell 里 import skills 报错 + 只读 rootfs 下缓存写不进的噪音)。纯代码改重启生效。
2026-06-03
- 修 docker sandbox 下 system prompt 焊死宿主路径:docker backend 时工具在容器跑但
_build_system_prompt注入的是宿主绝对路径(容器内不存在),LLM 据此 find 全空。docker 下task_dir+ 宪法 glob 范例换容器路径/workspace/<wd_rel>+ 去掉无意义 cwd 行;host 不变。 - 顺扫清掉 SKILL.md 里残留的宿主路径假设:patent 跨 skill 调 proposal 脚本改兄弟相对路径;research/patent/proposal/ppt 的硬编码
D:/projects/zcbot与废弃旧布局举例改双形态说明。 - 修 ppt 图标缓存写进只读挂载:种子图标库降为只读(glob 读),
fetch_icon.py新拉图标一律-o <task_dir>/assets/icons/(与「产物只写 task_dir」一致)。 - 默认镜像源改清华(pip+apt)/ 腾讯(npm):腾讯 PyPI 吐损坏 litellm wheel(镜像端文件损坏)。
- 回退
ZCBOT_WORKSPACE_DIRenv 覆盖,workspace 落数据盘改用 bind mount:env 覆盖与paths.py锚 ROOT 的相对存储冲突致三家分叉,改 bind mount(/data/zcbot/workspace→ROOT/workspace)。
2026-06-02
- 【已于 06-03 回退】
resolve_workspace加 env 覆盖ZCBOT_WORKSPACE_DIR:prod 想 workspace 落独立数据盘,回退因与相对存储锚点冲突。 - 修 embed 模式「登录页一闪而过」:
<body>首行加同步内联脚本,?embed=1立即加embed-modeclass 赶在#login绘制前隐藏。绘制时机问题非鉴权。
2026-06-01
deploy/update.sh加自更新重跑守卫:git pull改脚本自身时exec用新版本从头重跑(标记防死循环)。deploy/update.sh默认源改腾讯 + build 跳过改--skip-build+ 进度可见:根因=阿里 PyPI 同步滞后缺litellm>=1.83。- 修 MP host 工具的全量下载(IP 被封根因):
mp_search_summary没传分页致每搜一次整库级下载被 MP 判 abusive 封 IP;改num_chunks=1服务端限量。(宿主 IP 仍需邮件 support 解封。) - 加一键部署脚本
deploy/update.sh(Ubuntu/systemd):git pull → pip → db upgrade → docker build → restart → curl /healthz;钉死两点:migration 从 .env 抠ZCBOT_DB_URL、build 必须在 restart 之前。 - sandbox 镜像加中文字体:Dockerfile slim 起一个 CJK 字体没装致 matplotlib/mermaid 出中文方块,加
fonts-noto-cjk fonts-wqy-microhei。 - documents / Materials Project 带 key 能力改 host-side tools,key 不进 sandbox:新增
tools/documents.py+tools/materials_project.py,仅宿主 env 有 key 时注册,写文件绑 task_dir。 - 删
skills/pymatgen/materials.py::mp_rester():sandbox 内读 key 的旧入口,host tool 化后多余且违背「key 不进 sandbox」。
2026-05-29
- Seedream 5.0 i2i base64 通路 probe + DESIGN §8.1 落册:实测
/images/generations接受 base64 data URL → 内网部署无需对象存储中介。选 E+C 组合,本版仅 probe + design,tool 改造未启动。 - web 端 tool_call 标题行改显中文活动描述:修读错字段(
argumentsvsargs)+toolActivityLabel按 12 工具套中文动词。
2026-05-28
skills/review/SKILL.md加「长文档处理」段:阶段 1 骨架扫描(停下等用户挑章节)→ 阶段 2 分章深审 + 中间文件落盘。- 新增
config/models/local.yaml(family=local,r1/qwen3)接内网 OpenAI 兼容服务,涉密专用:关键thinking_mode=false(R1/Qwen3 天生推理,发 reasoning_effort 本地 vLLM 多半 400);不改默认模型。qwen3 跑通,r1 调试中。 - 修
LoadSkillTool在 docker backend 返 host 绝对路径:加container_skills_dir参数,docker 时返/sandbox/skills/<name>,references-heavy skill 自动 work。 - 新增
analyzeskill(科学问题分析/拆解/引导):四段式 PICO→Issue Tree→分支(Fishbone/First-principles+TRIZ/DoE)→路线图,定位协调器不执行任务,接力下游 skill。 - Python 3.10→3.12 升级(host + Dockerfile):mp-api 依赖链
NotRequired(3.11+)在 3.10 import 不进;顺手修executor_dockerPYTHONPATH/workspace→/sandbox:/workspace。 - 新增 3 个科学计算 skill(pymatgen / stats_ml / plot_pub):服务无机非金属材料 R&D。pymatgen 带
CEMENT_PHASES中英文相名映射 50+;stats_ml 纯指南;plot_pub 带apply_pub_style()出版级中文字体 fallback。挑 4 个 ★★★ fork 单装。 - DESIGN §7.5 增「image 体积 / 多 user 资源 / 后续加包策略」决策段:① image 大 ≠ 吃更多 RAM(layer 共享);② 多 user 瓶颈在并发 exec 不在 idle 容器;③ 新增依赖走「base 收敛 + per-user 持久化 venv + 使用频次沉淀」。
2026-05-27
- ppt skill 歧义反问 + general_v1 加「产物形式歧义先问」通用原则:「汇报方案」被误路由成 PPT,改先反问「PPT 还是文档」并升格到 system prompt 让新 skill 继承。
- ppt skill description 收紧路由:改显式白名单(PPT/幻灯片/.pptx/slide/deck)+ 显式反例(报告/文档/纪要不触发)。
- skill 热更新:
/v1/skills每次现扫:原启动扫一次须重启;改每次现扫(~3ms)。
2026-05-25
- dev SPA 前端依赖 CDN 本地化 + 升级稳定版:markdown 渲染(marked/dompurify/highlight.js)从 jsDelivr 改本地
vendor/,避免内网/跨境 CDN 抖动;test_static_vendor.py回归。 - dev SPA 一批上传/布局交互打磨:三类上传入口改 XHR 显进度 + 粘贴 chip 可预览可删;三栏右栏折叠 + 分隔线拖拽调宽(LS 持久化)。
- 接入博查 Web Search + Web Fetch:
web_search.py(Bocha,仅 env 有 key 挂)+web_fetch.py(httpx + html2text,SSRF 内网屏蔽)。
2026-05-22
- dev SPA 加 iframe embed 模式(
?embed=1&parent_origin=):父页 postMessage 握手拿 JWT,event.origin双向白名单,PLATFORM_KEY不下发浏览器;web/EMBED.md对接手册。 - embed 模式接受
task_idURL 参数定位 task:首次签发后selectTask,once标记只生效一次(401 重签不重置用户中途切的 task)。 - 媒体生成每账号每日配额(yaml 可配,默 20 图/5 视频):
check_daily_quota按服务器本地今日计,超额返中文提示不烧钱;tool 返串不贴 yaml 路径防泄漏 schema。 - 对外路径协议刚性化:
general_v1.md规定助手 echo 产物路径用 user_root 相对全形式<wd_name>/<rel>(简写致 chip 失效),跨产物 skill 统一;UI 一次性兼容历史简写。 - 豆包 Seedance 2.0 Fast 视频生成接入(文生视频)+ videogen skill:
tools/seedance.py(ark 建任务→轮询→download mp4,失败/cancel 不计费);build_agent 加video_variant+ cancel_check;skill BLOCKING 门槛更严(¥4 vs ¥0.22)。phase 1 仅 t2v,fast 上限 720p。 - dev SPA 移动端自适应 + 交互打磨:手机两档断点(平板 rail / 手机单列
.mobile-tabs,100dvh解 iOS、输入 ≥16px 防缩放);chat-input 支持 Ctrl+V 粘贴上传。
2026-05-21
- dev SPA UI 打磨:修 primary 按钮 hover 文字消失;4 个 modal 抽
.modal基类(style 589→522 行);新建任务/filter 工作目录回原生<select>+ sentinel + 二级 input(combobox 试过推翻)。 - sandbox 阻塞地位写进 DESIGN(§7.7/§7.8/§7.9):
shell.py::BLOCKED_PATTERNS是 trivial-bypass 装饰品(命令注入图灵完备),不继续加规则;正确防线在 OS 层 §7.5。本地 dogfood blast radius 限自身;SaaS 外部开放才 hard prereq。 - system prompt 注入 task 预选 skill 提示:
_build_system_prompt加task_skill参数,与 general_v1「对应 skill 先 load」组合 → 主动 load。否决完整 SKILL.md 预注入。 - imagegen skill 加 ⛔ 调 tool 前必须贴 prompt + BLOCKING 等确认:把模型脑内装配摊到对话层让用户最后过一眼防白烧 ¥0.22;诊断五维→六维加比例/尺寸。
- 新增 imagegen skill:单文件五步法(诊断模糊度→给推断+待确认→拍板→装配 prompt→调 seedream);mermaid vs seedream 选型三段式。
- 登录页加「管理员添加用户」入口 + 删 chat meta 条/tok 显示:
create_user(CLI/web 共用)+POST /v1/auth/admin/create_user校验ZCBOT_ADMIN_TOKEN。否决 User 表加 is_admin 列。 - 新增 documents skill(内部材料学科知识库 document_search API):四函数 Bearer 认证,search 返整篇 Markdown,反模式约束只 print 前 300 字防爆上下文;库=7 材料学科英文论文 100W+ 文件 + 跨语言语义检索;与 research(OpenAlex)互补。
- dev SPA SSE 客户端重连:
fetchSse拆 consume + 重连壳(1/2/4s 退避 ×3);后端stream_events入口检 run_status 非 running 立即吐 done 关流。 - research skill fetch_pdf 改走静态直链 + list 端点加直链 + pg_trgm GIN 索引:绕开 paper_pdf_view 路径 bug;
?search30s→几十 ms;SKILL 加「XML 优先 PDF」。 - 顶栏 token 累计修(
sync_task_tokens改走 messages SUM):切 streaming 后内存计数器永不更新,改现算 + backfill。 - 同 wd 并发软警告 banner +
/v1/tasks加run_status筛选:Claude Code 同款「信任 + 软警告」;否决硬挡/short_id 全产物隔离/clone task(DESIGN §7.9)。 - paper_server → research skill:范式判断走 skill(非 tool/MCP/裸 httpx),
skills/research/{SKILL.md,paper.py}三函数。
2026-05-20
- dev SPA artifact chip 演进:对话内 tool_call/result 挂产物 chip,门控落到产物工具白名单
ARTIFACT_PRODUCING_TOOLS={seedream,seedance},assistant 正文走 seenRels 去重 + allowInlineMedia 防二次 inline。 - CLAUDE.md 加「实施前先对方案」段。
- loop.py tool message 补
name字段 + backfill 历史:OpenAI tool spec 本有 name,缺它致历史回放无 banner/chip。 - dev SPA 输入区删上传按钮 + 加「✨ 润色」按钮:
POST /v1/tasks/{id}/optimize_prompt走 task model 装配 LLM,计费kind=prompt_optimize不污染顶栏。 - 顶栏加生图模型下拉 + 中间产物图片/视频内联展示:
GET /v1/image_models扫 yaml,build_agent(image_variant=);renderArtifactBarHtml走 blob URL inline,切 task 回收 blob。 - LLM 调用切 streaming(cancel 秒退)+ 发送/停止合并单按钮:
chat_stream(stream=True, include_usage=True)+stream_chunk_builder拼回,chunk 间 poll cancel;前端打字机靠 emit text delta。 - dev SPA seedream tool 透明性 banner:tool 返串首行
[seedream] model=...· cost=¥...,前端正则 parse 挂折叠徽章。 - 豆包 Seedream 5.0 接入 + 0007 cost_usd → cost_cny 全表统一币种:
tools/seedream.py走 ark_client,产物落<wd>/figures/;0007 全表 ×7.2 折 CNY;仅 ARK_API_KEY 设了才挂。 POST /v1/files/delete加recursive+ 顶层目录 task 引用闸:recursive 走 rmtree;顶层目录被 task 引用 → 409。- fs tool 输出渲染 user_root-relative 路径:
Tool加user_root+_display(p),消 chip 404 + 防 uuid/部署根泄漏。 POST /v1/tasks/{id}/clear清空对话:同事务 lock + 检 running + DELETE messages + reset 三列累计;usage_events 全不动(账单 source of truth)。
2026-05-19
- 0006 模型切换(c 模式 task 级 A 粒度)+ usage_events v2 表:
tasks.model_profile变 source-of-truth,顶栏下拉 PATCH 下条 send 生效;message 历史按messages.model_profile画切换点;usage_events 重建多态形态(units jsonb,chat 已接入)。 - dev SPA 登录撤回邮箱+密码,删 invites 表:「邀请码 env→invites 表」一日游撤回;复用 users.email + bcrypt;dev SPA 双 tab 登录。
- SENTINEL user 彻底撤(数据 + 代码):web 必走 JWT 后 sentinel 无角色;CASCADE 删 + 10 处代码删;
build_agent加*让 user_id 必填。 - 任务/文件行
⋯下拉菜单 + tool_result debounce 刷新右侧:单例浮层菜单(position:fixed)避 pane overflow 裁剪;tool_result debounce 500ms 刷新文件 panel。 - proposal skill mermaid 强制 + quality_check 加图相关 4 拦截:模型曾写满 ASCII 字符画;render_diagrams caption 强制必填+唯一,quality_check 加四条;
/v1/files/download加Cache-Control: no-cache。 - dev SPA 文件预览弹框:点击不再直接下载,90vw 模态按扩展名分派(image/pdf/text/md + docx-preview + SheetJS);vendor 入 git(~1MB)。
2026-05-18
- 入口归位:
cli.py→main.py,原main.py→core/agent_builder.py,删 CLI REPL,§7 E 撤:main.py原混三角色按 SoC 拆,CLI 只剩db/probe/web/user;dev SPA 已是 dogfood 主路径,REPL 无双 transport 维护税。 - 0004 schema 大瘦身:删 runs / usage_events 旧版,合 run_status/run_error 入 tasks;路由 run_id→task_id:单活 run 形态客户端只需 task_id。
POST /v1/files/rename+ 顶层目录 delete 加 task 引用闸:/v1/files/*升格唯一目录树 mutation 入口,DB-FS 一致性服务端内化;顶层目录走 DB-aware 分支(SELECT FOR UPDATE + 409 + check_no_subtask + UPDATE 先于 FS)。- task 级 cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮:Broker 加 per-task
threading.Event;Loop 加 cancel_check +_fill_cancelled_tool_results;同步 call 本身不可中断(后接 streaming 修)。 POST /v1/tasks/{id}/messages单活 run 锁 + 孤儿 reaper:同事务 SELECT FOR UPDATE + 活跃检查 + 标 running 三步原子;lifespan reaper 清 crash 孤儿。- proposal skill 流程图/结构图管线:
render_diagrams.py扫 mermaid → mmdc/mermaid.ink → png;render_docx 识别+ mermaid 围栏;图编号递增。 - system prompt skill 机制改「可选辅助」:简单问答/读代码/改 bug 不必硬套 skill;接 GET /v1/skills 下拉。
GET /v1/skills+ dev SPA skill 字段改下拉;dev SPA 全套 UI 中文化(技术字段不动)。
2026-05-17
- 0003 schema:name + working_dir + skill 三件套:任务标识与工作目录解耦;
TRUNCATE tasks CASCADE+ 加name NOT NULL;GET /v1/folders给 datalist。 GET /v1/tasks分页 + 多维筛选 + ordering:{page,page_size,count,results}+ 6 query;allowlist 防注入;默认-created_at。- task 硬删 API + dev SPA delete + 文件 per-row 删:
DELETE /v1/tasks/{id}删 DB 行(messages CASCADE)+ FS task_dir 不动(同 name 多 task 共享)。 - files API 全面 user-rooted + dotfile 过滤;files 面板 UX 项目名 + 修 root crumb bug。
- task_dir 改 eager mkdir:
build_agent+create_task都mkdir(exist_ok=True),name=项目声明。 - task = name-based 项目目录 + memory dotfile:废 UUID 派生 +
tasks/中间层;task_dir = workspace/users/<uid>/<name>/,同 name 共享;memory 搬.memory/;validate_task_name拒.起头。
2026-05-15
- §7 D 阶段
/v1JSON API 落地;Phase G Jinja2/HTMX UI 路线撤:删 templates + jinja2/markdown-it-py/pygments;SSE event 由 HTML 片段切 JSON;dev.html 留作 dogfood 主路径。 - §7 D' 过渡 auth(PLATFORM_KEY → JWT)+ dev SPA:pyjwt HS256 +
AuthConfig.from_env()fail-fast;数据隔离全Task.user_id == user_id;SSE 走 fetch + ReadableStream 手解。 - task_dir 改相对存储:DB 存 ROOT 内→相对 posix;
core/paths.py::{ROOT,to_db_path,from_db_path};alembic 0002 backfill;CLAUDE.md 加「开发期不写兼容层」。 - workspace 布局统一 per-user + litellm 启动 cost map 网络警告兜底(
LITELLM_LOCAL_MODEL_COST_MAP=True,冷启 ~5s→<1s)+ Phase G Jinja2/HTMX Web UI 全撤(沉淀的 sink/broker/no-subtask/files 安全归一保留)。
2026-05-14
- §7.1 心智模型修正:Folder-centric → Task 一等公民 + Dir 文件副视图:dir 不是 task 父容器,双视图正交。
- §7 B Steps 1-4 + 6:SQLAlchemy 2.x ORM(5 表)+ alembic +
cli db;state.json全废入 PG;check_no_subtask查前缀嵌套。
2026-05-12
- §7 改写:platform/core 多租户方案废弃,改 user-direct(folder-centric → task-primary;task/messages 入 PG;no-subtask;hard cascade)。
历史(2026-Q1 → 05-11)
- Phase 1-4:骨架 / 三 skill / run_python / Model Profile + Probing;ppt v3 商务红 + apply_brand + Iconify;素材摄取改 markitdown CLI。
- 05-06 → 05-11:Phase 6 部分(task + state.json + tokens 累计);TUI rich +
/resume;DESIGN §7 初版(05-12 重写);cli.py export+core/export_docx.py;atomic_write_text+core/memory.py;loop 事件流化铺 SSE 路。 - 06-04 token 优化启动(DESIGN §8.2):chat usage 记 cache hit/miss + LiteLLM cost=0 时按模型档案 CNY/Mtok 兜底;
run_python加script_path模式;core/context.py压缩旧 tool / load_skill / assistant tool_call arguments 不改持久化历史;llm_startSSE 输出context_*压缩统计。 - 06-05 记账给缓存命中折价:发现 task
tokens_in88.6% 是缓存命中却按全价记致虚高 2-3x;ModelCapabilities加cache_hit_cny_per_mtoken,成本公式拆三段;前端 hover tooltip 显真实成本 + 命中率(分母改 usage_events 同源,恒 ≤100%);历史走 backfill 脚本。
关键决策与偏差
| 项 | 决策 | 备注 |
|---|---|---|
| 工具基目录 | cwd(读)+ working_dir(写) | system prompt 同时注入两者绝对路径 |
| Workspace 布局 | workspace/users/<user_id>/{.memory/, <name>/} |
per-user 隔离;memory dotfile 防撞;同 name 多 task 共享 |
| Eval Suite | 不做 | 个人工具 dogfooding |
| 版本化 prompt | 直接 general_v1.md |
真要切再做 |
| run_python 沙盒 | subprocess + env 过滤 | Docker 在 §7 C 阶段 |
| 兼容层 | 开发期不写 | DB schema / 字段 / API 改动直接切,见 CLAUDE.md |
/v1/files/* 与 DB |
files API 作目录树唯一 mutation 入口,DB-FS 一致性服务端内化 | rename / delete 顶层目录 DB-aware |
| 单活 run | task 同时最多 1 个活 run | gate 在 post_message 同事务 SELECT FOR UPDATE |
| LLM 调用走 streaming | chat_stream + stream_chunk_builder 拼回;chunk 间 + tool_call 间 poll cancel |
cancel 延迟 100ms 级;content delta 即时 emit |
| 上下文压缩 | 加压力门槛,超阈值才压 | 护前缀缓存 + 不丢旧细节(§8.2 / 06-10) |
| 停机判据 | max_iterations 降为 backstop,靠进展信号掐空转 | _RepeatGuard + run 级 _stall(06-10) |
文件清单
core/capabilities.py 75 ← 模型档案增加 CNY/Mtok 计费兜底字段
core/llm.py 151 ← litellm 离线 cost map env + chat_stream(stream + include_usage)
core/loop.py 300 ← sink.emit + _stream_llm(chunk 间 poll cancel + emit delta)+ _RepeatGuard + usage cache 明细
core/context.py 95 ← LLM 调用前压缩旧 tool / load_skill 消息(带压力门槛),保 tool_call 协议字段
core/sinks.py 101
core/paths.py 50 ← task_dir db form 归一
core/probe.py 243
core/session.py 153 ← ORM
core/task.py 82 ← PG-backed TaskState
core/skills.py 180 ← 多来源 registry(SkillSource)+ source 标记 + 覆盖感知(user wins)+ load_errors + container_dir
core/memory.py 81 ← per-user `.memory/` dotfile
core/export_docx.py 383
core/storage/{__init__,engine,models,usage,utils}.py ← 4 表(0004-0007 演进);record_chat/image_usage
core/ark_client.py 105 ← 火山方舟 HTTP 客户端
core/agent_builder.py 340 ← 装配 lib(有 ARK_API_KEY 才挂 SeedreamTool);build_skill_registry 装两来源
core/executor.py / sandbox/{network,pool}.py / executor_docker.py ← Executor ABC + Docker per-user 容器池
tools/{base,fs,shell,run_python,skill_tool,skill_authoring,seedream,seedance,web_search,web_fetch,documents,materials_project}.py ← skill_authoring=save_skill/fork_skill(host-side 写 user .skills)
main.py ~210 ← 入口:web / db / probe / user / sandbox check
db/migrations/versions/ 0001-0008
web/app.py ~1360 ← /v1 JSON API + user_id 隔离 + run lock + cancel + files + pptx 预览 + skills(列表/正文/删)
web/auth.py ~190 ← 邮箱密码 + platform_key → JWT
web/broker.py / sinks.py / pptx_render.py
web/static/dev.html + js/*.js ← dev SPA 拆 15 个零构建 ES module(main.js 入口;skills.js=技能查看 modal)
web/static/vendor/ ~1 MB ← jszip / docx-preview / xlsx
─────────────────────────────────
Python 合计 ~3400 行(+ dev SPA + vendor 1MB);加 skills 脚本 + 配置,总仓库约 3800 行
下一步候选(性价比排序)
- CORS 收紧(~半小时,已接入真实用户应尽快做)——
allow_origins从*改读 env 域名 allowlist;与 OIDC 解耦。- 真 OIDC(~1 天,选做)——
/v1/auth/login内部换 ID token 校验(路由层 Depends 不动);邮箱密码长期保留并存。platform_key 信任模型可接受则可延后,真要弃 PLATFORM_KEY 共享密钥时再做(延后无技术债)。
- 真 OIDC(~1 天,选做)——
- §7 C Executor + sandbox 收尾(~3-5 天,按 §7.5 落地清单)—— 剩 Step 4 完整 egress proxy + Step 3b PGID kill 协议 + xfs project quota OS 层硬化。Stage C DoD = 6 条落地清单全完成 + 红队回归通过(metadata IP / PG IP block、残留进程清理、跨 user 网络隔离、egress allowlist)。多用户在线跑代码 hard prereq。
- Phase 6 context 第二步 task summary(~1 天)—— 旧消息压成一条 summary(区分硬约束/计划/文件路径/关键事实),不直接塞回旧 tool 原文。
§7 B + D + D' + 单活 run 锁 + cancel + 0004 schema 瘦身 + 入口归位 + Stage C 主体已完工。剩余:真 OIDC → C 收尾 → F(deploy/billing)。§7 E CLI 双模式撤;Phase G Jinja2/HTMX 撤(详见 DESIGN §7.9)。