fix(sandbox): 容器渲 mermaid 开箱即用(mmdc wrapper) + system 按 backend 注入运行环境段 + bump 0.12.7
接 --shm-size(0.12.5)。修两层让容器渲 mermaid 不再反复栽: 执行层 ── mmdc wrapper: - Dockerfile 给 /usr/local/bin/mmdc 套 wrapper,没显式 -p 时自动注入 -p /sandbox/puppeteer-config.json(含 --no-sandbox/--disable-dev-shm-usage), 裸调 `mmdc -i x -o y` 一次成;render_diagrams.py 等走 which mmdc 的脚本透明受益。 - 删掉没人读的 MERMAID_PUPPETEER_CONFIG env(mmdc 只认 -p)。 引导层 ── system prompt 按 backend 注入「运行环境」段: - general_v1.md 删写死的 "Windows+cmd" 平台段(线上是 docker=Ubuntu 容器+bash, 误报导致模型在 Linux 里打 cmd 构文)。 - agent_builder 注入 _CONTAINER_ENV_BLOCK(docker)/_HOST_ENV_BLOCK(host):写明 Linux/bash、渲图走本地 mmdc 别调境外在线服务(mermaid.ink 被墙,容器虽有外网但 渲图不该依赖出站)、mmdc/chromium/中文字体已装。 - 撤回上一轮加到 imagegen 的渲图引导(环境事实收归 system)。 顺带:RUN.md 修正把 sandbox 网络写成 --internal 无 outbound 的过时注释(实际 bridge+NAT 有外网,见 network.py)。 部署:Dockerfile 改动需 rebuild 镜像;prompt 改动重启 web 生效。 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
32bf6ae917
commit
211b008821
17
PROGRESS.md
17
PROGRESS.md
|
|
@ -21,6 +21,23 @@
|
||||||
|
|
||||||
## 已完成关键能力
|
## 已完成关键能力
|
||||||
|
|
||||||
|
### 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_CONFIG` env(mmdc 本就不认它,只认 `-p`)。
|
||||||
|
- **引导层**:imagegen skill「mermaid vs seedream」段加硬引导 ── 渲图直接 `mmdc -i x -o y`、⛔ 容器禁外网别试 mermaid.ink 等在线 API。
|
||||||
|
- 取舍:没开 first-class `render_mermaid` tool ── 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 挂超时
|
### 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_CONFIG` env)。
|
- 实测一个"生图测试"任务(`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_CONFIG` env)。
|
||||||
|
|
|
||||||
20
RUN.md
20
RUN.md
|
|
@ -366,13 +366,16 @@ sudo -u zcbot docker build \
|
||||||
# npm 源同款(@mermaid-js/mermaid-cli + 依赖,境内访问 registry.npmjs.org 也慢):
|
# npm 源同款(@mermaid-js/mermaid-cli + 依赖,境内访问 registry.npmjs.org 也慢):
|
||||||
# --build-arg NPM_REGISTRY=https://mirrors.cloud.tencent.com/npm/ # 腾讯云
|
# --build-arg NPM_REGISTRY=https://mirrors.cloud.tencent.com/npm/ # 腾讯云
|
||||||
# --build-arg NPM_REGISTRY=https://registry.npmmirror.com/ # 阿里(npmmirror)
|
# --build-arg NPM_REGISTRY=https://registry.npmmirror.com/ # 阿里(npmmirror)
|
||||||
# 镜像内自带 Chromium + mermaid-cli + puppeteer-config.json,proposal/patent skill
|
# 镜像内自带 Chromium + mermaid-cli + puppeteer-config.json;mmdc 被 wrapper 包了一层
|
||||||
# 的 render_diagrams.py 看到 MERMAID_PUPPETEER_CONFIG env 自动 -p 注入,
|
# (/usr/local/bin/mmdc → 自动注入 -p /sandbox/puppeteer-config.json,除非显式传 -p),
|
||||||
# host 上跑时该 env 没设,行为不变
|
# 所以容器内**裸调 `mmdc -i x.md -o x.png` 就能出图**,无需 --no-sandbox / 自写配置。
|
||||||
|
# render_diagrams.py 等走 `which mmdc` 的脚本透明受益(原靠 MERMAID_PUPPETEER_CONFIG
|
||||||
|
# env,已删 ── mmdc 本就不读它,改 wrapper 兜底)。host 上跑无 wrapper,行为不变
|
||||||
|
|
||||||
# 3) 创建 sandbox 网络(--internal,默认无 outbound)
|
# 3) 创建 sandbox 网络(bridge,dogfood 阶段保留 outbound NAT —— 让模型能 pip/curl 公网;
|
||||||
sudo -u zcbot docker network create --internal zcbot-sandbox-net
|
# iptables 仍挡内网/cloud metadata/PG。--internal 完全禁出站是外部用户开放时才改,见 §7.5 #2)
|
||||||
# 或 SandboxPool.setup_pool() 自动 ensure
|
sudo -u zcbot docker network create zcbot-sandbox-net
|
||||||
|
# 或 SandboxPool.setup_pool() 自动 ensure(ensure_network 即建非 internal bridge)
|
||||||
```
|
```
|
||||||
|
|
||||||
### Sandbox 相关 env(.env 加)
|
### Sandbox 相关 env(.env 加)
|
||||||
|
|
@ -694,8 +697,9 @@ sudo xfs_quota -x -c "limit -p bhard=10g zcbot_<user_uuid>" /opt
|
||||||
| 镜像 build npm 装 mermaid-cli 慢 / fail | npm 源境内慢。默认已用腾讯 `https://mirrors.cloud.tencent.com/npm/`(清华无 npm 源;npmmirror 访问不稳被弃)。备选:华为 `https://repo.huaweicloud.com/repository/npm/` / USTC `https://npmreg.mirrors.ustc.edu.cn/`,手动 build 加 `--build-arg NPM_REGISTRY=...` |
|
| 镜像 build npm 装 mermaid-cli 慢 / fail | npm 源境内慢。默认已用腾讯 `https://mirrors.cloud.tencent.com/npm/`(清华无 npm 源;npmmirror 访问不稳被弃)。备选:华为 `https://repo.huaweicloud.com/repository/npm/` / USTC `https://npmreg.mirrors.ustc.edu.cn/`,手动 build 加 `--build-arg NPM_REGISTRY=...` |
|
||||||
| 镜像 build apt 报 `OpenSSL error: ... unexpected eof while reading` | 某些 mirror HTTPS 端偶发 close_notify 缺失,OpenSSL 3 严格 fail(腾讯 / 阿里见过;清华一般不犯)。改用 http 形式:`--build-arg APT_MIRROR=http://mirrors.tuna.tsinghua.edu.cn`(apt 包 GPG 签名校验,无 HTTPS 安全收益)。Dockerfile 已配 apt retry=5 + 关 pipeline,重 build 一般直接过 |
|
| 镜像 build apt 报 `OpenSSL error: ... unexpected eof while reading` | 某些 mirror HTTPS 端偶发 close_notify 缺失,OpenSSL 3 严格 fail(腾讯 / 阿里见过;清华一般不犯)。改用 http 形式:`--build-arg APT_MIRROR=http://mirrors.tuna.tsinghua.edu.cn`(apt 包 GPG 签名校验,无 HTTPS 安全收益)。Dockerfile 已配 apt retry=5 + 关 pipeline,重 build 一般直接过 |
|
||||||
| 容器内 shell 写工作目录报 `Permission denied`(but `sandbox check` ⑤ HOST_UID aligned ok) | DockerExecutor 写死了 `--user 1000:1000` 不会自动跟 build 的 HOST_UID 同步(改 `--user zcbot` 后已修)。仍报错检查镜像内 `docker run --rm --entrypoint id zcbot-sandbox:latest zcbot` 输出 uid 是否 = `id -u $(whoami)` |
|
| 容器内 shell 写工作目录报 `Permission denied`(but `sandbox check` ⑤ HOST_UID aligned ok) | DockerExecutor 写死了 `--user 1000:1000` 不会自动跟 build 的 HOST_UID 同步(改 `--user zcbot` 后已修)。仍报错检查镜像内 `docker run --rm --entrypoint id zcbot-sandbox:latest zcbot` 输出 uid 是否 = `id -u $(whoami)` |
|
||||||
| 模型用 run_python 跑 `render_diagrams.py` 报 `mmdc returncode=1: Failed to launch chromium` | 容器内 chromium 缺 puppeteer no-sandbox 配置。镜像已落 `/sandbox/puppeteer-config.json` + ENV `MERMAID_PUPPETEER_CONFIG`,render_diagrams.py 已读 env 自动 -p 注入;仍跪查 `docker exec ... env \| grep MERMAID` 看 env 是否在 |
|
| 容器内 mmdc 渲 mermaid 报 `Failed to launch chromium` / `No usable sandbox` | chromium 在 `--cap-drop=ALL` 下自家 setuid sandbox 起不来,要 `--no-sandbox`。镜像已落 `/sandbox/puppeteer-config.json` + 给 mmdc 套了 wrapper 自动 `-p` 注入 ── **裸调 `mmdc -i x -o y` 就该成**。仍跪:`docker exec ... cat /usr/local/bin/mmdc` 看 wrapper 在不在(老镜像没 rebuild 则没有);或显式 `mmdc -p /sandbox/puppeteer-config.json -i x -o y` |
|
||||||
| 模型裸调 `mmdc -i x.md -o x.png`(自造 puppeteer config / 不走 render_diagrams.py)卡到 **timeout** 而非报错 | chromium 默认用 `/dev/shm`,docker 不传 `--shm-size` 时只 64MB → 起不来一直挂。已在 `pool.py` 给 `docker run` 加 `--shm-size`(默 512m,env `ZCBOT_SANDBOX_SHM_SIZE` / yaml `sandbox.shm_size`)。**已 running 的旧容器不会自动生效**,重启 web + 等 idle 回收(或 `docker rm -f zcbot-sandbox-<uid>`)后新起的才带。注:`mmdc` 自身不读 `MERMAID_PUPPETEER_CONFIG` env(只认 `-p`),裸调要么靠 shm-size 兜底要么显式 `-p /sandbox/puppeteer-config.json`。实测脚本 `deploy/sandbox/probe_mermaid.sh` |
|
| 容器内 mmdc 渲图卡到 **timeout** 而非报错 | chromium 默认用 `/dev/shm`,docker 不传 `--shm-size` 时只 64MB → 起不来一直挂。已在 `pool.py` 给 `docker run` 加 `--shm-size`(默 512m,env `ZCBOT_SANDBOX_SHM_SIZE` / yaml `sandbox.shm_size`)。**已 running 的旧容器不会自动生效**,重启 web + 等 idle 回收(或 `docker rm -f zcbot-sandbox-<uid>`)后新起的才带。实测脚本 `deploy/sandbox/probe_mermaid.sh` |
|
||||||
|
| 模型不渲本地 mmdc、反复试 `mermaid.ink` 等在线渲图服务还失败 | 容器**有外网**(bridge+NAT),但 `mermaid.ink` 等**境外服务易被墙/不稳**,渲图不该依赖出站。docker backend 的 system prompt「运行环境」段(`agent_builder.py` 注入)已写明"渲图走本地 mmdc、别调在线服务";撞到多半是 prompt 没更新 / 跑在 host backend。渲 mermaid 一律 `mmdc -i x.md -o x.png` |
|
||||||
| Export 报 "无可导出内容" | task 没 messages(只 system 不算);先发条消息再 export |
|
| Export 报 "无可导出内容" | task 没 messages(只 system 不算);先发条消息再 export |
|
||||||
| `NoSubtaskError: working_dir ... 前缀嵌套` | §7.4 no-subtask:同 user 不允许 working_dir 嵌套(child / parent)。**同项目多对话**用**完全相同**的 working_dir;否则改成 sibling(平级) |
|
| `NoSubtaskError: working_dir ... 前缀嵌套` | §7.4 no-subtask:同 user 不允许 working_dir 嵌套(child / parent)。**同项目多对话**用**完全相同**的 working_dir;否则改成 sibling(平级) |
|
||||||
| `main.py web` 启动后 curl 连不上 | 检查 proxy(`HTTP_PROXY` / `HTTPS_PROXY`):本地服务 127.0.0.1,系统 proxy 拦截会 502。临时 `unset HTTP_PROXY HTTPS_PROXY` 或 `curl --noproxy '*'`。验通:`curl --noproxy '*' http://127.0.0.1:8765/healthz` |
|
| `main.py web` 启动后 curl 连不上 | 检查 proxy(`HTTP_PROXY` / `HTTPS_PROXY`):本地服务 127.0.0.1,系统 proxy 拦截会 502。临时 `unset HTTP_PROXY HTTPS_PROXY` 或 `curl --noproxy '*'`。验通:`curl --noproxy '*' http://127.0.0.1:8765/healthz` |
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
|
# zcbot 版本号单一事实源:web/app.py 的 FastAPI version、/healthz 返回、前端展示都引这里。
|
||||||
# 改版本只动这一行。
|
# 改版本只动这一行。
|
||||||
__version__ = "0.12.5"
|
__version__ = "0.12.7"
|
||||||
|
|
|
||||||
|
|
@ -73,6 +73,23 @@ _MEDIA_TOOLS_BLOCK = """\
|
||||||
- 兜底硬约束:用户没主动要视频就别装饰性生成(比生图更严重的红线);同一目的不满意**绝不连发**(1 次错 = ¥4+60s,连发 2 次 = ¥8+2min);phase 1 仅文生视频,**不支持** image-to-video / video-to-video。"""
|
- 兜底硬约束:用户没主动要视频就别装饰性生成(比生图更严重的红线);同一目的不满意**绝不连发**(1 次错 = ¥4+60s,连发 2 次 = ¥8+2min);phase 1 仅文生视频,**不支持** image-to-video / video-to-video。"""
|
||||||
|
|
||||||
|
|
||||||
|
# 运行环境段(按 backend 注入,general_v1.md 的「平台」段指向这里)。环境事实(在哪 /
|
||||||
|
# 能否联网 / 装了啥)是全局不变量,放 system 比塞进某个 skill 高杠杆 —— 一句话省掉一整类
|
||||||
|
# 试错(外网试错 / 平台命令试错)。docker = 线上真实形态(Ubuntu 容器,无外网);host =
|
||||||
|
# 本地 dogfood(Windows),给一行最小提示免 general_v1 里那句指向落空。
|
||||||
|
_CONTAINER_ENV_BLOCK = """\
|
||||||
|
|
||||||
|
## 运行环境(容器)
|
||||||
|
你的 `shell` / `run_python` / 文件工具都在一个 **Linux(Ubuntu)容器**里执行 —— 是 **bash 不是 cmd**:unix 命令 / 管道 / 重定向正常用,`mkdir -p`、`&&`、`2>&1` 都行。
|
||||||
|
- **渲 mermaid 图一律走本地 `mmdc`**:`mmdc -i 图.md -o 图.png`(要矢量就 `-o 图.svg`;chromium 已配好,**不用加 `--no-sandbox`、不用自己写 puppeteer 配置**)。⛔ **别去调 `mermaid.ink` 等在线渲图服务** —— 境外、易被墙 / 不稳,实测有对话在上面反复试编码、改压缩,白烧上百 k token;本地 mmdc 一条命令就出图。
|
||||||
|
- 中文字体已装(matplotlib / mermaid 出图不乱码);常用 Python 库已预装;`/tmp` 可写、其余 rootfs 只读。"""
|
||||||
|
|
||||||
|
_HOST_ENV_BLOCK = """\
|
||||||
|
|
||||||
|
## 运行环境(本地 host)
|
||||||
|
`shell` 走的是 **Windows cmd.exe**(非 bash):避免 unix-only flag,`mkdir -p` 不识别 → 用 `run_python` 的 `os.makedirs(..., exist_ok=True)` 建目录;复杂管道/重定向用 run_python 更稳。"""
|
||||||
|
|
||||||
|
|
||||||
def load_config() -> dict:
|
def load_config() -> dict:
|
||||||
return yaml.safe_load((ROOT / "config" / "agent.yaml").read_text(encoding="utf-8")) or {}
|
return yaml.safe_load((ROOT / "config" / "agent.yaml").read_text(encoding="utf-8")) or {}
|
||||||
|
|
||||||
|
|
@ -250,14 +267,16 @@ def _build_system_prompt(
|
||||||
today 当场算,落 prompt 给 LLM 直接拼路径(避免 LLM 不知道当前日期)。
|
today 当场算,落 prompt 给 LLM 直接拼路径(避免 LLM 不知道当前日期)。
|
||||||
"""
|
"""
|
||||||
prompt = (ROOT / cfg["system_prompt"]).read_text(encoding="utf-8")
|
prompt = (ROOT / cfg["system_prompt"]).read_text(encoding="utf-8")
|
||||||
if skills.skills:
|
|
||||||
prompt += f"\n\n## 可用 skill (用 load_skill 加载完整指引)\n{skills.discovery_block()}"
|
|
||||||
# docker backend 下 shell/run_python/fs 工具全在容器里跑,容器把
|
# docker backend 下 shell/run_python/fs 工具全在容器里跑,容器把
|
||||||
# `<workspace>/users/<uid>` bind 到 `/workspace`、`--workdir /workspace/<wd>`
|
# `<workspace>/users/<uid>` bind 到 `/workspace`、`--workdir /workspace/<wd>`
|
||||||
# (executor_docker.py:99-100)。此时 prompt 给 agent 的所有可写/可读绝对路径
|
# (executor_docker.py:99-100)。此时 prompt 给 agent 的所有可写/可读绝对路径
|
||||||
# (含 .memory/ 写入锚点)都必须是**容器路径**,否则 LLM 拿着宿主绝对路径在沙盒里
|
# (含 .memory/ 写入锚点)都必须是**容器路径**,否则 LLM 拿着宿主绝对路径在沙盒里
|
||||||
# find 不到任何东西。host backend 不变,直接用宿主绝对路径。
|
# find 不到任何东西。host backend 不变,直接用宿主绝对路径。
|
||||||
is_docker = os.getenv("ZCBOT_SANDBOX_BACKEND", "host").lower() == "docker"
|
is_docker = os.getenv("ZCBOT_SANDBOX_BACKEND", "host").lower() == "docker"
|
||||||
|
# 运行环境段紧跟模板(平台/网络是基础事实,放前面);general_v1 的「平台」段指向这里。
|
||||||
|
prompt += _CONTAINER_ENV_BLOCK if is_docker else _HOST_ENV_BLOCK
|
||||||
|
if skills.skills:
|
||||||
|
prompt += f"\n\n## 可用 skill (用 load_skill 加载完整指引)\n{skills.discovery_block()}"
|
||||||
# .memory/ 在 agent 视角下的可写路径:docker 给容器路径,host 给宿主绝对路径。
|
# .memory/ 在 agent 视角下的可写路径:docker 给容器路径,host 给宿主绝对路径。
|
||||||
mem_dir_display = "/workspace/.memory" if is_docker else str(
|
mem_dir_display = "/workspace/.memory" if is_docker else str(
|
||||||
user_root(workspace_dir, user_id) / ".memory"
|
user_root(workspace_dir, user_id) / ".memory"
|
||||||
|
|
|
||||||
|
|
@ -95,7 +95,6 @@ ARG NPM_REGISTRY=https://registry.npmjs.org/
|
||||||
# Puppeteer 用容器内已装的 chromium 而非自带下载(省 ~300MB + 避免下载失败)
|
# Puppeteer 用容器内已装的 chromium 而非自带下载(省 ~300MB + 避免下载失败)
|
||||||
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
ENV PUPPETEER_SKIP_DOWNLOAD=true
|
||||||
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
|
ENV PUPPETEER_EXECUTABLE_PATH=/usr/bin/chromium
|
||||||
ENV MERMAID_PUPPETEER_CONFIG=/sandbox/puppeteer-config.json
|
|
||||||
|
|
||||||
RUN npm config set registry ${NPM_REGISTRY} \
|
RUN npm config set registry ${NPM_REGISTRY} \
|
||||||
&& npm install -g @mermaid-js/mermaid-cli@latest \
|
&& npm install -g @mermaid-js/mermaid-cli@latest \
|
||||||
|
|
@ -115,6 +114,27 @@ RUN mkdir -p /sandbox && cat > /sandbox/puppeteer-config.json <<'EOF'
|
||||||
}
|
}
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
|
# mmdc wrapper ── 容器 --cap-drop=ALL 下 chromium 自家 setuid sandbox 起不来,必须
|
||||||
|
# --no-sandbox(+ --disable-dev-shm-usage),这些在上面的 /sandbox/puppeteer-config.json 里。
|
||||||
|
# 但 mmdc **不读任何 env 自动加载**该 config(只认 -p/--puppeteerConfigFile);模型裸调
|
||||||
|
# `mmdc -i x.md -o x.png` 会因缺 --no-sandbox 直接跪 → 然后反复试 mermaid.ink 等在线 API
|
||||||
|
# (容器 internal network 禁外网,死路),实测一条对话这么烧掉 ~120k token。wrapper 在调用方
|
||||||
|
# 没显式传 -p 时自动注入这份 config,让裸调一次成;已显式 -p 则尊重不覆盖。proposal 的
|
||||||
|
# render_diagrams.py 等走 `which mmdc` 的脚本同样透明受益(原靠 MERMAID_PUPPETEER_CONFIG
|
||||||
|
# env,已删 ── wrapper 兜底,不再依赖那个谁都不读的 env)。
|
||||||
|
RUN mv /usr/local/bin/mmdc /usr/local/bin/mmdc.real \
|
||||||
|
&& cat > /usr/local/bin/mmdc <<'EOF'
|
||||||
|
#!/bin/sh
|
||||||
|
for a in "$@"; do
|
||||||
|
case "$a" in
|
||||||
|
-p|--puppeteerConfigFile|--puppeteerConfigFile=*)
|
||||||
|
exec /usr/local/bin/mmdc.real "$@" ;;
|
||||||
|
esac
|
||||||
|
done
|
||||||
|
exec /usr/local/bin/mmdc.real -p /sandbox/puppeteer-config.json "$@"
|
||||||
|
EOF
|
||||||
|
RUN chmod +x /usr/local/bin/mmdc
|
||||||
|
|
||||||
# fs 工具进容器(§7.5 #6,2026-05-26 修正)── tool_runner.py 在容器内通过
|
# fs 工具进容器(§7.5 #6,2026-05-26 修正)── tool_runner.py 在容器内通过
|
||||||
# `python /sandbox/tool_runner.py <name>` 调用 tools/fs.py 的 Tool 子类,read/write/
|
# `python /sandbox/tool_runner.py <name>` 调用 tools/fs.py 的 Tool 子类,read/write/
|
||||||
# edit/glob/grep 全在容器内执行,物理边界替代代码护栏。tools/ 目录与 host 同步
|
# edit/glob/grep 全在容器内执行,物理边界替代代码护栏。tools/ 目录与 host 同步
|
||||||
|
|
|
||||||
|
|
@ -40,7 +40,5 @@
|
||||||
**对外 echo 产物路径(回复 / 汇报用)一律用全形式 `<wd_name>/<rel>`** —— `<wd_name>` = 上方 task_dir 末段(如末段是 `生图测试` → `生图测试/figures/cover.png`、`基金申报/sections/01-绪论.md`)。**别简写**成 `figures/cover.png` 这种 task 内裸形式:Web UI 靠 `<wd_name>/` 前缀挂可点 chip(预览 / 下载),简写会失效。媒体 tool 的 `saved:` 行已是规范全形式,原样照抄即可。
|
**对外 echo 产物路径(回复 / 汇报用)一律用全形式 `<wd_name>/<rel>`** —— `<wd_name>` = 上方 task_dir 末段(如末段是 `生图测试` → `生图测试/figures/cover.png`、`基金申报/sections/01-绪论.md`)。**别简写**成 `figures/cover.png` 这种 task 内裸形式:Web UI 靠 `<wd_name>/` 前缀挂可点 chip(预览 / 下载),简写会失效。媒体 tool 的 `saved:` 行已是规范全形式,原样照抄即可。
|
||||||
|
|
||||||
## 平台
|
## 平台
|
||||||
当前是 Windows + cmd.exe。**避免用 unix-only flag**:
|
运行平台(Linux 容器 / Windows host)由系统消息里的「运行环境」段说明,以那段为准。
|
||||||
- 建目录用 `run_python` 的 `os.makedirs(path, exist_ok=True)`,**不要** `shell mkdir -p`(cmd 不识别 -p,会创建名为 '-p' 的字面目录;shell 工具已对此做兜底但仍以 run_python 为优先)
|
通用习惯:建目录优先 `run_python` 的 `os.makedirs(path, exist_ok=True)`;路径分隔符用 `/` 最稳;复杂 shell 逻辑(管道/重定向)拿不准就用 run_python。
|
||||||
- 路径分隔符用 `/` 或 `\\`,Python 内部都识别;字符串 raw 路径用 `r"..."`
|
|
||||||
- shell 工具走的是 cmd,不是 bash,管道/重定向语义可能不同 —— 复杂逻辑用 run_python 更稳
|
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue