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

Пересоздание агента в стиле Claude Code с использованием LangChain Deep Agents

Как работает система агента для программирования: от основного цикла до инструментов, планирования, памяти, подагентов и проверок в изолированной среде, пошагово пересозданная с использованием DeepAgents.

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

Источники: **Build Your Own Claude Code Using Langchin: A Deepdive Into LangChain’s Deep Agents**

Агент для программирования — это не чат-бот

Легко представить агента для программирования как более умный чат-бот, но это неверно. Возможности Claude Code обусловлены не только самой моделью, но и системой управления, созданной вокруг неё. Эта система является управляющим слоем: невидимой структурой, которая предоставляет модели инструменты для чтения файлов, среду для запуска кода, память о уже выполненных действиях и чек-лист для соблюдения плана. Без этой системы языковая модель остаётся просто генератором текста. С ней же та же модель ведёт себя как способный, автономный инженер по программному обеспечению.

Цель здесь — постепенно рассмотреть, как работает агент в стиле Claude Code, и пересоздать каждый его компонент с помощью библиотеки deepagents от LangChain. К концу всё станет понятно, так как вы сами построите такой агент.

Цикл в центре

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

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

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

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

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

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

Чистый двигатель

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

def run_agent_loop(client, user_message, tools, tool_functions, max_turns=20):
    messages = [{"role": "user", "content": user_message}]
    for _ in range(max_turns):
        # 1. Ask the model what to do next.
        response = client.messages.create(
            model="claude-sonnet-4-6", max_tokens=1024, tools=tools, messages=messages
        )
        messages = [*messages, {"role": "assistant", "content": response.content}]

        # 2. Plain text and no tool request? The job is done.
        tool_uses = [b for b in response.content if b.type == "tool_use"]
        if not tool_uses:
            return "".join(b.text for b in response.content if b.type == "text")
        # 3. Run each requested tool and hand the results back. 4. Repeat.
        results = [
            {"type": "tool_result", "tool_use_id": call.id,
             "content": tool_functions[call.name](**call.input)}
            for call in tool_uses
        ]
        messages = [*messages, {"role": "user", "content": results}]
    raise RuntimeError(f"agent loop did not finish within {max_turns} turns")

Всего получается примерно тридцать строк, а остальная часть структуры представляет собой цикл с дополнительными элементами. Более мощные инструменты помещаются в tool_functions. Планировщик записывает заметки в messages. Функция сжатия удаляет лишние данные из messages, когда их количество увеличивается. Подагент — это просто та же функция, вызываемая снова с новым, пустым списком messages. Проверка разрешений выполняется между моментом, когда модель запрашивает инструмент, и моментом его выполнения. Ограничение max_turns — это проявление паранойи, характерное для всех реальных систем управления, поскольку модель, застрявшая в цикле, будет тратить ресурсы бесконечно.

