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

319 lines
17 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Застосування 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 (з локальної машини):**
```bash
scp /шлях/до/agromatrix_stepan_noda1_prod.patch USER@144.76.224.179:/opt/microdao-daarion/
```
**Варіант B (якщо patch уже в репо):**
```bash
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:
```bash
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` повертає помилку (наприклад, через відмінний базовий коміт), можна спробувати:
```bash
git apply --reject --whitespace=fix agromatrix_stepan_noda1_prod.patch
# і вручну розв’язати файли *.rej, якщо з’являться.
```
---
## Крок 3 — Rebuild і запуск gateway
```bash
cd /opt/microdao-daarion
# Зібрати образ gateway і запустити контейнер
docker compose -f docker-compose.node1.yml up -d --build gateway
```
Сервіс у compose називається `gateway`, контейнер — `dagi-gateway-node1`. Якщо використовуєте інший compose-файл або ім’я сервісу, підставте їх.
---
## Крок 4 — Перевірка health
```bash
# Локально на сервері
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)
```bash
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
Якщо потрібно повернути стан до патчу:
```bash
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
```
Або ж повернути весь репо до попереднього коміту і перезібрати образ:
```bash
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):**
```bash
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`. Якщо тут `ImportError``STEPAN_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`.
**Перевірка наявності в контейнері:**
```bash
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`.
**Практичний контроль після деплою:**
```bash
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-контроль на сервері:**
```bash
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 = можна застосовувати).
Після застосування можна перевірити наявність змін, наприклад:
```bash
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**
```bash
ssh root@144.76.224.179 "cd /opt/microdao-daarion && docker compose -f docker-compose.node1.yml up -d --build gateway"
```
3. **Stepan реально увімкнувся (логи)**
```bash
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. **Оператори задані (обов’язково для "людського" режиму)**
```bash
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 (фрагмент після патчу):**
```python
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 (новий файл):**
```python
"""
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):
```bash
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`):
```bash
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 операторів і режиму** (масковано):
```bash
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.