现象
config.yaml 里的 sandbox.mounts[].host_path 在 make dev 和 make up两种模式下语义不一致,且生产模式下挂载失败时没有任何用户可见的错误——挂载会被静默丢弃。结果是:依赖自定义挂载的 skill或工具在本地开发时表现正常,部署到生产后悄无声息地读到空目录。
复现步骤
config.yaml:
sandbox:
use: deerflow.sandbox.local:LocalSandboxProvider
mounts:
- host_path: /home/me/project/.deer-flow/knowledge
container_path: /mnt/knowledge
read_only: true
其中 /home/me/project/.deer-flow/knowledge 是宿主机上真实存在的目录,里面放着某个 skill 通过 /mnt/knowledge/... 读取的文件。
- make dev → skill 能正常读到 /mnt/knowledge/...。
- make up(docker 生产)→ skill 看到 /mnt/knowledge 是空目录 / 不存在。
两种模式都没有用户可见的报错。生产模式下唯一的信号是 local_sandbox_provider.py 里一条 WARNING 日志(Mount host_path does not exist, skipping),实际运维一般不会盯着看。
根因
LocalSandboxProvider._setup_path_mappings()(backend/packages/harness/deerflow/sandbox/local/local_sandbox_provider.py)把 host_path 当成「gateway 进程所在文件系统里的路径」,并用
host_path.exists() 做门禁:
if host_path.exists():
mappings.append(PathMapping(container_path=..., local_path=str(host_path.resolve()), ...))
else:
logger.warning("Mount host_path does not exist, skipping: ...")
- make dev:gateway 直接跑在宿主机进程里,/home/me/.../knowledge 这种宿主机路径自然存在 → 挂载生效。
- make up:gateway 跑在 deer-flow-gateway 容器里,docker/docker-compose.yaml 只把 config.yaml、extensions_config.json、skills/、${DEER_FLOW_HOME} 这几项 bind 进容器,配置里其它任何
host_path 在容器内根本不存在 → host_path.exists() 返回 False → 挂载被丢弃。
变量名 host_path 进一步加深了误解:用户会以为「host」指 docker 宿主机,但在这段代码里它其实指的是「跑 LocalSandbox 的那个进程的文件系统」——生产模式下就是 gateway 容器内部。
影响
- 任何依赖 sandbox.mounts 的 skill / 知识库 / 工具包装,本地能跑、上生产就静默失效。
- 故障信号仅在启动日志的 DEBUG/WARNING 一行,agent 侧看到的是空目录而不是工具报错,排查成本很高。
期望行为
至少满足下面一条:
- 响亮失败:当 sandbox.mounts 条目解析到不存在的路径时,启动报 ERROR 并给出明确处理指引(甚至拒绝启动),不要静默丢弃。
- docker-aware 挂载声明:让 sandbox.mounts 成为唯一事实来源,make up 自动把它翻译成 docker-compose 的 bind 挂载,两种模式无需写两份。
- 文档 + 命名修正:最低限度地把 host_path 改成更不易误解的名字(如 source_path / process_path),并在 docs/CONFIGURATION.md 里明确说明:docker 生产模式下,这个路径必须在 gateway
容器内部存在,运维需要自行在 docker-compose.yaml 里加对应的 bind mount。
临时绕过
在 docker/docker-compose.yaml 的 gateway 服务下增加对应的 bind mount:
services:
gateway:
volumes:
- ${DEER_FLOW_REPO_ROOT}/.deer-flow/knowledge:/app/.deer-flow/knowledge:ro
同时把 config.yaml 里的 sandbox.mounts.host_path 改成容器内的路径(本例是 /app/.deer-flow/knowledge),不能写宿主机路径。然后 ./scripts/deploy.sh start 重建 gateway 容器即可生效。
现象
config.yaml里的sandbox.mounts[].host_path在make dev和make up两种模式下语义不一致,且生产模式下挂载失败时没有任何用户可见的错误——挂载会被静默丢弃。结果是:依赖自定义挂载的 skill或工具在本地开发时表现正常,部署到生产后悄无声息地读到空目录。复现步骤
config.yaml:其中 /home/me/project/.deer-flow/knowledge 是宿主机上真实存在的目录,里面放着某个 skill 通过 /mnt/knowledge/... 读取的文件。
两种模式都没有用户可见的报错。生产模式下唯一的信号是 local_sandbox_provider.py 里一条 WARNING 日志(Mount host_path does not exist, skipping),实际运维一般不会盯着看。
根因
LocalSandboxProvider._setup_path_mappings()(backend/packages/harness/deerflow/sandbox/local/local_sandbox_provider.py)把 host_path 当成「gateway 进程所在文件系统里的路径」,并用
host_path.exists() 做门禁:
if host_path.exists():
mappings.append(PathMapping(container_path=..., local_path=str(host_path.resolve()), ...))
else:
logger.warning("Mount host_path does not exist, skipping: ...")
host_path 在容器内根本不存在 → host_path.exists() 返回 False → 挂载被丢弃。
变量名 host_path 进一步加深了误解:用户会以为「host」指 docker 宿主机,但在这段代码里它其实指的是「跑 LocalSandbox 的那个进程的文件系统」——生产模式下就是 gateway 容器内部。
影响
期望行为
至少满足下面一条:
容器内部存在,运维需要自行在 docker-compose.yaml 里加对应的 bind mount。
临时绕过
在 docker/docker-compose.yaml 的 gateway 服务下增加对应的 bind mount:
services:
gateway:
volumes:
- ${DEER_FLOW_REPO_ROOT}/.deer-flow/knowledge:/app/.deer-flow/knowledge:ro
同时把 config.yaml 里的 sandbox.mounts.host_path 改成容器内的路径(本例是 /app/.deer-flow/knowledge),不能写宿主机路径。然后 ./scripts/deploy.sh start 重建 gateway 容器即可生效。