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:
caoqianming 2026-06-15 08:30:43 +08:00
parent 32bf6ae917
commit 211b008821
6 changed files with 74 additions and 16 deletions

View File

@ -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
View File

@ -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` |

View File

@ -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"

View File

@ -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"

View File

@ -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 同步

View File

@ -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 更稳