Инженерия циклов для ИИ-агентов: пять составляющих, четыре формы и способ создания в LangGraph.
Инженерия циклов для ИИ-агентов: пять частей, четыре распространённых формата, исследовательский агент с функцией проверки и повторной попытки в LangGraph, защита от сбоев и критерии, когда не следует использовать циклы.
Источники: Loop Engineering with Agents
Каждый ИИ-агент уже работает в цикле, и почти никто специально не проектирует этот цикл. Именно эта разница отделяет агента, который тихо выполняет задачу, от того, который за одну ночь тратит всё бюджетное финансирование, будучи уверенным в своем прогрессе. Существует специальное направление, направленное на преодоление этой проблемы — инжиниринг циклов.
Структурно всё довольно просто. На каждом этапе агент размышляет о том, что делать, выполняет действие, такое как вызов инструмента или поиск, анализирует полученные результаты и снова размышляет. Этот цикл рассуждений, действий и наблюдений лежит в основе таких инструментов, как Claude Code и Cursor. Некоторое время самым востребованным навыком считался инжиниринг промптов — максимально точное формулирование запросов. Появление моделей, способных действовать, искать информацию и читать собственные результаты, породило более серьезный вопрос: что определяет следующее действие агента и что заставляет его остановиться?
Инженерия циклов — это привычка намеренно отвечать на поставленные вопросы. Дизайнер определяет цель, критерии проверки и точку остановки, затем создаёт компактную систему, которая один раз формулирует задачу и позволяет агенту продолжать итерации до завершения работы. Ответственность смещается от пошагового указания агенту действий к формированию решений, необходимых для выполнения этих указаний.
Это сильная идея, но именно здесь начинаются сложности. Некачественные циклы тратят ресурсы бесконечно, никогда не заканчиваются или убеждают себя в том, что они движутся вперёд. Хорошие циклы экономичны, останавливаются в подходящий момент и дают надёжный результат. Умнее сформулированный запрос не сокращает эту разницу; лучший цикл — да.
В приведённых ниже примерах используется LangGraph, поскольку он позволяет легко увидеть структуру цикла, но ничто здесь не связано с конкретной платформой. Те же принципы применимы и к другим фреймворкам агентов или к ручно написанному коду. Фреймворки меняются, но концепции остаются прежними.
Пять составляющих цикла агента
Прежде чем решить, когда цикл является целесообразным, полезно знать, из чего он состоит. Почти каждый цикл агента имеет пять составляющих.
- Триггер. Некое событие запускает процесс, например, ввод слова «go» человеком, запуск планировщика, поступление тикета или открытие pull request. Без триггера агент находится в состоянии покоя.
- Цель с возможностью проверки. Цикл должен знать, как выглядит завершение, желательно в форме, которую может подтвердить машина. Улучшение приложения приводит к бесконечной работе: необходимо пройти все тесты или вернуть ответ, в котором все утверждения сопровождаются источниками, обеспечивая настоящую «финишную черту».
- Инструменты. Агент должен иметь возможность действовать и одновременно наблюдать за результатом — запускать код, читать ошибки, искать информацию в Интернете и анализировать результаты. Если среда никогда не отвечает, агент может только догадываться.
- Состояние. Это текущая картина выполнения задачи: количество сделанных попыток, полученные результаты, сколько раундов уже пройдено. Это самая сложная часть проектирования циклов, поскольку чрезмерно большое количество данных о состоянии маскирует полезные сигналы под шум.
- Правила остановки. Они определяют, как заканчивается цикл, и существует три варианта. Успех означает достижение цели. Сдача на месте означает исчерпание лимита попыток. Передача ответственности означает, что цикл столкнулся с чем-то рискованным или неясным, и вместо угадывания задача передаётся другому человеку.
Вся остальная часть обсуждения, касающаяся того, когда следует использовать цикл, когда — нет и как это делать, сводится к этим пяти принципам.
Когда цикл оправдан
Цикл становится необходимым, когда одного прохождения по модели недостаточно, и задача действительно требует от агента попыток, проверок и повторных попыток. На это указывают четыре сигнала.
- Результаты можно проверить. Это имеет наибольшее значение. Когда исход можно автоматически проверить — по результатам тестов, наличию реальных источников в ответе, корректности JSON-данных, — цикл может использовать эту проверку для поиска правильного решения. Проверка выполняет роль руля управления, а цикл без неё лишь тратит средства.
- Первая попытка часто бывает неверной, но её можно исправить. Код, черновики исследований и очистка неструктурированных данных улучшаются с каждым повторением. Агент создаёт что-то, замечает недостатки и их устраняет.
- Никто не знает, сколько шагов потребуется. Проблема одного типа сегодня может требовать двух шагов, а завтра — семи. Фиксированная процедура не может адаптироваться таким образом, в то время как цикл, продолжающийся до достижения цели, может.
Хорошим примером является исследовательский агент, который проверяет собственные выводы. Он составляет ответ, проверяет, подтверждаются ли каждые утверждения источниками, и возвращается к тем слабым местам для дальнейшего анализа. Он продолжает этот цикл до тех пор, пока ответ не станет достаточно убедительным или не будет достигнут лимит. Все четыре сигнала присутствуют, и именно такой агент реализован далее.
Типичные формы циклов
Большинство агентных циклов представляют собой вариации нескольких основных форм. Их распознавание помогает определить, какую форму требует проблема, прежде чем что-либо соединять вместе.
Генерация и проверка, иногда называемая отражением. На первом этапе создаётся результат, а на втором он анализируется, причём этот цикл повторяется до тех пор, пока проверка не пройдёт. Сигнал управления имеет бинарную форму: проверка либо одобряет результат, либо указывает на ошибки. Этот метод подходит для ситуаций, когда возможно проверить корректность, например для кода, который должен пройти тесты, или для исследований, утверждения которых требуют подтверждения. Именно такая структура формируется позже.
+-----------+ +-----------+
-->| Generate |----->| Verify |----- pass -----> [ done ]
+-----------+ +-----+-----+
^ |
+------- fail ------+
Используйте этот подход, когда существует объективный тест на корректность, и агент должен продолжать вносить исправления до тех пор, пока тест не пройдёт. Классическим примером является агент для написания SQL-запросов, который выполняет свой запрос и продолжает циклиться, пока инструкция не будет выполнена и не вернёт разумные результаты.
Оценщик и оптимизатор, также называемые генератором и счётчиком оценок. Счётчик оценок присваивает балл каждой попытке вместо простого «да» или «нет», а цикл направлен на достижение максимального балла, а не только на получение правильного результата. Этот подход полезен, когда нет чёткого критерия успешности или неудачи, но всё ещё можно оценить степень качества, например при настройке запросов, улучшении текстов рекламы или сортировке результатов.
+-----------+ +-----------+
-->| Generate |----->| Score |--- good enough --> [ done ]
+-----------+ +-----+-----+
^ |
+-- improve --------+
(use the score)
Выбирайте тот вариант, у которого качество можно оценить по шкале и при этом присвоить ему числовой показатель. Разработка слогана продукта с оценкой четкости и выразительности, а также последующая доработка лучшего варианта иллюстрируют эту идею.
Планируйте, выполняйте, перепланируйте. Планировщик определяет этапы, а исполнители их выполняют; когда на каком-либо этапе возникают неожиданности, процесс возвращается к планировщику для его корректировки. Этот цикл охватывает весь план, а не только один результат, поэтому он подходит для работ с открытым исходом и множеством этапов, таких как исследования или разработка программного обеспечения.
+-----------+ +-----------+
-->| Plan |----->| Execute |-- all steps done --> [ done ]
+-----------+ +-----+-----+
^ |
+-- replan ---------+
(hit a surprise)
Выбирайте этот вариант, когда задача состоит из множества шагов, и не все из них известны с самого начала. Миграция сервиса в новую платформу работает следующим образом: планируются шаги, начинается их выполнение, а затем план корректируется, как только какой-либо шаг выявляет неожиданную зависимость.
Запланированный цикл или цикл мониторинга. Вместо поэтапного выполнения задачи в течение одной сессии этот вариант активируется по таймеру или при наступлении определенного события для наблюдения и реагирования, например, для сортировки новых заявок, поиска сбоев в сборке или повторной попытки выполнения ненадежной задачи. Он охватывает промежуток времени и не ограничивается одной задачей.
+-----------+ +-----------+ +-----------+
-->| Wait |---->| Check |---->| Act |---+
| (timer / | +-----------+ +-----------+ |
| trigger) | |
+-----------+ <-------------------------------------+
(no fixed end — keeps watching over time)
Выбирайте этот вариант, когда отсутствует единая линия финиша, а цель заключается в постоянном наблюдении и реагировании, подобно проверке почтового ящика каждые несколько минут или еженочному анализу устаревших запросов на изменения.
Эти форматы не исключают друг друга. В реальных системах их часто комбинируют, например, запланированный цикл, который выполняет этапы генерации и проверки при каждом запуске. Наиболее распространенной формой является последовательность генерация и проверка, которая описывается ниже от начала до конца.
Создание цикла с помощью LangGraph
Агент, который создаётся, — это исследователь, который не доверяет своему первоначальному варианту ответа. При поступлении вопроса он собирает информацию, пишет ответ, а затем проверяет каждое утверждение на соответствие реальным источникам. Утверждения, не имеющие подтверждения, возвращаются для дальнейшего исследования. Цикл продолжается до тех пор, пока ответ не станет полностью достоверным, или пока не будет достигнут лимит попыток; в этом случае агент возвращает свою лучшую версию ответа и отмечает то, что всё ещё неясно. Короче говоря, он сначала отвечает, а затем убеждает себя в правильности ответа, прежде чем передать его.
Такое поведение самопроверки и есть цикл генерации и верификации, благодаря которому формируется качественный первоначальный вариант ответа.
LangGraph хорошо подходит для этой цели, поскольку описывает агента как граф. Узлы представляют собой элементы, выполняющие работу, а рёбра — стрелки, определяющие последующие действия. Рёбра могут указывать назад, что позволяет существование циклов. Простая линейная цепь не может повторяться, но граф может.
У исследователя три задачи, следовательно, три узла. Первые два разделяют работу между создателем и проверщиком, исполнителем и критиком, что является распространённой схемой в циклах.
+------------------+
START ----------> | Research | <-------------+
| (draft / revise) | |
+--------+---------+ |
| |
v | needs work
+------------------+ | (+ feedback)
| Verify | |
| (check claims) | |
+--------+---------+ |
| |
v |
+-----------+ needs work |
| Decide |-------------------+
| (router) |
+-----+-----+
| verified / out of tries
v
[ END ]
- Исследование — сбор материалов и подготовка проекта ответа.
- Проверка — анализ проекта и проверка того, подтверждены ли все утверждения.
- Решение — небольшой маршрутизатор, который читает вердикт и определяет дальнейшие действия: завершить работу или вернуться и изучить слабые места.
Пример работает под Python 3.12. Требуются три пакета:
pip install langgraph==1.2.6 langchain-openai==1.3.3 pydantic==2.13.4
langgraph— это движок графов и циклов, включающийStateGraph,STARTиEND.langchain-openaiоборачивает чат-модель OpenAI в объектChatOpenAI.pydanticопределяет структурированный результат, который возвращает проверщик.
Прежде чем запустить программу, в среде также должен находиться ключ API OpenAI:
export OPENAI_API_KEY="your-key-here"
from typing import TypedDict, Literal
from langgraph.graph import StateGraph, START, END
from langchain_openai import ChatOpenAI
from pydantic import BaseModel, Field
# A model client on OpenAI's Responses API, with the built-in web-search tool
# bound on — so the model can search the web itself, no extra package needed.
# "gpt-5" is a reasoning model; swap it for any current model you have access to.
llm = ChatOpenAI(model="gpt-5", use_responses_api=True)
searcher = llm.bind_tools([{"type": "web_search"}])
MAX_ITERATIONS = 3 # a stop rule, decided up front
# STATE — the shared memory passed between every node
class ResearchState(TypedDict):
question: str # what we're answering
draft: str # the current best answer
verdict: str # "verified" or "needs_work"
feedback: str # what the verifier said to fix
iterations: int # how many times we've looped
# The verifier returns a typed result, so the router gets a clean
# "verified" / "needs_work" to branch on instead of parsing prose.
class Verdict(BaseModel):
status: str = Field(description='"verified" or "needs_work"')
feedback: str = Field(description="claims lacking support, if any")
# NODE 1 — the maker: search the web, then draft (or revise) the answer.
def research(state: ResearchState) -> dict:
prompt = "Search the web, then answer the question. Back every claim with a source.\n"
if state.get("feedback"):
prompt += f"A reviewer flagged these gaps — fix them:\n{state['feedback']}\n"
prompt += f"\nQuestion: {state['question']}"
draft = searcher.invoke(prompt).text
return {"draft": draft, "iterations": state["iterations"] + 1}
# NODE 2 — the checker: search for evidence, then critique the draft.
def verify(state: ResearchState) -> dict:
evidence = searcher.invoke(
f"Search the web for evidence to fact-check claims about: {state['question']}"
).text
checker = llm.with_structured_output(Verdict)
result = checker.invoke(
"You are a fact-checker. Using the evidence below, reply 'verified' "
"only if every claim in the answer is supported; otherwise 'needs_work' "
"and list the unsupported claims as feedback.\n\n"
f"Evidence:\n{evidence}\n\nAnswer:\n{state['draft']}"
)
return {"verdict": result.status, "feedback": result.feedback}
# ROUTER — the heart of the loop. Reads the verdict, picks the next step.
def decide(state: ResearchState) -> Literal["research", "__end__"]:
if state["verdict"] == "verified":
return "__end__" # success: goal met
if state["iterations"] >= MAX_ITERATIONS:
return "__end__" # surrender: out of tries
return "research" # loop back and fix the gaps
# WIRE IT UP
graph = StateGraph(ResearchState)
graph.add_node("research", research)
graph.add_node("verify", verify)
graph.add_edge(START, "research") # trigger
graph.add_edge("research", "verify") # always verify a fresh draft
graph.add_conditional_edges("verify", decide, {
"research": "research", # the backward arrow = the loop
"__end__": END,
})
agent = graph.compile()
result = agent.invoke({
"question": "What were the main causes of the 2008 financial crisis?",
"draft": "", "verdict": "", "feedback": "", "iterations": 0,
})
print(result["draft"])
Выделяются два важных момента. Во-первых, оба узла подключаются к интернету с помощью встроенного инструмента OpenAI web_search, добавленного с использованием bind_tools. Узел research выполняет поиск перед составлением текста, чтобы его утверждения основывались на реальных результатах. Узел verify снова выполняет поиск, чтобы проверить черновик на основе свежих данных, а не на основе памяти модели, и именно этот второй поиск предоставляет целевой сигнал для дальнейшей обработки. Во-вторых, узел verify использует функцию with_structured_output, которая обязывает формировать структурированный результат в виде типизированного verdict и feedback вместо простого текста, что позволяет маршрутизатору надежно разветвлять работу без необходимости парсинга свободного текста.
Пять компонентов цикла однозначно соответствуют элементам кода:
- Триггер: вызов функции
invoke(...)с вопросом запускает процесс. - Цель для проверки: правила
verifyдолжны иметь статусverifiedилиneeds_work. - Инструменты: оба узла используют встроенный поиск в интернете, функцию
researchдля написания текста и функциюverifyдля его проверки. - Состояние: словарь
ResearchStateотвечает за перемещение черновика, отзывов и количества итераций между узлами. - Правила остановки: маршрутизатор
decideпрекращает работу при успешном завершении или при достижении значенияMAX_ITERATIONS.
Последний пункт имеет решающее значение, поскольку именно в маршрутизаторе реализуется логика циклов. Одна строка — «остановиться при проверке», «остановиться после трех попыток», в противном случае — вернуться назад — позволяет отличить агента, который надежно улучшает свой ответ, от того, который бесконечно тратит ресурсы.
В примере предусмотрены два варианта завершения. В реальных циклах обработки часто добавляется третий вариант — передача задачи человеку. Столкнувшись с рискованной или неоднозначной ситуацией, агенту лучше не делать догадок и не просто замолчать, а остановиться и передать всю собранную информацию для дальнейшей обработки. В маршрутизаторе это просто ещё один ветвление: когда уровень уверенности низок или действие чувствительно, маршрут escalate прерывает цикл и показывает черновик, оставшиеся вопросы и предпринятые попытки. Умение определить, когда не стоит принимать решение, является частью хорошего проектирования циклов.
Возможные сбои и средства их предотвращения
Как только цикл начинает работать автономно, появляется небольшой список предсказуемых сбоев, для каждого из которых существует стандартный механизм защиты.
- Бесконечный цикл. Агент никогда не считает, что выполнил свою задачу, и продолжает действовать до тех пор, пока что-то не сломается или деньги не исчезнут. Ограничение количества итераций представляет собой жесткий лимит, такой как
MAX_ITERATIONSвыше, — способ выхода, который не зависит от собственного мнения агента. - Сжигание токенов. С каждой итерацией пересылается всё больший объем контекста, и затраты растут незаметно. Чтобы снизить нагрузку на память, сохраняйте только то, что необходимо для следующего шага: текущую версию работы и конкретные замечания, но никогда полную историю действий.
- Воображаемый прогресс. Агент заявляет о успехе, которого на самом деле не достиг, например, утверждает, что проблема решена, хотя этого никогда не происходило. Не позволяйте ему оценивать собственную работу по критериям, имеющим реальное значение. Отдельный шаг проверки с объективным тестом, тестами, которые действительно выполняются, или источниками, которые действительно существуют, гарантирует, что завершенное действительно считается завершенным.
research получает feedback от верификатора.Когда не стоит использовать цикл
Циклы — мощный инструмент, поэтому их легко применять чрезмерно. В таких ситуациях лучше использовать что-то проще.
- Достаточно одного прохождения. Краткое изложение документа, перевод текста, ответ на фактический вопрос и классификация электронного письма — это задачи, которые можно выполнить за один раз. Использование цикла только увеличивает затраты, задержки и риски сбоев. Если подходит простая последовательность действий, используйте её.
- Успех невозможно определить. Когда цель субъективна или нечетка, или её может оценить только человек, у цикла нет ориентира, поэтому он может закончиться произвольно или бесконечно продолжаться. Цель, которую невозможно проверить, делает алгоритм игровым автоматом, а не циклом.
Ориентир: если условия успеха и условия прекращения работы невозможно сформулировать письменно, цикл еще не готов к реализации.
Чек-лист перед использованием цикла
Инженерия циклов изменяет способ получения результата. Написание безупречного запроса имеет меньшее значение, чем проектирование системы, которая управляет агентом, то есть его триггером, целью, инструментами, состоянием и правилами остановки. Тем не менее цикл — это лишь один из инструментов, а не стандартное решение. Сначала стоит задать пять вопросов:
- Можно ли автоматически проверить успех? Если нет, пропустите цикл, поскольку у него не будет ориентира для управления.
- Улучшается ли задача при повторении? Если достаточно одной попытки, держите всё простым.
- Что именно означает завершение работы? Запишите условие успеха до начала программирования.
- Когда он должен остановиться? Установите строгий лимит, чтобы цикл не мог работать бесконечно.
- Могут ли действия безопасно повторяться? Если итерация может причинить реальный ущерб, добавьте человеческий контрольный пункт или не используйте цикл на этом этапе.
Если на все пять вопросов дать честные ответы, результатом может стать надежный цикл, а не просто красивая видимость. Лучше всего подходит скромный первый проект: один этап исследования и проверки, описанный выше, при этом правила остановки должны быть четко определены еще до того, как что-либо начнет развиваться.
Последний момент: цикл делает не только то, что ускоряет работу. Он усиливает любые решения, принятые при его создании, повторяя как хорошие, так и плохие решения с скоростью машины. Следовательно, цель не в том, чтобы создать цикл и забыть о нем. Необходимо сохранять достаточно близкий контроль, чтобы понимать, что делает цикл после того, как он начнет работать самостоятельно, чтобы его создатель оставался его инженером, а не просто человеком, нажавшим кнопку запуска.
Связанная литература
- Harness, Loop and Graph: Three Nested Layers of a Coding-Agent System — Симптомы сбоя указывают на слой, который необходимо исправить. Как взаимодействуют элементы harness, loop и graph, где полезен многопроцессный подход, чего не могут гарантировать адверсарные проверки, и чек-лист для диагностики.
- Loop Engineering: Four Nested Loops for Self-Improving AI Agents — Как четыре вложенных контрольных цикла (ReAct, retry, queue, hill climbing) вместе с механизмом оценки, индикатором бюджета и проверкой на изменения делают агента надежным, на примере процесса оценки страховых случаев.