MAATRIX / Блог / Dify не подключается к модели: причины и решение

Dify не подключается к модели: причины и решение

Dify не подключается к модели: причины и решение

MAATRIX

Ключ вставлен, провайдер сохранён, а приложение отвечает «модель недоступна» или падает на первом запуске. В ветке 1.x между Dify и провайдером стоят ещё два звена — демон плагинов и сам плагин-провайдер, — поэтому жалоба «dify не видит модель» одинаково звучит для пяти разных поломок. Разберём по слоям — с дословными текстами ошибок и командами, которые показывают, где порвалось.

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Четыре слоя между Dify и моделью

До версии 1.0 список провайдеров был вшит в бэкенд. Сейчас каждый провайдер — плагин, и цепочка длиннее: браузер → apiplugin_daemon → процесс плагина со своим окружением Python → запрос к API провайдера. Ломается любое звено, а в консоли выглядит одинаково.

Начинайте не с настроек, а с состояния стека и журнала демона:

cd /opt/dify/docker
docker compose ps --format 'table {{.Service}}\t{{.Status}}'
docker compose logs --tail 80 plugin_daemon
docker compose logs --tail 200 api | grep -iE 'plugin|credential|provider'

Если plugin_daemon не Up или ходит по кругу перезапусков, дальше по настройкам идти бессмысленно: проверить ключ физически нечем. Иначе ориентируйтесь по тексту ошибки — он указывает на слой.

Что видноГде рвётсяКуда смотреть
Credentials validation failedплагин → провайдерключ, base URL, регион сервера
PluginInvokeError с InvokeConnectionErrorплагин → сетьDNS, адрес локальной модели, фаервол
Ошибка со словом plugin daemon, 500 при сохраненииapiplugin_daemonдемон, переменные PLUGIN_*
Провайдер настроен, но модели нет в приложениивнутри apiрабочее пространство, тип модели
403 unsupported_country_region_territoryпровайдерлокация и IP сервера

Деталь, экономящая часы: исходящий запрос делает не контейнер api, а процесс плагина внутри plugin_daemon. Сеть и ключи проверяйте изнутри того контейнера, который реально звонит наружу.

«Credentials validation failed»: что делает кнопка сохранения

Кнопка «Сохранить» — не запись в базу, а живой запрос: Dify передаёт ключ демону, тот запускает у плагина проверку учётных данных, плагин делает настоящий вызов к API. Текст ошибки почти всегда пришёл снаружи, и читать его надо с конца, из вложенного message:

[openai] Error: PluginInvokeError: {"args":{},"error_type":"CredentialsValidateFailedError",
"message":"Error code: 401 - {'error': {'message': 'Incorrect API key provided: sk-pr*****XYZ.
You can find your API key at https://platform.openai.com/account/api-keys',
'type': 'invalid_request_error', 'code': 'invalid_api_key'}}"}

