MAATRIX / Блог / Как собрать ИИ-ассистента для команды на Dify

Как собрать ИИ-ассистента для команды на Dify

Как собрать ИИ-ассистента для команды на Dify

MAATRIX

Половина внутренних вопросов в команде — повторяющиеся: где актуальная редакция регламента, что обещали клиенту в договоре, почему деплой падает на этом шаге. Публичный чат-бот тут бесполезен: ваших документов он не знает, а выгружать их в чужой сервис обычно нельзя. Dify ставится на ваш сервер, отвечает по вашим файлам и раздаётся сотрудникам по ролям. Ниже — сборка по шагам: от нарезки документов до ключа API.

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

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

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

Три слоя ассистента и выбор типа приложения

ИИ-ассистент для команды в Dify собирается из трёх слоёв. Модель подключается один раз на всё пространство в «Настройки → Поставщики моделей»; в ветке 1.x поставщики и инструменты — плагины, за них отвечает контейнер plugin_daemon, ставятся из Marketplace или файлом .difypkg. Знания — документы, нарезанные на фрагменты и уложенные в векторное хранилище; база отдельная от приложения и подключается сразу к нескольким ассистентам. Доставка — ссылка, виджет на портале, бот, вызов из CRM.

Следующее решение — тип приложения: от него зависят время отладки и счёт за токены.

ТипЧто этоКому подходитЧем неудобен
Чат-ботпромпт плюс база знанийсправочник по регламентамнет ветвлений и внешних вызовов
Chatflowграф из нод, с памятьюассистент отдела, несколько баздольше собирать и отлаживать
Агентмодель сама выбирает инструментнестандартные запросынепредсказуем, дороже по токенам
Workflowконвейер без истории диалогаразбор писем, отчётыне помнит прошлые сообщения
Генератор текстаодно поле — один ответписьма, описания, переводыконтекст не копится

Начинайте с чат-бота и одной базы. Собирать сразу агента для внутреннего справочника лишнее: он делает два-три вызова модели там, где хватило бы одного, и на жалобе «ассистент придумал пункт регламента» вы не покажете, какой шаг сломался. Есть и техническая деталь: у приложений типа «Агент» Service API отдаёт ответ только потоком, "response_mode": "blocking" не поддерживается, и интеграцию придётся писать под разбор SSE.

База знаний: нарезка, индексация и документы, которые не заработают

На входе — PDF, DOCX, TXT, Markdown, HTML, CSV, синхронизация с Notion или обход сайта. Размеры ограничены в .env: UPLOAD_FILE_SIZE_LIMIT=15 (мегабайты), NGINX_CLIENT_MAX_BODY_SIZE=15M и UPLOAD_FILE_BATCH_LIMIT=5 файлов за раз; первые два поднимают одновременно, иначе API ответит file_too_large.

Метод индексации. «Высокое качество» считает эмбеддинги каждого фрагмента и требует подключённой эмбеддинг-модели: вопрос «сколько дней отпуска на испытательном» найдёт абзац, где эти слова стоят в разных предложениях. «Экономичный» строит индекс по ключевым словам — бесплатно, но переформулировок не понимает. Ассистенту нужен первый режим; сменить метод потом можно только переиндексацией базы.

Сегментация. Задаются разделитель (по умолчанию \n\n), длина фрагмента и перекрытие. Для регламентов рабочая точка — 500–800 токенов с перекрытием около десятой части: короче — ответ теряет контекст пункта, длиннее — растёт счёт. Режим «родитель — потомок» ищет по мелким фрагментам, а в модель отдаёт крупный блок.

Что ломается молча, без единой ошибки в логах:

  • Сканы без текстового слоя. PDF, снятый камерой, извлекается в пустую строку: документ загружен, фрагментов ноль.
  • Таблицы Excel и вёрстка в две колонки. Порядок текста ломается при извлечении. Если таких документов много, переключите парсер: ETL_TYPE=Unstructured с адресом сервиса в UNSTRUCTURED_API_URL разбирает вёрстку аккуратнее штатного dify.
  • Документ навсегда «в очереди». Индексацию выполняет celery-воркер: упал контейнер worker — статус так и висит.

Проверять результат лучше запросом в базу:

cd /opt/dify/docker
docker compose exec -T db psql -U postgres -d dify -c \
  "select name, indexing_status, word_count from documents order by created_at desc limit 5;"
         name          | indexing_status | word_count
-----------------------+-----------------+------------
 reglament-2026.pdf    | completed       |      18420
 prikaz-114-scan.pdf   | completed       |          0
 dogovor-tipovoy.docx  | indexing        |       9310

Статус completed при нуле слов — тот самый скан: его надо прогнать через OCR и залить заново. Имена колонок между версиями меняются — схему покажет \d documents.

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

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

