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
32 KiB
Humanized Stepan v2 — Architecture Plan
Версія: 0.1-draft
Статус: plan (без коду)
Область змін: crews/agromatrix_crew/ + мінімальний торкання http_api.py
Принцип: fail-closed, backward-compatible, жодної нескінченної рекурсії
1. Проблеми поточної архітектури
| Симптом | Причина у коді |
|---|---|
| На "привіт" запускаються всі 5 під-агентів | run.py завжди викликає ops, iot, platform, spreadsheet, sustainability |
| Роботизовані відповіді | JSON-схема фінального агента, відсутня адаптація стилю |
| Степан не знає хто ти | Немає UserProfile, жодного звернення до memory-service |
| Степан не знає твою ферму | Немає FarmProfile |
| Після відповіді немає самоперевірки | Reflection відсутній |
| Оператор і звичайний користувач мають однакову відповідь | is_operator є, але стиль не змінюється |
Зміна detect_intent() ламає всю логіку |
Ключові слова захардкожені в одній функції |
2. Загальна схема нового потоку
handle_message(text, user_id, chat_id, ops_mode)
│
├─► [activation_gate.pre_check(text)] ← блокує рекурсію, лічить глибину
│
├─► [memory_manager.load(user_id)] ← UserProfile + FarmProfile
│ │ fallback: порожній профіль ← fail-safe
│
├─► [depth_classifier.classify(text, profile)]
│ │ → DepthDecision {mode, intent, crew_needed, confidence}
│ │ fallback: mode="deep" ← fail-closed: краще зробити більше
│
├─► if mode == "light":
│ [style_adapter.render(profile)] → system_prompt_prefix
│ Stepan відповідає сам (без під-агентів)
│ → response
│
├─► if mode == "deep":
│ [activation_gate.select_crew(DepthDecision, FarmProfile)]
│ → {ops?, iot?, platform?, spreadsheet?, sustainability?}
│ Запускати ТІЛЬКИ потрібних під-агентів
│ Stepan консолідує
│ → response
│
├─► [reflection_engine.reflect(response, profile, intent)] ← один прохід, не рекурсія
│ │ fallback: оригінальна відповідь
│
├─► [memory_manager.update_async(user_id, text, response)] ← не блокує
│
└─► return final_response
3. Нові модулі
3.1 depth_classifier.py
Розташування: crews/agromatrix_crew/depth_classifier.py
Відповідальність: визначити глибину запиту і які под-агенти взагалі потрібні.
Вхід:
text: str— текст повідомленняprofile: UserProfile | None— профіль користувачаfarm: FarmProfile | None— профіль ферми
Вихід: DepthDecision
@dataclass
class DepthDecision:
mode: Literal["light", "deep"] # ключовий перемикач
intent: str # human-readable intent
crew_needed: list[str] # підмножина: ops, iot, platform, spreadsheet, sustainability
confidence: float # 0..1, < 0.4 → force deep
reason: str # для audit логу
Логіка класифікації (rule-based, без LLM):
Light mode — якщо текст відповідає хоча б одному патерну:
LIGHT_PATTERNS = {
"greeting": ["привіт", "доброго", "hello", "hi", "добрий ранок", "добрий вечір"],
"thanks": ["дякую", "дякуй", "спасибі", "дякую степан"],
"ack": ["зрозумів", "ок", "добре", "чудово", "зрозуміла"],
"whoami_check": ["хто я", "мої права"],
"simple_status": ["який статус", "що зараз"],
}
Deep mode — якщо текст відповідає хоча б одному:
DEEP_PATTERNS = {
"planning": ["сплануй", "план на", "розробити план", "графік робіт"],
"multi_ops": ["по всіх полях", "кілька ділянок", "всі культури"],
"iot_alert": ["аномалія", "тривога", "sensors", "вологість впала"],
"analysis": ["план/факт", "план факт", "статистика", "зведення", "порівняй"],
"decision": ["що робити", "порадь", "проаналізуй", "виріши"],
"recording": ["запиши", "зафіксуй", "внеси", "додай операцію"],
}
Crew selection у deep mode:
crew_needed logic:
"ops" → "запиши" | "зафіксуй" | "внеси" | farmos keywords
"iot" → "датчик" | "вологість" | "temp" | "sensor" | FarmProfile.has_iot
"platform" → "статус сервісів" | "інтеграція" | "помилка підключення"
"spreadsheet" → "таблиц" | "excel" | "звіт" | "xlsx"
"sustainability" → "зведення" | "агрегація" | "підсумки"
Fail-safe: будь-який виняток → DepthDecision(mode="deep", intent="unknown", crew_needed=["ops","iot","platform","spreadsheet","sustainability"], confidence=0.0, reason="classifier_error").
3.2 memory_manager.py
Розташування: crews/agromatrix_crew/memory_manager.py
Відповідальність: завантажити, зберегти і оновити профілі через memory-service. Повна деградація до in-memory fallback.
API:
def load(user_id: str) -> tuple[UserProfile, FarmProfile]
def update(user_id: str, interaction: InteractionContext) -> None
Реалізація (sync, бо run.py sync):
- HTTP запити через
httpx.Client(sync), timeout 2s - При недоступності memory-service → використовує
_local_cache: dict(процесна пам'ять) _local_cacheзберігає до 200 записів, TTL 30 хвилин- Факт-ключі в memory-service:
user_profile:agromatrix:{user_id}farm_profile:agromatrix:{user_id}
- user_id для memory-service:
stepan:{user_id}(ізоляція від gateway-агентів)
Fail-safe:
try:
profile = _fetch_from_memory(user_id)
except Exception:
profile = UserProfile.default(user_id) # порожній, але валідний
logger.warning("memory_manager: fallback to default profile user=%s", user_id)
Не блокуючий update:
def update_async(user_id: str, interaction: InteractionContext):
"""Запускає оновлення в threading.Thread (daemon=True), не чекає результату."""
t = threading.Thread(target=_do_update, args=(user_id, interaction), daemon=True)
t.start()
3.3 style_adapter.py
Розташування: crews/agromatrix_crew/style_adapter.py
Відповідальність: сформувати prefix для system prompt Степана залежно від профілю.
Вхід: UserProfile, DepthDecision
Вихід: str — prefix для system prompt Степана
Рівні expertise:
novice: мова проста, уникай термінів, давай короткий приклад, 2-3 речення
intermediate: збалансована відповідь, терміни пояснюй в дужках, до 5 речень
expert: технічна відповідь, скорочений формат, опускай очевидне
Стилі:
brief: 1-2 речення, тільки суть
detailed: повний опис з контекстом
conversational: живий тон, питання-відповідь, можна питати уточнення
Формат prefix:
"Відповідай на рівні {expertise_label}.
Стиль: {style_label}.
Ти знаєш цього користувача: {name or 'агрономе'}.
Фермерський контекст: {farm_context_summary}."
Fail-safe: будь-який виняток → повертає порожній рядок, Степан працює зі стандартним backstory.
3.4 reflection_engine.py
Розташування: crews/agromatrix_crew/reflection_engine.py
Відповідальність: одноразова пост-обробка відповіді для відповідності профілю і стилю.
Механізм (без LLM для Light mode, з LLM для Deep mode):
Light mode reflection (rule-based):
- Відповідь > 500 символів і UserProfile.preferred_style == "brief" → обрізати до 3 речень
- Відповідь містить JSON-фрагменти → замінити на людський текст
- Відповідь містить технічні ідентифікатори (uuid, trace_id) → прибрати з відповіді користувачу
Deep mode reflection (LLM, one-shot):
Prompt:
"Оціни цю відповідь для {expertise_level} користувача:
[RESPONSE]
Якщо відповідь занадто технічна — спрости.
Якщо занадто довга для {preferred_style} — скороти.
Відповідай тільки виправленою відповіддю."
Anti-recursion guard:
# В reflection_engine.py — module-level flag
_REFLECTING: bool = False
def reflect(response: str, profile: UserProfile, trace_id: str) -> str:
global _REFLECTING
if _REFLECTING:
logger.warning("reflection: recursion guard active, skipping trace=%s", trace_id)
return response
_REFLECTING = True
try:
return _do_reflect(response, profile, trace_id)
except Exception:
return response
finally:
_REFLECTING = False
Fail-safe: будь-який виняток → повертає оригінальну відповідь без змін.
3.5 activation_gate.py
Розташування: crews/agromatrix_crew/activation_gate.py
Відповідальність:
- Pre-check: блокує подвійний виклик handle_message з того самого контексту
- Select: визначає мінімальний набір під-агентів для запуску
- Post-check: обмежує глибину делегування
Структура:
_CALL_DEPTH: threading.local # per-thread, не глобальне
MAX_DEPTH = 1 # Степан може делегувати, але не можна повторно входити в handle_message
def pre_check(trace_id: str) -> bool:
"""Повертає True якщо дозволено продовжувати, False якщо глибина перевищена."""
depth = getattr(_CALL_DEPTH, "depth", 0)
if depth >= MAX_DEPTH:
logger.error("activation_gate: max depth %d reached trace=%s", MAX_DEPTH, trace_id)
return False
_CALL_DEPTH.depth = depth + 1
return True
def release(trace_id: str):
"""Зменшити лічильник після завершення handle_message."""
_CALL_DEPTH.depth = max(0, getattr(_CALL_DEPTH, "depth", 0) - 1)
def select_crew(decision: DepthDecision, farm: FarmProfile) -> list[str]:
"""Повернути список під-агентів для запуску."""
needed = list(decision.crew_needed)
# Видалити IoT якщо FarmProfile.active_integrations не має iot
if "iot" in needed and not farm.has_iot_integration:
needed.remove("iot")
# Видалити spreadsheet якщо не запит до таблиць
if "spreadsheet" in needed and "spreadsheet" not in decision.intent:
needed.remove("spreadsheet")
return needed if needed else []
4. Структура UserProfile JSON
{
"_version": 1,
"_fact_key": "user_profile:agromatrix:{user_id}",
"user_id": "tg:123456789",
"agent": "agromatrix",
"name": "Іван",
"expertise_level": "intermediate",
"preferred_language": "uk",
"preferred_style": "conversational",
"last_seen": "2026-02-24T10:00:00Z",
"interaction_count": 42,
"known_intents": [
"plan_day",
"show_critical_tomorrow",
"iot_status"
],
"context_notes": [
"has_farmos_access",
"uses_thingsboard",
"prefers_short_answers"
],
"farm_profile_ref": "farm_profile:agromatrix:{user_id}",
"recent_topics": [
{"intent": "plan_day", "ts": "2026-02-24T09:00:00Z"},
{"intent": "iot_status", "ts": "2026-02-23T18:00:00Z"}
],
"operator": false,
"updated_at": "2026-02-24T10:00:00Z"
}
Поля та семантика:
| Поле | Тип | Опис |
|---|---|---|
expertise_level |
enum | novice / intermediate / expert; оновлюється автоматично після 10+ взаємодій |
preferred_style |
enum | brief / detailed / conversational |
interaction_count |
int | лічильник всіх взаємодій для авто-підвищення рівня |
known_intents |
list[str] | унікальні intents, накопичуються; use для FarmProfile автодоповнення |
context_notes |
list[str] | вільні мітки, збагачуються під час взаємодій |
recent_topics |
list[{intent, ts}] | останні 10 тем (для cold-start relief) |
operator |
bool | чи є цей user оператором (AGX_OPERATOR_IDS); read-only у memory |
5. Структура FarmProfile JSON
{
"_version": 1,
"_fact_key": "farm_profile:agromatrix:{user_id}",
"user_id": "tg:123456789",
"farm_name": "Ферма Калинівка",
"field_ids": ["field:north-01", "field:south-02"],
"crop_ids": ["crop:wheat-winter", "crop:corn-hybrid"],
"active_integrations": ["farmos", "thingsboard"],
"seasonal_context": {
"current_phase": "growing",
"active_operations": ["irrigation", "monitoring"],
"hemisphere": "north",
"approximate_month": 2
},
"iot_sensors": {
"has_iot_integration": true,
"sensor_types": ["soil_moisture", "temperature"],
"last_alert": null
},
"typical_intents": ["plan_day", "iot_status", "plan_vs_fact"],
"alert_thresholds": {
"soil_moisture_min": 20.0,
"temperature_min": -5.0,
"temperature_max": 38.0
},
"dict_pending_count": 0,
"updated_at": "2026-02-24T10:00:00Z"
}
Поля та семантика:
| Поле | Тип | Опис |
|---|---|---|
field_ids |
list[str] | заповнюються під час нормалізації терміну tool_dictionary |
crop_ids |
list[str] | аналогічно |
active_integrations |
list[str] | визначають які crew_agents потенційно потрібні |
seasonal_context |
object | підказки для планування і класифікатора глибини |
iot_sensors.has_iot_integration |
bool | ключ для activation_gate: чи включати IoT агента |
typical_intents |
list[str] | акумулюються; використовуються для Light/Deep розмежування |
dict_pending_count |
int | кеш кількості pending термінів для оператора |
alert_thresholds |
object | якщо IoT дані виходять за поріг → auto-trigger Deep mode |
6. Коли і як оновлюється профіль
UserProfile
| Подія | Що оновлюється | Коли |
|---|---|---|
| Будь-яка взаємодія | last_seen, interaction_count, recent_topics |
Завжди, після відповіді |
| Новий intent | known_intents.append(intent) |
Якщо intent не порожній |
| interaction_count >= 10 і всі intents — "planning" | expertise_level → intermediate |
При update |
| interaction_count >= 30 і є технічні intents | expertise_level → expert |
При update |
Оператор надіслав /profile set style brief |
preferred_style |
Одразу |
| FarmProfile змінений | farm_profile_ref sync |
При update |
FarmProfile
| Подія | Що оновлюється | Коли |
|---|---|---|
| tool_dictionary.normalize успішний | field_ids, crop_ids |
При нормалізації |
| Новий інтент з IoT | active_integrations, iot_sensors.has_iot_integration |
При Deep mode |
| Новий інтент з spreadsheet | active_integrations.append("spreadsheet") |
При Deep mode |
Оператор /farm update phase=sowing |
seasonal_context.current_phase |
Одразу |
| dict_review.stats() | dict_pending_count |
При ops_mode load |
7. Тригери Deep mode
Автоматичні (depth_classifier):
| Тригер | Умова |
|---|---|
| Планування | текст містить DEEP_PATTERNS["planning"] |
| Мультипольова операція | DEEP_PATTERNS["multi_ops"] |
| IoT аномалія | DEEP_PATTERNS["iot_alert"] АБО IoT дані з alert_thresholds порушені |
| Аналіз план/факт | DEEP_PATTERNS["analysis"] |
| Запис у farmOS | DEEP_PATTERNS["recording"] |
| Низька впевненість | confidence < 0.4 після класифікації |
| Нові терміни | tool_dictionary normalization повернув pending items |
| Перша взаємодія | interaction_count == 0 (невідомий користувач) |
Примусові (env/flag):
| Тригер | Механізм |
|---|---|
AGX_FORCE_DEEP=1 |
env в контейнері (тестування) |
Текст починається з --deep |
парситься в handle_message before classify |
| Оператор вручну | operator_commands + flag в trace |
8. Тригери запуску під-команди (активація crew_agent)
| Crew Agent | Тригер (keyword or FarmProfile) | Light може обійтись? |
|---|---|---|
ops |
"запиши", "внеси", "зафіксуй", "farmOS" | Ні |
iot |
"датчик", "вологість", "температура" + has_iot_integration=true |
Ні |
platform |
"статус", "перевір сервіс", "інтеграція впала" | Іноді (кешований статус) |
spreadsheet |
"таблиця", "excel", "звіт", "xlsx" | Ні |
sustainability |
"зведення", "агрегація", "підсумки по сезону" | Ні |
| всі одночасно | intent == "general" без профілю (fallback) |
Ні |
9. Ситуації, що залишаються Light mode
| Ситуація | Чому Light | Хто відповідає |
|---|---|---|
| Привітання будь-якого типу | Не потребує даних з farmOS/IoT | Степан з style_adapter |
| "Дякую", "ок", "зрозумів" | Підтвердження, не запит | Степан (2 слова) |
| /whoami, /pending, /approve | Operator commands | operator_commands.py (незмінний) |
| "Що ти вмієш?" | Довідка | Степан з профілем |
| Повторне питання тієї ж теми (< 5 хв) | recent_topics cache | Степан з кешем контексту |
| Simple status якщо кеш свіжий | FarmProfile.seasonal_context свіжий (< 1 год) | Степан без crew |
| Повідомлення < 4 слів | Незрозумілий запит → уточнення | Степан питає |
| Текст не пов'язаний з агрономією | Off-topic filter | Степан ввічливо redirects |
10. Принцип fail-safe
Ієрархія деградації:
Нормальна робота:
memory-service online → профілі загружені → класифікатор → вибір crew → рефлексія
Деградація 1 (memory недоступна):
fallback UserProfile.default() → класифікатор без персоналізації → crew → рефлексія skip
Деградація 2 (classifier помилка):
force Deep mode → всі crew → рефлексія skip
Деградація 3 (частина crew агентів впала):
інші crew продовжують → Степан синтезує з частковими даними
run_task_with_retry вже існує (max_retries=2)
Деградація 4 (OpenAI недоступний):
handle_stepan_message повертає "Помилка обробки. trace_id=..."
gateway вже обробляє це (stepan_disabled fallback)
Правила:
- Жодний модуль не може кинути виняток, що зупинить
handle_message - Кожен новий модуль wrap-ується в try/except з fallback
reflection_engineзавжди має повертатиstr, ніколиNoneабо винятокmemory_manager.update_asyncdaemon=True — смерть процесу не втрачає відповідь- При будь-якій помилці profile:
interaction_count=0,expertise_level="intermediate",preferred_style="conversational"
11. Як не створити нескінченну рекурсію
Три незалежні шари захисту:
Шар 1 — activation_gate (threading.local counter)
handle_message:
pre_check() → depth becomes 1
... робота ...
release() → depth back to 0
Якщо under_running_task викликає handle_message:
pre_check() → depth == 1 → MAX_DEPTH reached → return error response
threading.local — ізоляція per-thread, не заважає паралельним викликам з різних чатів.
Шар 2 — reflection_engine._REFLECTING flag
- Глобальний (module-level) булевий прапорець
- Встановлюється в
Trueперед LLM-рефлексією, скидається вfinally - Якщо рефлексія викличе щось що знову зайде в рефлексію → миттєво скидається
Шар 3 — Архітектурна заборона
- Під-агенти (ops, iot, platform, spreadsheet, sustainability) мають
allow_delegation=False - Жоден агент не має знань про
handle_messageабоrun.py depth_classifier,style_adapter,memory_manager— pure functions, без CrewAI, без LLM- Тільки
reflection_engine(Deep mode) і фінальна задача Степана — LLM-виклики
12. Де саме інтегрувати
12.1 crews/agromatrix_crew/run.py
Змінити:
# Новий imports (top)
from crews.agromatrix_crew.depth_classifier import classify, DepthDecision
from crews.agromatrix_crew.memory_manager import load_profiles, update_async
from crews.agromatrix_crew.style_adapter import build_prefix
from crews.agromatrix_crew.reflection_engine import reflect
from crews.agromatrix_crew.activation_gate import pre_check, release, select_crew
# handle_message:
# 1. pre_check (перше, до всього)
# 2. load_profiles (до classify)
# 3. classify (до побудови агентів)
# 4. if light → stepan_only_response
# 5. if deep → activation_gate.select_crew → run selected
# 6. reflect (після відповіді)
# 7. update_async (не блокуючий, daemon thread)
# 8. release (в finally)
Зберегти:
- Весь
route_operator_command/route_operator_text(operator_commands не змінюємо) tool_dictionary.normalize_from_text+ pending check (залишається до classify)run_task_with_retry(залишається для Deep mode)audit_event(залишається, розширюємо depth/mode в event)farmos_ui_hint(залишається)
НЕ змінювати:
- Сигнатуру
handle_message(text, user_id, chat_id, trace_id, ops_mode, last_pending_list) - Формат повернення (str, valid for JSON parse by http_api)
12.2 crews/agromatrix_crew/operator_commands.py
Додати команди:
/profile → показати UserProfile (user_id, expertise, style, last_seen, interaction_count)
/profile set <k>=<v> → оновити expertise_level або preferred_style
/farm → показати FarmProfile (коротко: поля, культури, інтеграції, сезон)
/farm update <k>=<v> → оновити seasonal_context.current_phase, порогові значення
Зберегти без змін:
/whoami,/pending,/approve,/reject,/apply_dict,/pending_statsis_operator()— не змінюватиroute_operator_command()— розширити case, не переписуватиroute_operator_text()— залишити
OPERATOR_COMMANDS set — додати "profile", "farm".
12.3 gateway-bot/http_api.py
Мінімальні зміни:
- Додати env
AGX_FORCE_DEEP→ якщо "1", передавати в metadata або через handle_message (ops_mode вже є, можна додати depth_override parameter) - Нічого більше не змінювати. handle_message вже приймає text, user_id, chat_id, trace_id, ops_mode.
Не змінювати:
- Маршрутизацію оператор/не-оператор (вже виправлена попереднім патчем)
- STEPAN_IMPORTS_OK logic
- doc_context logic
12.4 memory-service
Не змінювати сервіс. Використовуємо існуючий /facts/upsert і /facts/get.
Нові fact-ключі:
user_profile:agromatrix:{user_id} → UserProfile JSON (fact_value_json)
farm_profile:agromatrix:{user_id} → FarmProfile JSON (fact_value_json)
memory_manager.py в crews викликає memory-service по HTTP (sync httpx), URL з env:
AGX_MEMORY_SERVICE_URL=http://memory-service:8000
13. Схема файлів після впровадження
crews/agromatrix_crew/
├── __init__.py
├── run.py ← ЗМІНЕНО (нові модулі вмонтовані)
├── audit.py ← без змін
├── operator_commands.py ← РОЗШИРЕНО (/profile, /farm)
│
├── depth_classifier.py ← НОВИЙ
├── memory_manager.py ← НОВИЙ
├── style_adapter.py ← НОВИЙ
├── reflection_engine.py ← НОВИЙ
├── activation_gate.py ← НОВИЙ
│
├── agents/
│ ├── stepan_orchestrator.py ← backstory розширюється від style_adapter
│ ├── operations_agent.py ← без змін
│ ├── iot_agent.py ← без змін
│ ├── platform_agent.py ← без змін
│ ├── spreadsheet_agent.py ← без змін
│ └── sustainability_agent.py ← без змін
│
├── tasks/
│ ├── intake_and_plan.py ← без змін (лише для compatibility)
│ ├── execute_ops.py ← без змін
│ ├── execute_iot.py ← без змін
│ ├── execute_spreadsheets.py ← без змін
│ └── reporting.py ← без змін
│
└── tools/
└── __init__.py ← без змін
14. Порядок впровадження (поетапно)
Фаза 1 — Foundation (без змін у run.py)
memory_manager.py— реалізувати, написати unit-тест з mock memory-servicedepth_classifier.py— реалізувати rule-based, написати тести по кожному патернуactivation_gate.py— реалізувати pre_check/release/select_crew, тест на рекурсію
Фаза 2 — Light mode
4. style_adapter.py — реалізувати три рівні і три стилі
5. Модифікувати run.py: вставити Light mode path (якщо light → пропустити всі crew)
6. Smoke-test: надіслати "привіт" → відповідь без crew
Фаза 3 — Deep mode + Activation Gate
7. Модифікувати run.py: Deep mode використовує select_crew, не всіх 5 агентів
8. Тест: "сплануй тиждень" → ops + sustainability, але не iot (якщо has_iot=false)
Фаза 4 — Reflection + Profiles
9. reflection_engine.py — rule-based Light reflection (без LLM)
10. Оновити operator_commands.py — /profile, /farm
11. E2E тест: 3 взаємодії → перевірка UserProfile накопичення
Фаза 5 — Deep reflection (LLM)
12. Додати LLM-рефлексію тільки для Deep mode
13. Тест на рекурсію: перевірити _REFLECTING flag спрацьовує
15. Метрики успіху
| Метрика | Ціль |
|---|---|
| % запитів у Light mode (грітинги + прості) | > 30% від загального трафіку |
| Середній час відповіді Light mode | < 2s (без crew launch) |
| Середній час відповіді Deep mode | < 30s (тільки потрібні crew) |
| % запитів що запускають тільки 1-2 crew | > 50% від Deep запитів |
Оператор /profile — відображає дані |
100% (якщо memory-service online) |
| Fallback без memory-service | Gateway не падає (fail-safe) |
| Рекурсивний виклик handle_message | 0 (activation_gate блокує) |
16. Відкриті питання (потрібно вирішити перед реалізацією)
- Sync vs async memory_manager:
run.pysync, але memory-service async-HTTP. Поточне рішення — sync httpx.Client. Альтернатива: asyncio.run() в окремому thread. Потребує рішення. - UserProfile.expertise_level auto-upgrade: поріг 10/30 взаємодій — достатньо? Або враховувати час між взаємодіями?
- reflection LLM model: який LLM для рефлексії — той самий GPT-4, або дешевший GPT-3.5/Mistral? Вплив на latency та cost.
- FarmProfile cold-start: перша взаємодія — profile порожній. Deep mode завжди? Або запитати у користувача дані ферми?
- Multi-user farm: кілька операторів з однієї ферми — один FarmProfile чи кілька? Зараз
user_id-based. - Operator profile isolation: оператор і звичайний користувач можуть мати одне user_id якщо оператор пише без оператор-чату. Чи потрібна окрема UserProfile для ops-mode?
Документ готовий до review. Після погодження — розпочинати Фазу 1.