Files
microdao-daarion/agromatrix_stepan_noda1_APPLY.md
Apple fa749fa56c chore(infra): add NODA2 setup files, docker-compose configs and root config
- AGENTS.md: Sofiia Chief AI Architect role definition
- SOFIIA_IN_OPENCODE.md, SOFIIA_NODA2_SETUP.md: NODA2 setup documentation
- agromatrix_stepan_noda1_APPLY.md, agromatrix_stepan_noda1_prod.patch: AgroMatrix production patch
- docker-compose.memory-node2.yml: memory service for NODA2
- docker-compose.node2-sofiia-supervisor.yml: sofiia supervisor for NODA2
- gateway-bot/gateway_boot.py, monitor_prompt.txt, vision_guard.py: gateway extras
- models/Modelfile.qwen3.5-35b-a3b: Qwen model definition for NODA3
- opencode.json: OpenCode providers and agents config
- scripts/init-sofiia-memory.py, scripts/node2/*, start-memory-node2.sh: NODA2 init scripts
- setup_sofiia_node2.sh: NODA2 full setup script

Made-with: Cursor
2026-03-03 07:15:20 -08:00

17 KiB
Raw Blame History

Застосування production-patch AgroMatrix/Stepan на НОДА1

Патч: agromatrix_stepan_noda1_prod.patch — тільки зміни для стабілізації Степана/AgroMatrix. Без vision/router/інших агентів.


Передумови

  • Доступ по SSH на НОДА1 (144.76.224.179).
  • Репозиторій на сервері: /opt/microdao-daarion (або ваш DEPLOY_ROOT).
  • На сервері є каталоги: gateway-bot/, crews/, packages/agromatrix-tools/.

Крок 1 — Завантаження patch на НОДА1

Варіант A (з локальної машини):

scp /шлях/до/agromatrix_stepan_noda1_prod.patch USER@144.76.224.179:/opt/microdao-daarion/

Варіант B (якщо patch уже в репо):

ssh USER@144.76.224.179 "cd /opt/microdao-daarion && git pull && git show HEAD:agromatrix_stepan_noda1_prod.patch > agromatrix_stepan_noda1_prod.patch"
# або просто скопіювати вміст файла в репо на сервер

Варіант C (вміст патчу вставлений вручну): На сервері створити файл /opt/microdao-daarion/agromatrix_stepan_noda1_prod.patch з повним вмістом unified diff.


Крок 2 — Застосування patch (git apply)

На НОДА1:

cd /opt/microdao-daarion

# Переконатися, що немає незакомічених змін у задіяних файлах (або зробити backup)
git status

# Сухий прогон (перевірка, що патч застосується без конфліктів)
git apply --check agromatrix_stepan_noda1_prod.patch

# Застосувати
git apply agromatrix_stepan_noda1_prod.patch

Якщо git apply --check повертає помилку (наприклад, через відмінний базовий коміт), можна спробувати:

git apply --reject --whitespace=fix agromatrix_stepan_noda1_prod.patch
# і вручну розв’язати файли *.rej, якщо з’являться.

Крок 3 — Rebuild і запуск gateway

cd /opt/microdao-daarion

# Зібрати образ gateway і запустити контейнер
docker compose -f docker-compose.node1.yml up -d --build gateway

Сервіс у compose називається gateway, контейнер — dagi-gateway-node1. Якщо використовуєте інший compose-файл або ім’я сервісу, підставте їх.


Крок 4 — Перевірка health

# Локально на сервері
curl -s http://localhost:9300/health
curl -s http://localhost:9300/

# Ззовні (якщо порт 9300 відкритий)
curl -s http://144.76.224.179:9300/health

Очікується: HTTP 200 і JSON зі статусом сервісу.


Крок 5 — Перевірка логів (NameError / ImportError)

docker logs dagi-gateway-node1 2>&1 | tail -100
docker logs dagi-gateway-node1 2>&1 | grep -E "NameError|ImportError|Stepan mode|Stepan inproc|Stepan disabled"
  • Має з’явитися рядок типу Stepan mode=inproc (AGX_STEPAN_MODE) після першого звернення до AgroMatrix або при старті (залежно від реалізації логу).
  • Не повинно бути NameError: has_recent_interaction або ImportError через crews/agromatrix_tools після коректного монтування та env.

Якщо бачите Stepan disabled — перевірте AGX_REPO_ROOT, наявність томів crews і packages/agromatrix-tools у compose та що в контейнері є відповідні каталоги.


Крок 6 — Smoketests

  1. Doc follow-up (раніше 500)
    У Telegram-чаті агента AgroMatrix: надіслати документ, потім текстове питання по ньому.
    Очікується: відповідь без HTTP 500 (відповідь з RAG або повідомлення про відсутність відповіді в документі).

  2. Оператор: /whoami
    Від користувача з user_id у AGX_OPERATOR_IDS (або в чаті з AGX_OPERATOR_CHAT_ID): надіслати /whoami.
    Очікується: відповідь від Степана (whoami), не звичайний pipeline.

  3. Оператор: звичайний текст без slash
    Від того ж оператора: надіслати текст без слеша (наприклад «привіт»).
    Очікується: обробка через Степана (handle_stepan_message), не fallback у звичайний Router pipeline.

  4. Не-оператор: звичайний текст
    Від користувача, який не входить до операторів: звичайне повідомлення.
    Очікується: обробка стандартним Router pipeline, не через Степана.

Перед тестами 24 переконайтеся, що в env задані AGX_OPERATOR_IDS (та за потреби AGX_OPERATOR_CHAT_ID).


Rollback

Якщо потрібно повернути стан до патчу:

cd /opt/microdao-daarion

# Скасувати зміни в робочій копії (повернути файли до останнього коміту)
git checkout HEAD -- gateway-bot/http_api.py gateway-bot/services/doc_service.py gateway-bot/app.py gateway-bot/gateway_boot.py crews/agromatrix_crew/operator_commands.py docker-compose.node1.yml
rm -f gateway-bot/gateway_boot.py

# Перезібрати і перезапустити gateway
docker compose -f docker-compose.node1.yml up -d --build gateway

Або ж повернути весь репо до попереднього коміту і перезібрати образ:

git reset --hard HEAD
docker compose -f docker-compose.node1.yml up -d --build gateway

Env на НОДА1 (без секретів)

Переконайтеся, що в середовищі gateway (compose або .env) задані:

  • AGX_STEPAN_MODE=inproc (за замовчуванням)
  • AGX_REPO_ROOT=/app (в контейнері; на хості відповідає вашому DEPLOY_ROOT у путях томів)
  • AGX_OPERATOR_IDS=*** — список Telegram user_id операторів (через кому)
  • AGX_OPERATOR_CHAT_ID=*** — опційно, обмеження чату для операторів
  • OPENAI_API_KEY=*** — потрібен для inproc Степана

Секрети не виводьте в логи; у документації позначайте як KEY=***.


A. Імпорт gateway_boot (fail-closed)

У контейнері CMD: WORKDIR /app/gateway-bot і python -m uvicorn app:app. Тому поточний каталог для імпортів — /app/gateway-bot. У app.py використовується import gateway_boot (модуль у тій самій директорії, що й app.py), тож Python резолвить /app/gateway-bot/gateway_boot.py. Це коректно при volume ${DEPLOY_ROOT}/gateway-bot:/app/gateway-bot:ro.

Швидкий тест у контейнері (CWD /app/gateway-bot, як у uvicorn):

ssh root@144.76.224.179 'docker exec dagi-gateway-node1 sh -c "cd /app/gateway-bot && python3 -c \"import gateway_boot; print(\\\"gateway_boot OK\\\")\""'

Очікується: gateway_boot OK. Якщо тут ImportErrorSTEPAN_IMPORTS_OK ніколи не стане True і Степан залишиться тихо вимкненим.


B. Volume-монтування crews і packages/agromatrix-tools

У docker-compose.node1.yml томи змонтовані в контейнері так:

  • ${DEPLOY_ROOT:-.}/crews/app/crews
  • ${DEPLOY_ROOT:-.}/packages/agromatrix-tools/app/packages/agromatrix-tools

На хості це може бути /opt/microdao-daarion/crews, але в контейнері завжди /app/crews та /app/packages/agromatrix-tools.

Перевірка наявності в контейнері:

ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/crews | head"
ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/packages/agromatrix-tools | head"

Очікується: список файлів/каталогів (наприклад agromatrix_crew у crews, пакет у agromatrix-tools). Якщо No such file or directory — томи не змонтовані або compose не оновлено після патчу.


PYTHONPATH у контейнері (crews і agromatrix_tools)

Щоб працювало from crews.agromatrix_crew.run import ..., у sys.path має бути /app (батько каталога crews), а не /app/crews.
Щоб працювало import agromatrix_tools, у sys.path має бути /app/packages/agromatrix-tools (батьківська директорія пакета).

У патчі задано: PYTHONPATH=/app:/app/packages/agromatrix-tools. При потребі можна додати /app/gateway-bot, але uvicorn і так додає робочу директорію при python -m uvicorn app:app.

Практичний контроль після деплою:

ssh root@144.76.224.179 "docker exec dagi-gateway-node1 sh -c 'env | grep -E \"^PYTHONPATH=\" || true'"
ssh root@144.76.224.179 "docker exec dagi-gateway-node1 sh -c 'cd /app/gateway-bot && python3 -c \"import sys; print(\\\"\\n\\\".join(sys.path))\"' | head -20"

Читання STEPAN_IMPORTS_OK (без “заморожування”)

Щоб прапорець оновлювався після startup, усі місця мають робити import gateway_boot і читати gateway_boot.STEPAN_IMPORTS_OK (або getattr(gateway_boot, "STEPAN_IMPORTS_OK", False)).
Не використовувати from gateway_boot import STEPAN_IMPORTS_OK — значення “заморозиться” на момент імпорту і не відобразиться після встановлення в app.py.

Grep-контроль на сервері:

ssh root@144.76.224.179 "cd /opt/microdao-daarion && grep -Rn 'from gateway_boot import' gateway-bot || true"
ssh root@144.76.224.179 "cd /opt/microdao-daarion && grep -Rn 'import gateway_boot' gateway-bot"

Очікується: немає збігів для from gateway_boot import; є лише import gateway_boot (у app.py та http_api.py).


Compose env: лапки для AGX_OPERATOR_*

Якщо значення задані прямо в YAML (а не тільки через ${...}), краще завжди брати їх у лапки, щоб уникнути проблем парсингу (зокрема від’ємні chat_id):

  • AGX_OPERATOR_CHAT_ID: "-1001234567890"
  • AGX_OPERATOR_IDS: "123,456"

При використанні змінних середовища (AGX_OPERATOR_IDS=${AGX_OPERATOR_IDS:-}) лапки ставлять у .env або в значенні змінної при експорті.


Мінімальний фінальний чек-лист (після git apply і --build)

  1. Патч застосовано чисто
    Перед першим застосуванням: git apply --check agromatrix_stepan_noda1_prod.patch (код виходу 0 = можна застосовувати).
    Після застосування можна перевірити наявність змін, наприклад:

    ssh root@144.76.224.179 "cd /opt/microdao-daarion && test -f gateway-bot/gateway_boot.py && grep -q 'import gateway_boot' gateway-bot/app.py && echo OK"
    
  2. Rebuild gateway

    ssh root@144.76.224.179 "cd /opt/microdao-daarion && docker compose -f docker-compose.node1.yml up -d --build gateway"
    
  3. Stepan реально увімкнувся (логи)

    ssh root@144.76.224.179 "docker logs dagi-gateway-node1 --since 10m 2>&1 | grep -E 'Stepan mode|STEPAN_IMPORTS_OK|Stepan disabled|Stepan inproc|ImportError|ModuleNotFoundError' | tail -30"
    

    Очікується: згадка Stepan mode=inproc (після першого звернення до AgroMatrix) і відсутність "Stepan disabled". У логах після старту має з’явитися рядок STEPAN_IMPORTS_OK=True (додано в app.py після встановлення прапорця), тоді fail-closed коректно пройшов.

  4. Оператори задані (обов’язково для "людського" режиму)

    ssh root@144.76.224.179 "docker exec dagi-gateway-node1 env | grep -E '^AGX_OPERATOR_IDS=|^AGX_OPERATOR_CHAT_ID=' | sed 's/=.*/=***/'"
    

    Очікується: хоча б AGX_OPERATOR_IDS=*** (значення приховуються). Якщо змінних немає — операторські повідомлення не підуть у Степана.

  5. Опційно: резолв gateway_boot і томи (див. блоки A і B вище).


