zcbot/PROGRESS.md

28 KiB
Raw Blame History

实施进度

配合 DESIGN.md。本文件只记 phase 状态、决策偏差、文件量、下一步。每条 1-2 句:做了啥 + 关键判断;细节查 git log / git diff / DESIGN §7.9

最后更新:2026-05-21(system prompt 注入 task 预选 skill 提示)


状态

Phase 标题 状态 备注
1-3 骨架 + Skill + run_python 三个 skill;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(Executor)待。

已完成关键能力

2026-05-21

  • 新建任务弹窗 combobox 从 datalist 改自定义 dropdown(modal + 顶部 filter-wd 双处):接上一条迭代 —— <input list="folders-datalist"> 在 wd 因联动有了值之后,浏览器按前缀过滤把下拉过滤没了,用户只能点右侧三角看完整列表,体验比原 <select> 还差。改方案:抽 makeFolderCombo({input, panel, onPick}) 工厂函数 —— input 旁加 absolute 定位的 .combo-panel,focus/click 弹出完整列表、input 时子串过滤(不再前缀)、点击项填入(mousedown preventDefault 阻止 input blur 提前关 panel)、blur 延迟 120ms 兜底关闭、键盘 ↑↓ Enter Esc。.combo / .combo-panel 样式提到全局。modal nt-wd-sel 和顶部 filter-wd 都走该工厂,各自传 onPick(modal 置 wdManuallyEdited + updateWdHint,filter 走 loadTaskList)。datalist 元素 + loadFolderSuggestions 灌它的逻辑全删,ensureFoldersLoaded 改用 state.folders 是否定义判断。
  • 新建任务弹窗工作目录改 combobox + name 联动:web/static/dev.html modal 里 nt-wd-sel<select> 改成 <input list="folders-datalist">,删 + 新建目录… sentinel + 二级 nt-wd-new 输入框;加 wdManuallyEdited flag —— name 输入时若 flag=false 自动同步到 wd(programmatic 改 value 不触发 wd input 事件不会假阳性),wd 非空输入置 flag=true 脱钩,wd 清空重置 flag=false 但保持空(避免 backspace 想换名字时被立刻填回打断);submit 保留 working_dir || name fallback 兜底空值。loadFolderSuggestions 不再渲染 select options,只灌共享 datalist + 缓存到 state.folders 供 hint 比对"命中已有/新建"。label 文案 (可选,留空 → 用任务名...)(默认跟随任务名;可输入新名或选已有目录复用),更直观。
  • system prompt 注入 task 预选 skill 提示:core/agent_builder.py::_build_system_prompttask_skill 参数,非空时在"工作目录与 task 上下文"段加一行 - **task 预选 skill**: \` — 用户创建时声明的主 skill;空字符串走老路径,prompt 字节级一致。LLM 拿到这条事实 + general_v1.md:17-23已有的"对应 skill 领域先 load_skill" 规则自然组合 → 主动 load。否决"直接把完整 SKILL.md 预注入 prompt"方案 —— 那会把tasks.skill` 从 metadata 升格成 binding,需要同步改 DESIGN.md / 想清楚 PATCH 改 skill 的语义,投入产出比不划算;轻量提示保渐进披露三层架构不动。
  • imagegen skill 加 调 tool 前必须贴 prompt 给用户 + BLOCKING 等确认硬约束:用户反馈之前流程"模糊就问"不够,清楚的描述也可能模型脑里和用户脑里对不上,事后看图才发现白烧 ¥0.22。改:① 顶部流程一句话加" 把 prompt 完整贴给用户看 + 问改不改 → 用户明确确认后 → 调 seedream"步骤;② 加「调 tool 前的强制门(铁律)」段定义回复分类(可以/OK/画吧/嗯 算确认;改 X → 重贴重等;沉默/追问别的 → 继续等;模棱两可 → 追问到明确);③ 加「调 tool 前再过一道」段给具体贴 prompt 的对话格式(代码块 + 参数清单 + 预计花费 + 一句"开烧?改什么?");④ 调用范式段加"前置条件:已拿到明确确认才调";⑤ 反模式加两条(没贴就调 / 模棱两可当确认)。本质是把"模型脑内装配"摊到对话层让用户最后过一眼,装配 ≠ 授权调用。:用户反馈 skill 缺图片比例引导。原 SKILL 里 size 表写"比例只能正方形"是基于 doubao.yaml + tool 参数描述只列三个正方形例子的间接推断,无验证。改:① 诊断五维 → 六维,加"比例/尺寸"(ppt 16:9 / 海报 9:16 / 头像 1:1 / 公众号 2.35:1 / 书籍 3:4);② 一次性追问范式加比例项,上下文推断里给"做 ppt/海报/公众号/学术示意"四种用途的默认比例;③ size 参数表重写成"按用途选比例,再选分辨率",列常见 size 参考值 + 明确"非方形是按比例算的参考值,豆包是否原生支持需首次小调用验证";④ 失败解药表加比例错(改 size 不动 prompt)+ API 报错回退默认两条;⑤ 反模式加"不问比例就默认走 yaml 1:1"。承认 unknown:豆包 5.0 实际支持哪些非方形 size 没验证,首次用错就回退默认 + 让用户协商,不臆造。:两根因 —— ① general_v1.md 「媒体生成工具」段把 seedream 写成一级直觉(列了"画/出/来张"等关键词 + 直接调 tool 的 how-to),压过 skill discovery block 的微弱声音;② imagegen description 关键词覆盖窄(没有"画/绘制/艺术图/图片"等朴素词)。修法:system prompt 那段改成"调 seedream 前必须先 load_skill('imagegen')",细节判断全移到 skill 里,只留 ¥0.22 计费 + 不装饰生成 + 不连发三条兜底硬约束;imagegen description 扩 17 个触发词(画/绘制/出图/来张/艺术图/写实图/场景图...)。两层联动:一级 prompt 指引到 skill,二级 description 提匹配概率。
  • 新增 imagegen skill(引导用户说清楚生图需求):skills/imagegen/SKILL.md 单文件(参考 coding skill 范式无 scripts/references)。核心是"先诊断模糊度 → 一次性给推断 + 待确认项 → 用户拍板 → 装配 prompt → 调 seedream tool"五步法,防止用户一句"画个 XX"就直接烧 ¥0.22。五维清单(主体/场景/风格/构图/光线)缺 2 维以上就先问;mermaid vs seedream 选型给"默认倾向 mermaid + 反向选 seedream 信号 + 模糊时主动一句话问用户"三段式(没在 system prompt 那段流程图优先 mermaid 上一刀切,留 skill 层细化判断);size/watermark/search 默认值取舍 + 失败不复发的解药表 + 8 条反模式。seedream tool 本身不动,skill 仅是流程引导层。
  • 登录页加"+ 管理员添加用户"入口 + 删 chat meta 条/tok 显示:web/auth.pycreate_user() helper(CLI/web 共用,避免漂移)+ AuthConfig.admin_tokenZCBOT_ADMIN_TOKEN env 读(未设 → None);web/app.pyPOST /v1/auth/admin/create_user 校验共享口令后落库(503/403/400/409 分支);前端 dev.html 登录卡片右下加 ghost link + 弹窗(email/密码/管理员口令),成功后回填邮箱到登录表单提示"已创建请登录",不自动登录;同时删 chat 顶栏 ${n_messages} 条 · ${tokens} tok 一行(与左 task 列表重复)。否决"User 表加 is_admin 列 + 管理员 JWT"方案 —— 开发期成本不划算,env 共享口令(类 PLATFORM_KEY 范式)够用。
  • 新增 documents skill(内部材料学科知识库 document_search API):skills/documents/{SKILL.md, client.py},四函数 list_kb / search / download / health;走 https://ai.ctc-zc.com:8100/api Bearer 认证,env DOCUMENT_SEARCH_API_KEY + DOCUMENT_SEARCH_URL(可覆盖);search 默认返 md_content(整篇 Markdown 50K-200K 字符级),SKILL.md 反模式约束"只 print 前 300 字"防爆上下文;smoke 验证发现库实质是 7 个材料学科预收的英文学术论文(胶凝/陶瓷/玻璃/晶体/复合/耐火/检验检测,21W+ 文件)+ 跨语言语义检索,SKILL.md 据此校准(原写"主语料中文"是错的);与 research(OpenAlex)互补,documents 已 Markdown 化对 LLM 更友好,但仅覆盖材料领域。
  • dev SPA SSE 客户端重连(覆盖 --reload 抖动):fetchSse 拆出 consumeSseStream + 包重连壳(1s/2s/4s 退避,最多 3 次);reader EOF 未见 done/error 算异常关流触发重连;后端 stream_events 入口检 tasks.run_status,非 running/cancelling 立即吐 done 关流(否则进程重启后新 broker 内存空,客户端会无限挂 ping)。3 次仍失败 → 卡片末尾红色"连接已断开,请重发"。断开期间 LLM delta 丢失,接受。
  • research skill 三次迭代 fetch_pdf 改走静态直链:fetch_pdffetch_xml 同范式,从 paper["pdf_url"] 流式下载,绕开 paper_pdf_view 路径 bug(disk 路径计算错);smoke 5/5 PASS。
  • research skill 二次迭代 list 端点加 pdf_url / xml_url 直链 + 新增 fetch_xml + pg_trgm GIN 索引:serializer 后端拼直链(避免 LLM 拿 stale URL),0006_pg_trgm 给 title/first_author/institution 加 GIN 把 ?search=xxx 从 30s timeout 降到几十 ms;SKILL.md 加"XML 优先 PDF"原则(XML 已结构化免 OCR)。
  • 顶栏 token 累计修(sync_task_tokens 改走 messages SUM):5/20 切 streaming 后 LLM.TokenCounter 内存计数器永不更新;删 TokenCounter 整个类,sync_task_tokensSELECT SUM(tokens_in/out) FROM messages WHERE task_id=? 现算;backfill 4 个 task。
  • 同 wd 并发软警告 banner + /v1/tasksrun_status 筛选:Claude Code 同款"信任 + 软警告"范式;selectTask + SSE 收尾两点拉同 wd running task,黄底 banner 提示邻居;task header 📁 wd 仅在 name≠wdName 时显示。否决了 γ 硬挡 / short_id 全产物隔离 / clone task 三方案(详见 DESIGN §7.9 2026-05-21)。
  • paper_server → research skill:范式判断走 skill(非 tool / 非 MCP / 非裸 httpx),skills/research/{SKILL.md, paper.py},三函数 search / get_paper / fetch_pdf;run_python 注入 PYTHONPATH=base_dir 让子进程能 from skills.research.paper import;paper_server 侧补 retrieve 端点 + PaperFilterSet + serializer 加 abstract。

