📎 配套脚本附件正文未内嵌 · 共 6 个 · 点开可在线查看
📚 另需公共库(正文未重复内嵌,点名字直接看):_agent_cfg.py、_bootstrap.py、_config.py、_plugin_util.py
📂 查看全部 761 个脚本附件 →
⚠️ 脚本内含明文凭据(与本页正文一致),请勿在公网环境直接运行。
Dify · 专题七

Agent Shell provider 修复

沙箱、ssrf_proxy、工具节点。步骤未改,代码块已换成完整程序。

Dify_专题七_Agent_shell_provider修复_cursor.md

这是基于原文生成的 Cursor 版文档。

原文件未改动;本版只做三件事:统一阅读入口、修正已审出的明显问题、补充更适合快速排障的提示。

Cursor 版修订说明

阅读提示

运行环境:业务脚本均在 207 固定目录 /opt/workbuddy 执行,Python 为 /usr/bin/python3。 Windows 开发机仅保留 _probe_* 调试脚本;同步命令:python scripts_cursor/完整代码约定/00_部署_workbuddy到207.py。


原文主体(已纳入 Cursor 版纠错)

专题七:Agent 应用 Shell Provider 修复(DifyShellLayer)

阅读导航(何时打开 · 报错往哪查)

本文是单点修复/排障专题,配合《安装配置手册》使用;下文实战步骤与代码全部保留,此处只做索引。

现象本文还可能是
Agent 开了 Shell 但执行失败§2 三步确认sandbox/ssrf_proxy 未起
工作区看不到 Shell provider§2.1 安装plugin_daemon

本专题从原《Dify_专题九_其它配置问题解决.md》的第五节独立出来,专门讲:

在 Dify 里创建带 Shell / 代码执行能力的 Agent 应用时,shell 类工具报错或不可用 这一配置问题的解决。

所有涉及的配置、命令、检查手法均详细列出。本文是 Agent 应用侧配置,不是插件代码改动。


0. 背景:Dify 里的 Shell 执行是怎么跑起来的

Dify 的 Agent 应用要执行 Shell 命令 / 运行代码,依赖一个「Shell / 代码执行」provider。 本环境安装并启用的 provider 名为 DifyShellLayer(出现在 Agent 应用「工具」面板里的 provider 条目)。

它底层由 Dify 的沙箱体系承接,相关容器在 docker-compose.yml 里是几组独立服务:

容器职责
sandbox真正在隔离环境里跑代码的沙箱(gVisor / seccomp 隔离)
ssrf_proxy出站代理;Shell 工具里若要访问外网,请求走它
plugin_daemon插件 / provider 运行时;DifyShellLayer 这类 provider 由它加载与注册

所以「Shell 工具不可用」通常是下面三层任意一层没就绪:

  1. provider 没在 Agent 应用里启用;
  2. sandbox 服务没起来;
  3. plugin_daemon 没加载 / 注册该 provider。

排查与修复就按这三层逐层确认即可。


1. 现象(本次部署遇到的)

创建带「代码执行 / Shell」能力的 Agent 应用时:


2. 解决步骤(按顺序来)

2.1 确认底层沙箱服务在跑

完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.sh

bash
#!/usr/bin/env bash
set -euo pipefail

cd /dify/dify-main/docker
docker compose ps | grep -E "sandbox|ssrf_proxy|plugin_daemon"

正常应看到 sandbox、ssrf_proxy、plugin_daemon 状态都是 Up。

若 sandbox / ssrf_proxy 没起来,先把它们拉起:

完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_02.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_02.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker compose up -d sandbox ssrf_proxy
docker compose ps | grep -E "sandbox|ssrf_proxy"

2.2 确认 plugin_daemon 加载了 DifyShellLayer

完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_03.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_03.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker logs docker-plugin_daemon-1 --tail 80 | grep -iE "shell|DifyShellLayer|provider"
bash
docker compose restart plugin_daemon

2.3 核对 sandbox 端点环境变量(以实际 .env 为准)

Dify 服务通过环境变量找到 sandbox 与 ssrf 代理。在 docker/.env(或 docker-compose.yml 的 environment 段)里应有类似:

