Головна / Статті

Перебудова агента у стилі 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: простий цикл, оточений інструментами для планування, керування пам’яттю, делегування завдань та механізмами контролю, які знаходяться поза самим моделлю.