2026-05-20

  • dev SPA chip 二次校准:工具 I/O 走产物白名单(seedream/seedance);助手正文 echo 路径无条件挂 chip 绕开 seenRels + 强制 allowInlineMedia=false(防同图二次 inline)。
  • chip 维度解绑产物工具白名单 + renderArtifactBarHtmlallowInlineMedia 参数:gate 降级到"图片/视频是否 inline"层,chip 不再受产物白名单限。
  • loop.py tool message 补 name 字段 + backfill 历史:OpenAI tool spec 本来就有 name,缺它导致历史回放无 banner / 无 chip;一行 fix + 幂等脚本回填 17 条。
  • chip 抽取改产物工具白名单门控:ARTIFACT_PRODUCING_TOOLS = {seedream, seedance},grep/read/shell 等通用工具结果里 echo 的路径不再误挂 chip;assistant 正文不门控(seenRels 兜底)。
  • dev SPA 输入区删上传按钮 + 加" 润色"按钮:POST /v1/tasks/{id}/optimize_prompt 同步走 task.model_profile 装配 LLM,meta-prompt 含当前模型 + image variant 元数据;execCommand 插入接入 textarea 原生 undo 栈;计费写 usage_events.kind="prompt_optimize",sync_task_tokens 不污染顶栏。
  • 中间产物 chip / inline 图去重 + CLAUDE.md 加"实施前先对方案"段:renderMessages 顶部建 seenRels Set + pickFresh 闭包给 5 个渲染点共享;CLAUDE.md 新规:非平凡改动动手前先口头对方案。
  • 顶栏加生图模型下拉 + 中间产物图片/视频内联展示:GET /v1/image_models 扫 yaml image 段;build_agent(image_variant=...) 装 SeedreamTool;renderArtifactBarHtml_categorize(rel) 分支,image/video 走 blob URL inline,异步 upgradeMediaArtifacts 替换占位;切 task 时 _flushMediaArtifactCache 回收 blob。
  • LLM 调用切 streaming(cancel 秒退)+ 发送/停止合并单按钮:chat_stream(stream=True, include_usage=True) + litellm.stream_chunk_builder 拼回 response,chunk 间 poll cancel;前端打字机靠 _emit("text", delta=...) 激活(原有渲染逻辑早就备好);#chat-actionstate.streaming 切发送/停止/停止中三态。
  • dev SPA seedream tool 透明性 banner:tool 返串首行 [seedream] model=... · size=... · cost=¥... · elapsed=...s,前端正则 parse 挂折叠态徽章。
  • 豆包 Seedream 5.0 接入 + 0007 cost_usd → cost_cny 全表统一币种:config/media/doubao.yaml 独立命名空间(ARK_API_KEY env),tools/seedream.pycore/ark_client.py 同步调 /images/generations,产物落 <wd>/figures/<ts>-<rand>.png + 同名 .meta.json;record_image_usageprice_cny_per_image snapshot 进 units jsonb(调价防漂移);0007 全表 ×7.2 一次性折 CNY;仅当 ARK_API_KEY 设了才挂 tool
  • POST /v1/files/deleterecursive + 顶层目录 task 引用闸:recursive=Trueshutil.rmtree;顶层目录被 task 引用 → 409"先 DELETE task 再清";前端非空目录二次确认带子项数。
  • fs tool 输出渲染 user_root-relative 路径:tools/base.py::Tooluser_root + _display(p) helper,fs.py 五 tool 所有结果串走 helper;chip 锚点用 _workingDirName 取末段(绝对路径返空);assistant 正文也挂 chip。根因消 chip 404 + 防 uuid/部署根泄漏。
  • POST /v1/tasks/{id}/clear 清空对话:同事务 lock + 检 running 状态 + DELETE messages + reset task 三列累计 + run_status='idle';usage_events 全不动(账单 source of truth)。
  • dev SPA chip 一期(对话内 tool_call/result 挂 artifact chip):extractArtifactRels 正则锚定 <wd>/... + 末段需含 .(滤目录);.art-chip 点击委托 openFilePreview
  • task 级宪法文件 spec 命名约定 + spec_lockspec 简化:<YYYY-MM-DD>-<task_short_id>-<task_name>.spec.md,short_id 作主锚 + glob 字典序最大 = current;_build_system_prompt 注入 task_id / today;proposal/ppt SKILL.md 加"先 glob 检 spec → 询问沿用/重定调"分支。
  • dev SPA 左 pane 折叠改 VS Code rail 模式 + time-ago 锁宽跨行对齐:body.left-collapsedgrid-template-columns: 40px 1fr 320px,只显折叠按钮;time-agoflex-shrink:0; min-width:64px 让 [N 条][N tok][time] 整组位置稳。
  • 任务行 meta 数字槽位跨行对齐:tabular-nums + .num{flex-shrink:0;text-align:right;min-width:44px} + fmtTokens(n) 桶分级(1.2k / 123k);折叠按钮拆双入口(pane / header)。
  • dev SPA 左 pane 调宽 280→320px + 行精简 meta:删 id8 span 挪到 row title hover;副行恢复 inline ellipsis 三件套;white-space:nowrap 防 CJK 断行。
  • 任务列表 pager bar → IntersectionObserver 滚动加载:loadTaskList({append}) 双语义 + _taskLoadSeq token 抢占式;sentinel 三态文案;首 pane-head 补"共 N 个"总数显示。
  • 任务行加最近操作时间(updated_at + fmtTimeAgo):相对时间分级(刚刚 / N 分钟前 / N 小时前 / 昨天 HH:MM / MM-DD HH:MM / YYYY-MM-DD)+ title hover 完整时间。
  • 新建任务弹框工作目录改 <select> 下拉:含 + 新建目录… sentinel 触发备用 input;loadFolderSuggestions 同次灌 select + datalist(后者只服务左 pane filter)。
  • dev SPA 主页轻量美化:header brand wrapper(24px 红渐变 Z logo)+ pane-head 子层级 + 顶栏按钮"中性 → hover 上语义色" + 圆角 4→6 / modal 6→8 + 阴影加深。
  • config/models/glm.yaml:智谱 GLM 5.1 接入(litellm zai provider + bigmodel.cn):zai/glm-5.1 + api_base=https://open.bigmodel.cn/api/paas/v4 覆盖国际站默认;env ZHIPUAI_API_KEY;thinking_mode=false(GLM 协议是 extra_body.thinking.type=enabled 与 OpenAI/DeepSeek reasoning_effort 不同,留 TODO)。
  • files SPA UX 翻面(destination-first)+ 拖拽上传 + 修 checkbox 全局 width bug:模型从 select-then-pick-dest 改 at-dest-pull-sources;input{width:100%} 选择器排除 checkbox/radio/file;#pane-right 监听拖拽 + 红虚线 overlay 落 state.filesPath
  • POST /v1/files/{copy,move} 跨目录批量搬动:_validate_transfer 预检 helper 批量原子校验;move 加顶层目录 task 引用闸(维持"working_dir = 顶层目录"invariant),copy 无此闸。
  • working_dir 视为可重生 FS 视图:DB source of truth,FS 目录可独立删 / 用户手动 rmtree / 跨机器迁移丢失,下次跑自动 mkdir 重建;DELETE task 后空目录 best-effort rmdir 清孤儿;files delete 顶层目录闸去掉。

