diff --git a/DESIGN.md b/DESIGN.md index e531133..16ea72a 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -416,6 +416,23 @@ create index on usage_events (model_profile, created_at); | gVisor → **Firecracker / e2b** | 合规客户(PCI / HIPAA) / 高密度多租户(单机 100+ user) / gVisor 兼容性墙撞死(某 C 扩展跑不起来) | Firecracker 内存账每 VM 100MB+ 起步,zcbot 量级不划算;e2b 让数据出去执行再回来,与 storage_root 自持模型冲突 | | `docker exec` → **容器内 tool-runner**(unix socket RPC) | metric `docker_exec_overhead / total_tool_time > 30%` 持续两周 / "模型在容器内起长驻 web 服务并对外服务"工作流 / 单 task 一轮工具调用 >20 次 | 自管进程组清理 + 自管 cgroup 切片 + 协议自带状态污染面 + 失去 Docker 工具链观测(`docker stats` / `docker top` / `docker exec -it bash` 紧急介入)代价 >> 那 200ms × N 收益;**美学统一性 ≠ 升级理由** | +**Image 体积 / 多 user 资源 / 后续加包策略**(2026-05-28): + +zcbot-sandbox image 已 ~1.5G(python deps + chromium + nodejs + mermaid-cli),后续 domain 包(rdkit / pymatgen / ASE / pandoc-tex 等)还会进一步推大。先把三件事认知分开,避免把 image 大小当万能背锅侠: + +1. **Image 大 ≠ 运行时吃更多资源**。`deploy/sandbox/Dockerfile:120` 启动是 `tini → init.sh → sleep infinity`,空载 RSS 个位数 MB;image 里的 chromium / numpy / npm 不 exec 就只是磁盘字节,不进内存。layer 共享让 N 个 user 容器共用同一个 image,磁盘乘数 = 1。**真吃 RAM 的是 active exec**(chromium 渲 mermaid 瞬时 200-500MB,大数据集另算),跟 image 大小解耦,跟"模型让容器干啥"挂钩。 +2. **多 user 同时在线的瓶颈在并发 exec,不在 idle 容器**。100 个 idle per-user 容器约几百 MB RSS,可接受;10 个 user 同时让模型渲 mermaid 瞬时 2-5 GB,这才是瓶颈。**杠杆全在运行时**:`docker run --memory --cpus --pids-limit` 单容器硬顶 + 同 user 并发 exec semaphore + 整机 active user cap(超额排队 / 429)+ idle 5 分钟回收(已规划)。**减 image 体积对这条曲线不产生影响**,不在 image 上花减肥功夫。 +3. **新增依赖策略:base 收敛 + per-user 持久化 venv + 使用频次沉淀**: + - **(a) 全塞 base**:重 (torch 1G+ / texlive 2G+) 或长尾 domain 包不该进,build 慢 / pull 慢 / 所有 user 拍磁盘陪绑;但中高频 + 多 skill 共用 + 轻量(MB 级)的继续放 base(当前 `requirements.txt` 大致就是这类)。 + - **(b) 运行时临时装**:适合单次实验;**容器 idle 回收即丢**,冷启又装,高频复用差。 + - **(c) 多 image 按场景分**:per-user 容器模型下 user 不知道自己要哪个,切 skill 还得换 image —— 心智不通,**不采用**。 + - **采用:(a) + 持久化 per-user venv**。每个 user 一个持久化 venv 落 `/users//.venv/`(随 user_root bind mount 进 `/workspace/.venv/`,容器 idle 回收不丢);模型用 `pip install --target=/workspace/.venv/site-packages` 装到持久化路径,exec 时 `PYTHONPATH=/workspace/.venv/site-packages` 注入。**沉淀机制**:加 audit 统计哪些包累计被 >N% user 装过 → 下次 image build 时合并进 `requirements.txt`,base 跟着真实使用模式收敛而非拍脑袋。 + - **为什么不把 venv 放 named volume / image cache**:跨 user 共享 venv 破坏跨 user 隔离(包 install 脚本是任意代码,一个 user 装的 setup.py 可读另一 user 的 venv);per-user 隔离 venv 与 §7.5 跨 user 隔离 / 同 user 内不隔离的边界一致。 + - **为什么不依赖 pip cache**:pip cache 只省网络 / 不省落盘,装到 `/tmp` 容器一回收照样丢;venv 持久化才解决"冷启不重装"。 + - **npm / chromium / pandoc 等系统级保持 base**,无机非金属材料场景下 per-user node_modules 没必要分。 + - **沉淀阈值起步 >30% user 装过 ≥ 3 次** 进 base;过低噪声多,过高沉淀慢,后续按观察调。 + +`Image 大 = 资源占用大` 是表象关联,真因子是 active exec 行为 + 单 user 容器并发限制;`继续往 base 加包 vs 临时装` 是伪二选一,**长尾私有 + 高频沉淀**是更合身的形状。落地排期对应 Step 6 Executor 实施(`PROGRESS.md` Stage C):cgroup limits / 并发 semaphore / idle 回收 / per-user venv mount 一并进 `DockerExecutor`;audit 沉淀机制可延后,有 dogfood 安装日志再开。 ### 7.6 Core 代码改造(按依赖顺序) diff --git a/PROGRESS.md b/PROGRESS.md index dc1ca73..61fbb49 100644 --- a/PROGRESS.md +++ b/PROGRESS.md @@ -21,6 +21,10 @@ ## 已完成关键能力 +### 2026-05-28 + +- **DESIGN §7.5 增"image 体积 / 多 user 资源 / 后续加包策略"决策段**:dogfood 推进到加 npm + chromium + mermaid-cli 后,sandbox image 1.5G+,后续 domain 包(rdkit / pymatgen / ASE / pandoc-tex 等)还会进一步推大,把"image 大 = 资源占用大 = 多 user 容器爆炸"这条直觉关联的事实链拆开沉淀,免得未来花减肥功夫减错地方。三条认知校准:① **image 大 ≠ 运行时吃更多 RAM**(空载 `tini → sleep infinity` RSS 个位数 MB,layer 共享让磁盘乘数 = 1,真吃 RAM 的是 active exec 行为而非 image 字节);② **多 user 同时在线瓶颈在并发 exec 不在 idle 容器数**(杠杆全在 `docker run --memory --cpus --pids-limit` + 同 user 并发 semaphore + 整机 active user cap + idle 回收,减 image 体积对这条曲线无效);③ **新增依赖采用"base 收敛 + per-user 持久化 venv + 使用频次沉淀"**:base 只放高频共用轻量包(`requirements.txt` 当前形状),长尾 domain 包模型用 `pip install --target=/workspace/.venv/site-packages` 装到 per-user 持久化路径(随 user_root bind mount,idle 回收不丢);加 audit 统计哪些包累计被 >30% user 装过 ≥ 3 次 → 下次 build 合并进 `requirements.txt`,base 跟着真实使用模式收敛而非拍脑袋。否决:(a) 全塞 base —— 重包(torch / texlive)+ 长尾包打死磁盘 + 强迫所有 user 陪绑;(b) 运行时临时装(不持久化)—— 容器 idle 回收即丢冷启重装,高频复用差;(c) 多 image 按场景分 —— per-user 容器模型下 user 不知道选哪个,切 skill 还得换 image 心智不通;(d) per-user venv 用 named volume / 共享 image cache —— 包 install 脚本是任意代码,跨 user 共享 venv 破坏跨 user 隔离边界;(e) 依赖 pip cache 解决问题 —— pip cache 只省网络不省落盘,容器回收照样丢。落地点排进 Stage C Step 6 Executor 实施:cgroup limits / 并发 semaphore / idle 回收 / per-user venv mount 一并进 `DockerExecutor`(audit 沉淀机制延后,需要 dogfood 安装日志积累再开)。`RUN.md` 不动(当前无 CLI / env 变化,真做 venv mount 时再加);`DESIGN.md` §7.5 升级触发信号表后新增该段。 + ### 2026-05-27 - **ppt skill 歧义反问 + general_v1 加"产物形式歧义先问"通用原则**:上次 8109f20 把 ppt description 收紧成"白名单 + 反例"后,实测"MES**汇报方案**"这种请求还是被路由命中 —— 反例列表只覆盖"生成方案 / 写报告 / 出文档 / 做纪要"几个组合,但"汇报方案"未列入,而"汇报"在 LLM 语义里就有强烈的 PPT 联想重力(工作汇报 / 季度汇报多以幻灯片形式)足以压过"必须明确点名 PPT"的硬约束。**修法**:① `skills/ppt/SKILL.md:3` description 改三段(✅触发白名单 / ⛔不触发[只留明确指向文档的"报告 / 文档 / 纪要"] / ⚠️ 歧义先反问)—— 把"汇报 / 方案 / 材料"从反例摘出来,改成"先反问用户'PPT 还是 Word/Markdown 文档'再决定 load",把判断权还给用户而不是赌 LLM 路由词典;② `prompts/system/general_v1.md` Skill 机制段加一条系统级原则"产物形式歧义时先问",升格为跨 skill 通用约束(imagegen / videogen 各自 skill 内本来就有"问清楚再画"逻辑,现在抽到 system prompt 让新加 skill 也能继承)。**否决**:(a) 继续往反例里堆"汇报方案 / 汇报材料 / 汇报内容"—— 堆词典治标不治本,下次"做个 Q4 总结"又得加;(b) 路由层加 `required_keywords` 结构化字段,在 discovery_block 之前 grep 用户原话兜底 —— 跨多 skill 都得补字段,工程量大,短期 LLM 反问范式收益已够;(c) ppt skill load 后再反问 —— 路由命中后再反问就是已经误触发了,要在路由阶段拦。代价:用户已经心里清楚要 PPT 只是没说时,会觉得多一轮反问啰嗦;缓解靠反问句式短 + 暗示默认选项,一个字"PPT"就能过,比生成完整 deck 后推翻代价小一个数量级。`DESIGN.md` 不动(无架构变化);`RUN.md` 不动(纯 skill 元数据 + system prompt 文案)。