Существует проблема описанная тут.
Суть проста — если LLM (или агенты вокруг LLM) вызывают последовательно одни и те же запросы с одним и тем же контекстом, попадание в тупик/цикл — вопрос лишь времени. То есть в случае зацикливания между агентами/контекстами надо менять промты или сам контекст, или последовательность вызова агентов.
Это яркая демонстрация, когда агенты, несмотря на всю свою сложность, тупят хуже моей кошки. Кошка не будет в цикле проверять две пустые миски в поисках еды: она сделает это раз, ну или два, и пойдёт дальше. А агенты будут, если наткнутся на такую ситуацию.
Одно из решений
Проблема цикличности — это не вопрос «интеллекта» модели, а вопрос архитектуры.
Если контур обратной связи не умеет детектировать стагнацию, а промты по сути статичны, система неизбежно застрянет. Это наблюдается даже в сложных оркестрациях, где вроде бы всё продумано.
Чтобы система была устойчивой, нужны:
динамические промты,
адаптивные конфиги,
слой обратной связи,
модифицирующие агенты,
и эволюционный механизм восстановления прогресса.
Ниже — примеры кода, показывающие, как это реализовать на практике.
Динамические промты и динамический конфиг LLM
Промт — не священный текст, а пластичный материал.
Если агент застрял — промт должен уметь мутировать.
Agent User Layer
Это слой, который работает непосредственно с пользовательским запросом и оформляет задачу в максимально человеческом виде.
Но решение он не пытается сделать сам — лишь задаёт направление.
Feedback Catch Layer
Это слой, который наблюдает за выводами агентов и ловит момент, когда система «встала».
Например, можно использовать простое хеширование ответов, чтобы обнаружить циклы:
import hashlib def hash_text(text: str) -> str: return hashlib.sha256(text.encode()).hexdigest() class FeedbackCatcher: def __init__(self, window=5): self.window = window self.history = [] def check(self, output: str): h = hash_text(output) self.history.append(h) if len(self.history) > self.window: self.history.pop(0) if len(self.history) == self.window and len(set(self.history)) == 1: return «stuck» return «ok»
Использование:
fc = FeedbackCatcher() for _ in range(10): out = agent.step() if fc.check(out) == «stuck»: print(«Цикл обнаружен → требуется модификация.») break
Agent Modify Layer
Когда мы понимаем, что система застряла, вступает в игру модификатор промтов.
Простой пример мутаций:
def modify_prompt(original_prompt: str, attempt: int) -> str: mutations = [ «Сформулируй ответ иначе.», «Подумай альтернативным способом.», «Используй другой порядок шагов.», «Предложи неожиданный подход.», «Сфокусируйся только на ключевых факторах.» ] mutation = mutations[attempt % len(mutations)] return f»{original_prompt}nn# Модификация: {mutation}»
Использование:
prompt = base_prompt for attempt in range(5): output = llm(prompt) if «ошибка» not in output.lower(): break prompt = modify_prompt(prompt, attempt)
LLM-классификатор «stuck / progress / redundant»
Более продвинутый сценарий — когда само хеширование недостаточно.
def classify_step(prev_output: str, new_output: str) -> str: prompt = f»»» Ты анализируешь шаги агента. Предыдущий вывод: {prev_output} Новый вывод: {new_output} Классифицируй: — progress: есть смысловые изменения — redundant: выводы разные словами, смысл тот же — stuck: однотипные ответы, движения нет Ответь одним словом. «»» return meta_llm(prompt).strip().lower()
Использование:
prev = None for _ in range(20): out = agent.run() if prev: status = classify_step(prev, out) if status == «stuck»: print(«Stuck → нужно вмешиваться») break prev = out
Мета-агент, модифицирующий цепочку агентов
Если промтовые хаки не помогают — надо менять саму последовательность шагов.
def modify_chain(chain_description: str) -> str: prompt = f»»» Ты — мета-агент. Текущая цепочка: {chain_description} Предложи улучшенную версию, чтобы избежать зацикливания. Измени порядок шагов или добавь вспомогательные. «»» return meta_llm(prompt)
Эволюционный подход
Когда линейная коррекция не помогает, мы относимся к цепочке агентов как к популяции моделей.
Каждый прогон → новое поколение.
Метрика → фитнес.
Мутация → новый шанс избежать цикла.
Поколения агентов
def run_generation(prompt, generation_id): modified = f»{prompt}nn# Генерация {generation_id}: дай лучший вариант.» return llm(modified)
Фитнес-функция
def fitness(output: str) -> float: tokens = output.split() diversity = len(set(tokens)) / (len(tokens) + 1) return len(output) * diversity
Основной цикл
best_output = None best_score = -1 for gen in range(5): out = run_generation(base_prompt, gen) score = fitness(out) print(f»Gen {gen}: score={score}») if score > best_score: best_output = out best_score = score print(«Лучший результат:», best_output)
Mutation: внедрение хаоса в промт
import random def mutate_prompt(prompt: str) -> str: mutations = [ «Представь, что ты эксперт-ревизор.», «Смени формат на структурированный.», «Введи альтернативную гипотезу.», «Добавь контрпример.», «Используй другой стиль рассуждений.» ] return prompt + «nn» + random.choice(mutations)
Объединённый пример: мини-оркестратор
class ChainManager: def __init__(self, base_prompt): self.prompt = base_prompt self.fc = FeedbackCatcher() def step(self): output = llm(self.prompt) status = self.fc.check(output) if status == «stuck»: self.prompt = mutate_prompt(self.prompt) print(«→ Застревание. Промт мутирован.») return self.step() return output manager = ChainManager(«Ты — агент анализа данных. Реши задачу.») result = manager.step() print(result)
Итог
Пока LLM остаются статистическими машинами без внутренней модели мира, любые цепочки с повторяемым контекстом рано или поздно зациклятся.
Проблема не в «тупости» конкретной модели, а в том, что без динамики и адаптивности мы даём системе только один узкий коридор.
Чтобы построить устойчивые многошаговые графы агентов, нужны:
динамические промты;
адаптивные конфигурации;
слой отслеживания тупиков;
слой модификации цепочек;
эволюционный подход для восстановления прогресса.
Без этого агенты будут бесконечно проверять пустые миски — долго, уверенно и методично.
Больше по теме в канале AIGENTTO.
Источник: habr.com



























