Config policies (16 files): alert_routing, architecture_pressure, backlog, cost_weights, data_governance, incident_escalation, incident_intelligence, network_allowlist, nodes_registry, observability_sources, rbac_tools_matrix, release_gate, risk_attribution, risk_policy, slo_policy, tool_limits, tools_rollout Ops (22 files): Caddyfile, calendar compose, grafana voice dashboard, deployments/incidents logs, runbooks for alerts/audit/backlog/incidents/sofiia/voice, cron jobs, scripts (alert_triage, audit_cleanup, migrate_*, governance, schedule), task_registry, voice alerts/ha/latency/policy Docs (30+ files): HUMANIZED_STEPAN v2.7-v3 changelogs and runbooks, NODA1/NODA2 status and setup, audit index and traces, backlog, incident, supervisor, tools, voice, opencode, release, risk, aistalk, spacebot Made-with: Cursor
148 lines
7.8 KiB
Markdown
148 lines
7.8 KiB
Markdown
# Humanized Stepan — CHANGELOG v2.7
|
||
|
||
**Version:** v2.7
|
||
**Date:** 2026-02-25
|
||
**Базується на:** v2.6 (Jaccard guard, tone_constraints, 3-рівневі привітання, seeded RNG)
|
||
|
||
---
|
||
|
||
## Summary
|
||
|
||
- Додано **memory horizon**: `recent_topics` (до 5 записів) замість єдиного `last_topic`.
|
||
- Додано **human topic labels** (`last_topic_label`) — Степан оперує "план на завтра поле 12", а не "plan_day".
|
||
- Додано **`summarize_topic_label()`** — rule-based витяг 6–8 слів з тексту без дієслів-тригерів і стоп-слів.
|
||
- Light follow-up (≤6 слів + last_topic) **не додає шум** до `recent_topics` (`depth="light"` → `push` не відбувається).
|
||
- Contextual greeting (`interaction_count ≥ 8`) тепер: з ймовірністю 20% (seeded rng) підхоплює `recent_topics[-2]` — Степан "пам'ятає" більше однієї теми без подвійного згадування.
|
||
- **ZZR safety disclaimer**: якщо погодний тригер + обприскування/гербіцид/ЗЗР — автоматично додається `"Дозування та вікна застосування — за етикеткою препарату та регламентом."`.
|
||
- Додано **`tests/test_stepan_invariants.py`** — 25 тестів-інваріантів проти "повзучої ботячості".
|
||
|
||
---
|
||
|
||
## Key features (деталі)
|
||
|
||
### Memory horizon — `recent_topics`
|
||
|
||
```json
|
||
"recent_topics": [
|
||
{"label": "план на завтра поле 12", "intent": "plan_day", "ts": "2026-02-25T..."},
|
||
{"label": "датчики вологості поле 7", "intent": "iot_sensors", "ts": "2026-02-25T..."}
|
||
]
|
||
```
|
||
|
||
- Максимум 5 записів; старіші витісняються.
|
||
- `last_topic` і `last_topic_label` — backward-compat aliases на `recent_topics[-1]`.
|
||
- Dedup: якщо той самий `intent` + `label` підряд — не дублюється.
|
||
|
||
### summarize_topic_label
|
||
|
||
| Вхід | Вихід |
|
||
|---|---|
|
||
| `"зроби план на завтра по полю 12"` | `"План на завтра по полю 12"` |
|
||
| `"перевір датчики вологості поле 7"` | `"Датчики вологості поле 7"` |
|
||
| `"сплануй тижневий збір по полях"` | `"Тижневий збір по полях"` |
|
||
|
||
Правила: прибирається leading action verb (зроби/перевір/порахуй/…), стоп-слова, обрізка до 8 слів. Числа, поля, культури, дати зберігаються.
|
||
|
||
### ZZR disclaimer
|
||
|
||
Regex `_ZZR_RE` спрацьовує на: `обробк|обприскування|гербіцид|фунгіцид|ЗЗР|пестицид|інсектицид|протруювач`.
|
||
Застереження додається лише коли є **і** погодний тригер **і** ZZR-тригер в одному повідомленні.
|
||
|
||
### Invariant tests (anti-regression)
|
||
|
||
| Інваріант | Обмеження |
|
||
|---|---|
|
||
| INV-1: Greeting | ≤ 80 символів |
|
||
| INV-2: Thanks/Ack | ≤ 40 символів |
|
||
| INV-3: Заборонені фрази | "чим можу допомогти", "оберіть", "я як агент", "я бот" |
|
||
| INV-4: Технічні слова | container, uvicorn, trace_id, STEPAN_IMPORTS_OK |
|
||
| INV-5: ZZR disclaimer | при ZZR+погода → "за етикеткою" або "за регламентом" |
|
||
| INV-6: Horizon | `len(recent_topics) ≤ 5` після 7+ push |
|
||
| INV-7: Міграція | lazy, idempotent, backward-compat |
|
||
|
||
---
|
||
|
||
## Backward compatibility
|
||
|
||
| Аспект | Деталі |
|
||
|---|---|
|
||
| `_version` | 3 → 4 (нові поля `recent_topics`, `last_topic_label`) |
|
||
| Міграція | Lazy при `load_user_profile()` — виконується автоматично при першому зверненні |
|
||
| `last_topic` | Залишається як alias, завжди синхронізований з `recent_topics[-1].intent` |
|
||
| `last_topic_label` | Новий alias на `recent_topics[-1].label`; якщо нема — встановлюється під час міграції |
|
||
| `tone_constraints` | Вже в v2.6; міграція додає якщо відсутній |
|
||
| `update_profile_if_needed` | Новий параметр `depth="deep"` (default) — backward-compat, старі виклики не ламаються |
|
||
| `recent_topics` відсутній | Якщо профіль v3 без `recent_topics` — `migrate_profile_topics()` створює 1 елемент з `last_topic` |
|
||
|
||
Міграція `migrate_profile_topics()` — **idempotent**: повторний виклик не змінює вже мігрований профіль.
|
||
|
||
---
|
||
|
||
## Non-goals / not included
|
||
|
||
- Немає LLM у light mode або reflection.
|
||
- Немає змін в інфраструктурі (Dockerfile, compose, env).
|
||
- Немає змін у Gateway/http_api.py.
|
||
- Немає нових API ендпоінтів.
|
||
- Немає змін у поведінці deep mode orchestration.
|
||
- Немає змін у системному промпті (тільки хедер-версія).
|
||
|
||
---
|
||
|
||
## Tests
|
||
|
||
**Результат:** 101/101 зелених (без регресій з v2.6)
|
||
|
||
| Файл | Тестів | Опис |
|
||
|---|---|---|
|
||
| `tests/test_stepan_invariants.py` | 25 | Нові інваріанти anti-regression |
|
||
| `tests/test_stepan_acceptance.py` | 28 | Acceptance + v2.7 сесійні сценарії |
|
||
| `tests/test_stepan_light_reply.py` | ~26 | Light reply юніт-тести |
|
||
| `tests/test_stepan_memory_followup.py` | ~22 | Memory + follow-up класифікація |
|
||
|
||
```bash
|
||
# Тільки інваріанти
|
||
python3 -m pytest tests/test_stepan_invariants.py -v
|
||
|
||
# Acceptance
|
||
python3 -m pytest tests/test_stepan_acceptance.py -v
|
||
|
||
# Всі Stepan тести
|
||
python3 -m pytest tests/test_stepan_invariants.py tests/test_stepan_acceptance.py \
|
||
tests/test_stepan_light_reply.py tests/test_stepan_memory_followup.py -v
|
||
```
|
||
|
||
---
|
||
|
||
## Known limitations
|
||
|
||
### Timezone і daily seed
|
||
`date.today()` використовує локаль контейнера. Контейнер має бути в `Europe/Kyiv` (`TZ=Europe/Kyiv`), інакше "новий день" Степана настане о 22:00 або 23:00 за Київським часом. Перевірка:
|
||
```bash
|
||
docker exec dagi-gateway-node1 date
|
||
```
|
||
|
||
### Memory-service downtime
|
||
При недоступності — деградація до локального in-memory кешу (TTL 30 хв). Кеш не переживає рестарт контейнера. Профілі не зберігаються між сесіями якщо memory-service down > 30 хв.
|
||
|
||
### ZZR regex — можливий overreach
|
||
Слово `"обробка"` без агрохімічного контексту (напр. "обробка ґрунту") може спрацювати. Якщо в проді виявиться шум — звузити regex: вимагати ще одне слово з `[препарат|норма|л/га|кг/га|концентрат]`.
|
||
|
||
---
|
||
|
||
## Rollback
|
||
|
||
```bash
|
||
# Відкатити зміни у конкретних файлах
|
||
git checkout HEAD~1 -- crews/agromatrix_crew/memory_manager.py
|
||
git checkout HEAD~1 -- crews/agromatrix_crew/light_reply.py
|
||
git checkout HEAD~1 -- crews/agromatrix_crew/run.py
|
||
|
||
# Rebuild gateway (без секретів)
|
||
cd /opt/microdao-daarion
|
||
docker compose -f docker-compose.node1.yml up -d --build dagi-gateway-node1
|
||
|
||
# Перевірка
|
||
docker logs dagi-gateway-node1 --since 5m 2>&1 | grep -E "Stepan mode|STEPAN_IMPORTS_OK|error|Error" | tail -30
|
||
```
|