Код внутри сообщения раскладывает причину: 401 — ключ неверный или обрезанный, 403 — доступ запрещён, чаще всего по региону, 404 — не найдена модель или неправильный base URL, 429 — квота либо лимит. Кавычки, $ или { в маскированном ключе — скопировали лишнее: копипаст из мессенджера приносит перенос строки и неразрывный пробел U+00A0. Сверьте длину: проектные ключи OpenAI sk-proj- заметно длиннее ста символов, ключ Anthropic sk-ant-api03- — около ста восьми.

Самая честная проверка — повторить запрос руками из того же контейнера, минуя Dify:

read -rs KEY                      # ключ не попадёт в history
docker compose exec -T -e K="$KEY" plugin_daemon python3 - <<'EOF'
import os, urllib.request, urllib.error
req = urllib.request.Request(
    "https://api.openai.com/v1/models",
    headers={"Authorization": "Bearer " + os.environ["K"]})
try:
    print("OK", urllib.request.urlopen(req, timeout=15).status)
except urllib.error.HTTPError as e:
    print("HTTP", e.code, e.read()[:400].decode())
except Exception as e:
    print("NET", type(e).__name__, e)
EOF

Исходы читаются однозначно: OK 200 — ключ и сеть в порядке, причина внутри Dify; HTTP 401/403 — дословный вердикт провайдера; NET — контейнер не дошёл до сети, вопрос к DNS и фаерволу.

И главная развилка: валидация провайдера и вызов модели — разные запросы. Плагин при сохранении дёргает список моделей, приложение просит конкретную. Проектный ключ OpenAI видит /v1/models, но не имеет доступа к нужной модели: провайдер сохранится зелёным, а запуск вернёт 404 The model 'gpt-4o' does not exist or you do not have access to it.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Dify

Провайдер настроен, а модели нет в списке приложения

Здесь ключи ни при чём — модель просто не доезжает до выпадающего списка. Причин пять, каждая проверяется за минуту.

Не то рабочее пространство. Ключи привязаны к workspace, а не к аккаунту: коллега в собственном пространстве видит пустой список при полностью настроенном соседнем. Переключатель — в левом нижнем углу консоли. Что лежит в базе:

docker compose exec -T db psql -U postgres -d dify -c '\dt' | grep -i provider
docker compose exec -T db psql -U postgres -d dify \
  -c "select tenant_id, provider_name, provider_type, is_valid from providers order by tenant_id;"

Несколько разных tenant_id — верный признак, что ключ введён не там.

Не тот тип модели. Плагин объявляет модели по типам: llm, text-embedding, rerank, speech2text, tts, moderation. Узел LLM показывает только llm, база знаний в режиме High Quality — только эмбеддинги. Отсюда классическое «модель есть, а база знаний не создаётся»: у подключённого провайдера эмбеддингов нет вовсе. Системные роли разобраны в материале про установку и настройку Dify на VPS — незаполненные роли оставляют приложения без модели по умолчанию.

Кастомный провайдер без каталога. У совместимого с OpenAI API провайдера готового списка нет: каждую модель добавляют вручную, имя — ровно такое, какого ждёт сервер, плюс четыре поля: тип, режим (Chat или Completion), Model context size, Upper bound for max tokens. Опечатка в имени даёт 404 на первом вызове, неверный режим — 404 на другом пути, пустой размер контекста — молча обрезанные промпты.

Модель не умеет того, что просит узел. Агентному узлу нужна поддержка function calling; без неё модель не появится в подборе инструментов или проигнорирует вызовы. Обходной путь — режим ReAct: работает на любых моделях, но многословнее.

Приложение помнит удалённую модель. Оно хранит связку «провайдер + имя». После обновления или переустановки плагина модель в редакторе подсвечивается красным, а запуск падает на model not found. Правится руками: открыть селектор, выбрать модель заново, сохранить — в каждом узле каждого воркфлоу.

Ollama, vLLM и OpenAI-совместимый endpoint

Локальные модели дают отдельный класс отказов с узнаваемым симптомом:

[ollama] Error: PluginInvokeError: {"args":{},"error_type":"InvokeConnectionError",
"message":"HTTPConnectionPool(host='localhost', port=11434): Max retries exceeded with url:
/api/chat (Caused by NewConnectionError('...: Connection refused'))"}

Адрес localhost внутри контейнера ведёт в сам контейнер. Правильный — http://host.docker.internal:11434, и работает он только с пробросом шлюза. Запрос уходит от plugin_daemon, значит, extra_hosts нужен ему. Правку кладите в docker-compose.override.yaml, чтобы её не стёр git pull:

services:
  plugin_daemon:
    extra_hosts:
      - "host.docker.internal:host-gateway"

Ollama слушает только петлю. По умолчанию демон биндится на 127.0.0.1, и хост-гейтвей до него не достучится:

ss -lntp | grep 11434        # видите 127.0.0.1:11434 — вот и причина
sudo systemctl edit ollama   # [Service] Environment="OLLAMA_HOST=0.0.0.0:11434"
sudo systemctl restart ollama

После этого порт торчит наружу — сразу закройте его для всех, кроме докерных подсетей:

sudo ufw allow from 172.16.0.0/12 to any port 11434 proto tcp
sudo ufw deny 11434/tcp

Модель не скачана или названа иначе. Имя в Dify обязано совпадать с тегом до символа, включая :8b или :latest; сверяйтесь с ollama list, иначе получите model 'llama3.1' not found, try pulling it first. Как поднять сам движок — в статье про локальный запуск LLM через Ollama.

Base URL для OpenAI-совместимых серверов. Dify дописывает путь сам, и это главный источник 404 при подключении vLLM, llama.cpp или шлюза. Правильная форма — http://host.docker.internal:8000/v1. Полный путь .../v1/chat/completions превращается в /v1/chat/completions/chat/completions, адрес без /v1 — в /chat/completions; оба дают 404, который легко принять за «не та модель». Сервер проверяйте отдельно: curl -s http://127.0.0.1:8000/v1/models | head -c 200. Нужны единые ключи с бюджетами на несколько провайдеров — ставьте между Dify и моделями шлюз, его грабли собраны в разборе, почему LiteLLM не видит API-ключи.

Честное ограничение: Dify рядом с локальной моделью на одной машине — сложение требований, а не экономия; расчёт по памяти — в последнем разделе.

Ключ принят, а провайдер всё равно отказывает

Бывает, что значение доехало идеально и сеть в порядке, а отказ приходит всё равно. Причины уже не в Dify.

География IP. С российского адреса зарубежные провайдеры отвечают отказом независимо от ключа:

Error code: 403 - {'error': {'code': 'unsupported_country_region_territory',
'message': 'Country, region, or territory not supported'}}

Gemini возвращает {"code": 400, "message": "User location is not supported for the API use.", "status": "FAILED_PRECONDITION"}, Anthropic — сухой 403 без подробностей. Перевыпуск ключа не поможет: смотрят на исходящий адрес. В то же самое упирается и маркетплейс плагинов — провайдера может не выйти даже установить. Сторонний HTTP-прокси спасает (обход гео-блокировок нейросетей), но это лишняя точка отказа и чужой узел, через который идут ваши ключи.

Квота против лимита. Ответ insufficient_quota с кодом 429 — пустой баланс, а не превышение скорости; настоящий рейт-лимит приходит как rate_limit_exceeded. Разница важна: встроенные повторы в узле LLM лечат второе и бесполезны против первого.

Область действия ключа. Проектные ключи OpenAI sk-proj- привязаны к проекту: модель, не включённая в его настройках, отдаёт 404, а не 401. У ключа с урезанными правами будет Missing scopes. Это не «Dify не видит ключ», а «ключ не тот».

Azure OpenAI требует другого. Кроме ключа нужны endpoint, api_version и имя деплоймента вместо имени модели. Подставили gpt-4o туда, где ждут имя вашего деплоя, — получите DeploymentNotFound, выглядящий как отсутствующая модель.

DNS и таймауты. Если контейнер не резолвит имена, отказ выглядит как проблема ключа: docker compose exec plugin_daemon sh -c 'getent hosts api.openai.com || echo NO-DNS'. А длинные ответы рвутся по своему потолку: вызов плагина ограничен PLUGIN_MAX_EXECUTION_TIMEOUT (по умолчанию 600 секунд), и упёршаяся в него генерация обрывается на середине без внятного текста.

Всё работало и отвалилось: plugin_daemon, dify_plugin и SECRET_KEY

Отдельный сценарий: вчера модели отвечали, сегодня провайдеров нет вовсе или все вызовы падают. Почти всегда это последствие обновления или переноса.

Демону нужна собственная база. Он ходит в Postgres не в dify, а в базу из переменной DB_PLUGIN_DATABASE (по умолчанию dify_plugin). На чистом томе она создаётся при инициализации, при обновлении со старой ветки на существующем томе — нет. Демон уходит в цикл перезапусков, и в журнале одна строка причины:

FATAL: database "dify_plugin" does not exist

Лечится одной командой, данные не страдают:

docker compose exec -T db psql -U postgres -c "CREATE DATABASE dify_plugin;"
docker compose restart plugin_daemon

Ключи между сервисами разъехались. PLUGIN_DAEMON_KEY со стороны api обязан совпадать с SERVER_KEY демона, а PLUGIN_DIFY_INNER_API_KEY обслуживает обратный канал по адресу PLUGIN_DIFY_INNER_API_URL (по умолчанию http://api:5001). Расхождение даёт отказы авторизации при каждом вызове модели, хотя ключ провайдера верный. Меняете значение — меняйте с обеих сторон и пересоздавайте контейнеры (docker compose down && docker compose up -d): restart окружение не перечитывает.

Установка провайдера падает по таймауту. Демон собирает для плагина отдельное окружение Python и тянет зависимости из pypi.org. На слабом процессоре или узком канале это не укладывается в PLUGIN_PYTHON_ENV_INIT_TIMEOUT — по умолчанию 120 секунд; поднимите до 300–600 и пересоздайте контейнер. Туда же смотрите при «плагин установился, но не работает»: окружения живут в PLUGIN_WORKING_PATH (/app/storage/cwd), весят немало, и забитый диск ломает их молча — df -h, du -sh volumes/plugin_daemon.

Потеряли SECRET_KEY. Ключи провайдеров хранятся в базе зашифрованными, корень цепочки — SECRET_KEY из docker/.env. Скопировали при обновлении .env.example поверх рабочего файла или перенесли базу без .env — расшифровать старые записи нельзя ничем, кроме прежнего значения: провайдер числится настроенным, а вызовы падают. Выхода два — вернуть старый SECRET_KEY либо удалить провайдера и ввести ключи заново. Проверить, что он есть: grep '^SECRET_KEY=' /opt/dify/docker/.env. Остальные поломки стека — в разборе частых ошибок Dify на сервере.

Какой сервер под Dify брать в MAATRIX

Большинство причин выше конфигурационные, но три упираются в железо и адрес: региональный 403, таймаут сборки окружения плагина и OOM, уносящий демон плагинов. Последнее в интерфейсе выглядит именно как «Dify не видит модель», хотя это нехватка памяти.

Честный минимум: 2 vCPU, 4 ГБ RAM, 40–50 ГБ NVMe. Документационный порог: Postgres, Redis, векторная база, фронтенд, api, worker и демон плагинов на одной машине. Работать будет, но каждый провайдер — ещё один процесс со своим окружением Python: пять плагинов на четырёх гигабайтах уже ощутимы, а всплеск при индексации выбивает самый жирный. Swap тут не опция, а обязательный элемент; плагинов держите минимум.

Комфортный вариант: 4 vCPU, 8 ГБ RAM, 80 ГБ NVMe. Десяток плагинов, параллельные вызовы и сборка зависимостей перестают мешать друг другу, а таймауты установки уходят сами. Локальную модель считайте отдельно: 8 ГБ на платформу плюс ~4,5 ГБ весов модели 7B в Q4 — уже 16 ГБ, и практичнее вынести инференс на вторую машину.

Локация — Великобритания, Лондон. Прямое следствие раздела про 403: с британского адреса OpenAI, Anthropic и Google отвечают штатно, маркетплейс плагинов открывается, а до пользователей в Европе и России ближе, чем через Атлантику. Соседство с GDPR-контуром — плюс, когда в базу знаний идут документы европейских контрагентов; Франция подходит по тем же причинам. США берут ради американского IP и чистой подсети. Россия оправдана в одном сценарии: данные обязаны оставаться в РФ по 152-ФЗ и модели российские — тогда за плагинами всё равно идти через прокси.

Заказ занимает несколько минут: выбираете локацию и конфигурацию, отмечаете Dify из каталога apps.maatrix.io — приложение разворачивается автоматически на Ubuntu или Debian, вручную ставить стек не нужно. Адрес консоли и стартовые доступы появятся в личном кабинете, в разделе «Доступ». Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT. Первым делом после входа сохраните SECRET_KEY из docker/.env в менеджер паролей: это единственное, что не восстановить, когда ключи вдруг перестанут расшифровываться.

Развернуть за пару минут

Готовый образ на VPS MAATRIX: NVMe, AMD EPYC, root-доступ. Локации UK, США, Франция и РФ. Оплата картой РФ и по СБП.

Развернуть Dify

Обсудить статью, задать вопрос или начать новую тему

Есть вопрос по этой статье, идея для обсуждения или просто хотите поделиться опытом? Сообщество MAATRIX ждёт. Для общения, пожалуйста, зарегистрируйтесь в нашем личном кабинете.

Перейти в сообщество →

Частые вопросы

Ключ проходит проверку, но приложение пишет, что модель недоступна. Почему?

Валидация дёргает список моделей, а приложение просит конкретную — это разные запросы. Строка The model ... does not exist or you do not have access to it в логах api означает права ключа на модель или опечатку в имени, а не проблему с ключом.

Перенёс Dify на другой сервер: провайдеры числятся настроенными, но ни один вызов не работает.

Почти наверняка база приехала без прежнего SECRET_KEY из docker/.env — ключи зашифрованы и не расшифровываются. Верните старое значение либо удалите провайдеров и введите ключи заново.

Ollama стоит на том же сервере, а Dify отвечает Connection refused.

Нужны два условия сразу: адрес провайдера http://host.docker.internal:11434 и проброс extra_hosts: - "host.docker.internal:host-gateway" у сервиса plugin_daemon. Плюс сам Ollama обязан слушать не петлю — Environment="OLLAMA_HOST=0.0.0.0:11434" в юните, а порт 11434 закрыт фаерволом для всех, кроме докерных подсетей.

Нужны сами нейросети для контента?

Генерируйте изображения, видео и озвучку нейросетями на falapi.io — десятки моделей в одном окне. Оплата картой РФ и по СБП.