Развернуть Dify

Поиск и цитаты: как не дать ассистенту выдумывать

Настройки поиска живут у базы знаний и переопределяются в ноде поиска внутри Chatflow. Режима три: векторный, полнотекстовый и гибридный. Рабочему ассистенту нужен гибридный — во внутренних документах полно точных маркеров, которые вектор теряет: номера приказов, артикулы, аббревиатуры.

Качество решают два параметра. Top K — сколько фрагментов уйдёт в промпт; для базы регламентов разумно 3–5, лишние разбавляют контекст и тянут токены. Порог релевантности отсекает слабые совпадения, и перегнуть легко в обе стороны: высокий — ассистент на половину вопросов ответит, что ничего не нашёл; нулевой — в промпт поедет случайный абзац, и модель уверенно построит на нём выдумку. Переранжирование (rerank) точность поднимает, но честно: это лишний внешний вызов и ещё один сервис, которому вы отдаёте куски документов.

Прежде чем винить промпт, проверьте выборку: в базе знаний есть тест поиска, задайте туда пять реальных вопросов сотрудников. Не находится здесь — до модели дело не дойдёт, она не исправляет плохую выборку, а красиво её пересказывает. Включите и показ источников: сотрудник откроет первоисточник сам, а вы увидите, что процитирована старая редакция. Рамку задайте в системном промпте:

Отвечай только по фрагментам из блока «Контекст».
Если ответа в контексте нет — так и скажи: «В базе знаний этого нет»,
и подскажи, к кому обратиться. Не додумывай номера пунктов, суммы и даты.
В конце ответа перечисли названия документов, на которые опирался.

Роли, приглашения и почта: кто и что может внутри

Сотрудники приглашаются по почте в настройках рабочего пространства, каждому назначается роль.

РольЧто может
Владелецвсё, включая передачу прав и удаление пространства
Администраторприглашать и удалять участников, создавать и править приложения
Редакторправить приложения и базы знаний, участников не трогает
Обычный участникпользоваться приложениями, не редактировать
Оператор баз знанийтолько базы знаний, приложения недоступны

Пара «редактор — обычный участник» и позволяет отдать ассистента отделу, не раздавая право переписывать промпты, а оператор баз знаний — это кадровик или юрист, который обновляет документы и не трогает логику.

Здесь же засада самостоятельной установки — письма не уходят: пока в .env не задан почтовый транспорт, приглашение отправить нельзя, ссылку передают вручную. Вторая, обиднее: если внешние адреса не заполнены, приглашения, публикация приложения и предпросмотр файлов ссылаются на localhost.

CONSOLE_API_URL=https://ai.example.com
CONSOLE_WEB_URL=https://ai.example.com
SERVICE_API_URL=https://ai.example.com
APP_API_URL=https://ai.example.com
APP_WEB_URL=https://ai.example.com
FILES_URL=https://ai.example.com

MAIL_TYPE=smtp
SMTP_SERVER=smtp.example.com
SMTP_PORT=465
SMTP_USE_TLS=true
SMTP_USERNAME=dify@example.com
SMTP_PASSWORD=...
MAIL_DEFAULT_SEND_FROM=dify@example.com
INVITE_EXPIRY_HOURS=72

Приглашение живёт трое суток. Если сервер смотрит в интернет, проверьте в .env.example наличие ALLOW_REGISTER и ALLOW_CREATE_WORKSPACE и выставьте их в false.

Границы community-редакции называю прямо: SSO, LDAP и групповых политик доступа в ней нет — это функции платных изданий. Права выдаются на пространство целиком, разграничить «этот отдел видит только своего ассистента» штатно не выйдет. Нужна корпоративная авторизация — её ставят снаружи, прокси с OIDC перед nginx.

Каналы доставки и первая неделя эксплуатации

Веб-приложение. После «Опубликовать» приложение получает адрес вида https://ai.example.com/chat/<токен> — отдельный интерфейс без доступа к редактору. Его добавляют в раздел «Исследовать»: сама ссылка — по сути секрет, знающий её попадёт в чат.

Виджет на портале. Встраивается двумя тегами, кнопка появляется в углу страницы:

<script>
  window.difyChatbotConfig = { token: 'ВАШ_ТОКЕН', baseUrl: 'https://ai.example.com' }
</script>
<script src="https://ai.example.com/embed.min.js" id="ВАШ_ТОКЕН" defer></script>

Если вы сужали WEB_API_CORS_ALLOW_ORIGINS (по умолчанию там *), впишите домен интранета — иначе виджет не откроется, а причина будет видна только в консоли браузера как ошибка CORS.

Service API. Самый гибкий путь: бот в Telegram, кнопка в CRM, вызов из n8n. Ключ вида app-... выдаётся отдельно для каждого приложения в разделе «Доступ к API».

