Главная / Статьи

Циклы против графов: два способа проектирования архитектуры ИИ-агентов.

Инженерия циклов против инженерии графов для ИИ-агентов: как работает каждая архитектура, когда выбирать одну вместо другой, и чек-лист для использования перед запуском в производство.

Издатель: REACTAPP.TOP12 мин чтения2697 слов

Источники: Loop Engineering vs. Graph Engineering: The Architecture Shift Quietly Reshaping AI Agents

Два модных термина, один вопрос архитектуры

Все, кто следит за ИИ-агентами, которые ищут информацию в Интернете, вызывают инструменты, пишут код или самостоятельно выполняют рабочие процессы, наверняка недавно сталкивались с двумя терминами: инжиниринг циклов и инжиниринг графов. Они звучат как маркетинговые термины, но на самом деле относятся к реальным структурным решениям.

Возьмём агента, который тратит 40 000 токенов и шесть минут в реальном времени на решение вопроса, который должен был быть решён за три шага. С моделью и запросом всё в порядке. Проблема в самой архитектуре, поскольку агент не может понять, что уже пытался что-то сделать. Он просто постоянно повторяет этапы «размышление», «действие», «наблюдение», не имея никакой записи о своих собственных действиях.

Такого рода неудачи выносят проектирование агентов за рамки категории инструкций и переводят его в область архитектуры программного обеспечения. Если смотреть на это с этой точки зрения, практически каждая готовая к использованию система агентов основана на одном из двух подходов: циклах и графах. Далее рассматривается, что представляют собой эти подходы, как они реализуются, где возникают сбои, и почему переход от одного подхода к другому меняет подход инженеров к этим системам, выходя за рамки простой замены инструментов.

Что на самом деле делает агент

В любой момент времени агент выполняет одно из трех действий: он размышляет (запускает запрос к большой языковой модели), действует (запускает инструмент) или принимает решение о следующем шаге. Инженерия агентов в основном заключается в объединении этих трех функций в единое целое.

В первые несколько лет существования агентов на основе больших языковых моделей почти все использовали одинаковый подход — циклическую структуру. Это казалось очевидным решением. Агенты в стиле ReAct с самого начала работали именно так, и это соответствует описанию самого простого агента от Anthropic: модели, автономно использующей инструменты в рамках цикла.

Позже агентам стали поручаться более сложные задачи: несколько специализированных подагентов, возможность повторных попыток, необходимость утверждения человеком и сложные правила обработки. Одиночный цикл уже не мог справиться с такой нагрузкой. Команды захотели четко видеть структуру процесса, а не полагаться на то, что цикл в конечном итоге приведет к решению, и именно эта потребность привела к появлению графовой инженерии, которая описывает процесс с помощью узлов и ребер, не оставляя его в рамках повторяющихся итераций.

Ни один из этих подходов не является неправильным. Они решают разные задачи, поэтому каждый из них заслуживает тщательного рассмотрения.

Аналогия из кухни

Представьте, что вы учите кого-то готовить. Инженерия циклов представляет собой повторение одной и той же инструкции: попробовать суп, добавить соль, если это необходимо, снова попробовать и продолжать до тех пор, пока всё не будет в порядке. Это один цикл действия, проверки и, при необходимости, повторения. Просто, предсказуемо и легко представимо.

Инженерия графов похожа на полную рецептуру с ветвлениями. Нужно нарезать овощи, пока кто-то другой кипятит воду. Когда оба ингредиента готовы, их соединяют. Если человек, пробующий блюдо, хочет больше специй, кастрюлю возвращают на этап приправления, а в противном случае блюдо подают на стол. Здесь нет единой последовательности действий. Существует несколько путей, некоторые шаги выполняются одновременно, некоторые зависят от решения, и только определённые циклы повторяются, когда что-то идёт не так.

Что означает инженерия циклов

Цикл — это первоначальный и самый простой способ создания агента. Он работает в одном непрерывном цикле:

  1. Получение входных данных, то есть вопроса или цели пользователя.
  2. Подумайте о том, что делать дальше — это этап рассуждений модели.
  3. Примите действие, что обычно означает вызов инструмента, запуск кода или поиск информации.
  4. Оцените результат действия.
  5. Вернитесь к шагу 2 и продолжайте, пока агент не решит, что цель достигнута.

Люди часто называют это циклом «подумать, действовать, наблюдать», и многие из первых агентов были построены на этом принципе. В рамках практики инжиниринг циклов рассматривает агента как единый повторяющийся цикл: модель рассуждает, выбирает действие, выполняет его, анализирует результат и записывает его обратно в свой собственный контекст, затем снова начинает цикл, пока не придет к выводу, что задание выполнено или достигнут лимит.