完整 .env 片段:patches_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.env

运行/引用:patches_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.env

env
# ---- docker/.env(Dify 服务侧)----
SANDBOX_API_KEY=dify-sandbox-not-secret
SANDBOX_URL=http://sandbox:8194
SSRF_PROXY_HTTP_URL=http://ssrf_proxy:3128
SSRF_PROXY_HTTPS_URL=http://ssrf_proxy:3128

⚠️ 关键点:SANDBOX_URL / SSRF_PROXY_HTTP_URL 必须指向 compose 服务名(sandbox、ssrf_proxy),

不要写成 localhost 或 127.0.0.1。因为 Dify 的 api / worker 容器与 sandbox 不在同一网络命名空间,

写 localhost 在 Dify 容器里解析到的是容器自己,连不到 sandbox,Shell 工具就会 no available node。

改完 .env 后需重建相关服务让环境变量生效:

bash
docker compose up -d

2.4 在 Agent 应用里启用 DifyShellLayer(控制台侧)

以下是控制台操作的文字版(对应截图步骤):

  1. 打开 http://192.168.110.207/console,进入目标 Agent 应用;
  2. 进入「工具 / Tools」配置页;
  3. 在「已安装 Provider / 自定义工具」列表里找到 DifyShellLayer;
  4. 点击「添加 / 启用」,确认其状态变为「可用 / Enabled」;
  5. 回到 Agent 编排界面,把该 Shell / 代码执行工具挂到 Agent 的 tool 列表;
  6. 点「保存 / 发布」。

这一步是 Agent 应用侧配置,不是插件代码改动。只要 provider 已被 plugin_daemon 加载、sandbox 已起,

这里启用后即可用。无需改任何插件源码。

新 Agent 工作室经常对不上上面的截图路径。 找不到「工具 / 已安装 Provider」时,用控制台 API 确认插件已加载,再看快照里有没有挂上该工具:

完整程序:/opt/workbuddy/scripts_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.py

运行/引用:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.py

python
from dify_api import Dify, pp
import json

d = Dify("38205036@qq.com", "Difyai123456")
d.login()

# 1) 工作区已安装的工具 provider(DifyShellLayer 应出现在列表里)
for path in (
    "/workspaces/current/tools/builtin",
    "/workspaces/current/tool-providers",
    "/workspaces/current/tools",
):
    r = d.get(path)
    print("GET", path, "HTTP", r["code"], (r["text"] or "")[:300])

# 2) 新 Agent 真正生效的是快照,不是旧版 model_config
out, err = d.sh(
    "docker exec docker-db_postgres-1 psql -U postgres -d dify -t -A -c "
    "\"SELECT config_snapshot::json -> 'tools' "
    "FROM agent_config_snapshots "
    "WHERE id='01a0135c-320e-79dc-97a0-94da9b904c13'\""
)
print("snapshot.tools", out or err)
d.close()

哪个 GET 是 200,就以哪个为准(Dify 小版本路径会变)。snapshot.tools 里应能看到 Shell 相关项。改快照后 不要点发布(专题十一)。

2.5 验证

在 Agent 对话里发一条会触发 Shell 的指令(如「用 python 算一下 1+1」),观察:


3. 排错清单


4. 本次实战(真实遇到的情况)

本次部署中,创建带 Shell 能力的 Agent 时该工具一度不可用。按统一维度拆解。

现象:Agent 的「工具」面板里 Shell / 代码执行类工具显示为不可用 / 添加失败;或添加成功但运行时报 provider not ready / no available node。

定位(逐层排查):

  1. docker compose ps 确认 sandbox / ssrf_proxy / plugin_daemon 状态;
  2. docker logs docker-plugin_daemon-1 --tail 80 | grep -iE "shell|DifyShellLayer|provider" 看是否加载;
  3. 核对 .env 里 SANDBOX_URL。

改了什么(逐条):

完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_04.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_04.sh

bash
#!/usr/bin/env bash
set -euo pipefail

  cd /dify/dify-main/docker && docker compose up -d sandbox ssrf_proxy
  

