Как вы делаете agent с bounded rationality (ограниченные вычислительные ресурсы)?
Краткий тезис
Bounded rationality (ограниченная рациональность) — концепция, при которой агент принимает решения, оптимальные в рамках заданных ресурсных ограничений (число вызовов LLM, бюджет токенов, время ответа). На практике это реализуется комбинацией методов: pruning (pruning|отсечение неперспективных действий), иерархическое планирование (грубый план → детали), кэширование повторяющихся вызовов, early stopping (досрочная остановка при высокой уверенности) и мета-обучение (прогноз сложности задачи). Ключевая идея — не пытаться перебрать все варианты, а найти «достаточно хорошее» решение за выделенный бюджет.
1. Понятие bounded rationality в контексте AI-агентов
Термин заимствован из поведенческой экономики (Герберт Саймон, 1950-е). В классической теории агент всегда выбирает оптимальное действие (полная рациональность). В реальности вычислительные ресурсы, время и информация ограничены. Применительно к LLM-агентам ограничениями выступают:
- Лимит вызовов LLM — например, 5 обращений на задачу (экономическая причина: стоимость API, латентность).
- Бюджет токенов — максимальная длина контекста или суммарные токены генерации.
- Временной лимит — например, ответ должен быть получен за 2 секунды.
Агент с bounded rationality — это программная система, которая явно управляет этими ресурсами, балансируя между качеством ответа и затратами.
2. Основные архитектурные ограничения
| Ограничение | Пример (типичные цифры) | Влияние на архитектуру |
|---|---|---|
| Количество LLM-вызовов | ≤ 5 на задачу | Нужно избегать лишних цепочек мыслей |
| Бюджет токенов (input + output) | ≤ 4096 токенов | Уменьшать промпт, использовать сжатие |
| Максимальное время ответа | ≤ 2 с | Избегать длинных генераций, параллелить |
| Память (контекстное окно) | ≤ 128K токенов (у GPT-4) | Ограничивать историю шагов |
3. Метод 1: Pruning (отсечение неперспективных действий)
Pruning — техника, при которой на каждом шаге агент оценивает несколько возможных следующих действий, но выполняет только те, чья предсказанная полезность превышает порог. Позволяет сократить число вызовов LLM за счёт отказа от заведомо плохих вариантов.
Как это работает
- Агент генерирует k кандидатов действий (например, через LLM: «предложи 3 следующих шага»).
- Для каждого кандидата вычисляется score (быстрая эвристика: relevance, стоимость, вероятность успеха).
- Действия с score ниже порога отбрасываются.
- Выполняются только топ-m действий (m < k), и для каждого вызывается LLM.
Пример псевдокода
def act_with_pruning(state, budget_remaining):
candidates = propose_actions(state, n=5) # 5 вариантов
scores = [heuristic_score(c, state) for c in candidates]
# отсекаем нижние 60%
threshold = percentile(scores, 40)
selected = [c for c, s in zip(candidates, scores) if s >= threshold][:2]
for action in selected:
next_state = execute_action(action, budget_remaining - 1)
# рекурсия
if budget_remaining > 0:
return act_with_pruning(next_state, budget_remaining - 1)
Преимущества сокращение вызовов LLM на 30-50% без сильной потери качества. Недостатки требуется хорошая эвристика оценки.
4. Метод 2: Иерархическое планирование
Иерархическое планирование — двухуровневая стратегия: сначала агент строит грубый план (2-3 шага, без деталей), а затем детализирует каждый шаг отдельно. Это экономит вызовы, так как грубый план требует одного LLM-вызова, а детализация — только для релевантных шагов.
Процесс
- Level 1 (Plan) — LLM получает задачу и генерирует последовательность этапов (например, «1. Найти документы → 2. Анализ → 3. Ответ»).
- Level 2 (Execute) — для каждого этапа LLM-агент выполняет подзадачу, также с bounded rationality (те же методы).
Когда эффективно задачи с чёткой структурой (многошаговые RAG, написание кода, исследование).
Пример:
def hierarchical_agent(task, budget=5):
# тратим 1 вызов на план
plan = llm_plan(task, max_steps=3)
budget -= 1
for step in plan:
if budget <= 0:
partial_answer = aggregate_results(accumulated)
return partial_answer
sub_result = execute_step(step, budget=budget-1)
accumulated.append(sub_result)
budget -= 1
return final_answer(accumulated)
Преимущества явное распределение бюджета, снижение риска «закопаться» в деталях. Недостатки не подходит для очень динамичных задач.
5. Метод 3: Кэширование результатов
Кэширование — сохранение результатов одинаковых LLM-вызовов (одинаковый промпт, одинаковый контекст). Позволяет избежать повторных затрат ресурсов.
Типы кэша
- LRU-кэш для вызовов LLM (ключ: хэш промпта + температура).
- Кэш эмбеддингов — при retrieval в RAG: эмбеддинги запросов и документов часто переиспользуются.
- Кэш ответов агента — для идентичных подзадач.
Реализация (hash-based):
from functools import lru_cache
@lru_cache(maxsize=128)
def cached_llm_call(prompt: str, temperature: float) -> str:
return actual_llm_call(prompt, temperature)
Важно для агентов с bounded rationality кэш должен быть транзакционным — не тратить вызов, если ответ уже есть. При этом вызовы, которые попадают в кэш, не уменьшают бюджет (так как реально не выполняются). Это требует аккуратного учёта в метриках.
6. Метод 4: Early stopping (досрочная остановка)
Early stopping — прерывание дальнейших шагов агента, когда уверенность в ответе превышает порог. Уверенность может измеряться через:
- log-probability токенов ответа LLM,
- вероятность повторного выбора того же действия,
- наличие ответа в уже найденных документах (для RAG).
Пример стратегии
def agent_with_early_stop(state, budget):
for step in range(max_steps):
action = choose_action(state)
result = execute(action)
confidence = estimate_confidence(state, result)
if confidence > 0.95:
return result # досрочно
if budget <= 0:
return result
budget -= 1
Преимущества сокращает вызовы в среднем на 20-40%, особенно если задача простая. Недостатки риск пропустить уточняющие шаги, если порог слишком низок.
7. Метод 5: Мета-обучение (прогнозирование сложности задачи)
Мета-обучение — обучение отдельной модели (или самого LLM) предсказывать, сколько вызовов потребуется для данной задачи, и выбирать оптимальную стратегию (какой метод ограничения использовать).
Как реализовать
- Собираем историю: (задача, число вызовов, качество ответа).
- Обучаем регрессор (например, Gradient Boosting) на фичах задачи (длина запроса, количество сущностей, число шагов в плане и т.д.).
- Перед выполнением агент смотрит предсказание, и если бюджет мал — включает более агрессивный pruning или отключает некоторые шаги.
Псевдокод
meta_model = load_model('complexity_predictor.pkl')
def agent_with_meta(task, budget):
predicted_steps = meta_model.predict(extract_features(task))
if predicted_steps > budget * 1.5:
strategy = 'aggressive_pruning'
else:
strategy = 'default'
run_with_strategy(task, budget, strategy)
Преимущества адаптивность к разным задачам. Недостатки требует данных для обучения, может ошибаться на новых типах задач.
8. Сравнительная таблица методов
| Метод | Экономия вызовов | Сложность внедрения | Риск потери качества |
|---|---|---|---|
| Pruning | 30-50% | Средняя | Умеренный (если порог выбран плохо) |
| Иерархическое планирование | 20-40% | Высокая | Низкий (структурированные задачи) |
| Кэширование | 10-30% (зависит от повторяемости) | Низкая | Отсутствует (идеальное кэширование) |
| Early stopping | 20-40% | Средняя | Умеренный (ложноположительная уверенность) |
| Мета-обучение | 10-20% дополнительно | Высокая | Низкий (адаптация) |
9. Комбинированная архитектура агента с bounded rationality
На практике используется гибридный подход: контроллер бюджета распределяет лимиты между подсистемами.
Пример архитектуры
[Задача] → [Meta-предсказатель] → [Выбор стратегии]
↓
[Иерархический планировщик] → [список шагов]
↓
[Цикл выполнения с pruning и кэшем]
↓
[Early stopping проверка] → [Ответ]
Связь с RAG если агент использует retrieval, то bounded rationality особенно актуальна — каждый дополнительный поиск стоит эмбеддинга и ранжирования. Можно ограничить число чанков, которые извлекаются на каждом шаге (например, top-3 вместо top-10).
Пет-проект для закрепления
Задача Реализовать простого агента-поисковика, который отвечает на вопросы по документации (например, по Python). Ограничение: не более 5 вызовов LLM на задачу.
Инструменты Python, библиотека openai (или любой LLM), sentence-transformers для эмбеддингов, chromadb для векторного поиска.
Шаги:
- Загрузить документацию (10 статей), разбить на чанки по 300 токенов.
- Реализовать базового агента без ограничений (вызывать LLM для каждого шага: уточнить запрос, найти чанки, синтезировать ответ).
- Добавить ограничение в 5 вызовов. Замерить, сколько задач решается успешно.
- Внедрить pruning: после первого вызова (поиска) отсекать чанки с низкой релевантностью (score < 0.7) и не вызывать LLM для их обработки.
- Добавить early stopping: если уверенность ответа (по log-prob) > 0.9, сразу завершать.
- Сделать кэш для одинаковых запросов поиска (одинаковые эмбеддинги).
- Сравнить результаты: доля успешных ответов, среднее число вызовов, время.
Ожидаемый результат агент с bounded rationality будет использовать в среднем 3-4 вызова (против 6-8 без ограничений) при лишь небольшом падении точности (на 5-10%).
Связь с другими вопросами
| Вопрос | Тема |
|---|---|
| 582 | Архитектура агента с планированием |
| 584 | Мониторинг ресурсов агента |
| 580 | Основы Agentic RAG: паттерны |
| 575 | Ограничение латентности в RAG |
| 590 | Мета-обучение для выбора стратегии |
| 550 | Fine-tuning для эффективности агента |
Навигация
- Предыдущий: 582
- Следующий: 584
- Индекс: 00. Индекс разборов