Существует проблема описанная тут.
Суть проста — если 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}\n\n# Модификация: {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}\n\n# Генерация {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 + "\n\n" + 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.