Несколько причин объясняют его популярность:

  • Это лежит в основе известной модели ReAct (размышление в сочетании с действием) от Яо и его коллег, где следы размышлений переплетаются с действиями, и модели не нужно заранее завершать весь процесс мышления.
  • Большинство фреймворков для агентов также определяют «агента» именно так — как единую циклическую структуру с общим окном контекста и интерфейсом для вызова инструментов.
  • Для создания такого агента требуется всего несколько строк кода. Состоянием выступает текущий ход диалога, а условием завершения существует лишь одно. Для помощника по программированию, редактирующего файл, или исследовательского агента, ищущего ответы до тех пор, пока не будет удовлетворён, ничего более сложного не требуется.
  • В рекомендациях компании Anthropic также говорится об этом: начните с самого простого рабочего подхода и добавляйте структуру только тогда, когда это требуется задачей.

Проблема заключается в том, что всё, о чём должен размышлять цикл — включая то, что он уже пытался сделать, что не сработало и какова была цель — должно находиться в одном постоянно растущем окне контекста. У процесса нет отдельной памяти; он сохраняет лишь запись хода действий.

Как составляется цикл

Поток выполнения проходит от начала до конца следующим образом.

Пошагово:

  • Задача пользователя запускает цикл с определённой целью.
  • На этапе размышлений модель читает системное указание, историю разговора и последние данные наблюдений, затем решает, что делать дальше.
  • Проверка «Задача завершена?» не является отдельной системой; модель сама, исходя из своего контекста, определяет, завершена ли задача.
  • Если задача не завершена, модель выбирает инструмент и вызывает его.
  • Результат или ошибка инструмента возвращаются в виде данных наблюдений.
  • Это наблюдение добавляется к контексту, и цикл снова запускается, причем при каждом прохождении история рассуждений становится немного длиннее.
  • Когда модель оценивает выполненную работу, цикл завершается и возвращает окончательный ответ.
  • Компактная версия на Python показывает, насколько мало кода для этого требуется.

    def run_agent(task: str, tools: list, max_iterations: int = 10) -> str:
        context = [{"role": "user", "content": task}]
    
        for step in range(max_iterations):
            response = llm.generate(context, tools=tools)
    
            if response.is_final_answer:
                return response.content
    
            tool_result = execute_tool(response.tool_call)
            context.append({"role": "assistant", "content": response.content})
            context.append({"role": "tool", "content": tool_result})
    
        return "Stopped: max iterations reached"
    

    Что означает инжиниринг графов

    Инжиниринг графов — это явное представление процесса агента. Это набор узлов, каждый из которых представляет собой единицу работы, такую как вызов большой языковой модели, вызов инструмента или фрагмент бизнес-логики, соединённых рёбрами, которые могут быть фиксированными или условными. Все узлы читают и записывают данные в один общий объект состояния с указанным типом. Короче говоря, рабочий процесс превращается в карту взаимосвязанных шагов с путями между ними, заменяя единый цикл повторений.

    Узел может быть любым из следующих:

    • агент — специализированная ИИ-система, выполняющая одну задачу, такую как исследование, написание текста или анализ
    • вызов инструмента, например поиск в интернете, вычисления или запрос к базе данных
    • точка принятия решения, выбирающая следующий путь
    • точка объединения, сочетающая результаты нескольких путей

    Несколько путей могут выполняться параллельно, а другие являются условными и выполняются только при успешном прохождении теста. Петли также возможны, но появляются только там, где это необходимо, в качестве контролируемого механизма, возвращающего задачу на несколько шагов назад, без бессмысленного повторения всего заново.

    Почему графы привлекательны

    • Параллелизм. Независимые задачи могут выполняться одновременно и не ожидают друг друга в очереди.
    • Разветвление. Рабочий процесс может развиваться в разных направлениях в зависимости от ситуации, а не просто повторять одни и те же шаги.
    • Специализация. Каждый узел может быть самостоятельным экспертом с одной конкретной задачей, что ближе к способу распределения работы в человеческой команде.
    • Целенаправленные попытки повтора. Когда такой шаг, как проверка качества, проваливается, только этот шаг возвращает задачу на повтор, а все остальное остается нетронутым.
    • Видимость. Поскольку рабочий процесс представлен в виде карты, легче определить, где возникла ошибка и какой путь был выбран.

    LangGraph — это фреймворк, основанный на этой идее, который моделирует рабочие процессы как состоянийные циклические ориентированные графы. Граф может по-прежнему содержать циклы, поскольку у узла может быть ребро, ведущее обратно к более раннему узлу, что позволяет осуществлять повторные попытки и итерации. Изменяется лишь то, что цикл становится одним из многих рёбер, отображаемых на карте, и перестаёт быть основой всей архитектуры.

    Это в полной мере соответствует различию, которое проводит Anthropic между рабочими процессами, в которых ЯЗЫКИ больших моделей и инструменты следуют заранее определённым путям кода, контролируемым разработчиками, и агентами, в которых модель динамически управляет собственным процессом. Граф может отразить оба варианта в одной структуре: с фиксированными путями, где важна предсказуемость, и ветвями, управляемыми моделью, где ценится гибкость, причём разница видна на диаграмме.

    Как составляется граф

    Возьмём аналогичную задачу — агента, который должен провести исследования, написать код, проверить собственный результат и пересоздать его, — и представим её в виде графа.

    Пошагово:

    • START — это точка входа в граф.
    • Узел-маршрутизатор считывает текущее состояние и выбирает путь. Это может быть простое условие if/else или вызов ЯЗЫКА большой модели для классификации запроса.
  • Три специализированных узла — агент исследования, агент кода и агент проверки — каждый из них является автономным: они считывают состояние, выполняют свою задачу и записывают результаты обратно.
  • Узел агрегации или объединения преобразует результаты выполненной ветви в один единый объект состояния.
  • Узел повторной попытки обрабатывает сбои при проверке. Если валидация, выполняемая агентом проверки, проваливается, специальный канал направляет запрос обратно к маршрутизатору, и тот же модель не просто запрашивается заново. Путь выполнения находится в графе и не скрыт в строке запроса вроде «если это не сработает, попробуйте снова».
  • Точка END достигается после того, как состояние пройдет валидацию.
  • Вот упрощенный псевдокод в стиле, который обычно используют большинство фреймворков для графов.

    from graph_engine import StateGraph, START, END
    
    def router(state: dict) -> str:
        return state["task_type"]  # "research" | "code" | "review"
    
    def research_agent(state: dict) -> dict:
        state["results"]["research"] = do_research(state["query"])
        return state
    
    def code_agent(state: dict) -> dict:
        state["results"]["code"] = write_code(state["query"])
        return state
    
    def review_agent(state: dict) -> dict:
        state["valid"] = validate(state["results"])
        return state
    
    def aggregator(state: dict) -> dict:
        state["final_output"] = merge(state["results"])
        return state
    
    graph = StateGraph(schema=AgentState)
    graph.add_node("router", router)
    graph.add_node("research_agent", research_agent)
    graph.add_node("code_agent", code_agent)
    graph.add_node("review_agent", review_agent)
    graph.add_node("aggregator", aggregator)
    
    graph.add_conditional_edges("router", {
        "research": "research_agent",
        "code": "code_agent",
        "review": "review_agent",
    })
    graph.add_edge(["research_agent", "code_agent", "review_agent"], "aggregator")
    graph.add_conditional_edges("aggregator", {
        "valid": END,
        "invalid": "router",   # explicit retry edge
    })
    
    app = graph.compile()
    app.invoke({"query": "...", "results": {}, "task_type": "code"})
    

    Размещение двух шаблонов рядом позволяет легче увидеть их контраст.

    Почему индустрия переходит от циклов к графам

    Этот сдвиг произошел не внезапно. При попытке создания более сложных систем циклы столкнулись с реальными ограничениями по нескольким причинам.

    Реальная работа не происходит по прямой линии. Исследования, обслуживание клиентов, финансовый анализ и процессы обработки контента имеют множество ветвей. Если речь идет о выставлении счетов, необходимо следовать одному пути, а если это техническая проблема — другому. Циклы заставляют всё это умещаться в одну длинную цепочку условий, что быстро приводит к хаосу.

    Системам с несколькими агентами требовалась более подходящая структура. Когда в системе появляется несколько агентов — например, исследователь, резюматор и проверщик фактов — один цикл не может отразить ситуацию, когда два агента должны работать одновременно или когда один из них должен запуститься только в случае сбоя проверки. Графы естественным образом позволяют это представить.

    Со временем большее значение приобретают видимость и доверие. Раньше, когда агенты выполняли критически важные задачи, командам требовался пошаговый отчет о происходящем. Можно было нарисовать график, указывающий точно, где процесс шел хорошо, а где плохо, в то время как цикл представляет собой непрозрачный блок, выполняющий один и тот же код двадцать раз.

    Стоимость и скорость. Каждая потраченная итерация увеличивает время и часто приводит к финансовым потерям, поскольку каждый шаг обработки соответствует вызову языковой модели. Выполнение независимых шагов параллельно и возврат только той части, которую нужно пересделать, значительно эффективнее.

    Тестирование и обслуживание становятся проще. Можно построить, протестировать и изменить граф в виде отдельных узлов. Тесно связанный цикл обычно представляет собой логику «всё или ничего», и изменить его безопасно после запуска гораздо сложнее.

    Выбор между ними

    Ниже приведена приблизительная схема принятия решения.

    Используйте цикл, когда:

    • У задачи есть одна цель: один агент, одна задача и один набор инструментов
    • До того, как тратиться на архитектуру, необходимо проверить ранний прототип
    • Количество шагов невелико и довольно предсказуемо
    • Один человек отвечает за всё, и ничего не нужно передавать другой команде

    Используйте диаграмму в следующих случаях:

    • Участвуют два или более специализированных агента или ролей
    • Необходимо контролировать попытки повтора, альтернативные варианты или утверждения людьми
    • Части процесса могут выполняться одновременно и позже требуется их объединение
    • Необходимо отслеживать выполнение, делать контрольные точки или возобновлять его с определённой точки, чего нельзя сделать с повторяющимся отчётом
    • Отдельные люди или команды отвечают за отдельные сегменты, причём диаграмма служит соглашением между ними

    Вот полезный критерий оценки. Если в коде цикла есть комментарий вроде «На этом этапе мы уже должны были попробовать X», это указание на то, что вы неправильно моделируете граф внутри цикла, и, вероятно, пришло время нарисовать граф.

    Это не просто вопрос «либо/либо». Речь идет о том, чтобы подобрать инструмент под задачу. Простой чат-бот, отвечающий на вопросы по одному, или небольшой скрипт, выполняющий одно четкое действие до завершения, подходят для цикла. Задача с несколькими ветвящимися шагами, несколькими специалистами, работающими совместно, возможностью параллельной обработки или необходимостью четкого понимания того, что произошло и почему, лучше моделировать с помощью графа. Разумным стандартным подходом является начало с цикла, а переход к графу — в тот момент, когда логика «если — то» становится слишком сложной для понимания.

    Чек-лист для производства

    Перед внедрением любого из этих подходов полезно придерживаться следующих привычек:

    • Установите лимит на количество шагов. Оба подхода требуют механизма отключения, поскольку агент без ограничений не имеет предела по сумме расходов.
    • Сделайте проверку завершения выполнения возможной. Условие выхода должно быть чем-то, что можно проверить с помощью теста, а не просто догадкой, сформированной моделью.
    • Отслеживайте каждый шаг. Для каждого вызова большой языковой модели и каждого вызова инструмента создавайте отдельный элемент отслеживания в формате OpenTelemetry или аналогичном, избегая использования одного элемента отслеживания для всего процесса.
    • Ведите версионирование структуры. Храните определения в системе контроля версий, как и любой код, поскольку незаметные изменения в структуре могут тихо изменить поведение системы.
    • Проверяйте состояние на границах узлов. Проверяйте типизированные схемы состояния при входе и выходе из каждого узла, чтобы некорректные данные блокировались на ранней стадии.
  • Записывайте решения модели. Фиксируйте причины выбора определенного пути моделью, а также ее действия, поскольку именно это лежит в основе анализа после завершения работы.
  • Протестируйте механизм повторных попыток. Умышленно вызовите сбой валидации и убедитесь, что механизм повторных попыток или цикл действуют так, как ожидается, включая изменения в задержках и затратах ресурсов.
  • Измеряйте затраты и время на выполнение каждой задачи. Считайте количество операций в каждом узле графа и при каждом прохождении цикла, чтобы выявить, где тратится время или ресурсы на медленные или дорогостоящие операции.
  • Основная идея

    Цикл представляет собой минимальную структуру проектирования: один контекст, один цикл, при этом модель управляет потоком выполнения. Он подходит для простых агентов с одной функцией. Граф же описывает процесс через узлы, состояния и ребра, которые можно использовать для создания ветвлений, параллельного выполнения и умышленных повторных попыток.

    Самое существенное изменение заключается не в том, что графы стали лучше. Речь идет о переходе от процедурного мышления, при котором задаются вопросы о том, что будет дальше, к декларативному мышлению, при котором определяется форма системы. Графы хорошо сочетаются с механизмами отслеживания и наблюдаемости, необходимыми в производственных системах, в то время как циклы удобны для быстрой итерации.

    Большинство производственных систем в итоге представляют собой графы, построенные на основе циклов, по сути, это машины состояний, узлами которых являются небольшие циклы в стиле ReAct. Это делает эти два паттерна разными уровнями абстракции одной архитектуры, а не конкурентами. Для любого текущего проекта с агентами уместен вопрос о том, продолжает ли всё работать как один длинный цикл или пришло время представить его в виде графа.

    Связанная литература

  • Запись циклов, которые выполняет ИИ-агент в мобильном приложении — Четыре вопроса, на которые должен ответить каждый цикл агента, шесть вложенных циклов от нажатия до обработки данных и пять способов, как проверка может считаться завершенной, не обнаружив ничего.