2026-05-19

  • 0006 模型切换(c 模式 task 级 A 粒度)+ usage_events v2 表:tasks.model_profile 变 source-of-truth,顶栏下拉 PATCH 即换(A 粒度下条 send 生效);GET /v1/models 扫 yaml;message 历史按 messages.model_profile 切换点画 ── DeepSeek V4 Pro ──;usage_events 重建多态形态(units jsonb,chat 已接入,媒体扩展位预留)。
  • dev SPA 登录撤回 邮箱+密码,删 invites 表:前两条"邀请码 env → invites 表"一日游撤回;复用 users.email + bcrypt 哈希;/v1/auth/login_password + user add CLI;dev SPA 双 tab 登录(last-used LS 持久化)。
  • SENTINEL user 彻底撤(数据 + 代码):web 必走 JWT 后 sentinel 无角色;DB CASCADE 删 + 10 处代码删 import / fallback;build_agent* 让 user_id 必填(typechecker 拦多 user 函数)。
  • 任务/文件行 下拉菜单 + tool_result debounce 刷新右侧:单例浮层菜单(#floating-menu position:fixed)避开 pane overflow 裁剪;tool_result 事件 debounce 500ms 刷新文件 panel。
  • proposal skill mermaid 强制 + quality_check 加图相关 4 拦截 + /v1/files/downloadCache-Control: no-cache:模型曾写满 ASCII 字符画从未用 mermaid;render_diagrams caption 强制必填 + 同 task 唯一;quality_check 加"figures/ 有 png 但 sections 0 引用 / 围栏含 box-drawing / mermaid 缺首行 caption / caption 撞名"四条。
  • dev SPA 文件预览弹框:点击不再直接下载,90vw 模态按扩展名分派(image/pdf/text/md 已有 / docx 用 docx-preview / xlsx 用 SheetJS);vendor 入 git(~1MB)。

2026-05-18

  • 入口归位:cli.pymain.py,原 main.pycore/agent_builder.py,删 CLI REPL,§7 E 撤:main.py 原混三角色按 SoC 拆;git mv + 5 处 import 修;CLI 只剩 db / probe / web / user。dev SPA 已是 dogfood 主路径,REPL 无 --remote 双 transport 维护税。
  • 0004 schema 大瘦身:删 runs / usage_events 旧版,合 run_status / run_error 入 tasks;路由 run_id → task_id:单活 run 形态下客户端只需 task_id;broker 全 task_id 索引 + 加 start(task_id) 清上轮 done 标记。
  • POST /v1/files/rename + 顶层目录 delete 加 task 引用闸:/v1/files/* 升格为唯一目录树 mutation 入口,DB-FS 一致性服务端内化;顶层目录走 DB-aware 分支(SELECT FOR UPDATE + running/cancelling 409 + check_no_subtask + UPDATE 先于 FS rename)。
  • task-level cancel + AgentLoop 协作式 cancel + dev SPA stop 按钮:Broker 加 request_cancel / is_cancelled / clear_cancel(per-task threading.Event + setdefault);Loop 加 cancel_check callable + _fill_cancelled_tool_results 补 cancelled tool message;LLM 同步 call 本身不可中断(后接 streaming 修)。
  • POST /v1/tasks/{id}/messages 单活 run 锁 + 孤儿 reaper:同事务 SELECT FOR UPDATE + 活跃状态检查 + 标 running 三步原子;lifespan reaper 清进程 crash 留下的 running/cancelling 孤儿。
  • proposal skill 流程图/结构图管线:render_diagrams.py 扫 mermaid 块 → mmdc / mermaid.ink → png;render_docx add_picture 识别 ![](...) 单行 + mermaid 围栏特判;图编号 ctx['fig_no'] 递增。
  • system prompt skill 机制改"可选辅助":第 14 行从"永远 load 一下"改"简单问答/读代码/改 bug 不必硬套 skill";接 GET /v1/skills 下拉。
  • GET /v1/skills + dev SPA skill 字段改下拉:lifespan 启动扫一次挂 app.state(FS 静态运行中不变);<select> 首项空值,option 文案 name — description
  • dev SPA 全套 UI 中文化:静态 + 动态文案全本地化;技术字段(UUID / token / SSE event 名 / API 字段)不动。

2026-05-17

  • 0003 schema:name + working_dir + skill 三件套:任务标识与工作目录解耦;TRUNCATE tasks CASCADE + 字段改名 + 加 name TEXT NOT NULL;GET /v1/folders 给 dev SPA modal datalist。
  • GET /v1/tasks 分页 + 多维筛选 + ordering:{page,page_size,count,results} + 6 个 query(status/skill/working_dir/q ILIKE/ordering);allowlist 防注入;默认 -created_at
  • task 硬删 API + dev SPA delete 按钮 + 文件 per-row 删:DELETE /v1/tasks/{id} user_id 校验 + DB 行删(messages CASCADE)+ FS task_dir 不动(同 name 多 task 共享时 rmtree 易擦素材)。
  • files API 全面 user-rooted(去掉 task_id 前置):_safe_join 边界改 user_root + dotfile 过滤(.memory/ 隐藏);dev SPA loadFiles() 不再 gate on task_id。
  • files 面板 UX 项目名 + 修 root crumb bug:cur_rel == "." 不追加无意义 "." crumb;crumbs 第一格 label 从 "/" 改项目名。
  • task_dir 改 eager mkdir:build_agent 新建分支 + create_taskmkdir(parents=True, exist_ok=True);name = 项目声明,目录该 task 创建时存在。
  • task = name-based 项目目录 + memory dotfile:废 UUID 派生 + tasks/ 中间层;task_dir = workspace/users/<uid>/<name>/,同 name 多 task 共享;memory 搬 .memory/ dotfile;validate_task_name. 起头。

2026-05-15

  • §7 D 阶段 /v1 JSON API 落地;Phase G Jinja2/HTMX UI 路线撤:删 templates + CSS + jinja2/markdown-it-py/pygments 依赖;SSE event 由 HTML 片段切 JSON(event: <type> + data: <JSON>);dev SPA web/static/dev.html 留作本地 dogfood 主路径。
  • §7 D' 过渡 auth(PLATFORM_KEY → JWT)+ dev SPA:pyjwt HS256 + AuthConfig.from_env() fail-fast;数据隔离全 Task.user_id == user_id,跨 user 视 404;SSE 走 fetch + ReadableStream 手解(EventSource 不支持自定义 header)。
  • task_dir 改相对存储:DB 存 ROOT 内→相对 posix / ROOT 外→保留绝对;core/paths.py::{ROOT, to_db_path, from_db_path} 三出口;alembic 0002 一次 UPDATE backfill。CLAUDE.md 加"开发期不写兼容层"心智。
  • workspace 布局统一 per-user:workspace/users/<user_id>/{tasks/<uuid>,memory/}/;清旧数据不留兼容
  • litellm 启动 cost map 网络警告兜底:LITELLM_LOCAL_MODEL_COST_MAP=True 走本地 cost map,冷启动 ~5s → <1s。
  • Phase G G1-G6 Jinja2/HTMX Web UI (全撤,被 D + dev SPA 替换;沉淀的 sink / broker / no-subtask / files 安全归一保留)

2026-05-14

  • §7.1 心智模型修正:Folder-centric → Task 一等公民 + Dir 文件副视图:dir 不是 task 父容器,双视图正交;task_dir 留空 = 一次性对话 / 指定 = 项目化。
  • §7 B Steps 1-4 + 6:core/storage/{engine,models}.py SQLAlchemy 2.x ORM(5 表)+ alembic + cli db {upgrade,downgrade,current};state.json 全废,messages/TaskState 入 PG;check_no_subtask 同 user 下查前缀嵌套。

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-08:Phase 6 部分(task + state.json + tokens 累计);TUI rich Markdown + spinner 实时耗时;/resume [last|<id>] + 懒创建 + _cleanup_if_empty
  • 05-09 → 05-10:DESIGN §7 初版(05-12 重写);cli.py export + core/export_docx.py
  • 05-11:atomic_write_text + core/memory.py(core.md 入 prompt,extended/* 索引);loop 事件流化 sink.emit 铺 SSE 路。

关键决策与偏差

决策 备注
工具基目录 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 Windows 软链接麻烦,真要切再做
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 + litellm.stream_chunk_builder 拼回;cancel 在 chunk 间 + tool_call 之间 poll cancel 延迟 100ms 级;content delta 即时 emit 给前端打字机
发送/停止单按钮 UI 按 state.streaming 切态;streaming 期间 Enter 不触发停止 防误触

文件清单

core/capabilities.py        71
core/llm.py                151   ← litellm 离线 cost map env + chat_stream(stream=True + include_usage)
core/loop.py               268   ← §7 A sink.emit + _stream_llm(chunk 间 poll cancel + emit delta)
core/sinks.py              101   ← §7 A
core/ui.py                  38
core/paths.py               50   ← task_dir db form 归一
core/probe.py              243
core/session.py            153   ← §7 B Step 2-3: ORM
core/skills.py              81
core/task.py                82   ← §7 B Step 3: PG-backed TaskState
core/memory.py              81   ← per-user `.memory/` dotfile
core/export_docx.py        383
core/storage/__init__.py    29
core/storage/engine.py      80
core/storage/models.py     130   ← 4 表(0004 删 runs;0005 email UNIQUE;0006 usage_events v2 + messages.model_profile;0007 cost_usd→cny)
core/storage/usage.py      125   ← record_chat_usage(USD→CNY ×7.2)+ record_image_usage(单价 snapshot 进 units)
core/storage/utils.py      136
core/ark_client.py         105   ← 火山方舟 HTTP 客户端(seedream / 后续 seedance 共享)
core/agent_builder.py      325   ← 装配 lib(有 ARK_API_KEY 才挂 SeedreamTool)
tools/{base,fs,shell,run_python,skill_tool,seedream}.py  ~640 行
main.py                    ~210  ← 入口:web / db / probe / user
db/migrations/env.py        61
db/migrations/versions/
  0001_initial_schema.py   125
  0002_task_dir_relative.py 61
  0003_task_name_and_working_dir.py 51
  0004_drop_runs_usage_events.py 77
  0005_users_email_unique.py 28
  0006_usage_events_v2_and_message_model.py 60
  0007_cost_usd_to_cny.py    40
web/__init__.py              5
web/app.py                ~1320  ← /v1 JSON API + user_id 隔离 + run lock + cancel + files copy/move
web/auth.py                ~190  ← 邮箱密码 + platform_key → JWT
web/broker.py              121   ← in-process pub/sub + cancel signal(全 task_id 索引)
web/sinks.py                21
web/static/dev.html       ~2480  ← dev SPA(3 栏 + 文件预览 + 双 tab 登录 + 选入弹框 + 发送/停止单按钮 + 流式打字机)
web/static/vendor/        ~1 MB  ← jszip / docx-preview / xlsx
─────────────────────────────────
Python 合计              ~3400 行(+ dev.html 1700 静态 + vendor 1MB)

skills/ppt|proposal|coding|research/ 脚本 ~700 行 + SKILL.md / references / config / prompts(含 config/media/doubao.yaml)+ alembic.ini,总仓库约 3800 行。


下一步候选(性价比排序)

  1. 真 OIDC 接入 + CORS 收紧(~1 天)—— /v1/auth/login 内部换 OIDC ID token 校验(路由层 Depends 不动);CORS 改 platform 域名 allowlist。真发布给真实用户前必做
  2. §7 C Executor + sandbox(~2-3 天)—— run_python/shellExecutor.run(...),本地保留 subprocess、SaaS 走 docker;api_key_envKeyProvider 运行时注入。多用户在线跑代码前置。
  3. Phase 6 context 三层压缩(~1 天)—— 兜底,V4 长上下文一般用不到。

§7 B + D + D' + 单活 run 锁 + cancel + 0004 schema 瘦身 + 入口归位 主体已完工。剩余:真 OIDC → C(Executor)→ F(deploy / billing)。§7 E CLI 双模式撤;Phase G Jinja2/HTMX 撤(详见 DESIGN §7.9)。