У каждого агента-программиста есть этот «бьющийся сердце» — цикл агента, превращающий слова в действия, и он на удивление прост:

  1. Запрашиваем у модели, что делать дальше.
  2. Модель отвечает простым текстом или просит использовать инструмент.
  • Если требуется инструмент, система запускает его и возвращает результат модели.
  • Вернитесь к шагу 1.
  • Когда модель отвечает в обычном текстовом формате без запроса на использование инструмента, задача выполнена, поэтому остановитесь и покажите ответ.
  • Чтение файла, выполнение команды и редактирование кода — всё это проходит через один и тот же цикл. Представьте себе подрядчика, которому дают задание и чек-лист: проверяет следующий пункт, выполняет его, оценивает результат, снова делает выбор и сообщает об окончании работ только тогда, когда список опустеет. Цикл выполняет роль такого суждения подрядчика. Он также автоматически адаптируется: простой вопрос может потребовать одного прохода, тогда как масштабная переработка кода влечёт за собой десятки вызовов инструментов, и никому не нужно заранее определять количество шагов.

    Создание с помощью Deep Agents

    Нет необходимости вручную писать цикл while. Создание Deep Agent автоматически обеспечивает рабочий цикл. Начните с установки библиотеки и поставщика моделей.

    pip install deepagents langchain-anthropic
    

    Затем создайте самого простого возможного агента.

    from deepagents import create_deep_agent
    
    def get_weather(city: str) -> str:
        """Get the weather for a given city."""
        return f"It's always sunny in {city}!"
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[get_weather],
        system_prompt="You are a helpful assistant.",
    )
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "What's the weather in San Francisco?"}]}
    )
    

    Вызов create_deep_agent возвращает агента, у которого цикл уже настроен, и .invoke() запускает именно описанный выше цикл. Инструментальная среда не важничает тем, какая модель находится под спудом — OpenAI, Google или Anthropic; цикл остается прежним.

    Инструменты — это руки

    Если цикл является двигателем, то инструменты — это руки. Без них модель может только говорить; с хорошими инструментами она может читать кодовую базу, изменять её и проверять результат. Настоящие инструменты для программирования делятся на четыре категории:

    • Инструменты для чтения осматривают окружение, не изменяя ничего: read_file открывает файл, glob находит файлы по шаблону имени, например все файлы с расширением .py, а grep ищет в файлах определённый текст. Новички часто путают последние два инструмента, но glob находит файлы по имени, а grep — по содержимому.
  • Инструменты редактирования безопасно изменяют данные: write_file создаёт новые файлы, а edit_file изменяет часть существующего файла.
  • Инструмент выполнения запускает команды оболочки, те же самые, что вводятся в терминале.
  • Инструмент делегирования task передаёт подзадачу вспомогательному агенту.
  • Почему бы не позволить агенту использовать cat для чтения и sed для редактирования? Это возможно, но специально созданные инструменты имеют преимущества по двум причинам. Во-первых, это управление объёмом данных: специализированный инструмент чтения может оценить размер файла перед его загрузкой и, при необходимости, уменьшить его объём, в то время как простой cat выводит всё содержимое целиком, что может превысить лимит контекстного окна. Во-вторых, более чёткое управление правами: вызов известного инструмента edit_file сообщает системе точно, что происходит, что позволяет применять правила безопасности, тогда как произвольная команда оболочки представляет собой неопределённый и рискованный элемент.

    Здесь есть забавная особенность. Модели обучались на публичном Интернете, полном ответов на Stack Overflow, где используются cat и sed, поэтому даже самые продвинутые из них по привычке прибегают к прямому использованию оболочки. Системное сообщение должно настойчиво напоминать им использовать настоящие инструменты, а не сокращения оболочки.

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

    import subprocess
    from langchain_core.tools import tool
    
    MAX_OUTPUT_CHARS = 20_000  # roughly 5k tokens
    @tool
    def run_tests(path: str = ".") -> str:
        """Run the project's pytest suite and return its output.
        Output is truncated to the last 20k characters (failures appear at the
        end). The run is killed after 5 minutes.
        """
        try:
            result = subprocess.run(["pytest", path], capture_output=True,
                                    text=True, check=False, timeout=300)
        except subprocess.TimeoutExpired:
            return "pytest timed out after 300s"
        output = result.stdout + result.stderr
        if len(output) > MAX_OUTPUT_CHARS:
            return ("[... output truncated to the last 20,000 characters ...]\n"
                    + output[-MAX_OUTPUT_CHARS:])
        return output
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],            # merged in alongside the built-ins
        system_prompt="You are a coding assistant. Always run tests after editing.",
    )
    

    Декоратор @tool позволяет превратить обычную функцию в объект, доступный для использования моделью. Её документация имеет огромное значение, поскольку модель рассматривает её как руководство, указывающее, когда и как использовать данный инструмент.

    Планирование перед действием

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

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

    В Deep Agents это реализовано с помощью встроенного инструмента write_todos. Ничего не нужно настраивать, поскольку стандартный системный промпт учит агента планировать действия заранее и обновлять список на протяжении всего цикла.

    Управление контекстом

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

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

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

    Подагенты: разделяй и властвуй

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

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

    Deep Agents включает встроенный инструмент task для создания помощников. Специализированные инструменты, такие как специальный поисковик кода, описываются с помощью простых словарей.

    code_searcher = {
        "name": "code-searcher",
        "description": "Searches the codebase to find where specific logic lives. "
                       "Use this for any open-ended 'where is X?' question.",
        # ⚠️ NOTE: the key is `system_prompt`, NOT `prompt` (see correction below).
        "system_prompt": "You are an expert at navigating codebases. Use the grep "
                         "and glob tools to locate relevant files, then report a "
                         "concise summary of what you found and where. Do not make "
                         "any edits.",
    }
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],
        system_prompt="You are a coding assistant.",
        subagents=[code_searcher],
    )
    

    Теперь основной агент знает о существовании специалиста, что позволяет очистить его собственную память для выполнения настоящей работы по программированию.

    Безопасность и утверждение человеком

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

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

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

    Deep Agents размещает свои ограничения в этом механизме управления. Доступ к оболочке требует бэкенда, а встроенный механизм прерывания LangGraph приостанавливает выполнение перед рискованными операциями.

    from deepagents import create_deep_agent
    # Import path for backends can vary by version - check the current
    # "Backends" page in the Deep Agents docs.
    from deepagents.backends import LocalShellBackend
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        system_prompt="You are a coding assistant working inside this project.",
        backend=LocalShellBackend(),   # enables the `execute` shell tool
    )
    

    Если запрашивается инструмент с ограничениями доступа, цикл прерывается, и результат содержит нагрузку __interrupt__, в которой указывается задержанная операция и доступные варианты: approve, edit, reject или respond. После принятия решения выполнение возобновляется.

    from langgraph.types import Command
    
    result = agent.invoke({"messages": [...]}, config)
    result["__interrupt__"]   # the pending action + allowed decisions — show your user
    
    # You decide; the loop picks up exactly where it paused:
    agent.invoke(Command(resume={"decisions": [{"type": "approve"}]}), config)
    # ... or {"type": "reject"} — the tool is never run, and the model is told so.
    

    Реальные ограничения должны находиться в механизмах управления, а не только в командах.

    Память между сессиями

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

    Поскольку Deep Agents работает на LangGraph, возможность сохранения данных обеспечивается с помощью механизма сохранения состояния.

    from deepagents import create_deep_agent
    from langgraph.checkpoint.memory import InMemorySaver
    
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",
        tools=[run_tests],
        system_prompt="You are a coding assistant.",
        checkpointer=InMemorySaver(),   # remembers state within a session
    )
    # A "thread_id" ties messages together into one ongoing conversation.
    config = {"configurable": {"thread_id": "project-alpha"}}
    agent.invoke(
        {"messages": [{"role": "user", "content": "Start refactoring the auth module."}]},
        config=config,
    )
    # Later, same thread_id - the agent remembers the earlier turn:
    agent.invoke(
        {"messages": [{"role": "user", "content": "Now update the tests too."}]},
        config=config,
    )
    

    InMemorySaver действует только в течение срока работы программы. Для сохранения данных между перезагрузками замените его контроллером, основанным на базе данных.

    Всё в одном агенте

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

    from deepagents import create_deep_agent
    from deepagents.backends import LocalShellBackend
    from langchain_core.tools import tool
    from langgraph.checkpoint.memory import InMemorySaver
    
    # --- A custom tool (Part 2) — token-budgeted, see Part 2 for the full body ---
    @tool
    def run_tests(path: str = ".") -> str:
        """Run the project's pytest suite and return its (truncated) output."""
        import subprocess
        try:
            result = subprocess.run(["pytest", path], capture_output=True,
                                    text=True, check=False, timeout=300)
        except subprocess.TimeoutExpired:
            return "pytest timed out after 300s"
        output = result.stdout + result.stderr
        return output if len(output) <= 20_000 else "[... truncated ...]\n" + output[-20_000:]
    
    # --- A specialized subagent (Part 5) — note the `system_prompt` key ---
    code_searcher = {
        "name": "code-searcher",
        "description": "Finds where specific logic lives in the codebase. "
                       "Use for open-ended 'where is X?' questions.",
        "system_prompt": "You navigate codebases using grep and glob, then report a "
                         "concise summary of what you found. You never make edits.",
    }
    
    # --- A system prompt that teaches good behavior (Parts 2 & 3) ---
    SYSTEM_PROMPT = """You are a careful coding assistant.
    Workflow:
    1. Plan the task as a to-do list before doing anything.
    2. Use your built-in read, grep, and glob tools to explore - never the raw shell
       equivalents like cat or grep.
    3. Make focused edits.
    4. ALWAYS run the tests after editing, and fix anything that breaks.
    5. Delegate broad codebase searches to the code-searcher subagent.
    """
    
    # --- Assemble the agent (Parts 1, 4, 6, 7 handled by the harness) ---
    agent = create_deep_agent(
        model="anthropic:claude-sonnet-4-6",                  # Part 1: the loop, model-agnostic
        tools=[run_tests],                                     # Part 2: custom hands
        system_prompt=SYSTEM_PROMPT,                           # Parts 2 & 3: behavior + planning
        subagents=[code_searcher],                             # Part 5: delegation
        backend=LocalShellBackend(root_dir=".", virtual_mode=False),  # Part 6: shell access
        interrupt_on={"execute": True,                         # Part 6: the brakes —
                      "write_file": True, "edit_file": True},  #   approval before anything destructive
        checkpointer=InMemorySaver(),                          # Part 7: memory across turns
    )
    # Part 4 (context management) and built-in planning come on automatically.
    
    config = {"configurable": {"thread_id": "my-project"}}
    result = agent.invoke(
        {"messages": [{"role": "user", "content": "The login tests are failing. Fix them."}]},
        config=config,
    )
    print(result["messages"][-1].content)
    

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