Нюанс: AGX_STEPAN_MODE=http

Режим AGX_STEPAN_MODE=http зараз повертає 501 stub (Степан "офіційно" недоступний). Щоб у проді не випадково вимкнути Степана:

  • У docker-compose.node1.yml у секції gateway залишити явно AGX_STEPAN_MODE=inproc (або не задавати — тоді default з патчу inproc).
  • Режим http використовувати лише в тестових середовищах, коли з’явиться реалізація клієнта crewai-service.

Фрагмент патчу для перевірки імпортів (app.py + gateway_boot)

Нижче — заголовки diff і контекст імпортів у app.py, щоб переконатися, що gateway_boot імпортується як модуль з тієї самої директорії, де лежить app.py, і резолвиться в контейнері.

gateway-bot/app.py (фрагмент після патчу):

import logging
import os
import sys
from pathlib import Path

from fastapi import FastAPI
from fastapi.middleware.cors import CORSMiddleware

from http_api import router as gateway_router
from http_api_doc import router as doc_router

import gateway_boot   # <-- той самий каталог: /app/gateway-bot/gateway_boot.py

gateway-bot/gateway_boot.py (новий файл):

"""
Boot-time state for Gateway. Set by app startup; read by http_api.
"""
STEPAN_IMPORTS_OK = False