怎么重启服务: 完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_05.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_05.sh

bash
#!/usr/bin/env bash
set -euo pipefail

cd /dify/dify-main/docker
docker compose up -d sandbox ssrf_proxy        # 拉起沙箱
docker compose restart plugin_daemon           # 重新注册 provider
docker compose up -d                            # 让 .env 改动生效

怎么看日志: 完整脚本:scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_06.sh

运行/引用:bash /opt/workbuddy/scripts_bash_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_06.sh

bash
#!/usr/bin/env bash
set -euo pipefail

docker logs docker-plugin_daemon-1 --tail 80 | grep -iE "shell|DifyShellLayer|provider"
# 看到 load/register 日志、无报错 → provider 就绪

怎么判断正常:在 Agent 对话里发一条会触发 Shell 的指令(如「用 python 算一下 1+1」),工具被正常调用、返回结果正确;仍报 no available node 则回到 plugin_daemon 日志看 sandbox 是否真起、SANDBOX_URL 是否指对。

一句话:Shell 工具不可用 = provider 启用 + sandbox 服务 + SANDBOX_URL 指向 compose 服务名,三层都齐才行。


关联文档:

- 原收录于《Dify_专题九_其它配置问题解决.md》(专题九),现已独立成此专题;

- 插件代码级修复见《Dify_专题八_插件patch_future顺序子坑.md》(专题八);

- 知识库 / 向量库相关见《Dify_专题六_weaviate中继.md》(专题六)。


本专题 Python 程序一览

| 文件 | 用途 | |---|---|| 专题七_01_检查沙箱服务.py | 专题七 · 步骤 1:检查 sandbox / ssrf_proxy / plugin_daemon 服务状态。 | | 专题七_02_查ShellProvider与快照.py | 专题七 · 步骤 2:查工作区 Shell Provider 与新 Agent 快照 tools 配置。 |

专题七_01_检查沙箱服务.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题七_Agent_shell_provider修复/专题七_01_检查沙箱服务.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题七 · 步骤 1:检查 sandbox / ssrf_proxy / plugin_daemon 服务状态。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
from dify_api import Dify


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        out, err = d.sh(
            "cd %s && docker compose ps | grep -E 'sandbox|ssrf_proxy|plugin_daemon'" % C.DIFY_DOCKER_DIR
        )
        print(out or err)
        out2, _ = d.sh(
            "docker logs %s --tail 80 2>&1 | grep -iE 'shell|DifyShellLayer|provider'" % C.PLUGIN_CONTAINER
        )
        print("\n=== DifyShellLayer 相关日志 ===")
        print(out2.strip() or "(无匹配)")
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())

专题七_02_查ShellProvider与快照.py

运行:/usr/bin/python3 /opt/workbuddy/scripts_cursor/Dify_专题七_Agent_shell_provider修复/专题七_02_查ShellProvider与快照.py

完整补丁:patches_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.py

运行/引用:见 patches_cursor/Dify_专题七_Agent_shell_provider修复/专题七_内联_01.py

python
#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""专题七 · 步骤 2:查工作区 Shell Provider 与新 Agent 快照 tools 配置。"""
from __future__ import annotations

import sys

import _bootstrap  # noqa: F401
import _config as C
from dify_api import Dify


def main() -> int:
    d = Dify(C.DIFY_EMAIL, C.DIFY_PASSWORD)
    try:
        d.login()
        for path in (
            "/workspaces/current/tools/builtin",
            "/workspaces/current/tool-providers",
            "/workspaces/current/tools",
        ):
            r = d.get(path)
            print("GET", path, "HTTP", r["code"], (r["text"] or "")[:300])
        out, err = d.sh(
            "docker exec %s psql -U postgres -d dify -t -A -c "
            "\"SELECT config_snapshot::json -> 'tools' "
            "FROM agent_config_snapshots WHERE id='%s'\"" % (C.PG_CONTAINER, C.SNAP_ID)
        )
        print("\nsnapshot.tools:", out.strip() or err)
        return 0
    finally:
        d.close()


if __name__ == "__main__":
    sys.exit(main())