Начиная проект AI-агента с персистентной памятью, мы приняли необычное решение: никаких if/else в агентной логике.

Не "минимизируем". А ноль.

Полгода спустя — это лучшее архитектурное решение которое мы приняли.

Изучая код других агентных фреймворков, видели один паттерн:
if user_emotion == "angry":
apologize()
elif user_emotion == "happy":
celebrate()
elif context_length > 8000:
summarize()

... ещё 50 строк

Проблема очевидна: это не масштабируется. 10 условий → понятно. 100 условий → сложно. 1000 → невозможно.

Решили не идти этим путём с первого дня.

Принцип: всё вычисляется
Вместо категорий — непрерывные метрики

Классика:
if task.urgency == "high":
priority = 1
elif task.urgency == "medium":
priority = 2

Наш подход:
priority = (
deadline_pressure(task) * 0.4 +
importance_weight(task) * 0.3 +
context_multiplier() * 0.3
)

float от 0 до 1, не категории

Вместо ветв��ения — параллельный scoring
Классика:
if query_simple:
use_cache()
elif resources_available:
deep_search()
else:
hybrid()

Наш подход:
strategies = {
'cache': compute_cache_score(query),
'deep': compute_deep_score(query),
'hybrid': compute_hybrid_score(query)
}
best = max(strategies, key=strategies.get)
Каждый вариант оценивается независимо. Выбираем лучший.

Что это даёт

  1. Модульность
    Каждая scoring function тестируется отдельно. Нет coupling между компонентами.

  2. Композиция
    Одни и те же базовые функции комбинируются по-разному:
    score_v1 = urgency * 0.5 + importance * 0.5
    score_v2 = urgency * 0.3 + importance * 0.3 + context * 0.4

  3. Обучаемость
    Веса можно обновлять градиентно:
    error = actual - expected
    weights['urgency'] += learning_rate * error

  4. Объяснимость
    Не "if на строке 342 сработал", а "X scored 0.85 (temporal: 0.3, semantic: 0.4, resource: 0.15)".
    scoring/
    ├── temporal.py # urgency, deadlines
    ├── semantic.py # similarity, relevance
    ├── resource.py # efficiency
    └── user.py # context, preferences
    compose.py # weighted combinations
    select.py # argmax + execution

Типичный flow:

Вычисляем компоненты

scores = {
'temporal': temporal_score(ctx),
'semantic': semantic_score(ctx),
'resource': resource_score(ctx)
}

Композиция

total = weighted_sum(scores, weights)

Решение

if total > threshold:
execute()

Где if/else остались

Мы не фанатики. Условия есть для:
Input validation (if data is None: raise Error)
Safety checks (if action_dangerous: confirm())
Type dispatch (if isinstance(x, dict))

Принцип: if/else для защиты, не для логики решений.

Неожиданные бонусы

A/B testing из коробки:
final = current * (1 - ratio) + experimental * ratio
Graceful degradation:
Всегда возвращается best available option, даже если все scores низкие.

Прозрачный debugging:
Видим scores всех вариантов, не трейсим вложенные условия.

Ограничения
Требует думать математически ("как оценить численно?")

Overhead для тривиальных случаев

Нужна дисциплина (scoring functions должны быть comparable)

Отказ от if/else — архитектурный выбор с trade-offs.

Для нас сработало. Агент развивается полгода. Если бы начинали с if/else — уже утонули бы в рефакторинге. Практический совет: если начинаете агентный проект — попробуйте этот подход хотя бы для одного модуля. Через неделю поймёте подходит ли вам.

Обсудим в комментариях? Интересно услышать опыт коллег.