У контейнері: WORKDIR /app/gateway-bot, volume монтує gateway-bot у /app/gateway-bot, тому app.py і gateway_boot.py лежать поруч — import gateway_boot коректно знаходить модуль. Після startup у startup_stepan_check() при успішному імпорті виконується gateway_boot.STEPAN_IMPORTS_OK = True і лог STEPAN_IMPORTS_OK=True; інакше прапорець залишається False (fail-closed). У http_api використовується лише import gateway_boot і getattr(gateway_boot, "STEPAN_IMPORTS_OK", False) — не from gateway_boot import ..., щоб значення читалося в runtime після оновлення в app.py.


Підсумок перевірок “готово до прод”

Мінімально достатньо три пункти:

  1. Логи після старту (Stepan увімкнено, без ImportError):

    ssh root@144.76.224.179 "docker logs dagi-gateway-node1 --since 10m 2>&1 | grep -E 'Stepan mode|STEPAN_IMPORTS_OK|Stepan disabled|ImportError|ModuleNotFoundError' | tail -120"
    
  2. Volumes на місці (/app/crews, /app/packages/agromatrix-tools):

    ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/crews | head"
    ssh root@144.76.224.179 "docker exec dagi-gateway-node1 ls -la /app/packages/agromatrix-tools | head"
    
  3. Env операторів і режиму (масковано):

    ssh root@144.76.224.179 "docker exec dagi-gateway-node1 env | grep -E '^AGX_OPERATOR_IDS=|^AGX_OPERATOR_CHAT_ID=|^AGX_STEPAN_MODE=' | sed 's/=.*/=***/'"
    

Якщо ці три пункти зелені — патч відповідає fail-closed моделі і не повинен давати “тихих” падінь gateway при відсутніх crews/agromatrix-tools.