curl -s -X POST 'https://ai.example.com/v1/chat-messages' \
  -H 'Authorization: Bearer app-XXXXXXXXXXXXXXXX' \
  -H 'Content-Type: application/json' \
  -d '{"inputs":{},"query":"Сколько дней отпуска на испытательном сроке?",
       "response_mode":"blocking","conversation_id":"","user":"tg-482913"}'
{
  "event": "message",
  "message_id": "9da23599-e713-473b-982c-4328d4f5c78a",
  "conversation_id": "45701982-8118-4bc5-8e9b-64562b4555f2",
  "mode": "chat",
  "answer": "На испытательном сроке отпуск начисляется в общем порядке...",
  "metadata": { "usage": { "total_tokens": 1843 }, "retriever_resources": [] },
  "created_at": 1793457221
}

conversation_id хранит ваша сторона: не передадите его следующим запросом — диалог начнётся с нуля. Поле user обязательно: шлёт бот всем одинаковое значение — истории склеятся в один диалог, а логи станут нечитаемыми.

Ошибки читаются по коду в теле, а не по HTTP-статусу: provider_not_initialize — у поставщика модели не заданы учётные данные; app_unavailable — приложение не опубликовано или ключ от другого приложения; provider_quota_exceeded — кончился баланс у провайдера; invalid_param — чаще всего забытое поле user. Пустой заголовок даст Authorization header must be provided and start with 'Bearer'.

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

Про приватность честно: документы, векторы и логи остаются у вас, но при облачной модели текст вопроса вместе с фрагментами регламентов периметр всё же покидает. Токены при этом тратятся в трёх местах: вызовы LLM, эмбеддинг каждого вопроса при поиске и разовая индексация базы.

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

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

Честный минимум: 2 vCPU, 4 ГБ RAM, 40–60 ГБ NVMe. Ровно столько документация Dify называет минимальными требованиями, и ассистент отдела в 10–20 человек на этом живёт. Ограничения прямо: индексация — самый прожорливый момент, загрузка сотни документов разом на 4 ГБ может кончиться воркером, убитым по OOM, и ставить рядом что-то ещё уже нельзя. Диск набирается быстро: образы весят несколько гигабайт, плюс оригиналы файлов в volumes/app/storage и векторный индекс.

Комфортный вариант: 4 vCPU, 8 ГБ RAM, 80–100 ГБ NVMe. Спокойно переносится переиндексация базы на тысячи фрагментов, десятки одновременных диалогов, соседний контейнер с ботом и обновления без гадания, хватит ли памяти. Решите заменить облачные эмбеддинги локальными — считайте по весам: bge-m3 это 568 млн параметров, в fp16 порядка 1,1 ГБ весов плюс рантайм. Под локальную генеративную модель нужен отдельный сервер с GPU.

Локация — Великобритания, Лондон. Британский адрес нормально принимается API OpenAI, Anthropic и Google: региональных отказов, из-за которых ключи «не работают» с российского IP, здесь нет. При этом Лондон близко к пользователям — RTT из Москвы до него в разы меньше, чем до Нью-Йорка, а каждое сообщение проходит несколько последовательных обращений подряд. Плюс европейская юрисдикция и соседство с контуром GDPR — аргумент для службы безопасности. Работаете по 152-ФЗ — сам Dify ставят в российской локации, а к зарубежным моделям ходят отдельным маршрутом.

Заказ устроен просто. Dify есть в каталоге приложений apps.maatrix.io и разворачивается автоматически при заказе сервера — вручную ставить Docker и править compose-файл не нужно; работает на Ubuntu и Debian. Адрес панели и ключи появятся в личном кабинете, в разделе «Доступ»: заходите, создаёте администратора и подключаете модель. Оплата — картой российского банка, по СБП, криптовалютой или токеном MAAT; иностранная карта не нужна, хотя сервер стоит в Лондоне.

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

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

Развернуть Dify

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

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

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

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

Ассистент отвечает «не нашёл в документах», хотя нужный абзац в базе есть.

Проверяйте не промпт, а выборку: откройте тест поиска в базе знаний и задайте тот же вопрос. Пусто — дело в индексации или пороге релевантности: понизьте порог, поднимите Top K до 5, переключитесь на гибридный режим. Фрагменты находятся, а ассистент их игнорирует — смотрите разбор прогона: обычно нода поиска подключена не к той базе.

Как дать отделу кадров обновлять регламенты, но не дать сломать ассистента?

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

Можно ли подключить ассистента к Telegram?

Штатного коннектора в community-редакции нет, делается через Service API: бот шлёт POST /v1/chat-messages с ключом app-..., передавая Telegram user id в поле user и сохраняя conversation_id. Тот же путь работает через n8n, если писать код не хочется.

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

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