- 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
319 lines
17 KiB
Markdown
319 lines
17 KiB
Markdown
# Застосування 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, **не** через Степана.
|
||
|
||
Перед тестами 2–4 переконайтеся, що в 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.
|